1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.servlets;
20
21 import java.io.BufferedInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.UnsupportedEncodingException;
27 import java.nio.charset.Charset;
28 import java.nio.charset.StandardCharsets;
29 import java.nio.charset.UnsupportedCharsetException;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.Enumeration;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Map;
37
38 import javax.servlet.Filter;
39 import javax.servlet.FilterChain;
40 import javax.servlet.FilterConfig;
41 import javax.servlet.MultipartConfigElement;
42 import javax.servlet.ServletContext;
43 import javax.servlet.ServletException;
44 import javax.servlet.ServletRequest;
45 import javax.servlet.ServletResponse;
46 import javax.servlet.http.HttpServletRequest;
47 import javax.servlet.http.HttpServletRequestWrapper;
48 import javax.servlet.http.Part;
49
50 import org.eclipse.jetty.util.IO;
51 import org.eclipse.jetty.http.MimeTypes;
52 import org.eclipse.jetty.util.LazyList;
53 import org.eclipse.jetty.util.MultiMap;
54 import org.eclipse.jetty.util.MultiPartInputStreamParser;
55 import org.eclipse.jetty.util.StringUtil;
56 import org.eclipse.jetty.util.TypeUtil;
57 import org.eclipse.jetty.util.log.Log;
58 import org.eclipse.jetty.util.log.Logger;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public class MultiPartFilter implements Filter
86 {
87 private static final Logger LOG = Log.getLogger(MultiPartFilter.class);
88 public final static String CONTENT_TYPE_SUFFIX=".org.eclipse.jetty.servlet.contentType";
89 private final static String MULTIPART = "org.eclipse.jetty.servlet.MultiPartFile.multiPartInputStream";
90 private File tempdir;
91 private boolean _deleteFiles;
92 private ServletContext _context;
93 private int _fileOutputBuffer = 0;
94 private long _maxFileSize = -1L;
95 private long _maxRequestSize = -1L;
96 private int _maxFormKeys = Integer.getInteger("org.eclipse.jetty.server.Request.maxFormKeys", 1000);
97
98
99
100
101
102 public void init(FilterConfig filterConfig) throws ServletException
103 {
104 tempdir=(File)filterConfig.getServletContext().getAttribute("javax.servlet.context.tempdir");
105 _deleteFiles="true".equals(filterConfig.getInitParameter("deleteFiles"));
106 String fileOutputBuffer = filterConfig.getInitParameter("fileOutputBuffer");
107 if(fileOutputBuffer!=null)
108 _fileOutputBuffer = Integer.parseInt(fileOutputBuffer);
109 String maxFileSize = filterConfig.getInitParameter("maxFileSize");
110 if (maxFileSize != null)
111 _maxFileSize = Long.parseLong(maxFileSize.trim());
112 String maxRequestSize = filterConfig.getInitParameter("maxRequestSize");
113 if (maxRequestSize != null)
114 _maxRequestSize = Long.parseLong(maxRequestSize.trim());
115
116 _context=filterConfig.getServletContext();
117 String mfks = filterConfig.getInitParameter("maxFormKeys");
118 if (mfks!=null)
119 _maxFormKeys=Integer.parseInt(mfks);
120 }
121
122
123
124
125
126
127 public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
128 throws IOException, ServletException
129 {
130 HttpServletRequest srequest=(HttpServletRequest)request;
131 if(srequest.getContentType()==null||!srequest.getContentType().startsWith("multipart/form-data"))
132 {
133 chain.doFilter(request,response);
134 return;
135 }
136
137 InputStream in = new BufferedInputStream(request.getInputStream());
138 String content_type=srequest.getContentType();
139
140
141 MultiMap params = new MultiMap();
142 for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet())
143 {
144 Object value = entry.getValue();
145 if (value instanceof String[])
146 params.addValues(entry.getKey(), (String[])value);
147 else
148 params.add(entry.getKey(), value);
149 }
150
151 MultipartConfigElement config = new MultipartConfigElement(tempdir.getCanonicalPath(), _maxFileSize, _maxRequestSize, _fileOutputBuffer);
152 MultiPartInputStreamParser mpis = new MultiPartInputStreamParser(in, content_type, config, tempdir);
153 mpis.setDeleteOnExit(_deleteFiles);
154 request.setAttribute(MULTIPART, mpis);
155 try
156 {
157 Collection<Part> parts = mpis.getParts();
158 if (parts != null)
159 {
160 Iterator<Part> itor = parts.iterator();
161 while (itor.hasNext() && params.size() < _maxFormKeys)
162 {
163 Part p = itor.next();
164 MultiPartInputStreamParser.MultiPart mp = (MultiPartInputStreamParser.MultiPart)p;
165 if (mp.getFile() != null)
166 {
167 request.setAttribute(mp.getName(),mp.getFile());
168 if (mp.getContentDispositionFilename() != null)
169 {
170 params.add(mp.getName(), mp.getContentDispositionFilename());
171 if (mp.getContentType() != null)
172 params.add(mp.getName()+CONTENT_TYPE_SUFFIX, mp.getContentType());
173 }
174 }
175 else
176 {
177 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
178 IO.copy(p.getInputStream(), bytes);
179 params.add(p.getName(), bytes.toByteArray());
180 if (p.getContentType() != null)
181 params.add(p.getName()+CONTENT_TYPE_SUFFIX, p.getContentType());
182 }
183 }
184 }
185
186
187 chain.doFilter(new Wrapper(srequest,params),response);
188 }
189 finally
190 {
191 deleteFiles(request);
192 }
193 }
194
195
196
197 private void deleteFiles(ServletRequest request)
198 {
199 if (!_deleteFiles)
200 return;
201
202 MultiPartInputStreamParser mpis = (MultiPartInputStreamParser)request.getAttribute(MULTIPART);
203 if (mpis != null)
204 {
205 try
206 {
207 mpis.deleteParts();
208 }
209 catch (Exception e)
210 {
211 _context.log("Error deleting multipart tmp files", e);
212 }
213 }
214 request.removeAttribute(MULTIPART);
215 }
216
217
218
219
220
221 public void destroy()
222 {
223 }
224
225
226
227 private static class Wrapper extends HttpServletRequestWrapper
228 {
229 Charset _encoding=StandardCharsets.UTF_8;
230 MultiMap<Object> _params;
231
232
233
234
235
236 public Wrapper(HttpServletRequest request, MultiMap map)
237 {
238 super(request);
239 this._params=map;
240 }
241
242
243
244
245
246 @Override
247 public int getContentLength()
248 {
249 return 0;
250 }
251
252
253
254
255
256 @Override
257 public String getParameter(String name)
258 {
259 Object o=_params.get(name);
260 if (!(o instanceof byte[]) && LazyList.size(o)>0)
261 o=LazyList.get(o,0);
262
263 if (o instanceof byte[])
264 {
265 try
266 {
267 return getParameterBytesAsString(name, (byte[])o);
268 }
269 catch(Exception e)
270 {
271 LOG.warn(e);
272 }
273 }
274 else if (o!=null)
275 return String.valueOf(o);
276 return null;
277 }
278
279
280
281
282
283 @Override
284 public Map<String, String[]> getParameterMap()
285 {
286 Map<String, String[]> cmap = new HashMap<String,String[]>();
287
288 for ( Object key : _params.keySet() )
289 {
290 cmap.put((String)key,getParameterValues((String)key));
291 }
292
293 return Collections.unmodifiableMap(cmap);
294 }
295
296
297
298
299
300 @Override
301 public Enumeration<String> getParameterNames()
302 {
303 return Collections.enumeration(_params.keySet());
304 }
305
306
307
308
309
310 @Override
311 public String[] getParameterValues(String name)
312 {
313 List l=_params.getValues(name);
314 if (l==null || l.size()==0)
315 return new String[0];
316 String[] v = new String[l.size()];
317 for (int i=0;i<l.size();i++)
318 {
319 Object o=l.get(i);
320 if (o instanceof byte[])
321 {
322 try
323 {
324 v[i]=getParameterBytesAsString(name, (byte[])o);
325 }
326 catch(Exception e)
327 {
328 throw new RuntimeException(e);
329 }
330 }
331 else if (o instanceof String)
332 v[i]=(String)o;
333 }
334 return v;
335 }
336
337
338
339
340
341 @Override
342 public void setCharacterEncoding(String enc)
343 throws UnsupportedEncodingException
344 {
345 try
346 {
347 _encoding=Charset.forName(enc);
348 }
349 catch (UnsupportedCharsetException e)
350 {
351 throw new UnsupportedEncodingException(e.getMessage());
352 }
353 }
354
355
356
357 private String getParameterBytesAsString (String name, byte[] bytes)
358 throws UnsupportedEncodingException
359 {
360
361 Object ct = _params.getValue(name+CONTENT_TYPE_SUFFIX,0);
362
363 Charset contentType = _encoding;
364 if (ct != null)
365 {
366 String tmp = MimeTypes.getCharsetFromContentType((String)ct);
367 try
368 {
369 contentType = (tmp == null?_encoding:Charset.forName(tmp));
370 }
371 catch (UnsupportedCharsetException e)
372 {
373 throw new UnsupportedEncodingException(e.getMessage());
374 }
375 }
376
377 return new String(bytes,contentType);
378 }
379 }
380 }