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.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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue
Block a user