1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server.handler;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.net.MalformedURLException;
24 import java.nio.ByteBuffer;
25 import java.nio.channels.ReadableByteChannel;
26
27 import javax.servlet.AsyncContext;
28 import javax.servlet.RequestDispatcher;
29 import javax.servlet.ServletException;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32
33 import org.eclipse.jetty.http.HttpFields;
34 import org.eclipse.jetty.http.HttpHeader;
35 import org.eclipse.jetty.http.HttpMethod;
36 import org.eclipse.jetty.http.HttpStatus;
37 import org.eclipse.jetty.http.MimeTypes;
38 import org.eclipse.jetty.io.WriterOutputStream;
39 import org.eclipse.jetty.server.HttpOutput;
40 import org.eclipse.jetty.server.Request;
41 import org.eclipse.jetty.server.Response;
42 import org.eclipse.jetty.server.handler.ContextHandler.Context;
43 import org.eclipse.jetty.util.BufferUtil;
44 import org.eclipse.jetty.util.Callback;
45 import org.eclipse.jetty.util.URIUtil;
46 import org.eclipse.jetty.util.log.Log;
47 import org.eclipse.jetty.util.log.Logger;
48 import org.eclipse.jetty.util.resource.FileResource;
49 import org.eclipse.jetty.util.resource.Resource;
50
51
52
53
54
55
56
57
58
59
60
61
62 public class ResourceHandler extends HandlerWrapper
63 {
64 private static final Logger LOG = Log.getLogger(ResourceHandler.class);
65
66 ContextHandler _context;
67 Resource _baseResource;
68 Resource _defaultStylesheet;
69 Resource _stylesheet;
70 String[] _welcomeFiles={"index.html"};
71 MimeTypes _mimeTypes = new MimeTypes();
72 String _cacheControl;
73 boolean _directory;
74 boolean _etags;
75 int _minMemoryMappedContentLength=-1;
76 int _minAsyncContentLength=0;
77
78
79 public ResourceHandler()
80 {
81
82 }
83
84
85 public MimeTypes getMimeTypes()
86 {
87 return _mimeTypes;
88 }
89
90
91 public void setMimeTypes(MimeTypes mimeTypes)
92 {
93 _mimeTypes = mimeTypes;
94 }
95
96
97
98
99
100 public boolean isDirectoriesListed()
101 {
102 return _directory;
103 }
104
105
106
107
108
109 public void setDirectoriesListed(boolean directory)
110 {
111 _directory = directory;
112 }
113
114
115
116
117
118
119
120 public int getMinMemoryMappedContentLength()
121 {
122 return _minMemoryMappedContentLength;
123 }
124
125
126
127
128
129
130
131 public void setMinMemoryMappedContentLength(int minMemoryMappedFileSize)
132 {
133 _minMemoryMappedContentLength = minMemoryMappedFileSize;
134 }
135
136
137
138
139
140
141
142 public int getMinAsyncContentLength()
143 {
144 return _minAsyncContentLength;
145 }
146
147
148
149
150
151
152
153 public void setMinAsyncContentLength(int minAsyncContentLength)
154 {
155 _minAsyncContentLength = minAsyncContentLength;
156 }
157
158
159
160
161
162 public boolean isEtags()
163 {
164 return _etags;
165 }
166
167
168
169
170
171 public void setEtags(boolean etags)
172 {
173 _etags = etags;
174 }
175
176
177 @Override
178 public void doStart()
179 throws Exception
180 {
181 Context scontext = ContextHandler.getCurrentContext();
182 _context = (scontext==null?null:scontext.getContextHandler());
183
184 super.doStart();
185 }
186
187
188
189
190
191 public Resource getBaseResource()
192 {
193 if (_baseResource==null)
194 return null;
195 return _baseResource;
196 }
197
198
199
200
201
202 public String getResourceBase()
203 {
204 if (_baseResource==null)
205 return null;
206 return _baseResource.toString();
207 }
208
209
210
211
212
213
214 public void setBaseResource(Resource base)
215 {
216 _baseResource=base;
217 }
218
219
220
221
222
223 public void setResourceBase(String resourceBase)
224 {
225 try
226 {
227 setBaseResource(Resource.newResource(resourceBase));
228 }
229 catch (Exception e)
230 {
231 LOG.warn(e.toString());
232 LOG.debug(e);
233 throw new IllegalArgumentException(resourceBase);
234 }
235 }
236
237
238
239
240
241 public Resource getStylesheet()
242 {
243 if(_stylesheet != null)
244 {
245 return _stylesheet;
246 }
247 else
248 {
249 if(_defaultStylesheet == null)
250 {
251 try
252 {
253 _defaultStylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css"));
254 }
255 catch(IOException e)
256 {
257 LOG.warn(e.toString());
258 LOG.debug(e);
259 }
260 }
261 return _defaultStylesheet;
262 }
263 }
264
265
266
267
268
269 public void setStylesheet(String stylesheet)
270 {
271 try
272 {
273 _stylesheet = Resource.newResource(stylesheet);
274 if(!_stylesheet.exists())
275 {
276 LOG.warn("unable to find custom stylesheet: " + stylesheet);
277 _stylesheet = null;
278 }
279 }
280 catch(Exception e)
281 {
282 LOG.warn(e.toString());
283 LOG.debug(e);
284 throw new IllegalArgumentException(stylesheet);
285 }
286 }
287
288
289
290
291
292 public String getCacheControl()
293 {
294 return _cacheControl;
295 }
296
297
298
299
300
301 public void setCacheControl(String cacheControl)
302 {
303 _cacheControl=cacheControl;
304 }
305
306
307
308
309 public Resource getResource(String path) throws MalformedURLException
310 {
311 if (path==null || !path.startsWith("/"))
312 throw new MalformedURLException(path);
313
314 Resource base = _baseResource;
315 if (base==null)
316 {
317 if (_context==null)
318 return null;
319 base=_context.getBaseResource();
320 if (base==null)
321 return null;
322 }
323
324 try
325 {
326 path=URIUtil.canonicalPath(path);
327 return base.addPath(path);
328 }
329 catch(Exception e)
330 {
331 LOG.ignore(e);
332 }
333
334 return null;
335 }
336
337
338 protected Resource getResource(HttpServletRequest request) throws MalformedURLException
339 {
340 String servletPath;
341 String pathInfo;
342 Boolean included = request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null;
343 if (included != null && included.booleanValue())
344 {
345 servletPath = (String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
346 pathInfo = (String)request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
347
348 if (servletPath == null && pathInfo == null)
349 {
350 servletPath = request.getServletPath();
351 pathInfo = request.getPathInfo();
352 }
353 }
354 else
355 {
356 servletPath = request.getServletPath();
357 pathInfo = request.getPathInfo();
358 }
359
360 String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
361 return getResource(pathInContext);
362 }
363
364
365
366 public String[] getWelcomeFiles()
367 {
368 return _welcomeFiles;
369 }
370
371
372 public void setWelcomeFiles(String[] welcomeFiles)
373 {
374 _welcomeFiles=welcomeFiles;
375 }
376
377
378 protected Resource getWelcome(Resource directory) throws MalformedURLException, IOException
379 {
380 for (int i=0;i<_welcomeFiles.length;i++)
381 {
382 Resource welcome=directory.addPath(_welcomeFiles[i]);
383 if (welcome.exists() && !welcome.isDirectory())
384 return welcome;
385 }
386
387 return null;
388 }
389
390
391
392
393
394 @Override
395 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
396 {
397 if (baseRequest.isHandled())
398 return;
399
400 boolean skipContentBody = false;
401
402 if(!HttpMethod.GET.is(request.getMethod()))
403 {
404 if(!HttpMethod.HEAD.is(request.getMethod()))
405 {
406
407 super.handle(target, baseRequest, request, response);
408 return;
409 }
410 skipContentBody = true;
411 }
412
413 Resource resource = getResource(request);
414
415 if (resource==null || !resource.exists())
416 {
417
418 if (target.endsWith("/jetty-dir.css"))
419 {
420 resource = getStylesheet();
421 if (resource==null)
422 return;
423 response.setContentType("text/css");
424 }
425 else
426 {
427
428 super.handle(target, baseRequest, request, response);
429 return;
430 }
431 }
432
433
434 baseRequest.setHandled(true);
435
436
437 if (resource.isDirectory())
438 {
439 if (!request.getPathInfo().endsWith(URIUtil.SLASH))
440 {
441 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
442 return;
443 }
444
445 Resource welcome=getWelcome(resource);
446 if (welcome!=null && welcome.exists())
447 resource=welcome;
448 else
449 {
450 doDirectory(request,response,resource);
451 baseRequest.setHandled(true);
452 return;
453 }
454 }
455
456
457 long last_modified=resource.lastModified();
458 String etag=null;
459 if (_etags)
460 {
461
462 String ifnm = request.getHeader(HttpHeader.IF_NONE_MATCH.asString());
463 etag=resource.getWeakETag();
464 if (ifnm!=null && resource!=null && ifnm.equals(etag))
465 {
466 response.setStatus(HttpStatus.NOT_MODIFIED_304);
467 baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag);
468 return;
469 }
470 }
471
472
473 if (last_modified>0)
474 {
475 long if_modified=request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString());
476 if (if_modified>0 && last_modified/1000<=if_modified/1000)
477 {
478 response.setStatus(HttpStatus.NOT_MODIFIED_304);
479 return;
480 }
481 }
482
483
484 String mime=_mimeTypes.getMimeByExtension(resource.toString());
485 if (mime==null)
486 mime=_mimeTypes.getMimeByExtension(request.getPathInfo());
487 doResponseHeaders(response,resource,mime);
488 if (_etags)
489 baseRequest.getResponse().getHttpFields().put(HttpHeader.ETAG,etag);
490
491 if(skipContentBody)
492 return;
493
494
495
496 OutputStream out =null;
497 try {out = response.getOutputStream();}
498 catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());}
499
500
501 if (!(out instanceof HttpOutput))
502
503 resource.writeTo(out,0,resource.length());
504 else
505 {
506
507 int min_async_size=_minAsyncContentLength==0?response.getBufferSize():_minAsyncContentLength;
508
509 if (request.isAsyncSupported() &&
510 min_async_size>0 &&
511 resource.length()>=min_async_size)
512 {
513 final AsyncContext async = request.startAsync();
514 Callback callback = new Callback()
515 {
516 @Override
517 public void succeeded()
518 {
519 async.complete();
520 }
521
522 @Override
523 public void failed(Throwable x)
524 {
525 LOG.warn(x.toString());
526 LOG.debug(x);
527 async.complete();
528 }
529 };
530
531
532 if (_minMemoryMappedContentLength>0 &&
533 resource.length()>_minMemoryMappedContentLength &&
534 resource instanceof FileResource)
535 {
536 ByteBuffer buffer = BufferUtil.toBuffer(resource.getFile());
537 ((HttpOutput)out).sendContent(buffer,callback);
538 }
539 else
540 {
541
542 ReadableByteChannel channel= resource.getReadableByteChannel();
543 if (channel!=null)
544 ((HttpOutput)out).sendContent(channel,callback);
545 else
546 ((HttpOutput)out).sendContent(resource.getInputStream(),callback);
547 }
548 }
549 else
550 {
551
552 if (_minMemoryMappedContentLength>0 &&
553 resource.length()>_minMemoryMappedContentLength &&
554 resource instanceof FileResource)
555 {
556 ByteBuffer buffer = BufferUtil.toBuffer(resource.getFile());
557 ((HttpOutput)out).sendContent(buffer);
558 }
559 else
560 {
561 ReadableByteChannel channel= resource.getReadableByteChannel();
562 if (channel!=null)
563 ((HttpOutput)out).sendContent(channel);
564 else
565 ((HttpOutput)out).sendContent(resource.getInputStream());
566 }
567 }
568 }
569 }
570
571
572 protected void doDirectory(HttpServletRequest request,HttpServletResponse response, Resource resource)
573 throws IOException
574 {
575 if (_directory)
576 {
577 String listing = resource.getListHTML(request.getRequestURI(),request.getPathInfo().lastIndexOf("/") > 0);
578 response.setContentType("text/html; charset=UTF-8");
579 response.getWriter().println(listing);
580 }
581 else
582 response.sendError(HttpStatus.FORBIDDEN_403);
583 }
584
585
586
587
588
589
590
591
592
593 protected void doResponseHeaders(HttpServletResponse response, Resource resource, String mimeType)
594 {
595 if (mimeType!=null)
596 response.setContentType(mimeType);
597
598 long length=resource.length();
599
600 if (response instanceof Response)
601 {
602 HttpFields fields = ((Response)response).getHttpFields();
603
604 if (length>0)
605 ((Response)response).setLongContentLength(length);
606
607 if (_cacheControl!=null)
608 fields.put(HttpHeader.CACHE_CONTROL,_cacheControl);
609 }
610 else
611 {
612 if (length>Integer.MAX_VALUE)
613 response.setHeader(HttpHeader.CONTENT_LENGTH.asString(),Long.toString(length));
614 else if (length>0)
615 response.setContentLength((int)length);
616
617 if (_cacheControl!=null)
618 response.setHeader(HttpHeader.CACHE_CONTROL.asString(),_cacheControl);
619 }
620 }
621 }