[5382] HttpContentEncoder should not set chunked transfer-encoding for HTTP/1.0

Motivation:

When using HttpContentCompressor and the HttpResponse is protocol version 1.0, HttpContentEncoder.encode() should not set the transfer-encoding header to chunked. Chunked transfer-encoding is not valid for HTTP 1.0 - this causes ERR_CONTENT_DECODING_FAILED errors in chrome and similar failures in IE.

Modifications:

Skip HTTP/1.0 messages

Result:

Be able to serve HTTP/1.0 as well when HttpContentEncoder is in the pipeline.
This commit is contained in:
Norman Maurer 2016-06-10 11:45:14 +02:00
parent 78f6f07f99
commit 4a1e0ceb4d
2 changed files with 35 additions and 4 deletions

View File

@ -120,9 +120,11 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
* The HEAD method is identical to GET except that the server MUST NOT return a message-body * The HEAD method is identical to GET except that the server MUST NOT return a message-body
* in the response. * in the response.
* *
* This code is now inline with HttpClientDecoder.Decoder * Also we should pass through HTTP/1.0 as transfer-encoding: chunked is not supported.
*
* See https://github.com/netty/netty/issues/5382
*/ */
if (isPassthru(code, acceptEncoding)) { if (isPassthru(res.protocolVersion(), code, acceptEncoding)) {
if (isFull) { if (isFull) {
out.add(ReferenceCountUtil.retain(res)); out.add(ReferenceCountUtil.retain(res));
} else { } else {
@ -203,9 +205,10 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
} }
} }
private static boolean isPassthru(int code, CharSequence httpMethod) { private static boolean isPassthru(HttpVersion version, int code, CharSequence httpMethod) {
return code < 200 || code == 204 || code == 304 || return code < 200 || code == 204 || code == 304 ||
(httpMethod == ZERO_LENGTH_HEAD || (httpMethod == ZERO_LENGTH_CONNECT && code == 200)); (httpMethod == ZERO_LENGTH_HEAD || (httpMethod == ZERO_LENGTH_CONNECT && code == 200)) ||
version == HttpVersion.HTTP_1_0;
} }
private static void ensureHeaders(HttpObject msg) { private static void ensureHeaders(HttpObject msg) {

View File

@ -30,7 +30,10 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class HttpContentEncoderTest { public class HttpContentEncoderTest {
@ -332,6 +335,31 @@ public class HttpContentEncoderTest {
assertThat(ch.readOutbound(), is(nullValue())); assertThat(ch.readOutbound(), is(nullValue()));
} }
@Test
public void testHttp1_0() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder());
FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_0, HttpMethod.GET, "/");
assertTrue(ch.writeInbound(req));
HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK);
res.headers().set(HttpHeaderNames.CONTENT_LENGTH, HttpHeaderValues.ZERO);
assertTrue(ch.writeOutbound(res));
assertTrue(ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT));
assertTrue(ch.finish());
FullHttpRequest request = ch.readInbound();
assertTrue(request.release());
assertNull(ch.readInbound());
HttpResponse response = ch.readOutbound();
assertSame(res, response);
LastHttpContent content = ch.readOutbound();
assertSame(LastHttpContent.EMPTY_LAST_CONTENT, content);
content.release();
assertNull(ch.readOutbound());
}
private static void assertEmptyResponse(EmbeddedChannel ch) { private static void assertEmptyResponse(EmbeddedChannel ch) {
Object o = ch.readOutbound(); Object o = ch.readOutbound();
assertThat(o, is(instanceOf(HttpResponse.class))); assertThat(o, is(instanceOf(HttpResponse.class)));