diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java index fb65bdfffc..af53bacf7f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java @@ -33,7 +33,6 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -42,8 +41,11 @@ import static io.netty.buffer.Unpooled.*; /** * This decoder will decode Body and can handle POST BODY. + * + * You MUST call {@link #destroy()} after completion to release all resources. + * */ -public class HttpPostRequestDecoder implements Iterator { +public class HttpPostRequestDecoder { /** * Factory used to create InterfaceHttpData */ @@ -126,6 +128,8 @@ public class HttpPostRequestDecoder implements Iterator { */ private Attribute currentAttribute; + private boolean destroyed; + /** * * @param request @@ -273,12 +277,19 @@ public class HttpPostRequestDecoder implements Iterator { } } + private void checkDestroyed() { + if (destroyed) { + throw new IllegalStateException(HttpPostRequestDecoder.class.getSimpleName() + " was destroyed already"); + } + } + /** * True if this request is a Multipart request * * @return True if this request is a Multipart request */ public boolean isMultipart() { + checkDestroyed(); return isMultipart; } @@ -293,6 +304,8 @@ public class HttpPostRequestDecoder implements Iterator { * Need more chunks */ public List getBodyHttpDatas() throws NotEnoughDataDecoderException { + checkDestroyed(); + if (!isLastChunk) { throw new NotEnoughDataDecoderException(); } @@ -311,6 +324,8 @@ public class HttpPostRequestDecoder implements Iterator { * need more chunks */ public List getBodyHttpDatas(String name) throws NotEnoughDataDecoderException { + checkDestroyed(); + if (!isLastChunk) { throw new NotEnoughDataDecoderException(); } @@ -330,6 +345,8 @@ public class HttpPostRequestDecoder implements Iterator { * need more chunks */ public InterfaceHttpData getBodyHttpData(String name) throws NotEnoughDataDecoderException { + checkDestroyed(); + if (!isLastChunk) { throw new NotEnoughDataDecoderException(); } @@ -350,6 +367,8 @@ public class HttpPostRequestDecoder implements Iterator { * errors */ public HttpPostRequestDecoder offer(HttpContent content) throws ErrorDataDecoderException { + checkDestroyed(); + // Maybe we should better not copy here for performance reasons but this will need // more care by teh caller to release the content in a correct manner later // So maybe something to optimize on a later stage @@ -380,6 +399,8 @@ public class HttpPostRequestDecoder implements Iterator { * No more data will be available */ public boolean hasNext() throws EndOfDataDecoderException { + checkDestroyed(); + if (currentStatus == MultiPartStatus.EPILOGUE) { // OK except if end of list if (bodyListHttpDataRank >= bodyListHttpData.size()) { @@ -394,11 +415,16 @@ public class HttpPostRequestDecoder implements Iterator { * is called, there is no more available InterfaceHttpData. A subsequent * call to offer(httpChunk) could enable more data. * + * Be sure to call {@link InterfaceHttpData#release()} after you are done + * with processing to make sure to not leak any resources + * * @return the next available InterfaceHttpData or null if none * @throws EndOfDataDecoderException * No more data will be available */ public InterfaceHttpData next() throws EndOfDataDecoderException { + checkDestroyed(); + if (hasNext()) { return bodyListHttpData.get(bodyListHttpDataRank++); } @@ -1172,10 +1198,31 @@ public class HttpPostRequestDecoder implements Iterator { return null; } + /** + * Destroy the {@link HttpPostRequestDecoder} and release all it resources. After this method + * was called it is not possible to operate on it anymore. + */ + public void destroy() { + checkDestroyed(); + + cleanFiles(); + if (undecodedChunk != null && undecodedChunk.refCnt() > 0) { + undecodedChunk.release(); + undecodedChunk = null; + } + + // release all data which was not yet pulled + for (int i = bodyListHttpDataRank; i < bodyListHttpData.size(); i++) { + bodyListHttpData.get(i).release(); + } + } + /** * Clean all HttpDatas (on Disk) for the current request. */ public void cleanFiles() { + checkDestroyed(); + factory.cleanRequestHttpDatas(request); } @@ -1183,6 +1230,8 @@ public class HttpPostRequestDecoder implements Iterator { * Remove the given FileUpload from the list of FileUploads to clean */ public void removeHttpDataFromClean(InterfaceHttpData data) { + checkDestroyed(); + factory.removeHttpDataFromClean(request, data); } @@ -2043,11 +2092,6 @@ public class HttpPostRequestDecoder implements Iterator { return array; } - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - /** * Exception when try reading data from request in chunked format, and not * enough data are available (need more chunks) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java index 12f1ab45ff..b0b9daf3ed 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java @@ -15,10 +15,12 @@ */ package io.netty.handler.codec.http.multipart; +import io.netty.util.ReferenceCounted; + /** * Interface for all Objects that could be encoded/decoded using HttpPostRequestEncoder/Decoder */ -public interface InterfaceHttpData extends Comparable { +public interface InterfaceHttpData extends Comparable, ReferenceCounted { enum HttpDataType { Attribute, FileUpload, InternalAttribute } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java index 2a83f1985f..2637ff57f4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java @@ -17,6 +17,7 @@ package io.netty.handler.codec.http.multipart; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.util.AbstractReferenceCounted; import java.nio.charset.Charset; import java.util.ArrayList; @@ -26,7 +27,7 @@ import java.util.List; * This Attribute is only for Encoder use to insert special command between object if needed * (like Multipart Mixed mode) */ -final class InternalAttribute implements InterfaceHttpData { +final class InternalAttribute extends AbstractReferenceCounted implements InterfaceHttpData { private final List value = new ArrayList(); private final Charset charset; private int size; @@ -117,4 +118,9 @@ final class InternalAttribute implements InterfaceHttpData { public String getName() { return "InternalAttribute"; } + + @Override + protected void deallocate() { + // Do nothing + } } diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java index eef02422bd..8d517edbc7 100644 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java +++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java @@ -44,7 +44,6 @@ import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.IncompatibleDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException; import io.netty.handler.codec.http.multipart.InterfaceHttpData; import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; import io.netty.util.CharsetUtil; @@ -192,7 +191,6 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler datas; - try { - datas = decoder.getBodyHttpDatas(); - } catch (NotEnoughDataDecoderException e1) { - // Should not be! - e1.printStackTrace(); - responseContent.append(e1.getMessage()); - writeResponse(channel); - channel.close(); - return; - } - for (InterfaceHttpData data : datas) { - writeHttpData(data); - } - responseContent.append("\r\n\r\nEND OF CONTENT AT FINAL END\r\n"); - } - /** * Example of reading request by chunk and getting values from chunk to chunk */