From b22b2dc458bcbef9a4e0fb8320dac9e58002230e Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Wed, 17 Jan 2024 15:02:36 +0000 Subject: [PATCH] Migrate to using built-in http client (#1192) --- .../velocitypowered/proxy/VelocityServer.java | 7 ++ .../client/InitialLoginSessionHandler.java | 117 +++++++++--------- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index f113164b8..ddf302e78 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -71,6 +71,7 @@ import io.netty.channel.EventLoopGroup; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; +import java.net.http.HttpClient; import java.nio.file.Files; import java.nio.file.Path; import java.security.AccessController; @@ -143,6 +144,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { private final ConnectionManager cm; private final ProxyOptions options; + private final HttpClient httpClient; private @MonotonicNonNull VelocityConfiguration configuration; private @MonotonicNonNull KeyPair serverKeyPair; private final ServerMap servers; @@ -168,6 +170,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { scheduler = new VelocityScheduler(pluginManager); console = new VelocityConsole(this); cm = new ConnectionManager(this); + httpClient = HttpClient.newHttpClient(); servers = new ServerMap(this); serverListPingHandler = new ServerListPingHandler(this); this.options = options; @@ -591,6 +594,10 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { this.cm.closeEndpoints(false); } + public HttpClient getHttpClient() { + return httpClient; + } + public AsyncHttpClient getAsyncHttpClient() { return cm.getHttpClient(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java index 1409dc4ba..53fe21cad 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java @@ -42,19 +42,19 @@ import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; import com.velocitypowered.proxy.protocol.packet.ServerLogin; import io.netty.buffer.ByteBuf; import java.net.InetSocketAddress; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.MessageDigest; import java.util.Arrays; import java.util.Optional; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ThreadLocalRandom; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.asynchttpclient.ListenableFuture; -import org.asynchttpclient.Response; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** @@ -77,7 +77,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler { private boolean forceKeyAuthentication; InitialLoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection, - LoginInboundConnection inbound) { + LoginInboundConnection inbound) { this.server = Preconditions.checkNotNull(server, "server"); this.mcConnection = Preconditions.checkNotNull(mcConnection, "mcConnection"); this.inbound = Preconditions.checkNotNull(inbound, "inbound"); @@ -209,63 +209,60 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler { url += "&ip=" + urlFormParameterEscaper().escape(playerIp); } - ListenableFuture hasJoinedResponse = server.getAsyncHttpClient().prepareGet(url) - .execute(); - hasJoinedResponse.addListener(() -> { - if (mcConnection.isClosed()) { - // The player disconnected after we authenticated them. - return; - } - - // Go ahead and enable encryption. Once the client sends EncryptionResponse, encryption - // is enabled. - try { - mcConnection.enableEncryption(decryptedSharedSecret); - } catch (GeneralSecurityException e) { - logger.error("Unable to enable encryption for connection", e); - // At this point, the connection is encrypted, but something's wrong on our side and - // we can't do anything about it. - mcConnection.close(true); - return; - } - - try { - Response profileResponse = hasJoinedResponse.get(); - if (profileResponse.getStatusCode() == 200) { - final GameProfile profile = GENERAL_GSON.fromJson(profileResponse.getResponseBody(), - GameProfile.class); - // Not so fast, now we verify the public key for 1.19.1+ - if (inbound.getIdentifiedKey() != null - && inbound.getIdentifiedKey().getKeyRevision() == IdentifiedKey.Revision.LINKED_V2 - && inbound.getIdentifiedKey() instanceof IdentifiedKeyImpl) { - IdentifiedKeyImpl key = (IdentifiedKeyImpl) inbound.getIdentifiedKey(); - if (!key.internalAddHolder(profile.getId())) { - inbound.disconnect( - Component.translatable("multiplayer.disconnect.invalid_public_key")); - } + HttpRequest httpRequest = HttpRequest.newBuilder().uri(URI.create(url)).build(); + server.getHttpClient().sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString()) + .whenCompleteAsync((response, throwable) -> { + if (mcConnection.isClosed()) { + // The player disconnected after we authenticated them. + return; } - // All went well, initialize the session. - mcConnection.setActiveSessionHandler(StateRegistry.LOGIN, - new AuthSessionHandler(server, inbound, profile, 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); - inbound.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); - } catch (InterruptedException e) { - // not much we can do usefully - Thread.currentThread().interrupt(); - } - }, mcConnection.eventLoop()); + + if (throwable != null) { + logger.error("Unable to authenticate player", throwable); + inbound.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); + return; + } + + // Go ahead and enable encryption. Once the client sends EncryptionResponse, encryption + // is enabled. + try { + mcConnection.enableEncryption(decryptedSharedSecret); + } catch (GeneralSecurityException e) { + logger.error("Unable to enable encryption for connection", e); + // At this point, the connection is encrypted, but something's wrong on our side and + // we can't do anything about it. + mcConnection.close(true); + return; + } + + if (response.statusCode() == 200) { + final GameProfile profile = GENERAL_GSON.fromJson(response.body(), + GameProfile.class); + // Not so fast, now we verify the public key for 1.19.1+ + if (inbound.getIdentifiedKey() != null + && inbound.getIdentifiedKey().getKeyRevision() == IdentifiedKey.Revision.LINKED_V2 + && inbound.getIdentifiedKey() instanceof IdentifiedKeyImpl) { + IdentifiedKeyImpl key = (IdentifiedKeyImpl) inbound.getIdentifiedKey(); + if (!key.internalAddHolder(profile.getId())) { + inbound.disconnect( + Component.translatable("multiplayer.disconnect.invalid_public_key")); + } + } + // All went well, initialize the session. + mcConnection.setActiveSessionHandler(StateRegistry.LOGIN, + new AuthSessionHandler(server, inbound, profile, true)); + } else if (response.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 {} ({})", + response.sslSession(), login.getUsername(), playerIp); + inbound.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); + } + }, mcConnection.eventLoop()); } catch (GeneralSecurityException e) { logger.error("Unable to enable encryption", e); mcConnection.close(true);