Skip to content

Commit

Permalink
#3451: Improve length field prepending on bungee -> server connection
Browse files Browse the repository at this point in the history
Use alternative implementation of Varint21LengthFieldPrepender on bungee -> server connection for improved speed - it uses separate buffer to prepend the length to avoid copying large data buffer.
Not applied bungee -> client because encrypting 1-5 bytes of length separately through expensive jni call could make it not worth (not measured).
  • Loading branch information
Janmm14 authored and md-5 committed Mar 25, 2023
1 parent 2e6f0dd commit 2ef5e70
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package net.md_5.bungee.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;

/**
* Prepend length of the message as a Varint21 using an extra buffer for the length, avoiding copying packet data
*/
@ChannelHandler.Sharable
public class Varint21LengthFieldExtraBufPrepender extends MessageToMessageEncoder<ByteBuf>
{

@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception
{
int bodyLen = msg.readableBytes();
ByteBuf lenBuf = ctx.alloc().ioBuffer( Varint21LengthFieldPrepender.varintSize( bodyLen ) );
DefinedPacket.writeVarInt( bodyLen, lenBuf );
out.add( lenBuf );
out.add( msg.retain() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

/**
* Prepend length of the message as a Varint21 by writing length and data to a new buffer
*/
@ChannelHandler.Sharable
public class Varint21LengthFieldPrepender extends MessageToByteEncoder<ByteBuf>
{
Expand All @@ -20,7 +23,7 @@ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throw
out.writeBytes( msg );
}

private static int varintSize(int paramInt)
static int varintSize(int paramInt)
{
if ( ( paramInt & 0xFFFFFF80 ) == 0 )
{
Expand Down
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 @@ -182,7 +182,7 @@ public void operationComplete(ChannelFuture future) throws Exception
new Bootstrap()
.channel( PipelineUtils.getChannel( socketAddress ) )
.group( BungeeCord.getInstance().eventLoops )
.handler( PipelineUtils.BASE )
.handler( PipelineUtils.BASE_SERVERSIDE )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, BungeeCord.getInstance().getConfig().getRemotePingTimeout() )
.remoteAddress( socketAddress )
.connect()
Expand Down
2 changes: 1 addition & 1 deletion proxy/src/main/java/net/md_5/bungee/UserConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ public void connect(final ServerConnectRequest request)
@Override
protected void initChannel(Channel ch) throws Exception
{
PipelineUtils.BASE.initChannel( ch );
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 ) );
Expand Down
15 changes: 13 additions & 2 deletions proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.Util;
import net.md_5.bungee.api.ProxyServer;
Expand All @@ -42,6 +44,7 @@
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.Varint21FrameDecoder;
import net.md_5.bungee.protocol.Varint21LengthFieldExtraBufPrepender;
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;

public class PipelineUtils
Expand Down Expand Up @@ -82,9 +85,11 @@ protected void initChannel(Channel ch) throws Exception
}
}
};
public static final Base BASE = new Base();
public static final Base BASE = new Base( false );
public static final Base BASE_SERVERSIDE = new Base( true );
private static final KickStringWriter legacyKicker = new KickStringWriter();
private static final Varint21LengthFieldPrepender framePrepender = new Varint21LengthFieldPrepender();
private static final Varint21LengthFieldExtraBufPrepender serverFramePrepender = new Varint21LengthFieldExtraBufPrepender();
public static final String TIMEOUT_HANDLER = "timeout";
public static final String PACKET_DECODER = "packet-decoder";
public static final String PACKET_ENCODER = "packet-encoder";
Expand Down Expand Up @@ -152,9 +157,13 @@ public static Class<? extends DatagramChannel> getDatagramChannel()
private static final int HIGH_MARK = Integer.getInteger( "net.md_5.bungee.high_mark", 2 << 20 ); // 2 mb
private static final WriteBufferWaterMark MARK = new WriteBufferWaterMark( LOW_MARK, HIGH_MARK );

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

private boolean toServer = false;

@Override
public void initChannel(Channel ch) throws Exception
{
Expand All @@ -170,7 +179,9 @@ public void initChannel(Channel ch) throws Exception

ch.pipeline().addLast( FRAME_DECODER, new Varint21FrameDecoder() );
ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
ch.pipeline().addLast( FRAME_PREPENDER, framePrepender );
// No encryption bungee -> server, therefore use extra buffer to avoid copying everything for length prepending
// Not used bungee -> client as header would need to be encrypted separately through expensive JNI call
ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : framePrepender );

ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
}
Expand Down

0 comments on commit 2ef5e70

Please sign in to comment.