Fixed issue: NETTY-272 HttpMessageEncoder should not prepend/append extra data around HttpChunk content if Transfer-Encoding is not chunked.

* HttpMessageEncoder now does not add any extra data around HttpChunk content if Transfer-Encoding is not 'chunked'
* Moved the utility code that checks the existance of 'Transfer-Encoding: chunked' to HttpCodecUtil so that both HttpMessageEncoder and DefaultHttpMessage can use it
This commit is contained in:
Trustin Lee 2010-01-07 09:05:38 +00:00
parent 491baa0c7b
commit 661acd24c8
3 changed files with 59 additions and 37 deletions

View File

@ -112,19 +112,9 @@ public class DefaultHttpMessage implements HttpMessage {
public boolean isChunked() { public boolean isChunked() {
if (chunked) { if (chunked) {
return true; return true;
} else {
return HttpCodecUtil.isTransferEncodingChunked(this);
} }
List<String> chunked = getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
if (chunked.isEmpty()) {
return false;
}
for (String v: chunked) {
if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
return true;
}
}
return false;
} }
public void setChunked(boolean chunked) { public void setChunked(boolean chunked) {

View File

@ -16,6 +16,7 @@
package org.jboss.netty.handler.codec.http; package org.jboss.netty.handler.codec.http;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List;
import org.jboss.netty.util.CharsetUtil; import org.jboss.netty.util.CharsetUtil;
@ -159,4 +160,18 @@ class HttpCodecUtil {
"value must not end with '\\r' or '\\n':" + value); "value must not end with '\\r' or '\\n':" + value);
} }
} }
static boolean isTransferEncodingChunked(HttpMessage m) {
List<String> chunked = m.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
if (chunked.isEmpty()) {
return false;
}
for (String v: chunked) {
if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
return true;
}
}
return false;
}
} }

View File

@ -50,12 +50,14 @@ import org.jboss.netty.util.CharsetUtil;
* *
* @apiviz.landmark * @apiviz.landmark
*/ */
@ChannelPipelineCoverage("all") @ChannelPipelineCoverage("one")
public abstract class HttpMessageEncoder extends OneToOneEncoder { public abstract class HttpMessageEncoder extends OneToOneEncoder {
private static final ChannelBuffer LAST_CHUNK = private static final ChannelBuffer LAST_CHUNK =
copiedBuffer("0\r\n\r\n", CharsetUtil.US_ASCII); copiedBuffer("0\r\n\r\n", CharsetUtil.US_ASCII);
private volatile boolean chunked;
/** /**
* Creates a new instance. * Creates a new instance.
*/ */
@ -66,16 +68,21 @@ public abstract class HttpMessageEncoder extends OneToOneEncoder {
@Override @Override
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
if (msg instanceof HttpMessage) { if (msg instanceof HttpMessage) {
HttpMessage request = (HttpMessage) msg; HttpMessage m = (HttpMessage) msg;
boolean chunked = this.chunked = HttpCodecUtil.isTransferEncodingChunked(m);
ChannelBuffer header = ChannelBuffers.dynamicBuffer( ChannelBuffer header = ChannelBuffers.dynamicBuffer(
channel.getConfig().getBufferFactory()); channel.getConfig().getBufferFactory());
encodeInitialLine(header, request); encodeInitialLine(header, m);
encodeHeaders(header, request); encodeHeaders(header, m);
header.writeBytes(CRLF); header.writeBytes(CRLF);
ChannelBuffer content = request.getContent(); ChannelBuffer content = m.getContent();
if (!content.readable()) { if (!content.readable()) {
return header; // no content return header; // no content
} else if (chunked) {
throw new IllegalArgumentException(
"HttpMessage.content must be empty " +
"if Transfer-Encoding is chunked.");
} else { } else {
return wrappedBuffer(header, content); return wrappedBuffer(header, content);
} }
@ -83,28 +90,38 @@ public abstract class HttpMessageEncoder extends OneToOneEncoder {
if (msg instanceof HttpChunk) { if (msg instanceof HttpChunk) {
HttpChunk chunk = (HttpChunk) msg; HttpChunk chunk = (HttpChunk) msg;
if (chunk == HttpChunk.LAST_CHUNK) { if (chunked) {
return LAST_CHUNK.duplicate(); if (chunk == HttpChunk.LAST_CHUNK) {
} else if (chunk instanceof HttpChunkTrailer) { chunked = false;
ChannelBuffer trailer = ChannelBuffers.dynamicBuffer( return LAST_CHUNK.duplicate();
channel.getConfig().getBufferFactory()); } else if (chunk instanceof HttpChunkTrailer) {
trailer.writeByte((byte) '0'); ChannelBuffer trailer = ChannelBuffers.dynamicBuffer(
trailer.writeBytes(CRLF); channel.getConfig().getBufferFactory());
encodeTrailingHeaders(trailer, (HttpChunkTrailer) chunk); trailer.writeByte((byte) '0');
trailer.writeBytes(CRLF); trailer.writeBytes(CRLF);
return trailer; encodeTrailingHeaders(trailer, (HttpChunkTrailer) chunk);
} else { trailer.writeBytes(CRLF);
ChannelBuffer content = chunk.getContent(); return trailer;
int contentLength = content.readableBytes(); } else {
ChannelBuffer content = chunk.getContent();
int contentLength = content.readableBytes();
return wrappedBuffer( return wrappedBuffer(
copiedBuffer( copiedBuffer(
Integer.toHexString(contentLength), Integer.toHexString(contentLength),
CharsetUtil.US_ASCII), CharsetUtil.US_ASCII),
wrappedBuffer(CRLF), wrappedBuffer(CRLF),
content.slice(content.readerIndex(), contentLength), content.slice(content.readerIndex(), contentLength),
wrappedBuffer(CRLF)); wrappedBuffer(CRLF));
}
} else {
if (chunk == HttpChunk.LAST_CHUNK) {
return null;
} else {
return chunk.getContent();
}
} }
} }
// Unknown message type. // Unknown message type.