#3737: Use composite buffers where possible

This commit is contained in:
Outfluencer
2024-09-09 21:01:19 +10:00
committed by md_5
parent 477ea5983c
commit b309e4ac50
12 changed files with 123 additions and 23 deletions

View File

@@ -2,18 +2,23 @@ package net.md_5.bungee.compress;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
import java.util.zip.Deflater;
import lombok.Getter;
import lombok.Setter;
import net.md_5.bungee.jni.zlib.BungeeZlib;
import net.md_5.bungee.protocol.DefinedPacket;
public class PacketCompressor extends MessageToByteEncoder<ByteBuf>
public class PacketCompressor extends MessageToMessageEncoder<ByteBuf>
{
@Getter
private final BungeeZlib zlib = CompressFactory.zlib.newInstance();
@Setter
private int threshold = 256;
@Setter
private boolean compose = true;
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception
@@ -28,18 +33,25 @@ public class PacketCompressor extends MessageToByteEncoder<ByteBuf>
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception
{
int origSize = msg.readableBytes();
if ( origSize < threshold )
{
DefinedPacket.writeVarInt( 0, out );
out.writeBytes( msg );
if ( compose )
{
// create a virtual buffer to avoid copying of data
out.add( ctx.alloc().compositeDirectBuffer( 2 ).addComponents( true, ctx.alloc().directBuffer( 1 ).writeByte( 0 ), msg.retain() ) );
} else
{
out.add( ctx.alloc().directBuffer( origSize + 1 ).writeByte( 0 ).writeBytes( msg ) );
}
} else
{
DefinedPacket.writeVarInt( origSize, out );
zlib.process( msg, out );
ByteBuf buf = ctx.alloc().directBuffer( BungeeZlib.OUTPUT_BUFFER_SIZE );
DefinedPacket.writeVarInt( origSize, buf );
zlib.process( msg, buf );
out.add( buf );
}
}
}

View File

@@ -508,6 +508,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
ch.addBefore( PipelineUtils.FRAME_DECODER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
BungeeCipher encrypt = EncryptionUtil.getCipher( true, sharedKey );
ch.addBefore( PipelineUtils.FRAME_PREPENDER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
// disable use of composite buffers if we use natives
ch.updateComposite();
String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" );

View File

@@ -7,15 +7,19 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import lombok.Getter;
import lombok.Setter;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.compress.PacketCompressor;
import net.md_5.bungee.compress.PacketDecompressor;
import net.md_5.bungee.netty.cipher.CipherEncoder;
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.Varint21LengthFieldPrepender;
import net.md_5.bungee.protocol.packet.Kick;
public class ChannelWrapper
@@ -187,5 +191,36 @@ public class ChannelWrapper
{
ch.pipeline().remove( "decompress" );
}
// disable use of composite buffers if we use natives
updateComposite();
}
/*
* Should be called on encryption add and on compressor add or remove
*/
public void updateComposite()
{
CipherEncoder cipherEncoder = ch.pipeline().get( CipherEncoder.class );
PacketCompressor packetCompressor = ch.pipeline().get( PacketCompressor.class );
Varint21LengthFieldPrepender prepender = ch.pipeline().get( Varint21LengthFieldPrepender.class );
boolean compressorCompose = cipherEncoder == null || cipherEncoder.getCipher().allowComposite();
boolean prependerCompose = compressorCompose && ( packetCompressor == null || packetCompressor.getZlib().allowComposite() );
if ( prepender != null )
{
ProxyServer.getInstance().getLogger().log( Level.FINE, "set prepender compose to {0} for {1}", new Object[]
{
prependerCompose, ch
} );
prepender.setCompose( prependerCompose );
}
if ( packetCompressor != null )
{
ProxyServer.getInstance().getLogger().log( Level.FINE, "set packetCompressor compose to {0} for {1}", new Object[]
{
compressorCompose, ch
} );
packetCompressor.setCompose( compressorCompose );
}
}
}

View File

@@ -93,7 +93,6 @@ public class PipelineUtils
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";
@@ -202,7 +201,7 @@ public class PipelineUtils
ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
// 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( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : new Varint21LengthFieldPrepender() );
ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
}

View File

@@ -3,6 +3,7 @@ package net.md_5.bungee.netty.cipher;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.jni.cipher.BungeeCipher;
@@ -10,6 +11,7 @@ import net.md_5.bungee.jni.cipher.BungeeCipher;
public class CipherEncoder extends MessageToByteEncoder<ByteBuf>
{
@Getter
private final BungeeCipher cipher;
@Override