io.netty.handler.codec.http.multipart.DefaultHttpDataFactory Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
The newest version!
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http.multipart;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.codec.http.HttpRequest;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Default factory giving {@link Attribute} and {@link FileUpload} according to constructor.
*
* According to the constructor, {@link Attribute} and {@link FileUpload} can be:
*
* - MemoryAttribute, DiskAttribute or MixedAttribute
* - MemoryFileUpload, DiskFileUpload or MixedFileUpload
*
* A good example of releasing HttpData once all work is done is as follow:
* {@code
* for (InterfaceHttpData httpData: decoder.getBodyHttpDatas()) {
* httpData.release();
* factory.removeHttpDataFromClean(request, httpData);
* }
* factory.cleanAllHttpData();
* decoder.destroy();
* }
*/
public class DefaultHttpDataFactory implements HttpDataFactory {
/**
* Proposed default MINSIZE as 16 KB.
*/
public static final long MINSIZE = 0x4000;
/**
* Proposed default MAXSIZE = -1 as UNLIMITED
*/
public static final long MAXSIZE = -1;
private final boolean useDisk;
private final boolean checkSize;
private long minSize;
private long maxSize = MAXSIZE;
private Charset charset = HttpConstants.DEFAULT_CHARSET;
private String baseDir;
private boolean deleteOnExit; // false is a good default cause true leaks
/**
* Keep all {@link HttpData}s until cleaning methods are called.
* We need to use {@link IdentityHashMap} because different requests may be equal.
* See {@link DefaultHttpRequest#hashCode} and {@link DefaultHttpRequest#equals}.
* Similarly, when removing data items, we need to check their identities because
* different data items may be equal.
*/
private final Map> requestFileDeleteMap =
Collections.synchronizedMap(new IdentityHashMap>());
/**
* HttpData will be in memory if less than default size (16KB).
* The type will be Mixed.
*/
public DefaultHttpDataFactory() {
useDisk = false;
checkSize = true;
minSize = MINSIZE;
}
public DefaultHttpDataFactory(Charset charset) {
this();
this.charset = charset;
}
/**
* HttpData will be always on Disk if useDisk is True, else always in Memory if False
*/
public DefaultHttpDataFactory(boolean useDisk) {
this.useDisk = useDisk;
checkSize = false;
}
public DefaultHttpDataFactory(boolean useDisk, Charset charset) {
this(useDisk);
this.charset = charset;
}
/**
* HttpData will be on Disk if the size of the file is greater than minSize, else it
* will be in memory. The type will be Mixed.
*/
public DefaultHttpDataFactory(long minSize) {
useDisk = false;
checkSize = true;
this.minSize = minSize;
}
public DefaultHttpDataFactory(long minSize, Charset charset) {
this(minSize);
this.charset = charset;
}
/**
* Override global {@link DiskAttribute#baseDirectory} and {@link DiskFileUpload#baseDirectory} values.
*
* @param baseDir directory path where to store disk attributes and file uploads.
*/
public void setBaseDir(String baseDir) {
this.baseDir = baseDir;
}
/**
* Override global {@link DiskAttribute#deleteOnExitTemporaryFile} and
* {@link DiskFileUpload#deleteOnExitTemporaryFile} values.
*
* @param deleteOnExit true if temporary files should be deleted with the JVM, false otherwise.
*/
public void setDeleteOnExit(boolean deleteOnExit) {
this.deleteOnExit = deleteOnExit;
}
@Override
public void setMaxLimit(long maxSize) {
this.maxSize = maxSize;
}
/**
* @return the associated list of {@link HttpData} for the request
*/
private List getList(HttpRequest request) {
List list = requestFileDeleteMap.get(request);
if (list == null) {
list = new ArrayList();
requestFileDeleteMap.put(request, list);
}
return list;
}
@Override
public Attribute createAttribute(HttpRequest request, String name) {
if (useDisk) {
Attribute attribute = new DiskAttribute(name, charset, baseDir, deleteOnExit);
attribute.setMaxSize(maxSize);
List list = getList(request);
list.add(attribute);
return attribute;
}
if (checkSize) {
Attribute attribute = new MixedAttribute(name, minSize, charset, baseDir, deleteOnExit);
attribute.setMaxSize(maxSize);
List list = getList(request);
list.add(attribute);
return attribute;
}
MemoryAttribute attribute = new MemoryAttribute(name);
attribute.setMaxSize(maxSize);
return attribute;
}
@Override
public Attribute createAttribute(HttpRequest request, String name, long definedSize) {
if (useDisk) {
Attribute attribute = new DiskAttribute(name, definedSize, charset, baseDir, deleteOnExit);
attribute.setMaxSize(maxSize);
List list = getList(request);
list.add(attribute);
return attribute;
}
if (checkSize) {
Attribute attribute = new MixedAttribute(name, definedSize, minSize, charset, baseDir, deleteOnExit);
attribute.setMaxSize(maxSize);
List list = getList(request);
list.add(attribute);
return attribute;
}
MemoryAttribute attribute = new MemoryAttribute(name, definedSize);
attribute.setMaxSize(maxSize);
return attribute;
}
/**
* Utility method
*/
private static void checkHttpDataSize(HttpData data) {
try {
data.checkSize(data.length());
} catch (IOException ignored) {
throw new IllegalArgumentException("Attribute bigger than maxSize allowed");
}
}
@Override
public Attribute createAttribute(HttpRequest request, String name, String value) {
if (useDisk) {
Attribute attribute;
try {
attribute = new DiskAttribute(name, value, charset, baseDir, deleteOnExit);
attribute.setMaxSize(maxSize);
} catch (IOException e) {
// revert to Mixed mode
attribute = new MixedAttribute(name, value, minSize, charset, baseDir, deleteOnExit);
attribute.setMaxSize(maxSize);
}
checkHttpDataSize(attribute);
List list = getList(request);
list.add(attribute);
return attribute;
}
if (checkSize) {
Attribute attribute = new MixedAttribute(name, value, minSize, charset, baseDir, deleteOnExit);
attribute.setMaxSize(maxSize);
checkHttpDataSize(attribute);
List list = getList(request);
list.add(attribute);
return attribute;
}
try {
MemoryAttribute attribute = new MemoryAttribute(name, value, charset);
attribute.setMaxSize(maxSize);
checkHttpDataSize(attribute);
return attribute;
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public FileUpload createFileUpload(HttpRequest request, String name, String filename,
String contentType, String contentTransferEncoding, Charset charset,
long size) {
if (useDisk) {
FileUpload fileUpload = new DiskFileUpload(name, filename, contentType,
contentTransferEncoding, charset, size, baseDir, deleteOnExit);
fileUpload.setMaxSize(maxSize);
checkHttpDataSize(fileUpload);
List list = getList(request);
list.add(fileUpload);
return fileUpload;
}
if (checkSize) {
FileUpload fileUpload = new MixedFileUpload(name, filename, contentType,
contentTransferEncoding, charset, size, minSize, baseDir, deleteOnExit);
fileUpload.setMaxSize(maxSize);
checkHttpDataSize(fileUpload);
List list = getList(request);
list.add(fileUpload);
return fileUpload;
}
MemoryFileUpload fileUpload = new MemoryFileUpload(name, filename, contentType,
contentTransferEncoding, charset, size);
fileUpload.setMaxSize(maxSize);
checkHttpDataSize(fileUpload);
return fileUpload;
}
@Override
public void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data) {
if (!(data instanceof HttpData)) {
return;
}
// Do not use getList because it adds empty list to requestFileDeleteMap
// if request is not found
List list = requestFileDeleteMap.get(request);
if (list == null) {
return;
}
// Can't simply call list.remove(data), because different data items may be equal.
// Need to check identity.
Iterator i = list.iterator();
while (i.hasNext()) {
HttpData n = i.next();
if (n == data) {
i.remove();
// Remove empty list to avoid memory leak
if (list.isEmpty()) {
requestFileDeleteMap.remove(request);
}
return;
}
}
}
@Override
public void cleanRequestHttpData(HttpRequest request) {
List list = requestFileDeleteMap.remove(request);
if (list != null) {
for (HttpData data : list) {
data.release();
}
}
}
@Override
public void cleanAllHttpData() {
Iterator>> i = requestFileDeleteMap.entrySet().iterator();
while (i.hasNext()) {
Entry> e = i.next();
// Calling i.remove() here will cause "java.lang.IllegalStateException: Entry was removed"
// at e.getValue() below
List list = e.getValue();
for (HttpData data : list) {
data.release();
}
i.remove();
}
}
@Override
public void cleanRequestHttpDatas(HttpRequest request) {
cleanRequestHttpData(request);
}
@Override
public void cleanAllHttpDatas() {
cleanAllHttpData();
}
}