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:
Lin Gao 2020-06-02 20:29:20 +08:00 committed by GitHub
parent 0dc94e4965
commit de134da720
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 2 deletions

View File

@ -85,7 +85,17 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
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);

View File

@ -300,7 +300,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);
}
/**

View File

@ -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);