Motivation: Client can split data into different numbers of fragments and sometimes the last frame may contain trash data that doesn't affect decompression process. Modification: Added check if last frame is `ContinuationWebSocketFrame` and decompression data is empty then don't throw an exception. Result: Fixes #9770
This commit is contained in:
parent
46cff5399f
commit
b7d685648e
|
@ -135,9 +135,13 @@ abstract class DeflateDecoder extends WebSocketExtensionDecoder {
|
||||||
// Correctly handle empty frames
|
// Correctly handle empty frames
|
||||||
// See https://github.com/netty/netty/issues/4348
|
// See https://github.com/netty/netty/issues/4348
|
||||||
if (!emptyDeflateBlock && readable && compositeDecompressedContent.numComponents() <= 0) {
|
if (!emptyDeflateBlock && readable && compositeDecompressedContent.numComponents() <= 0) {
|
||||||
|
// Sometimes after fragmentation the last frame
|
||||||
|
// May contain left-over data that doesn't affect decompression
|
||||||
|
if (!(msg instanceof ContinuationWebSocketFrame)) {
|
||||||
compositeDecompressedContent.release();
|
compositeDecompressedContent.release();
|
||||||
throw new CodecException("cannot read uncompressed buffer");
|
throw new CodecException("cannot read uncompressed buffer");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.isFinalFragment() && noContext) {
|
if (msg.isFinalFragment() && noContext) {
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
|
@ -328,4 +328,62 @@ public class PerMessageDeflateDecoderTest {
|
||||||
assertFalse(decoderChannel.finish());
|
assertFalse(decoderChannel.finish());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFragmentedFrameWithLeftOverInLastFragment() {
|
||||||
|
String hexDump = "677170647a777a737574656b707a787a6f6a7561756578756f6b7868616371716c657a6d64697479766d726f6" +
|
||||||
|
"269746c6376777464776f6f72767a726f64667278676764687775786f6762766d776d706b76697773777a7072" +
|
||||||
|
"6a6a737279707a7078697a6c69616d7461656d646278626d786f66666e686e776a7a7461746d7a776668776b6" +
|
||||||
|
"f6f736e73746575637a6d727a7175707a6e74627578687871767771697a71766c64626d78726d6d7675756877" +
|
||||||
|
"62667963626b687a726d676e646263776e67797264706d6c6863626577616967706a78636a72697464756e627" +
|
||||||
|
"977616f79736475676f76736f7178746a7a7479626c64636b6b6778637768746c62";
|
||||||
|
EmbeddedChannel encoderChannel = new EmbeddedChannel(
|
||||||
|
ZlibCodecFactory.newZlibEncoder(ZlibWrapper.NONE, 9, 15, 8));
|
||||||
|
EmbeddedChannel decoderChannel = new EmbeddedChannel(new PerMessageDeflateDecoder(false));
|
||||||
|
|
||||||
|
ByteBuf originPayload = Unpooled.wrappedBuffer(ByteBufUtil.decodeHexDump(hexDump));
|
||||||
|
assertTrue(encoderChannel.writeOutbound(originPayload.duplicate().retain()));
|
||||||
|
|
||||||
|
ByteBuf compressedPayload = encoderChannel.readOutbound();
|
||||||
|
compressedPayload = compressedPayload.slice(0, compressedPayload.readableBytes() - 4);
|
||||||
|
|
||||||
|
int oneThird = compressedPayload.readableBytes() / 3;
|
||||||
|
|
||||||
|
TextWebSocketFrame compressedFrame1 = new TextWebSocketFrame(
|
||||||
|
false, WebSocketExtension.RSV1, compressedPayload.slice(0, oneThird));
|
||||||
|
ContinuationWebSocketFrame compressedFrame2 = new ContinuationWebSocketFrame(
|
||||||
|
false, WebSocketExtension.RSV3, compressedPayload.slice(oneThird, oneThird));
|
||||||
|
ContinuationWebSocketFrame compressedFrame3 = new ContinuationWebSocketFrame(
|
||||||
|
false, WebSocketExtension.RSV3, compressedPayload.slice(oneThird * 2, oneThird));
|
||||||
|
int offset = oneThird * 3;
|
||||||
|
ContinuationWebSocketFrame compressedFrameWithExtraData = new ContinuationWebSocketFrame(
|
||||||
|
true, WebSocketExtension.RSV3, compressedPayload.slice(offset,
|
||||||
|
compressedPayload.readableBytes() - offset));
|
||||||
|
|
||||||
|
// check that last fragment contains only one extra byte
|
||||||
|
assertEquals(1, compressedFrameWithExtraData.content().readableBytes());
|
||||||
|
assertEquals(1, compressedFrameWithExtraData.content().getByte(0));
|
||||||
|
|
||||||
|
// write compressed frames
|
||||||
|
assertTrue(decoderChannel.writeInbound(compressedFrame1.retain()));
|
||||||
|
assertTrue(decoderChannel.writeInbound(compressedFrame2.retain()));
|
||||||
|
assertTrue(decoderChannel.writeInbound(compressedFrame3.retain()));
|
||||||
|
assertTrue(decoderChannel.writeInbound(compressedFrameWithExtraData));
|
||||||
|
|
||||||
|
// read uncompressed frames
|
||||||
|
TextWebSocketFrame uncompressedFrame1 = decoderChannel.readInbound();
|
||||||
|
ContinuationWebSocketFrame uncompressedFrame2 = decoderChannel.readInbound();
|
||||||
|
ContinuationWebSocketFrame uncompressedFrame3 = decoderChannel.readInbound();
|
||||||
|
ContinuationWebSocketFrame uncompressedExtraData = decoderChannel.readInbound();
|
||||||
|
assertFalse(uncompressedExtraData.content().isReadable());
|
||||||
|
|
||||||
|
ByteBuf uncompressedPayload = Unpooled.wrappedBuffer(uncompressedFrame1.content(), uncompressedFrame2.content(),
|
||||||
|
uncompressedFrame3.content(), uncompressedExtraData.content());
|
||||||
|
assertEquals(originPayload, uncompressedPayload);
|
||||||
|
|
||||||
|
assertTrue(originPayload.release());
|
||||||
|
assertTrue(uncompressedPayload.release());
|
||||||
|
|
||||||
|
assertTrue(encoderChannel.finishAndReleaseAll());
|
||||||
|
assertFalse(decoderChannel.finish());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user