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