[Beta] Implement own HTTP client for online mode checks, instead of asynchttpclient

This commit is contained in:
md_5 2013-07-05 09:29:28 +10:00
parent c0d581d41f
commit be29799f5a
8 changed files with 69 additions and 59 deletions

View File

@ -25,12 +25,6 @@
<version>14.0.1</version> <version>14.0.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.7.17</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-event</artifactId> <artifactId>bungeecord-event</artifactId>

View File

@ -2,7 +2,6 @@ package net.md_5.bungee.api;
import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.api.plugin.PluginManager;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.ning.http.client.AsyncHttpClient;
import java.io.File; import java.io.File;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collection; import java.util.Collection;
@ -218,15 +217,6 @@ public abstract class ProxyServer
*/ */
public abstract TaskScheduler getScheduler(); public abstract TaskScheduler getScheduler();
/**
* Gets the the web client used by this proxy to facilitate making web
* requests. Care should be taken to ensure that all operations are non
* blocking where applicable.
*
* @return the server's {@link AsyncHttpClient} instance
*/
public abstract AsyncHttpClient getHttpClient();
/** /**
* Get the current number of connected users. The default implementation is * Get the current number of connected users. The default implementation is
* more efficient than {@link #getPlayers()} as it does not take a lock or * more efficient than {@link #getPlayers()} as it does not take a lock or

View File

@ -6,10 +6,6 @@ import net.md_5.bungee.reconnect.SQLReconnectHandler;
import net.md_5.bungee.scheduler.BungeeScheduler; import net.md_5.bungee.scheduler.BungeeScheduler;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.providers.netty.NettyAsyncHttpProvider;
import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelException; import io.netty.channel.ChannelException;
@ -123,11 +119,6 @@ public class BungeeCord extends ProxyServer
@Getter @Getter
private final TaskScheduler scheduler = new BungeeScheduler(); private final TaskScheduler scheduler = new BungeeScheduler();
@Getter @Getter
private final AsyncHttpClient httpClient = new AsyncHttpClient(
new NettyAsyncHttpProvider(
new AsyncHttpClientConfig.Builder().setAsyncHttpClientProviderConfig(
new NettyAsyncHttpProviderConfig().addProperty( NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE, executors ) ).setExecutorService( executors ).build() ) );
@Getter
private ConsoleReader consoleReader; private ConsoleReader consoleReader;
@Getter @Getter
private final Logger logger; private final Logger logger;
@ -302,7 +293,6 @@ public class BungeeCord extends ProxyServer
{ {
BungeeCord.this.isRunning = false; BungeeCord.this.isRunning = false;
httpClient.close();
executors.shutdown(); executors.shutdown();
stopListeners(); stopListeners();

View File

@ -1,14 +1,11 @@
package net.md_5.bungee.connection; package net.md_5.bungee.connection;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.Response;
import io.netty.util.concurrent.ScheduledFuture; import io.netty.util.concurrent.ScheduledFuture;
import java.io.DataInput; import java.io.DataInput;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -33,6 +30,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ProxyPingEvent; import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.http.HttpClient;
import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.CipherDecoder; import net.md_5.bungee.netty.CipherDecoder;
@ -258,35 +256,37 @@ public class InitialHandler extends PacketHandler implements PendingConnection
String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" ); String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" );
String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash; String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash;
bungee.getHttpClient().prepareGet( authURL ).execute( new AsyncCompletionHandler<Response>()
Callback<String> handler = new Callback<String>()
{ {
@Override @Override
public Response onCompleted(Response response) throws Exception public void done(String result, Throwable error)
{ {
if ( "YES".equals( response.getResponseBody() ) ) if ( error == null )
{ {
finish(); if ( "YES".equals( result ) )
{
finish();
} else
{
disconnect( "Not authenticated with Minecraft.net" );
}
} else } else
{ {
disconnect( "Not authenticated with Minecraft.net" ); disconnect( bungee.getTranslation( "mojang_fail" ) );
bungee.getLogger().log( Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", error );
} }
return response;
} }
};
@Override HttpClient.get( authURL, ch.getHandle().eventLoop(), handler );
public void onThrowable(Throwable t)
{
disconnect( bungee.getTranslation( "mojang_fail" ) );
bungee.getLogger().log( Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", t );
}
} );
} else } else
{ {
finish(); finish();
} }
} }
private void finish() throws GeneralSecurityException private void finish()
{ {
// Check for multiple connections // Check for multiple connections
ProxiedPlayer old = bungee.getPlayer( handshake.getUsername() ); ProxiedPlayer old = bungee.getPlayer( handshake.getUsername() );

View File

@ -4,7 +4,8 @@ import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoopGroup; import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoop;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.DefaultHttpRequest;
@ -13,21 +14,22 @@ import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import java.net.URI; import java.net.URI;
import lombok.RequiredArgsConstructor; import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.Callback;
@RequiredArgsConstructor @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class HttpClient public class HttpClient
{ {
private final EventLoopGroup eventLoop; public static int TIMEOUT = 5000;
public static void main(String[] args) public static void get(String url, EventLoop eventLoop, final Callback<String> callback)
{ {
new HttpClient( new NioEventLoopGroup( 1 ) ).get( "https://session.minecraft.net/" ); Preconditions.checkNotNull( url, "url" );
} Preconditions.checkNotNull( eventLoop, "eventLoop" );
Preconditions.checkNotNull( callback, "callBack" );
public void get(String url)
{
final URI uri = URI.create( url ); final URI uri = URI.create( url );
Preconditions.checkNotNull( uri.getScheme(), "scheme" ); Preconditions.checkNotNull( uri.getScheme(), "scheme" );
@ -56,16 +58,20 @@ public class HttpClient
{ {
if ( future.isSuccess() ) if ( future.isSuccess() )
{ {
HttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath() ); String path = uri.getRawPath() + ( ( uri.getRawQuery() == null ) ? "" : "?" + uri.getRawQuery() );
HttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, path );
request.headers().set( HttpHeaders.Names.HOST, uri.getHost() ); request.headers().set( HttpHeaders.Names.HOST, uri.getHost() );
future.channel().write( request ); future.channel().write( request );
} else } else
{ {
callback.done( null, future.cause() );
} }
} }
}; };
new Bootstrap().channel( NioSocketChannel.class ).group( eventLoop ).handler( new HttpInitializer( ssl ) ).remoteAddress( uri.getHost(), port ).connect().addListener( future ); new Bootstrap().channel( NioSocketChannel.class ).group( eventLoop ).handler( new HttpInitializer( callback, ssl ) ).
option( ChannelOption.CONNECT_TIMEOUT_MILLIS, TIMEOUT ).remoteAddress( uri.getHost(), port ).connect().addListener( future );
} }
} }

View File

@ -8,28 +8,53 @@ import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.Callback;
@RequiredArgsConstructor
public class HttpHandler extends SimpleChannelInboundHandler<HttpObject> public class HttpHandler extends SimpleChannelInboundHandler<HttpObject>
{ {
private final Callback<String> callback;
private final StringBuilder buffer = new StringBuilder();
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
try
{
callback.done( null, cause );
} finally
{
ctx.channel().close();
}
}
@Override @Override
protected void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception protected void messageReceived(ChannelHandlerContext ctx, HttpObject msg) throws Exception
{ {
if ( msg instanceof HttpResponse ) if ( msg instanceof HttpResponse )
{ {
HttpResponse response = (HttpResponse) msg; HttpResponse response = (HttpResponse) msg;
if ( response.getStatus() != HttpResponseStatus.OK ) if ( response.getStatus().code() != 200 )
{ {
throw new IllegalStateException( "Expected HTTP response 200 OK, got " + response.getStatus() );
} }
} }
if ( msg instanceof HttpContent ) if ( msg instanceof HttpContent )
{ {
HttpContent content = (HttpContent) msg; HttpContent content = (HttpContent) msg;
String s = content.content().toString( Charset.forName( "UTF-8" ) ); buffer.append( content.content().toString( Charset.forName( "UTF-8" ) ) );
if ( msg instanceof LastHttpContent ) if ( msg instanceof LastHttpContent )
{ {
ctx.channel().close(); try
{
callback.done( buffer.toString(), null );
} finally
{
ctx.channel().close();
}
} }
} }
} }

View File

@ -4,20 +4,25 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.http.HttpClientCodec; import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.Callback;
@RequiredArgsConstructor @RequiredArgsConstructor
public class HttpInitializer extends ChannelInitializer<Channel> public class HttpInitializer extends ChannelInitializer<Channel>
{ {
private final Callback<String> callback;
private final boolean ssl; private final boolean ssl;
@Override @Override
protected void initChannel(Channel ch) throws Exception protected void initChannel(Channel ch) throws Exception
{ {
ch.pipeline().addLast( "timeout", new ReadTimeoutHandler( HttpClient.TIMEOUT, TimeUnit.MILLISECONDS ) );
if ( ssl ) if ( ssl )
{ {
SSLContext context = SSLContext.getInstance( "TLS" ); SSLContext context = SSLContext.getInstance( "TLS" );
@ -32,6 +37,6 @@ public class HttpInitializer extends ChannelInitializer<Channel>
ch.pipeline().addLast( "ssl", new SslHandler( engine ) ); ch.pipeline().addLast( "ssl", new SslHandler( engine ) );
} }
ch.pipeline().addLast( "http", new HttpClientCodec() ); ch.pipeline().addLast( "http", new HttpClientCodec() );
ch.pipeline().addLast( "handler", new HttpHandler() ); ch.pipeline().addLast( "handler", new HttpHandler( callback ) );
} }
} }

View File

@ -12,7 +12,7 @@ public class LogDispatcher extends Thread
public LogDispatcher(BungeeLogger logger) public LogDispatcher(BungeeLogger logger)
{ {
super( "BungeeCord Logger Thread - " + logger ); super( "BungeeCord Logger Thread" );
this.logger = logger; this.logger = logger;
} }