1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.multipart;
17
18 import io.netty.handler.codec.DecoderException;
19 import io.netty.handler.codec.http.HttpConstants;
20 import io.netty.handler.codec.http.HttpContent;
21 import io.netty.handler.codec.http.HttpHeaderNames;
22 import io.netty.handler.codec.http.HttpHeaderValues;
23 import io.netty.handler.codec.http.HttpRequest;
24 import io.netty.util.internal.ObjectUtil;
25 import io.netty.util.internal.StringUtil;
26
27 import java.nio.charset.Charset;
28 import java.util.List;
29
30
31
32
33
34
35
36 public class HttpPostRequestDecoder implements InterfaceHttpPostRequestDecoder {
37
38 static final int DEFAULT_DISCARD_THRESHOLD = 10 * 1024 * 1024;
39
40 private final InterfaceHttpPostRequestDecoder decoder;
41
42
43
44
45
46
47
48
49
50
51
52 public HttpPostRequestDecoder(HttpRequest request) {
53 this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), request, HttpConstants.DEFAULT_CHARSET);
54 }
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) {
69 this(factory, request, HttpConstants.DEFAULT_CHARSET);
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) {
87 ObjectUtil.checkNotNull(factory, "factory");
88 ObjectUtil.checkNotNull(request, "request");
89 ObjectUtil.checkNotNull(charset, "charset");
90
91
92 if (isMultipart(request)) {
93 decoder = new HttpPostMultipartRequestDecoder(factory, request, charset);
94 } else {
95 decoder = new HttpPostStandardRequestDecoder(factory, request, charset);
96 }
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 protected enum MultiPartStatus {
130 NOTSTARTED, PREAMBLE, HEADERDELIMITER, DISPOSITION, FIELD, FILEUPLOAD, MIXEDPREAMBLE, MIXEDDELIMITER,
131 MIXEDDISPOSITION, MIXEDFILEUPLOAD, MIXEDCLOSEDELIMITER, CLOSEDELIMITER, PREEPILOGUE, EPILOGUE
132 }
133
134
135
136
137
138 public static boolean isMultipart(HttpRequest request) {
139 String mimeType = request.headers().get(HttpHeaderNames.CONTENT_TYPE);
140 if (mimeType != null && mimeType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString())) {
141 return getMultipartDataBoundary(mimeType) != null;
142 }
143 return false;
144 }
145
146
147
148
149
150
151 protected static String[] getMultipartDataBoundary(String contentType) {
152
153 String[] headerContentType = splitHeaderContentType(contentType);
154 final String multiPartHeader = HttpHeaderValues.MULTIPART_FORM_DATA.toString();
155 if (headerContentType[0].regionMatches(true, 0, multiPartHeader, 0 , multiPartHeader.length())) {
156 int mrank;
157 int crank;
158 final String boundaryHeader = HttpHeaderValues.BOUNDARY.toString();
159 if (headerContentType[1].regionMatches(true, 0, boundaryHeader, 0, boundaryHeader.length())) {
160 mrank = 1;
161 crank = 2;
162 } else if (headerContentType[2].regionMatches(true, 0, boundaryHeader, 0, boundaryHeader.length())) {
163 mrank = 2;
164 crank = 1;
165 } else {
166 return null;
167 }
168 String boundary = StringUtil.substringAfter(headerContentType[mrank], '=');
169 if (boundary == null) {
170 throw new ErrorDataDecoderException("Needs a boundary value");
171 }
172 if (boundary.charAt(0) == '"') {
173 String bound = boundary.trim();
174 int index = bound.length() - 1;
175 if (bound.charAt(index) == '"') {
176 boundary = bound.substring(1, index);
177 }
178 }
179 final String charsetHeader = HttpHeaderValues.CHARSET.toString();
180 if (headerContentType[crank].regionMatches(true, 0, charsetHeader, 0, charsetHeader.length())) {
181 String charset = StringUtil.substringAfter(headerContentType[crank], '=');
182 if (charset != null) {
183 return new String[] {"--" + boundary, charset};
184 }
185 }
186 return new String[] {"--" + boundary};
187 }
188 return null;
189 }
190
191 @Override
192 public boolean isMultipart() {
193 return decoder.isMultipart();
194 }
195
196 @Override
197 public void setDiscardThreshold(int discardThreshold) {
198 decoder.setDiscardThreshold(discardThreshold);
199 }
200
201 @Override
202 public int getDiscardThreshold() {
203 return decoder.getDiscardThreshold();
204 }
205
206 @Override
207 public List<InterfaceHttpData> getBodyHttpDatas() {
208 return decoder.getBodyHttpDatas();
209 }
210
211 @Override
212 public List<InterfaceHttpData> getBodyHttpDatas(String name) {
213 return decoder.getBodyHttpDatas(name);
214 }
215
216 @Override
217 public InterfaceHttpData getBodyHttpData(String name) {
218 return decoder.getBodyHttpData(name);
219 }
220
221 @Override
222 public InterfaceHttpPostRequestDecoder offer(HttpContent content) {
223 return decoder.offer(content);
224 }
225
226 @Override
227 public boolean hasNext() {
228 return decoder.hasNext();
229 }
230
231 @Override
232 public InterfaceHttpData next() {
233 return decoder.next();
234 }
235
236 @Override
237 public InterfaceHttpData currentPartialHttpData() {
238 return decoder.currentPartialHttpData();
239 }
240
241 @Override
242 public void destroy() {
243 decoder.destroy();
244 }
245
246 @Override
247 public void cleanFiles() {
248 decoder.cleanFiles();
249 }
250
251 @Override
252 public void removeHttpDataFromClean(InterfaceHttpData data) {
253 decoder.removeHttpDataFromClean(data);
254 }
255
256
257
258
259
260
261 private static String[] splitHeaderContentType(String sb) {
262 int aStart;
263 int aEnd;
264 int bStart;
265 int bEnd;
266 int cStart;
267 int cEnd;
268 aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0);
269 aEnd = sb.indexOf(';');
270 if (aEnd == -1) {
271 return new String[] { sb, "", "" };
272 }
273 bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd + 1);
274 if (sb.charAt(aEnd - 1) == ' ') {
275 aEnd--;
276 }
277 bEnd = sb.indexOf(';', bStart);
278 if (bEnd == -1) {
279 bEnd = HttpPostBodyUtil.findEndOfString(sb);
280 return new String[] { sb.substring(aStart, aEnd), sb.substring(bStart, bEnd), "" };
281 }
282 cStart = HttpPostBodyUtil.findNonWhitespace(sb, bEnd + 1);
283 if (sb.charAt(bEnd - 1) == ' ') {
284 bEnd--;
285 }
286 cEnd = HttpPostBodyUtil.findEndOfString(sb);
287 return new String[] { sb.substring(aStart, aEnd), sb.substring(bStart, bEnd), sb.substring(cStart, cEnd) };
288 }
289
290
291
292
293
294 public static class NotEnoughDataDecoderException extends DecoderException {
295 private static final long serialVersionUID = -7846841864603865638L;
296
297 public NotEnoughDataDecoderException() {
298 }
299
300 public NotEnoughDataDecoderException(String msg) {
301 super(msg);
302 }
303
304 public NotEnoughDataDecoderException(Throwable cause) {
305 super(cause);
306 }
307
308 public NotEnoughDataDecoderException(String msg, Throwable cause) {
309 super(msg, cause);
310 }
311 }
312
313
314
315
316 public static class EndOfDataDecoderException extends DecoderException {
317 private static final long serialVersionUID = 1336267941020800769L;
318 }
319
320
321
322
323 public static class ErrorDataDecoderException extends DecoderException {
324 private static final long serialVersionUID = 5020247425493164465L;
325
326 public ErrorDataDecoderException() {
327 }
328
329 public ErrorDataDecoderException(String msg) {
330 super(msg);
331 }
332
333 public ErrorDataDecoderException(Throwable cause) {
334 super(cause);
335 }
336
337 public ErrorDataDecoderException(String msg, Throwable cause) {
338 super(msg, cause);
339 }
340 }
341 }