More values other than chunked defined in Transfer-Encoding header leads to decode failure (#10321)
Motivation: `containsValue()` will check if there are multiple values defined in the specific header name, we need to use this method instead of `contains()` for the `Transfer-Encoding` header to cover the case that multiple values defined, like: `Transfer-Encoding: gzip, chunked` Modification: Change from `contains()` to `containsValue()` in `HttpUtil.isTransferEncodingChunked()` method. Result: Fixes #10320
This commit is contained in:
parent
4c7e017199
commit
4b6ceb2ca0
@ -76,12 +76,23 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
|
||||
cleanup();
|
||||
final HttpMessage message = (HttpMessage) msg;
|
||||
final HttpHeaders headers = message.headers();
|
||||
// Determine the content encoding.
|
||||
|
||||
// Determine the content encoding.
|
||||
String contentEncoding = headers.get(HttpHeaderNames.CONTENT_ENCODING);
|
||||
if (contentEncoding != null) {
|
||||
contentEncoding = contentEncoding.trim();
|
||||
} else {
|
||||
contentEncoding = IDENTITY;
|
||||
String transferEncoding = headers.get(HttpHeaderNames.TRANSFER_ENCODING);
|
||||
if (transferEncoding != null) {
|
||||
int idx = transferEncoding.indexOf(",");
|
||||
if (idx != -1) {
|
||||
contentEncoding = transferEncoding.substring(0, idx).trim();
|
||||
} else {
|
||||
contentEncoding = transferEncoding.trim();
|
||||
}
|
||||
} else {
|
||||
contentEncoding = IDENTITY;
|
||||
}
|
||||
}
|
||||
decoder = newContentDecoder(contentEncoding);
|
||||
|
||||
|
@ -301,7 +301,7 @@ public final class HttpUtil {
|
||||
* @return True if transfer encoding is chunked, otherwise false
|
||||
*/
|
||||
public static boolean isTransferEncodingChunked(HttpMessage message) {
|
||||
return message.headers().contains(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED, true);
|
||||
return message.headers().containsValue(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -563,6 +563,78 @@ public class HttpContentDecoderTest {
|
||||
assertEquals(0, content.refCnt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferCodingGZIP() {
|
||||
String requestStr = "POST / HTTP/1.1\r\n" +
|
||||
"Content-Length: " + GZ_HELLO_WORLD.length + "\r\n" +
|
||||
"Transfer-Encoding: gzip\r\n" +
|
||||
"\r\n";
|
||||
HttpRequestDecoder decoder = new HttpRequestDecoder();
|
||||
HttpContentDecoder decompressor = new HttpContentDecompressor();
|
||||
EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor);
|
||||
|
||||
channel.writeInbound(Unpooled.copiedBuffer(requestStr.getBytes()));
|
||||
channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD));
|
||||
|
||||
HttpRequest request = channel.readInbound();
|
||||
assertTrue(request.decoderResult().isSuccess());
|
||||
assertFalse(request.headers().contains(HttpHeaderNames.CONTENT_LENGTH));
|
||||
|
||||
HttpContent content = channel.readInbound();
|
||||
assertTrue(content.decoderResult().isSuccess());
|
||||
assertEquals(HELLO_WORLD, content.content().toString(CharsetUtil.US_ASCII));
|
||||
content.release();
|
||||
|
||||
LastHttpContent lastHttpContent = channel.readInbound();
|
||||
assertTrue(lastHttpContent.decoderResult().isSuccess());
|
||||
lastHttpContent.release();
|
||||
|
||||
assertHasInboundMessages(channel, false);
|
||||
assertHasOutboundMessages(channel, false);
|
||||
assertFalse(channel.finish());
|
||||
channel.releaseInbound();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransferCodingGZIPAndChunked() {
|
||||
String requestStr = "POST / HTTP/1.1\r\n" +
|
||||
"Host: example.com\r\n" +
|
||||
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
||||
"Trailer: My-Trailer\r\n" +
|
||||
"Transfer-Encoding: gzip, chunked\r\n" +
|
||||
"\r\n";
|
||||
HttpRequestDecoder decoder = new HttpRequestDecoder();
|
||||
HttpContentDecoder decompressor = new HttpContentDecompressor();
|
||||
EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor);
|
||||
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(requestStr, CharsetUtil.US_ASCII)));
|
||||
|
||||
String chunkLength = Integer.toHexString(GZ_HELLO_WORLD.length);
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(chunkLength + "\r\n", CharsetUtil.US_ASCII)));
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD)));
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n".getBytes(CharsetUtil.US_ASCII))));
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("0\r\n", CharsetUtil.US_ASCII)));
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("My-Trailer: 42\r\n\r\n", CharsetUtil.US_ASCII)));
|
||||
|
||||
HttpRequest request = channel.readInbound();
|
||||
assertTrue(request.decoderResult().isSuccess());
|
||||
assertTrue(request.headers().containsValue(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED, true));
|
||||
assertFalse(request.headers().contains(HttpHeaderNames.CONTENT_LENGTH));
|
||||
|
||||
HttpContent chunk1 = channel.readInbound();
|
||||
assertTrue(chunk1.decoderResult().isSuccess());
|
||||
assertEquals(HELLO_WORLD, chunk1.content().toString(CharsetUtil.US_ASCII));
|
||||
chunk1.release();
|
||||
|
||||
LastHttpContent chunk2 = channel.readInbound();
|
||||
assertTrue(chunk2.decoderResult().isSuccess());
|
||||
assertEquals("42", chunk2.trailingHeaders().get("My-Trailer"));
|
||||
chunk2.release();
|
||||
|
||||
assertFalse(channel.finish());
|
||||
channel.releaseInbound();
|
||||
}
|
||||
|
||||
private static byte[] gzDecompress(byte[] input) {
|
||||
ZlibDecoder decoder = ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(decoder);
|
||||
|
Loading…
Reference in New Issue
Block a user