Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-17 05:20:14 +01:00
Remove connection pool for HTTP connections for now.
The upstream feature this relies on will be gone in Netty 5.x and there is probably more benefit in using the connection's event loop to connect to Mojang's authentication servers.
Dieser Commit ist enthalten in:
Ursprung
432d570474
Commit
45574ce952
@ -1,5 +1,6 @@
|
|||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
|
import static com.google.common.net.UrlEscapers.urlFormParameterEscaper;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
||||||
import static com.velocitypowered.proxy.VelocityServer.GSON;
|
import static com.velocitypowered.proxy.VelocityServer.GSON;
|
||||||
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
|
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
|
||||||
@ -120,10 +121,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
String playerIp = ((InetSocketAddress) mcConnection.getRemoteAddress()).getHostString();
|
String playerIp = ((InetSocketAddress) mcConnection.getRemoteAddress()).getHostString();
|
||||||
String url = String.format(MOJANG_HASJOINED_URL,
|
String url = String.format(MOJANG_HASJOINED_URL,
|
||||||
UrlEscapers.urlFormParameterEscaper().escape(login.getUsername()), serverId,
|
urlFormParameterEscaper().escape(login.getUsername()), serverId,
|
||||||
UrlEscapers.urlFormParameterEscaper().escape(playerIp));
|
urlFormParameterEscaper().escape(playerIp));
|
||||||
server.getHttpClient()
|
server.getHttpClient()
|
||||||
.get(new URL(url))
|
.get(new URL(url), mcConnection.eventLoop())
|
||||||
.thenAcceptAsync(profileResponse -> {
|
.thenAcceptAsync(profileResponse -> {
|
||||||
if (mcConnection.isClosed()) {
|
if (mcConnection.isClosed()) {
|
||||||
// The player disconnected after we authenticated them.
|
// The player disconnected after we authenticated them.
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
package com.velocitypowered.proxy.network.http;
|
package com.velocitypowered.proxy.network.http;
|
||||||
|
|
||||||
import com.google.common.base.VerifyException;
|
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import io.netty.bootstrap.Bootstrap;
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.pool.AbstractChannelPoolMap;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.pool.ChannelPoolHandler;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.pool.ChannelPoolMap;
|
import io.netty.channel.EventLoop;
|
||||||
import io.netty.channel.pool.FixedChannelPool;
|
|
||||||
import io.netty.channel.pool.SimpleChannelPool;
|
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpClientCodec;
|
import io.netty.handler.codec.http.HttpClientCodec;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
@ -25,8 +21,8 @@ import javax.net.ssl.SSLEngine;
|
|||||||
|
|
||||||
public class NettyHttpClient {
|
public class NettyHttpClient {
|
||||||
|
|
||||||
private final ChannelPoolMap<HostAndSsl, SimpleChannelPool> poolMap;
|
|
||||||
private final String userAgent;
|
private final String userAgent;
|
||||||
|
private final VelocityServer server;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the HTTP client.
|
* Initializes the HTTP client.
|
||||||
@ -35,48 +31,16 @@ public class NettyHttpClient {
|
|||||||
*/
|
*/
|
||||||
public NettyHttpClient(VelocityServer server) {
|
public NettyHttpClient(VelocityServer server) {
|
||||||
this.userAgent = server.getVersion().getName() + "/" + server.getVersion().getVersion();
|
this.userAgent = server.getVersion().getName() + "/" + server.getVersion().getVersion();
|
||||||
Bootstrap bootstrap = server.initializeGenericBootstrap();
|
this.server = server;
|
||||||
this.poolMap = new AbstractChannelPoolMap<HostAndSsl, SimpleChannelPool>() {
|
|
||||||
@Override
|
|
||||||
protected SimpleChannelPool newPool(HostAndSsl key) {
|
|
||||||
return new FixedChannelPool(bootstrap.remoteAddress(key.address), new ChannelPoolHandler() {
|
|
||||||
@Override
|
|
||||||
public void channelReleased(Channel channel) throws Exception {
|
|
||||||
channel.pipeline().remove("collector");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelAcquired(Channel channel) throws Exception {
|
|
||||||
// We don't do anything special when acquiring channels. The channel handler cleans up
|
|
||||||
// after each connection is used.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelCreated(Channel channel) throws Exception {
|
|
||||||
if (key.ssl) {
|
|
||||||
SslContext context = SslContextBuilder.forClient().protocols("TLSv1.2").build();
|
|
||||||
// Unbelievably, Java doesn't automatically check the CN to make sure we're talking
|
|
||||||
// to the right host! Therefore, we provide the intended host name and port, along
|
|
||||||
// with asking Java very nicely if it could check the hostname in the certificate
|
|
||||||
// for us.
|
|
||||||
SSLEngine engine = context.newEngine(channel.alloc(), key.address.getHostString(),
|
|
||||||
key.address.getPort());
|
|
||||||
engine.getSSLParameters().setEndpointIdentificationAlgorithm("HTTPS");
|
|
||||||
channel.pipeline().addLast("ssl", new SslHandler(engine));
|
|
||||||
}
|
|
||||||
channel.pipeline().addLast("http", new HttpClientCodec());
|
|
||||||
}
|
|
||||||
}, 8);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts an HTTP GET request to the specified URL.
|
* Attempts an HTTP GET request to the specified URL.
|
||||||
* @param url the URL to fetch
|
* @param url the URL to fetch
|
||||||
|
* @param loop the event loop to use
|
||||||
* @return a future representing the response
|
* @return a future representing the response
|
||||||
*/
|
*/
|
||||||
public CompletableFuture<SimpleHttpResponse> get(URL url) {
|
public CompletableFuture<SimpleHttpResponse> get(URL url, EventLoop loop) {
|
||||||
String host = url.getHost();
|
String host = url.getHost();
|
||||||
int port = url.getPort();
|
int port = url.getPort();
|
||||||
boolean ssl = url.getProtocol().equals("https");
|
boolean ssl = url.getProtocol().equals("https");
|
||||||
@ -84,27 +48,37 @@ public class NettyHttpClient {
|
|||||||
port = ssl ? 443 : 80;
|
port = ssl ? 443 : 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostAndSsl key = new HostAndSsl(InetSocketAddress.createUnresolved(host, port), ssl);
|
InetSocketAddress address = InetSocketAddress.createUnresolved(host, port);
|
||||||
|
|
||||||
CompletableFuture<SimpleHttpResponse> reply = new CompletableFuture<>();
|
CompletableFuture<SimpleHttpResponse> reply = new CompletableFuture<>();
|
||||||
poolMap.get(key)
|
server.initializeGenericBootstrap(loop)
|
||||||
.acquire()
|
.handler(new ChannelInitializer<Channel>() {
|
||||||
.addListener(future -> {
|
@Override
|
||||||
if (future.isSuccess()) {
|
protected void initChannel(Channel ch) throws Exception {
|
||||||
Channel channel = (Channel) future.getNow();
|
if (ssl) {
|
||||||
if (channel == null) {
|
SslContext context = SslContextBuilder.forClient().protocols("TLSv1.2").build();
|
||||||
throw new VerifyException("Null channel retrieved from pool!");
|
// Unbelievably, Java doesn't automatically check the CN to make sure we're talking
|
||||||
|
// to the right host! Therefore, we provide the intended host name and port, along
|
||||||
|
// with asking Java very nicely if it could check the hostname in the certificate
|
||||||
|
// for us.
|
||||||
|
SSLEngine engine = context.newEngine(ch.alloc(), address.getHostString(),
|
||||||
|
address.getPort());
|
||||||
|
engine.getSSLParameters().setEndpointIdentificationAlgorithm("HTTPS");
|
||||||
|
ch.pipeline().addLast("ssl", new SslHandler(engine));
|
||||||
}
|
}
|
||||||
channel.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply));
|
ch.pipeline().addLast("http", new HttpClientCodec());
|
||||||
|
ch.pipeline().addLast("collector", new SimpleHttpResponseCollector(reply));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.connect(address)
|
||||||
|
.addListener((ChannelFutureListener) future -> {
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
Channel channel = future.channel();
|
||||||
|
|
||||||
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
|
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
|
||||||
HttpMethod.GET, url.getPath() + "?" + url.getQuery());
|
HttpMethod.GET, url.getPath() + "?" + url.getQuery());
|
||||||
request.headers().add(HttpHeaderNames.HOST, url.getHost());
|
request.headers().add(HttpHeaderNames.HOST, url.getHost());
|
||||||
request.headers().add(HttpHeaderNames.USER_AGENT, userAgent);
|
request.headers().add(HttpHeaderNames.USER_AGENT, userAgent);
|
||||||
channel.writeAndFlush(request);
|
channel.writeAndFlush(request, channel.voidPromise());
|
||||||
|
|
||||||
// Make sure to release this connection
|
|
||||||
reply.whenComplete((resp, err) -> poolMap.get(key).release(channel));
|
|
||||||
} else {
|
} else {
|
||||||
reply.completeExceptionally(future.cause());
|
reply.completeExceptionally(future.cause());
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ class SimpleHttpResponseCollector extends ChannelInboundHandlerAdapter {
|
|||||||
private final StringBuilder buffer = new StringBuilder();
|
private final StringBuilder buffer = new StringBuilder();
|
||||||
private final CompletableFuture<SimpleHttpResponse> reply;
|
private final CompletableFuture<SimpleHttpResponse> reply;
|
||||||
private int httpCode;
|
private int httpCode;
|
||||||
private boolean canKeepAlive;
|
|
||||||
|
|
||||||
SimpleHttpResponseCollector(CompletableFuture<SimpleHttpResponse> reply) {
|
SimpleHttpResponseCollector(CompletableFuture<SimpleHttpResponse> reply) {
|
||||||
this.reply = reply;
|
this.reply = reply;
|
||||||
@ -29,16 +28,13 @@ class SimpleHttpResponseCollector extends ChannelInboundHandlerAdapter {
|
|||||||
HttpResponse response = (HttpResponse) msg;
|
HttpResponse response = (HttpResponse) msg;
|
||||||
HttpResponseStatus status = response.status();
|
HttpResponseStatus status = response.status();
|
||||||
this.httpCode = status.code();
|
this.httpCode = status.code();
|
||||||
this.canKeepAlive = HttpUtil.isKeepAlive(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg instanceof HttpContent) {
|
if (msg instanceof HttpContent) {
|
||||||
buffer.append(((HttpContent) msg).content().toString(StandardCharsets.UTF_8));
|
buffer.append(((HttpContent) msg).content().toString(StandardCharsets.UTF_8));
|
||||||
|
|
||||||
if (msg instanceof LastHttpContent) {
|
if (msg instanceof LastHttpContent) {
|
||||||
if (!canKeepAlive) {
|
ctx.close();
|
||||||
ctx.close();
|
|
||||||
}
|
|
||||||
reply.complete(new SimpleHttpResponse(httpCode, buffer.toString()));
|
reply.complete(new SimpleHttpResponse(httpCode, buffer.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren