[#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.ByteBuf;
|
||||||
import io.netty.buffer.MessageBuf;
|
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.DefaultHttpContent;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpConstants;
|
import io.netty.handler.codec.http.HttpConstants;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
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 io.netty.handler.stream.ChunkedMessageInput;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -77,7 +82,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
/**
|
/**
|
||||||
* Request to encode
|
* Request to encode
|
||||||
*/
|
*/
|
||||||
private final FullHttpRequest request;
|
private final HttpRequest request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default charset to use
|
* Default charset to use
|
||||||
@ -130,7 +135,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the request is not a POST
|
* 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,
|
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
|
||||||
HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
|
HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
|
||||||
}
|
}
|
||||||
@ -148,7 +153,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the request is not a POST
|
* 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 {
|
throws ErrorDataEncoderException {
|
||||||
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
|
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
|
* if the request is not a POST
|
||||||
*/
|
*/
|
||||||
public HttpPostRequestEncoder(
|
public HttpPostRequestEncoder(
|
||||||
HttpDataFactory factory, FullHttpRequest request, boolean multipart, Charset charset,
|
HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset,
|
||||||
EncoderMode encoderMode)
|
EncoderMode encoderMode)
|
||||||
throws ErrorDataEncoderException {
|
throws ErrorDataEncoderException {
|
||||||
if (factory == null) {
|
if (factory == null) {
|
||||||
@ -635,7 +640,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
* @throws ErrorDataEncoderException
|
* @throws ErrorDataEncoderException
|
||||||
* if the encoding is in error or if the finalize were already done
|
* 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
|
// Finalize the multipartHttpDatas
|
||||||
if (!headerFinalized) {
|
if (!headerFinalized) {
|
||||||
if (isMultipart) {
|
if (isMultipart) {
|
||||||
@ -701,13 +706,22 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
HttpHeaders.setTransferEncodingChunked(request);
|
HttpHeaders.setTransferEncodingChunked(request);
|
||||||
request.content().clear();
|
|
||||||
|
// wrap to hide the possible content
|
||||||
|
return new WrappedHttpRequest(request);
|
||||||
} else {
|
} else {
|
||||||
// get the only one body and set it to the request
|
// get the only one body and set it to the request
|
||||||
HttpContent chunk = nextChunk();
|
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);
|
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.ClientCookieEncoder;
|
||||||
import io.netty.handler.codec.http.DefaultCookie;
|
import io.netty.handler.codec.http.DefaultCookie;
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
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.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
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.HttpVersion;
|
||||||
import io.netty.handler.codec.http.QueryStringEncoder;
|
import io.netty.handler.codec.http.QueryStringEncoder;
|
||||||
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
|
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
|
||||||
@ -220,8 +222,8 @@ public class HttpUploadClient {
|
|||||||
Channel channel = bootstrap.connect(host, port).sync().channel();
|
Channel channel = bootstrap.connect(host, port).sync().channel();
|
||||||
|
|
||||||
// Prepare the HTTP request.
|
// Prepare the HTTP request.
|
||||||
FullHttpRequest request =
|
HttpRequest request =
|
||||||
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
|
new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
|
||||||
|
|
||||||
// Use the PostBody encoder
|
// Use the PostBody encoder
|
||||||
HttpPostRequestEncoder bodyRequestEncoder = null;
|
HttpPostRequestEncoder bodyRequestEncoder = null;
|
||||||
@ -304,7 +306,7 @@ public class HttpUploadClient {
|
|||||||
Channel channel = bootstrap.connect(host, port).sync().channel();
|
Channel channel = bootstrap.connect(host, port).sync().channel();
|
||||||
|
|
||||||
// Prepare the HTTP request.
|
// Prepare the HTTP request.
|
||||||
FullHttpRequest request =
|
HttpRequest request =
|
||||||
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
|
new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
|
||||||
|
|
||||||
// Use the PostBody encoder
|
// Use the PostBody encoder
|
||||||
@ -338,7 +340,7 @@ public class HttpUploadClient {
|
|||||||
|
|
||||||
// finalize request
|
// finalize request
|
||||||
try {
|
try {
|
||||||
bodyRequestEncoder.finalizeRequest();
|
request = bodyRequestEncoder.finalizeRequest();
|
||||||
} catch (ErrorDataEncoderException e) {
|
} catch (ErrorDataEncoderException e) {
|
||||||
// if an encoding error occurs
|
// if an encoding error occurs
|
||||||
e.printStackTrace();
|
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.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
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.QueryStringDecoder;
|
||||||
import io.netty.handler.codec.http.ServerCookieEncoder;
|
import io.netty.handler.codec.http.ServerCookieEncoder;
|
||||||
import io.netty.handler.codec.http.multipart.Attribute;
|
import io.netty.handler.codec.http.multipart.Attribute;
|
||||||
@ -96,12 +97,6 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
|||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||||
if (msg instanceof HttpRequest) {
|
if (msg instanceof HttpRequest) {
|
||||||
// clean previous FileUpload if Any
|
|
||||||
if (decoder != null) {
|
|
||||||
decoder.cleanFiles();
|
|
||||||
decoder = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequest request = this.request = (HttpRequest) msg;
|
HttpRequest request = this.request = (HttpRequest) msg;
|
||||||
URI uri = new URI(request.getUri());
|
URI uri = new URI(request.getUri());
|
||||||
if (!uri.getPath().startsWith("/form")) {
|
if (!uri.getPath().startsWith("/form")) {
|
||||||
@ -172,38 +167,48 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter
|
|||||||
// Chunk version
|
// Chunk version
|
||||||
responseContent.append("Chunks: ");
|
responseContent.append("Chunks: ");
|
||||||
readingChunks = true;
|
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
|
// check if the decoder was constructed before
|
||||||
HttpContent chunk = (HttpContent) msg;
|
// if not it handles the form get
|
||||||
try {
|
if (decoder != null) {
|
||||||
decoder.offer(chunk);
|
if (msg instanceof HttpContent) {
|
||||||
} catch (ErrorDataDecoderException e1) {
|
// New chunk is received
|
||||||
e1.printStackTrace();
|
HttpContent chunk = (HttpContent) msg;
|
||||||
responseContent.append(e1.getMessage());
|
try {
|
||||||
writeResponse(ctx.channel());
|
decoder.offer(chunk);
|
||||||
ctx.channel().close();
|
} catch (ErrorDataDecoderException e1) {
|
||||||
return;
|
e1.printStackTrace();
|
||||||
}
|
responseContent.append(e1.getMessage());
|
||||||
responseContent.append('o');
|
writeResponse(ctx.channel());
|
||||||
// example of reading chunk by chunk (minimize memory usage due to
|
ctx.channel().close();
|
||||||
// Factory)
|
return;
|
||||||
readHttpDataChunkByChunk();
|
}
|
||||||
// example of reading only if at the end
|
responseContent.append('o');
|
||||||
if (chunk instanceof HttpContent) {
|
// example of reading chunk by chunk (minimize memory usage due to
|
||||||
readHttpDataAllReceive(ctx.channel());
|
// Factory)
|
||||||
writeResponse(ctx.channel());
|
readHttpDataChunkByChunk();
|
||||||
readingChunks = false;
|
// 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
|
* Example of reading all InterfaceHttpData from finished transfer
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user