diff --git a/proxy/build.gradle b/proxy/build.gradle index fd38a2004..e8b070bfb 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -69,8 +69,6 @@ dependencies { implementation(platform("net.kyori:adventure-bom:${adventureVersion}")) implementation("net.kyori:adventure-nbt") - implementation 'org.asynchttpclient:async-http-client:2.12.3' - implementation 'com.spotify:completable-futures:0.3.5' implementation 'com.electronwill.night-config:toml:3.6.4' diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 4caa2b788..65158e915 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -68,6 +68,7 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.http.HttpClient; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -100,7 +101,6 @@ import net.kyori.adventure.translation.TranslationRegistry; import net.kyori.adventure.util.UTF8ResourceBundleControl; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.asynchttpclient.AsyncHttpClient; import org.checkerframework.checker.nullness.qual.EnsuresNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; @@ -546,7 +546,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { shutdown(true); } - public AsyncHttpClient getAsyncHttpClient() { + public HttpClient getAsyncHttpClient() { return cm.getHttpClient(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java index 37dca2e72..2d8e56012 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java @@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; @@ -37,15 +36,17 @@ import com.velocitypowered.api.util.ProxyVersion; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.util.InformationUtils; -import java.net.ConnectException; -import java.net.UnknownHostException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import net.kyori.adventure.identity.Identity; @@ -59,10 +60,6 @@ import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextDecoration; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.asynchttpclient.AsyncHttpClient; -import org.asynchttpclient.BoundRequestBuilder; -import org.asynchttpclient.ListenableFuture; -import org.asynchttpclient.Response; import org.checkerframework.checker.nullness.qual.NonNull; public class VelocityCommand implements SimpleCommand { @@ -380,92 +377,72 @@ public class VelocityCommand implements SimpleCommand { dump.add("plugins", InformationUtils.collectPluginInfo(server)); source.sendMessage(Component.text().content("Uploading gathered information...").build()); - AsyncHttpClient httpClient = ((VelocityServer) server).getAsyncHttpClient(); + HttpClient httpClient = ((VelocityServer) server).getAsyncHttpClient(); - BoundRequestBuilder request = - httpClient.preparePost("https://dump.velocitypowered.com/documents"); - request.setHeader("Content-Type", "text/plain"); - request.addHeader("User-Agent", server.getVersion().getName() + "/" - + server.getVersion().getVersion()); - request.setBody( - InformationUtils.toHumanReadableString(dump).getBytes(StandardCharsets.UTF_8)); + httpClient.sendAsync( + HttpRequest.newBuilder() + .uri(URI.create("https://dump.velocitypowered.com/documents")) + .POST(BodyPublishers.ofString( + InformationUtils.toHumanReadableString(dump), StandardCharsets.UTF_8 + )) + .header("Content-Type", "text/plain") + .header("User-Agent", server.getVersion().getName() + "/" + + server.getVersion().getVersion()) + .build(), + BodyHandlers.ofString() + ).thenAccept(response -> { + if (response.statusCode() != 200) { + source.sendMessage(Component.text() + .content("An error occurred while communicating with the Velocity servers. " + + "The servers may be temporarily unavailable or there is an issue " + + "with your network settings. You can find more information in the " + + "log or console of your Velocity server.") + .color(NamedTextColor.RED).build()); + logger.error("Invalid status code while POST-ing Velocity dump: " + + response.statusCode()); + logger.error("Headers: \n--------------BEGIN HEADERS--------------\n" + + response.headers().toString() + + "\n---------------END HEADERS---------------"); + return; + } - ListenableFuture future = request.execute(); - future.addListener(() -> { try { - Response response = future.get(); - if (response.getStatusCode() != 200) { - source.sendMessage(Component.text() - .content("An error occurred while communicating with the Velocity servers. " - + "The servers may be temporarily unavailable or there is an issue " - + "with your network settings. You can find more information in the " - + "log or console of your Velocity server.") - .color(NamedTextColor.RED).build()); - logger.error("Invalid status code while POST-ing Velocity dump: " - + response.getStatusCode()); - logger.error("Headers: \n--------------BEGIN HEADERS--------------\n" - + response.getHeaders().toString() - + "\n---------------END HEADERS---------------"); - return; - } - JsonObject key = InformationUtils.parseString( - response.getResponseBody(StandardCharsets.UTF_8)); + JsonObject key = InformationUtils.parseString(response.body()); if (!key.has("key")) { throw new JsonSyntaxException("Missing Dump-Url-response"); } String url = "https://dump.velocitypowered.com/" - + key.get("key").getAsString() + ".json"; + + key.get("key").getAsString() + ".json"; source.sendMessage(Component.text() - .content("Created an anonymised report containing useful information about " - + "this proxy. If a developer requested it, you may share the " - + "following link with them:") - .append(Component.newline()) - .append(Component.text(">> " + url) - .color(NamedTextColor.GREEN) - .clickEvent(ClickEvent.openUrl(url))) - .append(Component.newline()) - .append(Component.text("Note: This link is only valid for a few days") - .color(NamedTextColor.GRAY) - ).build()); - } catch (InterruptedException e) { - source.sendMessage(Component.text() - .content("Could not complete the request, the command was interrupted." - + "Please refer to the proxy-log or console for more information.") - .color(NamedTextColor.RED).build()); - logger.error("Failed to complete dump command, " - + "the executor was interrupted: " + e.getMessage()); - e.printStackTrace(); - } catch (ExecutionException e) { - TextComponent.Builder message = Component.text() - .content("An error occurred while attempting to upload the gathered " - + "information to the Velocity servers.") - .append(Component.newline()) - .color(NamedTextColor.RED); - if (e.getCause() instanceof UnknownHostException - || e.getCause() instanceof ConnectException) { - message.append(Component.text( - "Likely cause: Invalid system DNS settings or no internet connection")); - } - source.sendMessage(message - .append(Component.newline() - .append(Component.text( - "Error details can be found in the proxy log / console")) - ).build()); - - logger.error("Failed to complete dump command, " - + "the executor encountered an Exception: " + e.getCause().getMessage()); - e.getCause().printStackTrace(); + .content("Created an anonymised report containing useful information about " + + "this proxy. If a developer requested it, you may share the " + + "following link with them:") + .append(Component.newline()) + .append(Component.text(">> " + url) + .color(NamedTextColor.GREEN) + .clickEvent(ClickEvent.openUrl(url))) + .append(Component.newline()) + .append(Component.text("Note: This link is only valid for a few days") + .color(NamedTextColor.GRAY) + ).build()); } catch (JsonParseException e) { source.sendMessage(Component.text() - .content("An error occurred on the Velocity servers and the dump could not " - + "be completed. Please contact the Velocity staff about this problem. " - + "If you do, provide the details about this error from the Velocity " - + "console or server log.") - .color(NamedTextColor.RED).build()); + .content("An error occurred on the Velocity servers and the dump could not " + + "be completed. Please contact the Velocity staff about this problem. " + + "If you do, provide the details about this error from the Velocity " + + "console or server log.") + .color(NamedTextColor.RED).build()); logger.error("Invalid response from the Velocity servers: " + e.getMessage()); e.printStackTrace(); } - }, MoreExecutors.directExecutor()); + }).exceptionally(err -> { + source.sendMessage(Component.text() + .content("An error occurred whilst gathering the dump. More information is available " + + "in the server console.") + .color(NamedTextColor.RED).build()); + logger.error("Unable to post dump to the Velocity servers", err); + return null; + }); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index 4ab7fe628..bf5401489 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -50,8 +50,14 @@ import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; import com.velocitypowered.proxy.protocol.packet.ServerLogin; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; import com.velocitypowered.proxy.protocol.packet.SetCompression; +import com.velocitypowered.proxy.util.InformationUtils; import io.netty.buffer.ByteBuf; import java.net.InetSocketAddress; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse.BodyHandlers; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.MessageDigest; @@ -132,9 +138,15 @@ public class LoginSessionHandler implements MinecraftSessionHandler { url += "&ip=" + urlFormParameterEscaper().escape(playerIp); } - ListenableFuture hasJoinedResponse = server.getAsyncHttpClient().prepareGet(url) - .execute(); - hasJoinedResponse.addListener(() -> { + server.getAsyncHttpClient().sendAsync( + HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("User-Agent", server.getVersion().getName() + "/" + + server.getVersion().getVersion()) + .GET() + .build(), + BodyHandlers.ofString() + ).thenAcceptAsync(hasJoinedResponse -> { if (mcConnection.isClosed()) { // The player disconnected after we authenticated them. return; @@ -152,31 +164,30 @@ public class LoginSessionHandler implements MinecraftSessionHandler { return; } - try { - Response profileResponse = hasJoinedResponse.get(); - if (profileResponse.getStatusCode() == 200) { - // All went well, initialize the session. - initializePlayer(GENERAL_GSON.fromJson(profileResponse.getResponseBody(), - GameProfile.class), true); - } else if (profileResponse.getStatusCode() == 204) { - // Apparently an offline-mode user logged onto this online-mode proxy. - inbound.disconnect(Component.translatable("velocity.error.online-mode-only", - NamedTextColor.RED)); - } else { - // Something else went wrong - logger.error( - "Got an unexpected error code {} whilst contacting Mojang to log in {} ({})", - profileResponse.getStatusCode(), login.getUsername(), playerIp); - inbound.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); - } - } catch (ExecutionException e) { - logger.error("Unable to authenticate with Mojang", e); + if (hasJoinedResponse.statusCode() == 200) { + // All went well, initialize the session. + initializePlayer(GENERAL_GSON.fromJson(hasJoinedResponse.body(), GameProfile.class), + true); + } else if (hasJoinedResponse.statusCode() == 204) { + // Apparently an offline-mode user logged onto this online-mode proxy. + inbound.disconnect(Component.translatable("velocity.error.online-mode-only", + NamedTextColor.RED)); + } else { + // Something else went wrong + logger.error( + "Got an unexpected error code {} whilst contacting Mojang to log in {} ({})", + hasJoinedResponse.statusCode(), login.getUsername(), playerIp); inbound.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); - } catch (InterruptedException e) { - // not much we can do usefully - Thread.currentThread().interrupt(); } - }, mcConnection.eventLoop()); + }, mcConnection.eventLoop()).exceptionally(err -> { + mcConnection.eventLoop().execute(() -> { + if (!mcConnection.isClosed()) { + logger.error("Unable to enable encryption", err); + mcConnection.close(true); + } + }); + return null; + }); } catch (GeneralSecurityException e) { logger.error("Unable to enable encryption", e); mcConnection.close(true); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index 9bf9e78b0..32f7946bd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -17,9 +17,6 @@ package com.velocitypowered.proxy.network; -import static org.asynchttpclient.Dsl.asyncHttpClient; -import static org.asynchttpclient.Dsl.config; - import com.google.common.base.Preconditions; import com.velocitypowered.api.event.proxy.ListenerBoundEvent; import com.velocitypowered.api.event.proxy.ListenerCloseEvent; @@ -38,15 +35,11 @@ import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.epoll.EpollChannelOption; import io.netty.util.concurrent.GlobalEventExecutor; import java.net.InetSocketAddress; +import java.net.http.HttpClient; import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.asynchttpclient.AsyncHttpClient; -import org.asynchttpclient.RequestBuilder; -import org.asynchttpclient.filter.FilterContext; -import org.asynchttpclient.filter.FilterContext.FilterContextBuilder; -import org.asynchttpclient.filter.RequestFilter; import org.checkerframework.checker.nullness.qual.Nullable; public final class ConnectionManager { @@ -67,7 +60,7 @@ public final class ConnectionManager { public final BackendChannelInitializerHolder backendChannelInitializer; private final SeparatePoolInetNameResolver resolver; - private final AsyncHttpClient httpClient; + private final HttpClient httpClient; /** * Initalizes the {@code ConnectionManager}. @@ -84,20 +77,7 @@ public final class ConnectionManager { this.backendChannelInitializer = new BackendChannelInitializerHolder( new BackendChannelInitializer(this.server)); this.resolver = new SeparatePoolInetNameResolver(GlobalEventExecutor.INSTANCE); - this.httpClient = asyncHttpClient(config() - .setEventLoopGroup(this.workerGroup) - .setUserAgent(server.getVersion().getName() + "/" + server.getVersion().getVersion()) - .addRequestFilter(new RequestFilter() { - @Override - public FilterContext filter(FilterContext ctx) { - return new FilterContextBuilder<>(ctx) - .request(new RequestBuilder(ctx.getRequest()) - .setNameResolver(resolver) - .build()) - .build(); - } - }) - .build()); + this.httpClient = HttpClient.newBuilder().build(); } public void logChannelInformation() { @@ -241,7 +221,7 @@ public final class ConnectionManager { return this.serverChannelInitializer; } - public AsyncHttpClient getHttpClient() { + public HttpClient getHttpClient() { return httpClient; }