HttpObjectDecoder ignores HTTP trailer header when empty line is rece… (#8799)
* HttpObjectDecoder ignores HTTP trailer header when empty line is received in seperate ByteBuf Motivation: When the empty line that termines the trailers was sent in a seperate ByteBuf we did ignore the previous parsed trailers and just returned none. Modifications: - Correct respect previous parsed trailers. - Add unit test. Result: Fixes https://github.com/netty/netty/issues/8736
This commit is contained in:
parent
ab0c33adff
commit
9725cae033
@ -633,49 +633,50 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
|
|||||||
if (line == null) {
|
if (line == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
CharSequence lastHeader = null;
|
LastHttpContent trailer = this.trailer;
|
||||||
if (line.length() > 0) {
|
if (line.length() == 0 && trailer == null) {
|
||||||
LastHttpContent trailer = this.trailer;
|
// We have received the empty line which signals the trailer is complete and did not parse any trailers
|
||||||
if (trailer == null) {
|
// before. Just return an empty last content to reduce allocations.
|
||||||
trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
|
return LastHttpContent.EMPTY_LAST_CONTENT;
|
||||||
}
|
|
||||||
do {
|
|
||||||
char firstChar = line.charAt(0);
|
|
||||||
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
|
|
||||||
List<String> current = trailer.trailingHeaders().getAll(lastHeader);
|
|
||||||
if (!current.isEmpty()) {
|
|
||||||
int lastPos = current.size() - 1;
|
|
||||||
//please do not make one line from below code
|
|
||||||
//as it breaks +XX:OptimizeStringConcat optimization
|
|
||||||
String lineTrimmed = line.toString().trim();
|
|
||||||
String currentLastPos = current.get(lastPos);
|
|
||||||
current.set(lastPos, currentLastPos + lineTrimmed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
splitHeader(line);
|
|
||||||
CharSequence headerName = name;
|
|
||||||
if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) &&
|
|
||||||
!HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(headerName) &&
|
|
||||||
!HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(headerName)) {
|
|
||||||
trailer.trailingHeaders().add(headerName, value);
|
|
||||||
}
|
|
||||||
lastHeader = name;
|
|
||||||
// reset name and value fields
|
|
||||||
name = null;
|
|
||||||
value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
line = headerParser.parse(buffer);
|
|
||||||
if (line == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} while (line.length() > 0);
|
|
||||||
|
|
||||||
this.trailer = null;
|
|
||||||
return trailer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return LastHttpContent.EMPTY_LAST_CONTENT;
|
CharSequence lastHeader = null;
|
||||||
|
if (trailer == null) {
|
||||||
|
trailer = this.trailer = new DefaultLastHttpContent(Unpooled.EMPTY_BUFFER, validateHeaders);
|
||||||
|
}
|
||||||
|
while (line.length() > 0) {
|
||||||
|
char firstChar = line.charAt(0);
|
||||||
|
if (lastHeader != null && (firstChar == ' ' || firstChar == '\t')) {
|
||||||
|
List<String> current = trailer.trailingHeaders().getAll(lastHeader);
|
||||||
|
if (!current.isEmpty()) {
|
||||||
|
int lastPos = current.size() - 1;
|
||||||
|
//please do not make one line from below code
|
||||||
|
//as it breaks +XX:OptimizeStringConcat optimization
|
||||||
|
String lineTrimmed = line.toString().trim();
|
||||||
|
String currentLastPos = current.get(lastPos);
|
||||||
|
current.set(lastPos, currentLastPos + lineTrimmed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
splitHeader(line);
|
||||||
|
CharSequence headerName = name;
|
||||||
|
if (!HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(headerName) &&
|
||||||
|
!HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(headerName) &&
|
||||||
|
!HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(headerName)) {
|
||||||
|
trailer.trailingHeaders().add(headerName, value);
|
||||||
|
}
|
||||||
|
lastHeader = name;
|
||||||
|
// reset name and value fields
|
||||||
|
name = null;
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
line = headerParser.parse(buffer);
|
||||||
|
if (line == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.trailer = null;
|
||||||
|
return trailer;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean isDecodingRequest();
|
protected abstract boolean isDecodingRequest();
|
||||||
|
@ -634,4 +634,33 @@ public class HttpResponseDecoderTest {
|
|||||||
assertThat(message.decoderResult().cause(), instanceOf(PrematureChannelClosureException.class));
|
assertThat(message.decoderResult().cause(), instanceOf(PrematureChannelClosureException.class));
|
||||||
assertNull(channel.readInbound());
|
assertNull(channel.readInbound());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTrailerWithEmptyLineInSeparateBuffer() {
|
||||||
|
HttpResponseDecoder decoder = new HttpResponseDecoder();
|
||||||
|
EmbeddedChannel channel = new EmbeddedChannel(decoder);
|
||||||
|
|
||||||
|
String headers = "HTTP/1.1 200 OK\r\n"
|
||||||
|
+ "Transfer-Encoding: chunked\r\n"
|
||||||
|
+ "Trailer: My-Trailer\r\n";
|
||||||
|
assertFalse(channel.writeInbound(Unpooled.copiedBuffer(headers.getBytes(CharsetUtil.US_ASCII))));
|
||||||
|
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", CharsetUtil.US_ASCII)));
|
||||||
|
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)));
|
||||||
|
|
||||||
|
HttpResponse response = channel.readInbound();
|
||||||
|
assertEquals(2, response.headers().size());
|
||||||
|
assertEquals("chunked", response.headers().get(HttpHeaderNames.TRANSFER_ENCODING));
|
||||||
|
assertEquals("My-Trailer", response.headers().get(HttpHeaderNames.TRAILER));
|
||||||
|
|
||||||
|
LastHttpContent lastContent = channel.readInbound();
|
||||||
|
assertEquals(1, lastContent.trailingHeaders().size());
|
||||||
|
assertEquals("42", lastContent.trailingHeaders().get("My-Trailer"));
|
||||||
|
assertEquals(0, lastContent.content().readableBytes());
|
||||||
|
lastContent.release();
|
||||||
|
|
||||||
|
assertFalse(channel.finish());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user