1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.servlet;
20
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.util.ArrayList;
28 import java.util.Enumeration;
29 import java.util.List;
30 import java.util.Map;
31
32 import javax.servlet.RequestDispatcher;
33 import javax.servlet.ServletContext;
34 import javax.servlet.ServletException;
35 import javax.servlet.UnavailableException;
36 import javax.servlet.http.HttpServlet;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpServletResponse;
39
40 import org.eclipse.jetty.http.HttpContent;
41 import org.eclipse.jetty.http.HttpFields;
42 import org.eclipse.jetty.http.HttpHeaderValues;
43 import org.eclipse.jetty.http.HttpHeaders;
44 import org.eclipse.jetty.http.HttpMethods;
45 import org.eclipse.jetty.http.MimeTypes;
46 import org.eclipse.jetty.io.Buffer;
47 import org.eclipse.jetty.io.ByteArrayBuffer;
48 import org.eclipse.jetty.io.WriterOutputStream;
49 import org.eclipse.jetty.server.AbstractHttpConnection;
50 import org.eclipse.jetty.server.Connector;
51 import org.eclipse.jetty.server.Dispatcher;
52 import org.eclipse.jetty.server.HttpOutput;
53 import org.eclipse.jetty.server.InclusiveByteRange;
54 import org.eclipse.jetty.server.ResourceCache;
55 import org.eclipse.jetty.server.Response;
56 import org.eclipse.jetty.server.handler.ContextHandler;
57 import org.eclipse.jetty.server.nio.NIOConnector;
58 import org.eclipse.jetty.server.ssl.SslConnector;
59 import org.eclipse.jetty.util.IO;
60 import org.eclipse.jetty.util.MultiPartOutputStream;
61 import org.eclipse.jetty.util.QuotedStringTokenizer;
62 import org.eclipse.jetty.util.URIUtil;
63 import org.eclipse.jetty.util.log.Log;
64 import org.eclipse.jetty.util.log.Logger;
65 import org.eclipse.jetty.util.resource.FileResource;
66 import org.eclipse.jetty.util.resource.Resource;
67 import org.eclipse.jetty.util.resource.ResourceCollection;
68 import org.eclipse.jetty.util.resource.ResourceFactory;
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143 public class DefaultServlet extends HttpServlet implements ResourceFactory
144 {
145 private static final Logger LOG = Log.getLogger(DefaultServlet.class);
146
147 private static final long serialVersionUID = 4930458713846881193L;
148 private ServletContext _servletContext;
149 private ContextHandler _contextHandler;
150
151 private boolean _acceptRanges=true;
152 private boolean _dirAllowed=true;
153 private boolean _welcomeServlets=false;
154 private boolean _welcomeExactServlets=false;
155 private boolean _redirectWelcome=false;
156 private boolean _gzip=true;
157 private boolean _pathInfoOnly=false;
158 private boolean _etags=false;
159
160 private Resource _resourceBase;
161 private ResourceCache _cache;
162
163 private MimeTypes _mimeTypes;
164 private String[] _welcomes;
165 private Resource _stylesheet;
166 private boolean _useFileMappedBuffer=false;
167 private ByteArrayBuffer _cacheControl;
168 private String _relativeResourceBase;
169 private ServletHandler _servletHandler;
170 private ServletHolder _defaultHolder;
171
172
173
174 @Override
175 public void init()
176 throws UnavailableException
177 {
178 _servletContext=getServletContext();
179 _contextHandler = initContextHandler(_servletContext);
180
181 _mimeTypes = _contextHandler.getMimeTypes();
182
183 _welcomes = _contextHandler.getWelcomeFiles();
184 if (_welcomes==null)
185 _welcomes=new String[] {"index.html","index.jsp"};
186
187 _acceptRanges=getInitBoolean("acceptRanges",_acceptRanges);
188 _dirAllowed=getInitBoolean("dirAllowed",_dirAllowed);
189 _redirectWelcome=getInitBoolean("redirectWelcome",_redirectWelcome);
190 _gzip=getInitBoolean("gzip",_gzip);
191 _pathInfoOnly=getInitBoolean("pathInfoOnly",_pathInfoOnly);
192
193 if ("exact".equals(getInitParameter("welcomeServlets")))
194 {
195 _welcomeExactServlets=true;
196 _welcomeServlets=false;
197 }
198 else
199 _welcomeServlets=getInitBoolean("welcomeServlets", _welcomeServlets);
200
201 if (getInitParameter("aliases")!=null)
202 _contextHandler.setAliases(getInitBoolean("aliases",false));
203
204 boolean aliases=_contextHandler.isAliases();
205 if (!aliases && !FileResource.getCheckAliases())
206 throw new IllegalStateException("Alias checking disabled");
207 if (aliases)
208 _servletContext.log("Aliases are enabled! Security constraints may be bypassed!!!");
209
210 _useFileMappedBuffer=getInitBoolean("useFileMappedBuffer",_useFileMappedBuffer);
211
212 _relativeResourceBase = getInitParameter("relativeResourceBase");
213
214 String rb=getInitParameter("resourceBase");
215 if (rb!=null)
216 {
217 if (_relativeResourceBase!=null)
218 throw new UnavailableException("resourceBase & relativeResourceBase");
219 try{_resourceBase=_contextHandler.newResource(rb);}
220 catch (Exception e)
221 {
222 LOG.warn(Log.EXCEPTION,e);
223 throw new UnavailableException(e.toString());
224 }
225 }
226
227 String css=getInitParameter("stylesheet");
228 try
229 {
230 if(css!=null)
231 {
232 _stylesheet = Resource.newResource(css);
233 if(!_stylesheet.exists())
234 {
235 LOG.warn("!" + css);
236 _stylesheet = null;
237 }
238 }
239 if(_stylesheet == null)
240 {
241 _stylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css"));
242 }
243 }
244 catch(Exception e)
245 {
246 LOG.warn(e.toString());
247 LOG.debug(e);
248 }
249
250 String t=getInitParameter("cacheControl");
251 if (t!=null)
252 _cacheControl=new ByteArrayBuffer(t);
253
254 String resourceCache = getInitParameter("resourceCache");
255 int max_cache_size=getInitInt("maxCacheSize", -2);
256 int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
257 int max_cached_files=getInitInt("maxCachedFiles", -2);
258 if (resourceCache!=null)
259 {
260 if (max_cache_size!=-1 || max_cached_file_size!= -2 || max_cached_files!=-2)
261 LOG.debug("ignoring resource cache configuration, using resourceCache attribute");
262 if (_relativeResourceBase!=null || _resourceBase!=null)
263 throw new UnavailableException("resourceCache specified with resource bases");
264 _cache=(ResourceCache)_servletContext.getAttribute(resourceCache);
265
266 LOG.debug("Cache {}={}",resourceCache,_cache);
267 }
268
269 _etags = getInitBoolean("etags",_etags);
270
271 try
272 {
273 if (_cache==null && max_cached_files>0)
274 {
275 _cache= new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_etags);
276
277 if (max_cache_size>0)
278 _cache.setMaxCacheSize(max_cache_size);
279 if (max_cached_file_size>=-1)
280 _cache.setMaxCachedFileSize(max_cached_file_size);
281 if (max_cached_files>=-1)
282 _cache.setMaxCachedFiles(max_cached_files);
283 }
284 }
285 catch (Exception e)
286 {
287 LOG.warn(Log.EXCEPTION,e);
288 throw new UnavailableException(e.toString());
289 }
290
291 _servletHandler= (ServletHandler) _contextHandler.getChildHandlerByClass(ServletHandler.class);
292 for (ServletHolder h :_servletHandler.getServlets())
293 if (h.getServletInstance()==this)
294 _defaultHolder=h;
295
296
297 if (LOG.isDebugEnabled())
298 LOG.debug("resource base = "+_resourceBase);
299 }
300
301
302
303
304
305
306
307
308
309 protected ContextHandler initContextHandler(ServletContext servletContext)
310 {
311 ContextHandler.Context scontext=ContextHandler.getCurrentContext();
312 if (scontext==null)
313 {
314 if (servletContext instanceof ContextHandler.Context)
315 return ((ContextHandler.Context)servletContext).getContextHandler();
316 else
317 throw new IllegalArgumentException("The servletContext " + servletContext + " " +
318 servletContext.getClass().getName() + " is not " + ContextHandler.Context.class.getName());
319 }
320 else
321 return ContextHandler.getCurrentContext().getContextHandler();
322 }
323
324
325 @Override
326 public String getInitParameter(String name)
327 {
328 String value=getServletContext().getInitParameter("org.eclipse.jetty.servlet.Default."+name);
329 if (value==null)
330 value=super.getInitParameter(name);
331 return value;
332 }
333
334
335 private boolean getInitBoolean(String name, boolean dft)
336 {
337 String value=getInitParameter(name);
338 if (value==null || value.length()==0)
339 return dft;
340 return (value.startsWith("t")||
341 value.startsWith("T")||
342 value.startsWith("y")||
343 value.startsWith("Y")||
344 value.startsWith("1"));
345 }
346
347
348 private int getInitInt(String name, int dft)
349 {
350 String value=getInitParameter(name);
351 if (value==null)
352 value=getInitParameter(name);
353 if (value!=null && value.length()>0)
354 return Integer.parseInt(value);
355 return dft;
356 }
357
358
359
360
361
362
363
364
365
366 public Resource getResource(String pathInContext)
367 {
368 Resource r=null;
369 if (_relativeResourceBase!=null)
370 pathInContext=URIUtil.addPaths(_relativeResourceBase,pathInContext);
371
372 try
373 {
374 if (_resourceBase!=null)
375 {
376 r = _resourceBase.addPath(pathInContext);
377 if (!_contextHandler.checkAlias(pathInContext,r))
378 r=null;
379 }
380 else if (_servletContext instanceof ContextHandler.Context)
381 {
382 r = _contextHandler.getResource(pathInContext);
383 }
384 else
385 {
386 URL u = _servletContext.getResource(pathInContext);
387 r = _contextHandler.newResource(u);
388 }
389
390 if (LOG.isDebugEnabled())
391 LOG.debug("Resource "+pathInContext+"="+r);
392 }
393 catch (IOException e)
394 {
395 LOG.ignore(e);
396 }
397
398 if((r==null || !r.exists()) && pathInContext.endsWith("/jetty-dir.css"))
399 r=_stylesheet;
400
401 return r;
402 }
403
404
405 @Override
406 protected void doGet(HttpServletRequest request, HttpServletResponse response)
407 throws ServletException, IOException
408 {
409 String servletPath=null;
410 String pathInfo=null;
411 Enumeration<String> reqRanges = null;
412 Boolean included =request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI)!=null;
413 if (included!=null && included.booleanValue())
414 {
415 servletPath=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
416 pathInfo=(String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
417 if (servletPath==null)
418 {
419 servletPath=request.getServletPath();
420 pathInfo=request.getPathInfo();
421 }
422 }
423 else
424 {
425 included = Boolean.FALSE;
426 servletPath = _pathInfoOnly?"/":request.getServletPath();
427 pathInfo = request.getPathInfo();
428
429
430 reqRanges = request.getHeaders(HttpHeaders.RANGE);
431 if (!hasDefinedRange(reqRanges))
432 reqRanges = null;
433 }
434
435 String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
436 boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH);
437
438
439
440 Resource resource=null;
441 HttpContent content=null;
442 try
443 {
444
445 String pathInContextGz=null;
446 boolean gzip=false;
447 if (!included.booleanValue() && _gzip && reqRanges==null && !endsWithSlash )
448 {
449
450 pathInContextGz=pathInContext+".gz";
451 if (_cache==null)
452 resource=getResource(pathInContextGz);
453 else
454 {
455 content=_cache.lookup(pathInContextGz);
456 resource=(content==null)?null:content.getResource();
457 }
458
459
460 if (resource!=null && resource.exists() && !resource.isDirectory())
461 {
462
463 response.addHeader(HttpHeaders.VARY,HttpHeaders.ACCEPT_ENCODING);
464
465
466 String accept=request.getHeader(HttpHeaders.ACCEPT_ENCODING);
467 if (accept!=null && accept.indexOf("gzip")>=0)
468 gzip=true;
469 }
470 }
471
472
473 if (!gzip)
474 {
475 if (_cache==null)
476 resource=getResource(pathInContext);
477 else
478 {
479 content=_cache.lookup(pathInContext);
480 resource=content==null?null:content.getResource();
481 }
482 }
483
484 if (LOG.isDebugEnabled())
485 LOG.debug("uri="+request.getRequestURI()+" resource="+resource+(content!=null?" content":""));
486
487
488 if (resource==null || !resource.exists())
489 {
490 if (included)
491 throw new FileNotFoundException("!" + pathInContext);
492 response.sendError(HttpServletResponse.SC_NOT_FOUND);
493 }
494 else if (!resource.isDirectory())
495 {
496 if (endsWithSlash && _contextHandler.isAliases() && pathInContext.length()>1)
497 {
498 String q=request.getQueryString();
499 pathInContext=pathInContext.substring(0,pathInContext.length()-1);
500 if (q!=null&&q.length()!=0)
501 pathInContext+="?"+q;
502 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(_servletContext.getContextPath(),pathInContext)));
503 }
504 else
505 {
506
507 if (content==null)
508 content=new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),response.getBufferSize(),_etags);
509
510 if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
511 {
512 if (gzip)
513 {
514 response.setHeader(HttpHeaders.CONTENT_ENCODING,"gzip");
515 String mt=_servletContext.getMimeType(pathInContext);
516 if (mt!=null)
517 response.setContentType(mt);
518 }
519 sendData(request,response,included.booleanValue(),resource,content,reqRanges);
520 }
521 }
522 }
523 else
524 {
525 String welcome=null;
526
527 if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null))
528 {
529 StringBuffer buf=request.getRequestURL();
530 synchronized(buf)
531 {
532 int param=buf.lastIndexOf(";");
533 if (param<0)
534 buf.append('/');
535 else
536 buf.insert(param,'/');
537 String q=request.getQueryString();
538 if (q!=null&&q.length()!=0)
539 {
540 buf.append('?');
541 buf.append(q);
542 }
543 response.setContentLength(0);
544 response.sendRedirect(response.encodeRedirectURL(buf.toString()));
545 }
546 }
547
548 else if (null!=(welcome=getWelcomeFile(pathInContext)))
549 {
550 LOG.debug("welcome={}",welcome);
551 if (_redirectWelcome)
552 {
553
554 response.setContentLength(0);
555 String q=request.getQueryString();
556 if (q!=null&&q.length()!=0)
557 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)+"?"+q));
558 else
559 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)));
560 }
561 else
562 {
563
564 RequestDispatcher dispatcher=request.getRequestDispatcher(welcome);
565 if (dispatcher!=null)
566 {
567 if (included.booleanValue())
568 dispatcher.include(request,response);
569 else
570 {
571 request.setAttribute("org.eclipse.jetty.server.welcome",welcome);
572 dispatcher.forward(request,response);
573 }
574 }
575 }
576 }
577 else
578 {
579 content=new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),_etags);
580 if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
581 sendDirectory(request,response,resource,pathInContext);
582 }
583 }
584 }
585 catch(IllegalArgumentException e)
586 {
587 LOG.warn(Log.EXCEPTION,e);
588 if(!response.isCommitted())
589 response.sendError(500, e.getMessage());
590 }
591 finally
592 {
593 if (content!=null)
594 content.release();
595 else if (resource!=null)
596 resource.release();
597 }
598
599 }
600
601
602 private boolean hasDefinedRange(Enumeration<String> reqRanges)
603 {
604 return (reqRanges!=null && reqRanges.hasMoreElements());
605 }
606
607
608 @Override
609 protected void doPost(HttpServletRequest request, HttpServletResponse response)
610 throws ServletException, IOException
611 {
612 doGet(request,response);
613 }
614
615
616
617
618
619 @Override
620 protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
621 {
622 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
623 }
624
625
626 @Override
627 protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
628 throws ServletException, IOException
629 {
630 resp.setHeader("Allow", "GET,HEAD,POST,OPTIONS");
631 }
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646 private String getWelcomeFile(String pathInContext) throws MalformedURLException, IOException
647 {
648 if (_welcomes==null)
649 return null;
650
651 String welcome_servlet=null;
652 for (int i=0;i<_welcomes.length;i++)
653 {
654 String welcome_in_context=URIUtil.addPaths(pathInContext,_welcomes[i]);
655 Resource welcome=getResource(welcome_in_context);
656 if (welcome!=null && welcome.exists())
657 return _welcomes[i];
658
659 if ((_welcomeServlets || _welcomeExactServlets) && welcome_servlet==null)
660 {
661 Map.Entry entry=_servletHandler.getHolderEntry(welcome_in_context);
662 if (entry!=null && entry.getValue()!=_defaultHolder &&
663 (_welcomeServlets || (_welcomeExactServlets && entry.getKey().equals(welcome_in_context))))
664 welcome_servlet=welcome_in_context;
665
666 }
667 }
668 return welcome_servlet;
669 }
670
671
672
673
674 protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, Resource resource, HttpContent content)
675 throws IOException
676 {
677 try
678 {
679 if (!request.getMethod().equals(HttpMethods.HEAD) )
680 {
681 if (_etags)
682 {
683 String ifm=request.getHeader(HttpHeaders.IF_MATCH);
684 if (ifm!=null)
685 {
686 boolean match=false;
687 if (content!=null && content.getETag()!=null)
688 {
689 QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifm,", ",false,true);
690 while (!match && quoted.hasMoreTokens())
691 {
692 String tag = quoted.nextToken();
693 if (content.getETag().toString().equals(tag))
694 match=true;
695 }
696 }
697
698 if (!match)
699 {
700 Response r = Response.getResponse(response);
701 r.reset(true);
702 r.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
703 return false;
704 }
705 }
706
707 String ifnm=request.getHeader(HttpHeaders.IF_NONE_MATCH);
708 if (ifnm!=null && content!=null && content.getETag()!=null)
709 {
710
711 if (content.getETag().toString().equals(request.getAttribute("o.e.j.s.GzipFilter.ETag")))
712 {
713 Response r = Response.getResponse(response);
714 r.reset(true);
715 r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
716 r.getHttpFields().put(HttpHeaders.ETAG_BUFFER,ifnm);
717 return false;
718 }
719
720
721
722 if (content.getETag().toString().equals(ifnm))
723 {
724 Response r = Response.getResponse(response);
725 r.reset(true);
726 r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
727 r.getHttpFields().put(HttpHeaders.ETAG_BUFFER,content.getETag());
728 return false;
729 }
730
731
732 QuotedStringTokenizer quoted = new QuotedStringTokenizer(ifnm,", ",false,true);
733 while (quoted.hasMoreTokens())
734 {
735 String tag = quoted.nextToken();
736 if (content.getETag().toString().equals(tag))
737 {
738 Response r = Response.getResponse(response);
739 r.reset(true);
740 r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
741 r.getHttpFields().put(HttpHeaders.ETAG_BUFFER,content.getETag());
742 return false;
743 }
744 }
745
746 return true;
747 }
748 }
749
750 String ifms=request.getHeader(HttpHeaders.IF_MODIFIED_SINCE);
751 if (ifms!=null)
752 {
753
754 Response r = Response.getResponse(response);
755
756 if (content!=null)
757 {
758 Buffer mdlm=content.getLastModified();
759 if (mdlm!=null)
760 {
761 if (ifms.equals(mdlm.toString()))
762 {
763 r.reset(true);
764 r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
765 if (_etags)
766 r.getHttpFields().add(HttpHeaders.ETAG_BUFFER,content.getETag());
767 r.flushBuffer();
768 return false;
769 }
770 }
771 }
772
773 long ifmsl=request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
774 if (ifmsl!=-1)
775 {
776 if (resource.lastModified()/1000 <= ifmsl/1000)
777 {
778 r.reset(true);
779 r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
780 if (_etags)
781 r.getHttpFields().add(HttpHeaders.ETAG_BUFFER,content.getETag());
782 r.flushBuffer();
783 return false;
784 }
785 }
786 }
787
788
789 long date=request.getDateHeader(HttpHeaders.IF_UNMODIFIED_SINCE);
790
791 if (date!=-1)
792 {
793 if (resource.lastModified()/1000 > date/1000)
794 {
795 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
796 return false;
797 }
798 }
799
800 }
801 }
802 catch(IllegalArgumentException iae)
803 {
804 if(!response.isCommitted())
805 response.sendError(400, iae.getMessage());
806 throw iae;
807 }
808 return true;
809 }
810
811
812
813 protected void sendDirectory(HttpServletRequest request,
814 HttpServletResponse response,
815 Resource resource,
816 String pathInContext)
817 throws IOException
818 {
819 if (!_dirAllowed)
820 {
821 response.sendError(HttpServletResponse.SC_FORBIDDEN);
822 return;
823 }
824
825 byte[] data=null;
826 String base = URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH);
827
828
829 if (_resourceBase != null)
830 {
831
832 if (_resourceBase instanceof ResourceCollection)
833 resource=_resourceBase.addPath(pathInContext);
834 }
835
836 else if (_contextHandler.getBaseResource() instanceof ResourceCollection)
837 resource=_contextHandler.getBaseResource().addPath(pathInContext);
838
839 String dir = resource.getListHTML(base,pathInContext.length()>1);
840 if (dir==null)
841 {
842 response.sendError(HttpServletResponse.SC_FORBIDDEN,
843 "No directory");
844 return;
845 }
846
847 data=dir.getBytes("UTF-8");
848 response.setContentType("text/html; charset=UTF-8");
849 response.setContentLength(data.length);
850 response.getOutputStream().write(data);
851 }
852
853
854 protected void sendData(HttpServletRequest request,
855 HttpServletResponse response,
856 boolean include,
857 Resource resource,
858 HttpContent content,
859 Enumeration reqRanges)
860 throws IOException
861 {
862 boolean direct;
863 long content_length;
864 if (content==null)
865 {
866 direct=false;
867 content_length=resource.length();
868 }
869 else
870 {
871 Connector connector = AbstractHttpConnection.getCurrentConnection().getConnector();
872 direct=connector instanceof NIOConnector && ((NIOConnector)connector).getUseDirectBuffers() && !(connector instanceof SslConnector);
873 content_length=content.getContentLength();
874 }
875
876
877
878 OutputStream out =null;
879 boolean written;
880 try
881 {
882 out = response.getOutputStream();
883
884
885 written = out instanceof HttpOutput
886 ? ((HttpOutput)out).isWritten()
887 : AbstractHttpConnection.getCurrentConnection().getGenerator().isWritten();
888 }
889 catch(IllegalStateException e)
890 {
891 out = new WriterOutputStream(response.getWriter());
892 written=true;
893 }
894
895 if ( reqRanges == null || !reqRanges.hasMoreElements() || content_length<0)
896 {
897
898 if (include)
899 {
900 resource.writeTo(out,0,content_length);
901 }
902 else
903 {
904
905 if (content!=null && !written && out instanceof HttpOutput)
906 {
907 if (response instanceof Response)
908 {
909 writeOptionHeaders(((Response)response).getHttpFields());
910 ((AbstractHttpConnection.Output)out).sendContent(content);
911 }
912 else
913 {
914 Buffer buffer = direct?content.getDirectBuffer():content.getIndirectBuffer();
915 if (buffer!=null)
916 {
917 writeHeaders(response,content,content_length);
918 ((AbstractHttpConnection.Output)out).sendContent(buffer);
919 }
920 else
921 {
922 writeHeaders(response,content,content_length);
923 resource.writeTo(out,0,content_length);
924 }
925 }
926 }
927 else
928 {
929
930 writeHeaders(response,content,written?-1:content_length);
931
932
933 Buffer buffer = (content==null)?null:content.getIndirectBuffer();
934 if (buffer!=null)
935 buffer.writeTo(out);
936 else
937 resource.writeTo(out,0,content_length);
938 }
939 }
940 }
941 else
942 {
943
944 List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length);
945
946
947 if (ranges==null || ranges.size()==0)
948 {
949 writeHeaders(response, content, content_length);
950 response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
951 response.setHeader(HttpHeaders.CONTENT_RANGE,
952 InclusiveByteRange.to416HeaderRangeString(content_length));
953 resource.writeTo(out,0,content_length);
954 return;
955 }
956
957
958
959 if ( ranges.size()== 1)
960 {
961 InclusiveByteRange singleSatisfiableRange =
962 (InclusiveByteRange)ranges.get(0);
963 long singleLength = singleSatisfiableRange.getSize(content_length);
964 writeHeaders(response,content,singleLength );
965 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
966 response.setHeader(HttpHeaders.CONTENT_RANGE,
967 singleSatisfiableRange.toHeaderRangeString(content_length));
968 resource.writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
969 return;
970 }
971
972
973
974
975
976 writeHeaders(response,content,-1);
977 String mimetype=(content.getContentType()==null?null:content.getContentType().toString());
978 if (mimetype==null)
979 LOG.warn("Unknown mimetype for "+request.getRequestURI());
980 MultiPartOutputStream multi = new MultiPartOutputStream(out);
981 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
982
983
984
985
986 String ctp;
987 if (request.getHeader(HttpHeaders.REQUEST_RANGE)!=null)
988 ctp = "multipart/x-byteranges; boundary=";
989 else
990 ctp = "multipart/byteranges; boundary=";
991 response.setContentType(ctp+multi.getBoundary());
992
993 InputStream in=resource.getInputStream();
994 long pos=0;
995
996
997 int length=0;
998 String[] header = new String[ranges.size()];
999 for (int i=0;i<ranges.size();i++)
1000 {
1001 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
1002 header[i]=ibr.toHeaderRangeString(content_length);
1003 length+=
1004 ((i>0)?2:0)+
1005 2+multi.getBoundary().length()+2+
1006 (mimetype==null?0:HttpHeaders.CONTENT_TYPE.length()+2+mimetype.length())+2+
1007 HttpHeaders.CONTENT_RANGE.length()+2+header[i].length()+2+
1008 2+
1009 (ibr.getLast(content_length)-ibr.getFirst(content_length))+1;
1010 }
1011 length+=2+2+multi.getBoundary().length()+2+2;
1012 response.setContentLength(length);
1013
1014 for (int i=0;i<ranges.size();i++)
1015 {
1016 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
1017 multi.startPart(mimetype,new String[]{HttpHeaders.CONTENT_RANGE+": "+header[i]});
1018
1019 long start=ibr.getFirst(content_length);
1020 long size=ibr.getSize(content_length);
1021 if (in!=null)
1022 {
1023
1024 if (start<pos)
1025 {
1026 in.close();
1027 in=resource.getInputStream();
1028 pos=0;
1029 }
1030 if (pos<start)
1031 {
1032 in.skip(start-pos);
1033 pos=start;
1034 }
1035 IO.copy(in,multi,size);
1036 pos+=size;
1037 }
1038 else
1039
1040 (resource).writeTo(multi,start,size);
1041
1042 }
1043 if (in!=null)
1044 in.close();
1045 multi.close();
1046 }
1047 return;
1048 }
1049
1050
1051 protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
1052 throws IOException
1053 {
1054 if (content.getContentType()!=null && response.getContentType()==null)
1055 response.setContentType(content.getContentType().toString());
1056
1057 if (response instanceof Response)
1058 {
1059 Response r=(Response)response;
1060 HttpFields fields = r.getHttpFields();
1061
1062 if (content.getLastModified()!=null)
1063 fields.put(HttpHeaders.LAST_MODIFIED_BUFFER,content.getLastModified());
1064 else if (content.getResource()!=null)
1065 {
1066 long lml=content.getResource().lastModified();
1067 if (lml!=-1)
1068 fields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
1069 }
1070
1071 if (count != -1)
1072 r.setLongContentLength(count);
1073
1074 writeOptionHeaders(fields);
1075
1076 if (_etags)
1077 fields.put(HttpHeaders.ETAG_BUFFER,content.getETag());
1078 }
1079 else
1080 {
1081 long lml=content.getResource().lastModified();
1082 if (lml>=0)
1083 response.setDateHeader(HttpHeaders.LAST_MODIFIED,lml);
1084
1085 if (count != -1)
1086 {
1087 if (count<Integer.MAX_VALUE)
1088 response.setContentLength((int)count);
1089 else
1090 response.setHeader(HttpHeaders.CONTENT_LENGTH,Long.toString(count));
1091 }
1092
1093 writeOptionHeaders(response);
1094
1095 if (_etags)
1096 response.setHeader(HttpHeaders.ETAG,content.getETag().toString());
1097 }
1098 }
1099
1100
1101 protected void writeOptionHeaders(HttpFields fields) throws IOException
1102 {
1103 if (_acceptRanges)
1104 fields.put(HttpHeaders.ACCEPT_RANGES_BUFFER,HttpHeaderValues.BYTES_BUFFER);
1105
1106 if (_cacheControl!=null)
1107 fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl);
1108 }
1109
1110
1111 protected void writeOptionHeaders(HttpServletResponse response) throws IOException
1112 {
1113 if (_acceptRanges)
1114 response.setHeader(HttpHeaders.ACCEPT_RANGES,"bytes");
1115
1116 if (_cacheControl!=null)
1117 response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString());
1118 }
1119
1120
1121
1122
1123
1124
1125
1126 @Override
1127 public void destroy()
1128 {
1129 if (_cache!=null)
1130 _cache.flushCache();
1131 super.destroy();
1132 }
1133
1134 }