[#1293] Fix handling of chunked requests in HttpPostRequestEncoder and the multipart examples
This commit is contained in:
parent
a4a92ee14a
commit
a170f05b4b
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user