package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpConstants;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpDataFactory;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.stream.ChunkedInput;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.util.internal.ThreadLocalRandom;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.regex.Pattern;
import com.system.distribute.file.FileChunk;
import static io.netty.buffer.Unpooled.*;
/**
*
*
*
* @author zhuyuping
* @version 1.0
* @created 2014-7-28
* @function:覆盖掉netty的源文件,实现大文件分割后的 上传
*/
public class MyHttpPostRequestEncoder implements ChunkedInput<HttpContent>{
/**
* Different modes to use to encode form data.
*/
public enum EncoderMode {
/**
* Legacy mode which should work for most. It is known to not work with OAUTH. For OAUTH use
* {@link EncoderMode#RFC3986}. The W3C form recommentations this for submitting post form data.
*/
RFC1738,
/**
* Mode which is more new and is used for OAUTH
*/
RFC3986
}
private static final Map<Pattern, String> percentEncodings = new HashMap<Pattern, String>();
static {
percentEncodings.put(Pattern.compile("\\*"), "%2A");
percentEncodings.put(Pattern.compile("\\+"), "%20");
percentEncodings.put(Pattern.compile("%7E"), "~");
}
/**
* Factory used to create InterfaceHttpData
*/
private final HttpDataFactory factory;
/**
* Request to encode
*/
private final HttpRequest request;
/**
* Default charset to use
*/
private final Charset charset;
/**
* Chunked false by default
*/
private boolean isChunked;
/**
* InterfaceHttpData for Body (without encoding)
*/
private final List<InterfaceHttpData> bodyListDatas;
/**
* The final Multipart List of InterfaceHttpData including encoding
*/
final List<InterfaceHttpData> multipartHttpDatas;
/**
* Does this request is a Multipart request
*/
private final boolean isMultipart;
/**
* If multipart, this is the boundary for the flobal multipart
*/
String multipartDataBoundary;
/**
* If multipart, there could be internal multiparts (mixed) to the global multipart. Only one level is allowed.
*/
String multipartMixedBoundary;
/**
* To check if the header has been finalized
*/
private boolean headerFinalized;
private final EncoderMode encoderMode;
/**
*
* @param request
* the request to encode
* @param multipart
* True if the FORM is a ENCTYPE="multipart/form-data"
* @throws NullPointerException
* for request
* @throws ErrorDataEncoderException
* if the request is not a POST
*/
public MyHttpPostRequestEncoder(HttpRequest request, boolean multipart) throws ErrorDataEncoderException {
this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, multipart,
HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
}
/**
*
* @param factory
* the factory used to create InterfaceHttpData
* @param request
* the request to encode
* @param multipart
* True if the FORM is a ENCTYPE="multipart/form-data"
* @throws NullPointerException
* for request and factory
* @throws ErrorDataEncoderException
* if the request is not a POST
*/
public MyHttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart)
throws ErrorDataEncoderException {
this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET, EncoderMode.RFC1738);
}
/**
*
* @param factory
* the factory used to create InterfaceHttpData
* @param request
* the request to encode
* @param multipart
* True if the FORM is a ENCTYPE="multipart/form-data"
* @param charset
* the charset to use as default
* @param encoderMode
* the mode for the encoder to use. See {@link EncoderMode} for the details.
* @throws NullPointerException
* for request or charset or factory
* @throws ErrorDataEncoderException
* if the request is not a POST
*/
public MyHttpPostRequestEncoder(
HttpDataFactory factory, HttpRequest request, boolean multipart, Charset charset,
EncoderMode encoderMode)
throws ErrorDataEncoderException {
if (factory == null) {
throw new NullPointerException("factory");
}
if (request == null) {
throw new NullPointerException("request");
}
if (charset == null) {
throw new NullPointerException("charset");
}
if (request.getMethod() != HttpMethod.POST) {
throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST");
}
this.request = request;
this.charset = charset;
this.factory = factory;
// Fill default values
bodyListDatas = new ArrayList<InterfaceHttpData>();
// default mode
isLastChunk = false;
isLastChunkSent = false;
isMultipart = multipart;
multipartHttpDatas = new ArrayList<InterfaceHttpData>();
this.encoderMode = encoderMode;
if (isMultipart) {
initDataMultipart();
}
}
/**
* Clean all HttpDatas (on Disk) for the current request.
*/
public void cleanFiles() {
factory.cleanRequestHttpDatas(request);
}
/**
* Does the last non empty chunk already encoded so that next chunk will be empty (last chunk)
*/
private boolean isLastChunk;
/**
* Last chunk already sent
*/
private boolean isLastChunkSent;
/**
* The current FileUpload that is currently in encode process
*/
private FileUpload currentFileUpload;
/**
* While adding a FileUpload, is the multipart currently in Mixed Mode
*/
private boolean duringMixedMode;
/**
* Global Body size
*/
private long globalBodySize;
/**
* True if this request is a Multipart request
*
* @return True if this request is a Multipart request
*/
public boolean isMultipart() {
return isMultipart;
}
/**
* Init the delimiter for Global Part (Data).
*/
private