Skip to content

Commit

Permalink
#3776: Expose ChannelInitializerHolder in protocol module
Browse files Browse the repository at this point in the history
  • Loading branch information
Outfluencer authored and md-5 committed Feb 15, 2025
1 parent 4dad940 commit 0070421
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 33 deletions.
53 changes: 53 additions & 0 deletions api/src/main/java/net/md_5/bungee/api/ProxyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;

public abstract class ProxyServer
{
Expand Down Expand Up @@ -311,4 +312,56 @@ public static void setInstance(ProxyServer instance)
*/
public abstract Title createTitle();

/**
* Get the unsafe methods of this class.
*
* @return the unsafe method interface
*/
public abstract Unsafe unsafe();

public interface Unsafe
{

/**
* Gets the frontend channel initializer
*
* @return the frontend channel initializer
*/
BungeeChannelInitializer getFrontendChannelInitializer();

/**
* Set the frontend channel initializer of this proxy
*
* @param channelInitializer the frontend channelInitializer to set
*/
void setFrontendChannelInitializer(BungeeChannelInitializer channelInitializer);

/**
* Gets the backend channel initializer
*
* @return the backend channel initializer
*/
BungeeChannelInitializer getBackendChannelInitializer();

/**
* Set the backend channel initializer of this proxy
*
* @param channelInitializer the backend channelInitializer to set
*/
void setBackendChannelInitializer(BungeeChannelInitializer channelInitializer);

/**
* Gets the server info channel initializer
*
* @return the server info channel initializer
*/
BungeeChannelInitializer getServerInfoChannelInitializer();

/**
* Set the server info channel initializer of this proxy
*
* @param channelInitializer the server info channelInitializer to set
*/
void setServerInfoChannelInitializer(BungeeChannelInitializer channelInitializer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.md_5.bungee.protocol.channel;

import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
* This class hold a netty channel initializer that calls the given {@link ChannelAcceptor}.
* Use {@link BungeeChannelInitializer#create(ChannelAcceptor)} to create a new instance.
* <p>
* Please note that this API is unsafe and doesn't provide any guarantees about
* the stability of the channel pipeline or the API itself. Use at your own
* risk.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class BungeeChannelInitializer
{

public abstract ChannelAcceptor getChannelAcceptor();

public abstract ChannelInitializer<Channel> getChannelInitializer();

/**
* Creates a new instance of BungeeChannelInitializer
*
* @param acceptor the {@link ChannelAcceptor} that will accept the channel
* and initializer the pipeline
* @return {@link BungeeChannelInitializer} containing a cached
* {@link ChannelInitializer} that will call the acceptor
*/
public static BungeeChannelInitializer create(ChannelAcceptor acceptor)
{
return new BungeeChannelInitializer()
{
@Getter
private final ChannelAcceptor channelAcceptor = acceptor;

@Getter // cache the ChannelInitializer
private final ChannelInitializer<Channel> channelInitializer = new ChannelInitializer<Channel>()
{
@Override
protected void initChannel(Channel channel) throws Exception
{
if ( !getChannelAcceptor().accept( channel ) )
{
channel.close();
}
}
};
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package net.md_5.bungee.protocol.channel;

import io.netty.channel.Channel;

@FunctionalInterface
public interface ChannelAcceptor
{

/**
* Inside this method the pipeline should be initialized.
*
* @param channel the channel to be accepted and initialized
* @return if the channel was accepted
*/
boolean accept(Channel channel);
}
24 changes: 23 additions & 1 deletion proxy/src/main/java/net/md_5/bungee/BungeeCord.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.query.RemoteQuery;
import net.md_5.bungee.scheduler.BungeeScheduler;
Expand Down Expand Up @@ -188,6 +189,21 @@ public static BungeeCord getInstance()
return (BungeeCord) ProxyServer.getInstance();
}

private final Unsafe unsafe = new Unsafe()
{
@Getter
@Setter
private BungeeChannelInitializer frontendChannelInitializer;

@Getter
@Setter
private BungeeChannelInitializer backendChannelInitializer;

@Getter
@Setter
private BungeeChannelInitializer serverInfoChannelInitializer;
};

@SuppressFBWarnings("DM_DEFAULT_ENCODING")
public BungeeCord() throws IOException
{
Expand Down Expand Up @@ -360,7 +376,7 @@ public void operationComplete(ChannelFuture future) throws Exception
.channel( PipelineUtils.getServerChannel( info.getSocketAddress() ) )
.option( ChannelOption.SO_REUSEADDR, true ) // TODO: Move this elsewhere!
.childAttr( PipelineUtils.LISTENER, info )
.childHandler( PipelineUtils.SERVER_CHILD )
.childHandler( unsafe().getFrontendChannelInitializer().getChannelInitializer() )
.group( eventLoops )
.localAddress( info.getSocketAddress() )
.bind().addListener( listener );
Expand Down Expand Up @@ -831,4 +847,10 @@ public Title createTitle()
{
return new BungeeTitle();
}

@Override
public Unsafe unsafe()
{
return unsafe;
}
}
2 changes: 1 addition & 1 deletion proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public void operationComplete(ChannelFuture future) throws Exception
new Bootstrap()
.channel( PipelineUtils.getChannel( socketAddress ) )
.group( BungeeCord.getInstance().eventLoops )
.handler( PipelineUtils.BASE_SERVERSIDE )
.handler( ProxyServer.getInstance().unsafe().getServerInfoChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, BungeeCord.getInstance().getConfig().getRemotePingTimeout() )
.remoteAddress( socketAddress )
.connect()
Expand Down
1 change: 1 addition & 0 deletions proxy/src/main/java/net/md_5/bungee/ServerConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public void exception(Throwable t) throws Exception
@Override
public void connected(ChannelWrapper channel) throws Exception
{
channel.setVersion( user.getPendingConnection().getVersion() );
this.ch = channel;

this.handshakeHandler = new ForgeServerHandler( user, ch, target );
Expand Down
20 changes: 4 additions & 16 deletions proxy/src/main/java/net/md_5/bungee/UserConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.util.internal.PlatformDependent;
import java.net.InetSocketAddress;
Expand Down Expand Up @@ -50,8 +48,6 @@
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.MinecraftDecoder;
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
Expand Down Expand Up @@ -362,17 +358,6 @@ private void connect0(final ServerConnectRequest request)

pendingConnects.add( target );

ChannelInitializer initializer = new ChannelInitializer()
{
@Override
protected void initChannel(Channel ch) throws Exception
{
PipelineUtils.BASE_SERVERSIDE.initChannel( ch );
ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
}
};
ChannelFutureListener listener = new ChannelFutureListener()
{
@Override
Expand Down Expand Up @@ -401,13 +386,16 @@ public void operationComplete(ChannelFuture future) throws Exception
{
sendMessage( bungee.getTranslation( "fallback_kick", connectionFailMessage( future.cause() ) ) );
}
} else
{
future.channel().pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
}
}
};
Bootstrap b = new Bootstrap()
.channel( PipelineUtils.getChannel( target.getAddress() ) )
.group( ch.getHandle().eventLoop() )
.handler( initializer )
.handler( bungee.unsafe().getBackendChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, request.getConnectTimeout() )
.remoteAddress( target.getAddress() );
// Windows is bugged, multi homed users will just have to live with random connecting IPs
Expand Down
47 changes: 32 additions & 15 deletions proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
Expand Down Expand Up @@ -51,33 +50,33 @@
import net.md_5.bungee.protocol.Varint21FrameDecoder;
import net.md_5.bungee.protocol.Varint21LengthFieldExtraBufPrepender;
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
import net.md_5.bungee.protocol.channel.ChannelAcceptor;

public class PipelineUtils
{

public static final AttributeKey<ListenerInfo> LISTENER = AttributeKey.valueOf( "ListerInfo" );
public static final ChannelInitializer<Channel> SERVER_CHILD = new ChannelInitializer<Channel>()

private static void setChannelInitializerHolders()
{
@Override
protected void initChannel(Channel ch) throws Exception
ProxyServer.getInstance().unsafe().setFrontendChannelInitializer( BungeeChannelInitializer.create( ch ->
{
SocketAddress remoteAddress = ( ch.remoteAddress() == null ) ? ch.parent().localAddress() : ch.remoteAddress();

if ( BungeeCord.getInstance().getConnectionThrottle() != null && BungeeCord.getInstance().getConnectionThrottle().throttle( remoteAddress ) )
{
ch.close();
return;
return false;
}

ListenerInfo listener = ch.attr( LISTENER ).get();

if ( BungeeCord.getInstance().getPluginManager().callEvent( new ClientConnectEvent( remoteAddress, listener ) ).isCancelled() )
{
ch.close();
return;
return false;
}

BASE.initChannel( ch );
BASE.accept( ch );
ch.pipeline().addBefore( FRAME_DECODER, LEGACY_DECODER, new LegacyDecoder() );
ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) );
ch.pipeline().addAfter( FRAME_PREPENDER, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) );
Expand All @@ -88,10 +87,24 @@ protected void initChannel(Channel ch) throws Exception
{
ch.pipeline().addFirst( new HAProxyMessageDecoder() );
}
}
};
public static final Base BASE = new Base( false );
public static final Base BASE_SERVERSIDE = new Base( true );

return true;
} ) );

ProxyServer.getInstance().unsafe().setBackendChannelInitializer( BungeeChannelInitializer.create( ch ->
{
PipelineUtils.BASE_SERVERSIDE.accept( ch );
ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, ProxyServer.getInstance().getProtocolVersion() ) );
ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, ProxyServer.getInstance().getProtocolVersion() ) );

return true;
} ) );

ProxyServer.getInstance().unsafe().setServerInfoChannelInitializer( BungeeChannelInitializer.create( BASE_SERVERSIDE ) );
}

private static final ChannelAcceptor BASE = new Base( false );
private static final ChannelAcceptor BASE_SERVERSIDE = new Base( true );
private static final KickStringWriter legacyKicker = new KickStringWriter();
private static final Varint21LengthFieldExtraBufPrepender serverFramePrepender = new Varint21LengthFieldExtraBufPrepender();
public static final String TIMEOUT_HANDLER = "timeout";
Expand Down Expand Up @@ -137,6 +150,8 @@ protected void initChannel(Channel ch) throws Exception
}
}
}

setChannelInitializerHolders();
}

public static EventLoopGroup newEventLoopGroup(int threads, ThreadFactory factory)
Expand Down Expand Up @@ -179,13 +194,13 @@ public static Class<? extends DatagramChannel> getDatagramChannel()

@NoArgsConstructor // for backwards compatibility
@AllArgsConstructor
public static final class Base extends ChannelInitializer<Channel>
public static final class Base implements ChannelAcceptor
{

private boolean toServer = false;

@Override
public void initChannel(Channel ch) throws Exception
public boolean accept(Channel ch)
{
try
{
Expand All @@ -204,6 +219,8 @@ public void initChannel(Channel ch) throws Exception
ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : new Varint21LengthFieldPrepender() );

ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );

return true;
}
}
}

0 comments on commit 0070421

Please sign in to comment.