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();
|
cleanup();
|
||||||
final HttpMessage message = (HttpMessage) msg;
|
final HttpMessage message = (HttpMessage) msg;
|
||||||
final HttpHeaders headers = message.headers();
|
final HttpHeaders headers = message.headers();
|
||||||
// Determine the content encoding.
|
|
||||||
|
// Determine the content encoding.
|
||||||
String contentEncoding = headers.get(HttpHeaderNames.CONTENT_ENCODING);
|
String contentEncoding = headers.get(HttpHeaderNames.CONTENT_ENCODING);
|
||||||
if (contentEncoding != null) {
|
if (contentEncoding != null) {
|
||||||
contentEncoding = contentEncoding.trim();
|
contentEncoding = contentEncoding.trim();
|
||||||
} else {
|
} 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);
|
decoder = newContentDecoder(contentEncoding);
|
||||||
|
|
||||||
|
@ -301,7 +301,7 @@ public final class HttpUtil {
|
|||||||
* @return True if transfer encoding is chunked, otherwise false
|
* @return True if transfer encoding is chunked, otherwise false
|
||||||
*/
|
*/
|
||||||
public static boolean isTransferEncodingChunked(HttpMessage message) {
|
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());
|
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) {
|
private static byte[] gzDecompress(byte[] input) {
|
||||||
ZlibDecoder decoder = ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP);
|
ZlibDecoder decoder = ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP);
|
||||||
EmbeddedChannel channel = new EmbeddedChannel(decoder);
|
EmbeddedChannel channel = new EmbeddedChannel(decoder);
|
||||||
|
Loading…
Reference in New Issue
Block a user