/*
* 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:
*
* http://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 Attribute
and FileUpload
according to constructor. According to the constructor, Attribute
and FileUpload
can be:
- MemoryAttribute, DiskAttribute or MixedAttribute
- MemoryFileUpload, DiskFileUpload or MixedFileUpload
/**
* Default factory giving {@link Attribute} and {@link FileUpload} according to constructor.
*
* <p>According to the constructor, {@link Attribute} and {@link FileUpload} can be:</p>
* <ul>
* <li>MemoryAttribute, DiskAttribute or MixedAttribute</li>
* <li>MemoryFileUpload, DiskFileUpload or MixedFileUpload</li>
* </ul>
*/
public class DefaultHttpDataFactory implements HttpDataFactory {
Proposed default MINSIZE as 16 KB.
/**
* Proposed default MINSIZE as 16 KB.
*/
public static final long MINSIZE = 0x4000;
Proposed default MAXSIZE = -1 as UNLIMITED
/**
* 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;
Keep all HttpData
s until cleaning methods are called. We need to use IdentityHashMap
because different requests may be equal. See DefaultHttpRequest.hashCode
and DefaultHttpRequest.equals
. Similarly, when removing data items, we need to check their identities because different data items may be equal. /**
* 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<HttpRequest, List<HttpData>> requestFileDeleteMap =
Collections.synchronizedMap(new IdentityHashMap<HttpRequest, List<HttpData>>());
HttpData will be in memory if less than default size (16KB).
The type will be Mixed.
/**
* 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
/**
* 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.
/**
* 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
public void setMaxLimit(long maxSize) {
this.maxSize = maxSize;
}
Returns: the associated list of HttpData
for the request
/**
* @return the associated list of {@link HttpData} for the request
*/
private List<HttpData> getList(HttpRequest request) {
List<HttpData> list = requestFileDeleteMap.get(request);
if (list == null) {
list = new ArrayList<HttpData>();
requestFileDeleteMap.put(request, list);
}
return list;
}
@Override
public Attribute createAttribute(HttpRequest request, String name) {
if (useDisk) {
Attribute attribute = new DiskAttribute(name, charset);
attribute.setMaxSize(maxSize);
List<HttpData> list = getList(request);
list.add(attribute);
return attribute;
}
if (checkSize) {
Attribute attribute = new MixedAttribute(name, minSize, charset);
attribute.setMaxSize(maxSize);
List<HttpData> 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);
attribute.setMaxSize(maxSize);
List<HttpData> list = getList(request);
list.add(attribute);
return attribute;
}
if (checkSize) {
Attribute attribute = new MixedAttribute(name, definedSize, minSize, charset);
attribute.setMaxSize(maxSize);
List<HttpData> list = getList(request);
list.add(attribute);
return attribute;
}
MemoryAttribute attribute = new MemoryAttribute(name, definedSize);
attribute.setMaxSize(maxSize);
return attribute;
}
Utility method
/**
* 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);
attribute.setMaxSize(maxSize);
} catch (IOException e) {
// revert to Mixed mode
attribute = new MixedAttribute(name, value, minSize, charset);
attribute.setMaxSize(maxSize);
}
checkHttpDataSize(attribute);
List<HttpData> list = getList(request);
list.add(attribute);
return attribute;
}
if (checkSize) {
Attribute attribute = new MixedAttribute(name, value, minSize, charset);
attribute.setMaxSize(maxSize);
checkHttpDataSize(attribute);
List<HttpData> 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);
fileUpload.setMaxSize(maxSize);
checkHttpDataSize(fileUpload);
List<HttpData> list = getList(request);
list.add(fileUpload);
return fileUpload;
}
if (checkSize) {
FileUpload fileUpload = new MixedFileUpload(name, filename, contentType,
contentTransferEncoding, charset, size, minSize);
fileUpload.setMaxSize(maxSize);
checkHttpDataSize(fileUpload);
List<HttpData> 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<HttpData> 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<HttpData> 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<HttpData> list = requestFileDeleteMap.remove(request);
if (list != null) {
for (HttpData data : list) {
data.release();
}
}
}
@Override
public void cleanAllHttpData() {
Iterator<Entry<HttpRequest, List<HttpData>>> i = requestFileDeleteMap.entrySet().iterator();
while (i.hasNext()) {
Entry<HttpRequest, List<HttpData>> e = i.next();
// Calling i.remove() here will cause "java.lang.IllegalStateException: Entry was removed"
// at e.getValue() below
List<HttpData> list = e.getValue();
for (HttpData data : list) {
data.release();
}
i.remove();
}
}
@Override
public void cleanRequestHttpDatas(HttpRequest request) {
cleanRequestHttpData(request);
}
@Override
public void cleanAllHttpDatas() {
cleanAllHttpData();
}
}