Allow to also write FileRegion and ChunkInput when using HTTP chunked. Fixes [#1693]

This commit is contained in:
Norman Maurer 2013-08-05 08:22:04 +02:00
parent ea1dca8105
commit 60b889375c
2 changed files with 43 additions and 13 deletions

View File

@ -86,7 +86,7 @@ public final class HttpClientCodec
@Override
protected void encode(
ChannelHandlerContext ctx, HttpObject msg, List<Object> out) throws Exception {
ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
if (msg instanceof HttpRequest && !done) {
queue.offer(((HttpRequest) msg).getMethod());
}

View File

@ -18,8 +18,10 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.FileRegion;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import java.util.List;
import java.util.Map;
@ -39,7 +41,7 @@ import static io.netty.handler.codec.http.HttpConstants.*;
* To implement the encoder of such a derived protocol, extend this class and
* implement all abstract methods properly.
*/
public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageToMessageEncoder<HttpObject> {
public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageToMessageEncoder<Object> {
private static final byte[] CRLF = { CR, LF };
private static final byte[] ZERO_CRLF = { '0', CR, LF };
private static final byte[] ZERO_CRLF_CRLF = { '0', CR, LF, CR, LF };
@ -53,7 +55,7 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
private int state = ST_INIT;
@Override
protected void encode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out) throws Exception {
protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
if (msg instanceof HttpMessage) {
if (state != ST_INIT) {
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
@ -70,25 +72,22 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
out.add(buf);
state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
}
if (msg instanceof HttpContent) {
if (msg instanceof HttpContent || msg instanceof ByteBuf || msg instanceof FileRegion) {
if (state == ST_INIT) {
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
}
HttpContent chunk = (HttpContent) msg;
ByteBuf content = chunk.content();
int contentLength = content.readableBytes();
int contentLength = contentLength(msg);
if (state == ST_CONTENT_NON_CHUNK) {
if (contentLength > 0) {
out.add(content.retain());
out.add(encodeAndRetain(msg));
} else {
// Need to produce some output otherwise an
// IllegalstateException will be thrown
out.add(Unpooled.EMPTY_BUFFER);
}
if (chunk instanceof LastHttpContent) {
if (msg instanceof LastHttpContent) {
state = ST_INIT;
}
} else if (state == ST_CONTENT_CHUNK) {
@ -98,12 +97,12 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
buf.writeBytes(length);
buf.writeBytes(CRLF);
out.add(buf);
out.add(content.retain());
out.add(encodeAndRetain(msg));
out.add(ctx.alloc().buffer(CRLF.length).writeBytes(CRLF));
}
if (chunk instanceof LastHttpContent) {
HttpHeaders headers = ((LastHttpContent) chunk).trailingHeaders();
if (msg instanceof LastHttpContent) {
HttpHeaders headers = ((LastHttpContent) msg).trailingHeaders();
if (headers.isEmpty()) {
out.add(ctx.alloc().buffer(ZERO_CRLF_CRLF.length).writeBytes(ZERO_CRLF_CRLF));
} else {
@ -128,6 +127,37 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
}
}
@Override
public boolean acceptOutboundMessage(Object msg) throws Exception {
return msg instanceof HttpObject || msg instanceof ByteBuf || msg instanceof FileRegion;
}
private static Object encodeAndRetain(Object msg) {
if (msg instanceof ByteBuf) {
return ((ByteBuf) msg).retain();
}
if (msg instanceof HttpContent) {
return ((HttpContent) msg).content().retain();
}
if (msg instanceof FileRegion) {
return ((FileRegion) msg).retain();
}
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
}
private static int contentLength(Object msg) {
if (msg instanceof HttpContent) {
return ((HttpContent) msg).content().readableBytes();
}
if (msg instanceof ByteBuf) {
return ((ByteBuf) msg).readableBytes();
}
if (msg instanceof FileRegion) {
return (int) ((FileRegion) msg).count();
}
throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
}
private static void encodeHeaders(ByteBuf buf, HttpHeaders headers) {
for (Map.Entry<String, String> h: headers) {
encodeHeader(buf, h.getKey(), h.getValue());