Add a HttpPostRequestDecoder.destory() method which should be used to destroy the decoder and free up resources

* Also make the contract more clear about who needs to call release()
This commit is contained in:
Norman Maurer 2013-07-05 08:31:07 +02:00
parent b26e61d689
commit b63f3488b7
4 changed files with 63 additions and 34 deletions

View File

@ -33,7 +33,6 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@ -42,8 +41,11 @@ import static io.netty.buffer.Unpooled.*;
/** /**
* This decoder will decode Body and can handle POST BODY. * This decoder will decode Body and can handle POST BODY.
*
* You <strong>MUST</strong> call {@link #destroy()} after completion to release all resources.
*
*/ */
public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> { public class HttpPostRequestDecoder {
/** /**
* Factory used to create InterfaceHttpData * Factory used to create InterfaceHttpData
*/ */
@ -126,6 +128,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
*/ */
private Attribute currentAttribute; private Attribute currentAttribute;
private boolean destroyed;
/** /**
* *
* @param request * @param request
@ -273,12 +277,19 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
} }
} }
private void checkDestroyed() {
if (destroyed) {
throw new IllegalStateException(HttpPostRequestDecoder.class.getSimpleName() + " was destroyed already");
}
}
/** /**
* True if this request is a Multipart request * True if this request is a Multipart request
* *
* @return True if this request is a Multipart request * @return True if this request is a Multipart request
*/ */
public boolean isMultipart() { public boolean isMultipart() {
checkDestroyed();
return isMultipart; return isMultipart;
} }
@ -293,6 +304,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* Need more chunks * Need more chunks
*/ */
public List<InterfaceHttpData> getBodyHttpDatas() throws NotEnoughDataDecoderException { public List<InterfaceHttpData> getBodyHttpDatas() throws NotEnoughDataDecoderException {
checkDestroyed();
if (!isLastChunk) { if (!isLastChunk) {
throw new NotEnoughDataDecoderException(); throw new NotEnoughDataDecoderException();
} }
@ -311,6 +324,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* need more chunks * need more chunks
*/ */
public List<InterfaceHttpData> getBodyHttpDatas(String name) throws NotEnoughDataDecoderException { public List<InterfaceHttpData> getBodyHttpDatas(String name) throws NotEnoughDataDecoderException {
checkDestroyed();
if (!isLastChunk) { if (!isLastChunk) {
throw new NotEnoughDataDecoderException(); throw new NotEnoughDataDecoderException();
} }
@ -330,6 +345,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* need more chunks * need more chunks
*/ */
public InterfaceHttpData getBodyHttpData(String name) throws NotEnoughDataDecoderException { public InterfaceHttpData getBodyHttpData(String name) throws NotEnoughDataDecoderException {
checkDestroyed();
if (!isLastChunk) { if (!isLastChunk) {
throw new NotEnoughDataDecoderException(); throw new NotEnoughDataDecoderException();
} }
@ -350,6 +367,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* errors * errors
*/ */
public HttpPostRequestDecoder offer(HttpContent content) throws ErrorDataDecoderException { public HttpPostRequestDecoder offer(HttpContent content) throws ErrorDataDecoderException {
checkDestroyed();
// Maybe we should better not copy here for performance reasons but this will need // 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 // more care by teh caller to release the content in a correct manner later
// So maybe something to optimize on a later stage // So maybe something to optimize on a later stage
@ -380,6 +399,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* No more data will be available * No more data will be available
*/ */
public boolean hasNext() throws EndOfDataDecoderException { public boolean hasNext() throws EndOfDataDecoderException {
checkDestroyed();
if (currentStatus == MultiPartStatus.EPILOGUE) { if (currentStatus == MultiPartStatus.EPILOGUE) {
// OK except if end of list // OK except if end of list
if (bodyListHttpDataRank >= bodyListHttpData.size()) { if (bodyListHttpDataRank >= bodyListHttpData.size()) {
@ -394,11 +415,16 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* is called, there is no more available InterfaceHttpData. A subsequent * is called, there is no more available InterfaceHttpData. A subsequent
* call to offer(httpChunk) could enable more data. * 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 * @return the next available InterfaceHttpData or null if none
* @throws EndOfDataDecoderException * @throws EndOfDataDecoderException
* No more data will be available * No more data will be available
*/ */
public InterfaceHttpData next() throws EndOfDataDecoderException { public InterfaceHttpData next() throws EndOfDataDecoderException {
checkDestroyed();
if (hasNext()) { if (hasNext()) {
return bodyListHttpData.get(bodyListHttpDataRank++); return bodyListHttpData.get(bodyListHttpDataRank++);
} }
@ -1172,10 +1198,31 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
return null; 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. * Clean all HttpDatas (on Disk) for the current request.
*/ */
public void cleanFiles() { public void cleanFiles() {
checkDestroyed();
factory.cleanRequestHttpDatas(request); factory.cleanRequestHttpDatas(request);
} }
@ -1183,6 +1230,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* Remove the given FileUpload from the list of FileUploads to clean * Remove the given FileUpload from the list of FileUploads to clean
*/ */
public void removeHttpDataFromClean(InterfaceHttpData data) { public void removeHttpDataFromClean(InterfaceHttpData data) {
checkDestroyed();
factory.removeHttpDataFromClean(request, data); factory.removeHttpDataFromClean(request, data);
} }
@ -2043,11 +2092,6 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
return array; return array;
} }
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/** /**
* Exception when try reading data from request in chunked format, and not * Exception when try reading data from request in chunked format, and not
* enough data are available (need more chunks) * enough data are available (need more chunks)

View File

@ -15,10 +15,12 @@
*/ */
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.util.ReferenceCounted;
/** /**
* Interface for all Objects that could be encoded/decoded using HttpPostRequestEncoder/Decoder * Interface for all Objects that could be encoded/decoded using HttpPostRequestEncoder/Decoder
*/ */
public interface InterfaceHttpData extends Comparable<InterfaceHttpData> { public interface InterfaceHttpData extends Comparable<InterfaceHttpData>, ReferenceCounted {
enum HttpDataType { enum HttpDataType {
Attribute, FileUpload, InternalAttribute Attribute, FileUpload, InternalAttribute
} }

View File

@ -17,6 +17,7 @@ package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.util.AbstractReferenceCounted;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; 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 * This Attribute is only for Encoder use to insert special command between object if needed
* (like Multipart Mixed mode) * (like Multipart Mixed mode)
*/ */
final class InternalAttribute implements InterfaceHttpData { final class InternalAttribute extends AbstractReferenceCounted implements InterfaceHttpData {
private final List<ByteBuf> value = new ArrayList<ByteBuf>(); private final List<ByteBuf> value = new ArrayList<ByteBuf>();
private final Charset charset; private final Charset charset;
private int size; private int size;
@ -117,4 +118,9 @@ final class InternalAttribute implements InterfaceHttpData {
public String getName() { public String getName() {
return "InternalAttribute"; return "InternalAttribute";
} }
@Override
protected void deallocate() {
// Do nothing
}
} }

View File

@ -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.EndOfDataDecoderException;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; 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.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;
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -192,7 +191,6 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
readHttpDataChunkByChunk(); readHttpDataChunkByChunk();
// example of reading only if at the end // example of reading only if at the end
if (chunk instanceof LastHttpContent) { if (chunk instanceof LastHttpContent) {
readHttpDataAllReceive(ctx.channel());
writeResponse(ctx.channel()); writeResponse(ctx.channel());
readingChunks = false; readingChunks = false;
@ -205,32 +203,11 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
private void reset() { private void reset() {
request = null; request = null;
// clean previous FileUpload if Any // destroy the decoder to release all resources
decoder.cleanFiles(); decoder.destroy();
decoder = null; decoder = null;
} }
/**
* Example of reading all InterfaceHttpData from finished transfer
*/
private void readHttpDataAllReceive(Channel channel) {
List<InterfaceHttpData> 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 * Example of reading request by chunk and getting values from chunk to chunk
*/ */