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