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.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 <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
*/
@ -126,6 +128,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
*/
private Attribute currentAttribute;
private boolean destroyed;
/**
*
* @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
*
* @return True if this request is a Multipart request
*/
public boolean isMultipart() {
checkDestroyed();
return isMultipart;
}
@ -293,6 +304,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* Need more chunks
*/
public List<InterfaceHttpData> getBodyHttpDatas() throws NotEnoughDataDecoderException {
checkDestroyed();
if (!isLastChunk) {
throw new NotEnoughDataDecoderException();
}
@ -311,6 +324,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* need more chunks
*/
public List<InterfaceHttpData> getBodyHttpDatas(String name) throws NotEnoughDataDecoderException {
checkDestroyed();
if (!isLastChunk) {
throw new NotEnoughDataDecoderException();
}
@ -330,6 +345,8 @@ public class HttpPostRequestDecoder implements Iterator<InterfaceHttpData> {
* 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<InterfaceHttpData> {
* 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<InterfaceHttpData> {
* 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<InterfaceHttpData> {
* 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<InterfaceHttpData> {
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<InterfaceHttpData> {
* 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<InterfaceHttpData> {
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)

View File

@ -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<InterfaceHttpData> {
public interface InterfaceHttpData extends Comparable<InterfaceHttpData>, ReferenceCounted {
enum HttpDataType {
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.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<ByteBuf> value = new ArrayList<ByteBuf>();
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
}
}

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.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<HttpObj
readHttpDataChunkByChunk();
// example of reading only if at the end
if (chunk instanceof LastHttpContent) {
readHttpDataAllReceive(ctx.channel());
writeResponse(ctx.channel());
readingChunks = false;
@ -205,32 +203,11 @@ public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObj
private void reset() {
request = null;
// clean previous FileUpload if Any
decoder.cleanFiles();
// destroy the decoder to release all resources
decoder.destroy();
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
*/