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:
parent
b26e61d689
commit
b63f3488b7
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user