[#1293] Fix handling of chunked requests in HttpPostRequestEncoder and the multipart examples

This commit is contained in:
Norman Maurer 2013-05-06 15:00:18 +02:00
parent a4a92ee14a
commit a170f05b4b
3 changed files with 196 additions and 44 deletions

View File

@ -17,12 +17,17 @@ package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.stream.ChunkedMessageInput;
import java.io.File;
@ -77,7 +82,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
/**
* Request to encode
*/
private final FullHttpRequest request;
private final HttpRequest request;
/**
* Default charset to use
@ -130,7 +135,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* @throws ErrorDataEncoderException
* if the request is not a POST
*/
public HttpPostRequestEncoder(FullHttpRequest request, boolean multipart) throws ErrorDataEncoderException {
public HttpPostRequestEncoder(HttpRequest request, boolean multipart) throws ErrorDataEncoderException {
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
}
@ -148,7 +153,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* @throws ErrorDataEncoderException
* if the request is not a POST
*/
public HttpPostRequestEncoder(HttpDataFactory factory, FullHttpRequest request, boolean multipart)
public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart)
throws ErrorDataEncoderException {
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
}
@ -171,7 +176,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* if the request is not a POST
*/
public HttpPostRequestEncoder(
HttpDataFactory factory, FullHttpRequest request, boolean multipart, Charset charset,
HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset,
EncoderMode encoderMode)
throws ErrorDataEncoderException {
if (factory == null) {
@ -635,7 +640,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* @throws ErrorDataEncoderException
* if the encoding is in error or if the finalize were already done
*/
public FullHttpRequest finalizeRequest() throws ErrorDataEncoderException {
public HttpRequest finalizeRequest() throws ErrorDataEncoderException {
// Finalize the multipartHttpDatas
if (!headerFinalized) {
if (isMultipart) {
@ -701,13 +706,22 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
}
}
HttpHeaders.setTransferEncodingChunked(request);
request.content().clear();
// wrap to hide the possible content
return new WrappedHttpRequest(request);
} else {
// get the only one body and set it to the request
HttpContent chunk = nextChunk();
request.content().clear().writeBytes(chunk.content());
if (request instanceof FullHttpRequest) {
FullHttpRequest fullRequest = (FullHttpRequest) request;
if (!fullRequest.content().equals(chunk.content())) {
fullRequest.content().clear().writeBytes(chunk.content());
}
return fullRequest;
} else {
return new WrappedFullHttpRequest(request, chunk);
}
}
return request;
}
/**
@ -1052,4 +1066,135 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
super(msg, cause);
}
}
private static class WrappedHttpRequest implements HttpRequest {
private final HttpRequest request;
WrappedHttpRequest(HttpRequest request) {
this.request = request;
}
@Override
public HttpRequest setProtocolVersion(HttpVersion version) {
request.setProtocolVersion(version);
return this;
}
@Override
public HttpRequest setMethod(HttpMethod method) {
request.setMethod(method);
return this;
}
@Override
public HttpRequest setUri(String uri) {
request.setUri(uri);
return this;
}
@Override
public HttpMethod getMethod() {
return request.getMethod();
}
@Override
public String getUri() {
return request.getUri();
}
@Override
public HttpVersion getProtocolVersion() {
return request.getProtocolVersion();
}
@Override
public HttpHeaders headers() {
return request.headers();
}
@Override
public DecoderResult getDecoderResult() {
return request.getDecoderResult();
}
@Override
public void setDecoderResult(DecoderResult result) {
request.setDecoderResult(result);
}
}
private static final class WrappedFullHttpRequest extends WrappedHttpRequest implements FullHttpRequest {
private final HttpContent content;
private WrappedFullHttpRequest(HttpRequest request, HttpContent content) {
super(request);
this.content = content;
}
@Override
public FullHttpRequest setProtocolVersion(HttpVersion version) {
super.setProtocolVersion(version);
return this;
}
@Override
public FullHttpRequest setMethod(HttpMethod method) {
super.setMethod(method);
return this;
}
@Override
public FullHttpRequest setUri(String uri) {
super.setUri(uri);
return this;
}
@Override
public FullHttpRequest copy() {
DefaultFullHttpRequest copy = new DefaultFullHttpRequest(
getProtocolVersion(), getMethod(), getUri(), content().copy());
copy.headers().set(headers());
copy.trailingHeaders().set(trailingHeaders());
return copy;
}
@Override
public FullHttpRequest retain(int increment) {
content.retain(increment);
return this;
}
@Override
public FullHttpRequest retain() {
content.retain();
return this;
}
@Override
public ByteBuf content() {
return content.content();
}
@Override
public HttpHeaders trailingHeaders() {
if (content instanceof LastHttpContent) {
return ((LastHttpContent) content).trailingHeaders();
} else {
return HttpHeaders.EMPTY_HEADERS;
}
}
@Override
public int refCnt() {
return content.refCnt();
}
@Override
public boolean release() {
return content.release();
}
@Override
public boolean release(int decrement) {
return content.release(decrement);
}
}
}

View File

@ -23,9 +23,11 @@ import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.ClientCookieEncoder;
import io.netty.handler.codec.http.DefaultCookie;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringEncoder;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
@ -220,8 +222,8 @@ public class HttpUploadClient {
Channel channel = bootstrap.connect(host, port).sync().channel();
// Prepare the HTTP request.
FullHttpRequest request =
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
HttpRequest request =
new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
// Use the PostBody encoder
HttpPostRequestEncoder bodyRequestEncoder = null;
@ -304,7 +306,7 @@ public class HttpUploadClient {
Channel channel = bootstrap.connect(host, port).sync().channel();
// Prepare the HTTP request.
FullHttpRequest request =
HttpRequest request =
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
// Use the PostBody encoder
@ -338,7 +340,7 @@ public class HttpUploadClient {
// finalize request
try {
bodyRequestEncoder.finalizeRequest();
request = bodyRequestEncoder.finalizeRequest();
} catch (ErrorDataEncoderException e) {
// if an encoding error occurs
e.printStackTrace();

View File

@ -30,6 +30,7 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.ServerCookieEncoder;
import io.netty.handler.codec.http.multipart.Attribute;
@ -96,12 +97,6 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
// clean previous FileUpload if Any
if (decoder != null) {
decoder.cleanFiles();
decoder = null;
}
HttpRequest request = this.request = (HttpRequest) msg;
URI uri = new URI(request.getUri());
if (!uri.getPath().startsWith("/form")) {
@ -172,38 +167,48 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
// Chunk version
responseContent.append("Chunks: ");
readingChunks = true;
} else {
// Not chunk version
readHttpDataAllReceive(ctx.channel());
responseContent.append("\r\n\r\nEND OF NOT CHUNKED CONTENT\r\n");
writeResponse(ctx.channel());
}
}
if (msg instanceof HttpContent) {
// New chunk is received
HttpContent chunk = (HttpContent) msg;
try {
decoder.offer(chunk);
} catch (ErrorDataDecoderException e1) {
e1.printStackTrace();
responseContent.append(e1.getMessage());
writeResponse(ctx.channel());
ctx.channel().close();
return;
}
responseContent.append('o');
// example of reading chunk by chunk (minimize memory usage due to
// Factory)
readHttpDataChunkByChunk();
// example of reading only if at the end
if (chunk instanceof HttpContent) {
readHttpDataAllReceive(ctx.channel());
writeResponse(ctx.channel());
readingChunks = false;
// check if the decoder was constructed before
// if not it handles the form get
if (decoder != null) {
if (msg instanceof HttpContent) {
// New chunk is received
HttpContent chunk = (HttpContent) msg;
try {
decoder.offer(chunk);
} catch (ErrorDataDecoderException e1) {
e1.printStackTrace();
responseContent.append(e1.getMessage());
writeResponse(ctx.channel());
ctx.channel().close();
return;
}
responseContent.append('o');
// example of reading chunk by chunk (minimize memory usage due to
// Factory)
readHttpDataChunkByChunk();
// example of reading only if at the end
if (chunk instanceof LastHttpContent) {
readHttpDataAllReceive(ctx.channel());
writeResponse(ctx.channel());
readingChunks = false;
reset();
}
}
}
}
private void reset() {
request = null;
// clean previous FileUpload if Any
decoder.cleanFiles();
decoder = null;
}
/**
* Example of reading all InterfaceHttpData from finished transfer
*/