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