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()) LOG.debug("resource base = "+_resourceBase);
284 }
285
286
287
288
289
290
291
292
293
294 protected ContextHandler initContextHandler(ServletContext servletContext)
295 {
296 ContextHandler.Context scontext=ContextHandler.getCurrentContext();
297 if (scontext==null)
298 {
299 if (servletContext instanceof ContextHandler.Context)
300 return ((ContextHandler.Context)servletContext).getContextHandler();
301 else
302 throw new IllegalArgumentException("The servletContext " + servletContext + " " +
303 servletContext.getClass().getName() + " is not " + ContextHandler.Context.class.getName());
304 }
305 else
306 return ContextHandler.getCurrentContext().getContextHandler();
307 }
308
309
310 @Override
311 public String getInitParameter(String name)
312 {
313 String value=getServletContext().getInitParameter("org.eclipse.jetty.servlet.Default."+name);
314 if (value==null)
315 value=super.getInitParameter(name);
316 return value;
317 }
318
319
320 private boolean getInitBoolean(String name, boolean dft)
321 {
322 String value=getInitParameter(name);
323 if (value==null || value.length()==0)
324 return dft;
325 return (value.startsWith("t")||
326 value.startsWith("T")||
327 value.startsWith("y")||
328 value.startsWith("Y")||
329 value.startsWith("1"));
330 }
331
332
333 private int getInitInt(String name, int dft)
334 {
335 String value=getInitParameter(name);
336 if (value==null)
337 value=getInitParameter(name);
338 if (value!=null && value.length()>0)
339 return Integer.parseInt(value);
340 return dft;
341 }
342
343
344
345
346
347
348
349
350
351 public Resource getResource(String pathInContext)
352 {
353 Resource r=null;
354 if (_relativeResourceBase!=null)
355 pathInContext=URIUtil.addPaths(_relativeResourceBase,pathInContext);
356
357 try
358 {
359 if (_resourceBase!=null)
360 {
361 r = _resourceBase.addPath(pathInContext);
362 }
363 else
364 {
365 URL u = _servletContext.getResource(pathInContext);
366 r = _contextHandler.newResource(u);
367 }
368
369 if (LOG.isDebugEnabled())
370 LOG.debug("Resource "+pathInContext+"="+r);
371 }
372 catch (IOException e)
373 {
374 LOG.ignore(e);
375 }
376
377 if((r==null || !r.exists()) && pathInContext.endsWith("/jetty-dir.css"))
378 r=_stylesheet;
379
380 return r;
381 }
382
383
384 @Override
385 protected void doGet(HttpServletRequest request, HttpServletResponse response)
386 throws ServletException, IOException
387 {
388 String servletPath=null;
389 String pathInfo=null;
390 Enumeration<String> reqRanges = null;
391 Boolean included =request.getAttribute(Dispatcher.INCLUDE_REQUEST_URI)!=null;
392 if (included!=null && included.booleanValue())
393 {
394 servletPath=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
395 pathInfo=(String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
396 if (servletPath==null)
397 {
398 servletPath=request.getServletPath();
399 pathInfo=request.getPathInfo();
400 }
401 }
402 else
403 {
404 included = Boolean.FALSE;
405 servletPath = _pathInfoOnly?"/":request.getServletPath();
406 pathInfo = request.getPathInfo();
407
408
409 reqRanges = request.getHeaders(HttpHeaders.RANGE);
410 if (!hasDefinedRange(reqRanges))
411 reqRanges = null;
412 }
413
414 String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
415 boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH);
416
417
418 String pathInContextGz=null;
419 boolean gzip=false;
420 if (!included.booleanValue() && _gzip && reqRanges==null && !endsWithSlash )
421 {
422 String accept=request.getHeader(HttpHeaders.ACCEPT_ENCODING);
423 if (accept!=null && accept.indexOf("gzip")>=0)
424 gzip=true;
425 }
426
427
428 Resource resource=null;
429 HttpContent content=null;
430
431 try
432 {
433
434 if (gzip)
435 {
436 pathInContextGz=pathInContext+".gz";
437
438 if (_cache==null)
439 {
440 resource=getResource(pathInContextGz);
441 }
442 else
443 {
444 content=_cache.lookup(pathInContextGz);
445 resource=(content==null)?null:content.getResource();
446 }
447
448 if (resource==null || !resource.exists() || resource.isDirectory())
449 {
450 gzip=false;
451 pathInContextGz=null;
452 }
453 }
454
455
456 if (!gzip)
457 {
458 if (_cache==null)
459 resource=getResource(pathInContext);
460 else
461 {
462 content=_cache.lookup(pathInContext);
463 resource=content==null?null:content.getResource();
464 }
465 }
466
467 if (LOG.isDebugEnabled())
468 LOG.debug("uri="+request.getRequestURI()+" resource="+resource+(content!=null?" content":""));
469
470
471 if (resource==null || !resource.exists())
472 {
473 if (included)
474 throw new FileNotFoundException("!" + pathInContext);
475 response.sendError(HttpServletResponse.SC_NOT_FOUND);
476 }
477 else if (!resource.isDirectory())
478 {
479 if (endsWithSlash && _contextHandler.isAliases() && pathInContext.length()>1)
480 {
481 String q=request.getQueryString();
482 pathInContext=pathInContext.substring(0,pathInContext.length()-1);
483 if (q!=null&&q.length()!=0)
484 pathInContext+="?"+q;
485 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(_servletContext.getContextPath(),pathInContext)));
486 }
487 else
488 {
489
490 if (content==null)
491 content=new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()),response.getBufferSize());
492
493 if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
494 {
495 if (gzip)
496 {
497 response.setHeader(HttpHeaders.CONTENT_ENCODING,"gzip");
498 String mt=_servletContext.getMimeType(pathInContext);
499 if (mt!=null)
500 response.setContentType(mt);
501 }
502 sendData(request,response,included.booleanValue(),resource,content,reqRanges);
503 }
504 }
505 }
506 else
507 {
508 String welcome=null;
509
510 if (!endsWithSlash || (pathInContext.length()==1 && request.getAttribute("org.eclipse.jetty.server.nullPathInfo")!=null))
511 {
512 StringBuffer buf=request.getRequestURL();
513 synchronized(buf)
514 {
515 int param=buf.lastIndexOf(";");
516 if (param<0)
517 buf.append('/');
518 else
519 buf.insert(param,'/');
520 String q=request.getQueryString();
521 if (q!=null&&q.length()!=0)
522 {
523 buf.append('?');
524 buf.append(q);
525 }
526 response.setContentLength(0);
527 response.sendRedirect(response.encodeRedirectURL(buf.toString()));
528 }
529 }
530
531 else if (null!=(welcome=getWelcomeFile(pathInContext)))
532 {
533 LOG.debug("welcome={}",welcome);
534 if (_redirectWelcome)
535 {
536
537 response.setContentLength(0);
538 String q=request.getQueryString();
539 if (q!=null&&q.length()!=0)
540 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)+"?"+q));
541 else
542 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths( _servletContext.getContextPath(),welcome)));
543 }
544 else
545 {
546
547 RequestDispatcher dispatcher=request.getRequestDispatcher(welcome);
548 if (dispatcher!=null)
549 {
550 if (included.booleanValue())
551 dispatcher.include(request,response);
552 else
553 {
554 request.setAttribute("org.eclipse.jetty.server.welcome",welcome);
555 dispatcher.forward(request,response);
556 }
557 }
558 }
559 }
560 else
561 {
562 content=new HttpContent.ResourceAsHttpContent(resource,_mimeTypes.getMimeByExtension(resource.toString()));
563 if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
564 sendDirectory(request,response,resource,pathInContext);
565 }
566 }
567 }
568 catch(IllegalArgumentException e)
569 {
570 LOG.warn(Log.EXCEPTION,e);
571 if(!response.isCommitted())
572 response.sendError(500, e.getMessage());
573 }
574 finally
575 {
576 if (content!=null)
577 content.release();
578 else if (resource!=null)
579 resource.release();
580 }
581
582 }
583
584
585 private boolean hasDefinedRange(Enumeration<String> reqRanges)
586 {
587 return (reqRanges!=null && reqRanges.hasMoreElements());
588 }
589
590
591 @Override
592 protected void doPost(HttpServletRequest request, HttpServletResponse response)
593 throws ServletException, IOException
594 {
595 doGet(request,response);
596 }
597
598
599
600
601
602 @Override
603 protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
604 {
605 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
606 }
607
608
609 @Override
610 protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
611 throws ServletException, IOException
612 {
613 resp.setHeader("Allow", "GET,HEAD,POST,OPTIONS");
614 }
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629 private String getWelcomeFile(String pathInContext) throws MalformedURLException, IOException
630 {
631 if (_welcomes==null)
632 return null;
633
634 String welcome_servlet=null;
635 for (int i=0;i<_welcomes.length;i++)
636 {
637 String welcome_in_context=URIUtil.addPaths(pathInContext,_welcomes[i]);
638 Resource welcome=getResource(welcome_in_context);
639 if (welcome!=null && welcome.exists())
640 return _welcomes[i];
641
642 if ((_welcomeServlets || _welcomeExactServlets) && welcome_servlet==null)
643 {
644 Map.Entry entry=_servletHandler.getHolderEntry(welcome_in_context);
645 if (entry!=null && entry.getValue()!=_defaultHolder &&
646 (_welcomeServlets || (_welcomeExactServlets && entry.getKey().equals(welcome_in_context))))
647 welcome_servlet=welcome_in_context;
648
649 }
650 }
651 return welcome_servlet;
652 }
653
654
655
656
657 protected boolean passConditionalHeaders(HttpServletRequest request,HttpServletResponse response, Resource resource, HttpContent content)
658 throws IOException
659 {
660 try
661 {
662 if (!request.getMethod().equals(HttpMethods.HEAD) )
663 {
664 String ifms=request.getHeader(HttpHeaders.IF_MODIFIED_SINCE);
665 if (ifms!=null)
666 {
667 if (content!=null)
668 {
669 Buffer mdlm=content.getLastModified();
670 if (mdlm!=null)
671 {
672 if (ifms.equals(mdlm.toString()))
673 {
674 response.reset();
675 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
676 response.flushBuffer();
677 return false;
678 }
679 }
680 }
681
682 long ifmsl=request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
683 if (ifmsl!=-1)
684 {
685 if (resource.lastModified()/1000 <= ifmsl/1000)
686 {
687 response.reset();
688 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
689 response.flushBuffer();
690 return false;
691 }
692 }
693 }
694
695
696 long date=request.getDateHeader(HttpHeaders.IF_UNMODIFIED_SINCE);
697
698 if (date!=-1)
699 {
700 if (resource.lastModified()/1000 > date/1000)
701 {
702 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
703 return false;
704 }
705 }
706
707 }
708 }
709 catch(IllegalArgumentException iae)
710 {
711 if(!response.isCommitted())
712 response.sendError(400, iae.getMessage());
713 throw iae;
714 }
715 return true;
716 }
717
718
719
720 protected void sendDirectory(HttpServletRequest request,
721 HttpServletResponse response,
722 Resource resource,
723 String pathInContext)
724 throws IOException
725 {
726 if (!_dirAllowed)
727 {
728 response.sendError(HttpServletResponse.SC_FORBIDDEN);
729 return;
730 }
731
732 byte[] data=null;
733 String base = URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH);
734
735
736 if (_resourceBase instanceof ResourceCollection)
737 resource=_resourceBase.addPath(pathInContext);
738 else if (_contextHandler.getBaseResource() instanceof ResourceCollection)
739 resource=_contextHandler.getBaseResource().addPath(pathInContext);
740
741 String dir = resource.getListHTML(base,pathInContext.length()>1);
742 if (dir==null)
743 {
744 response.sendError(HttpServletResponse.SC_FORBIDDEN,
745 "No directory");
746 return;
747 }
748
749 data=dir.getBytes("UTF-8");
750 response.setContentType("text/html; charset=UTF-8");
751 response.setContentLength(data.length);
752 response.getOutputStream().write(data);
753 }
754
755
756 protected void sendData(HttpServletRequest request,
757 HttpServletResponse response,
758 boolean include,
759 Resource resource,
760 HttpContent content,
761 Enumeration reqRanges)
762 throws IOException
763 {
764 boolean direct;
765 long content_length;
766 if (content==null)
767 {
768 direct=false;
769 content_length=resource.length();
770 }
771 else
772 {
773 Connector connector = HttpConnection.getCurrentConnection().getConnector();
774 direct=connector instanceof NIOConnector && ((NIOConnector)connector).getUseDirectBuffers() && !(connector instanceof SslConnector);
775 content_length=content.getContentLength();
776 }
777
778
779
780 OutputStream out =null;
781 boolean written;
782 try
783 {
784 out = response.getOutputStream();
785
786
787 written = out instanceof HttpOutput
788 ? ((HttpOutput)out).isWritten()
789 : HttpConnection.getCurrentConnection().getGenerator().isWritten();
790 }
791 catch(IllegalStateException e)
792 {
793 out = new WriterOutputStream(response.getWriter());
794 written=true;
795 }
796
797 if ( reqRanges == null || !reqRanges.hasMoreElements() || content_length<0)
798 {
799
800 if (include)
801 {
802 resource.writeTo(out,0,content_length);
803 }
804 else
805 {
806
807 if (content!=null && !written && out instanceof HttpOutput)
808 {
809 if (response instanceof Response)
810 {
811 writeOptionHeaders(((Response)response).getHttpFields());
812 ((HttpConnection.Output)out).sendContent(content);
813 }
814 else
815 {
816 Buffer buffer = direct?content.getDirectBuffer():content.getIndirectBuffer();
817 if (buffer!=null)
818 {
819 writeHeaders(response,content,content_length);
820 ((HttpConnection.Output)out).sendContent(buffer);
821 }
822 else
823 {
824 writeHeaders(response,content,content_length);
825 resource.writeTo(out,0,content_length);
826 }
827 }
828 }
829 else
830 {
831
832 writeHeaders(response,content,written?-1:content_length);
833
834
835 Buffer buffer = (content==null)?null:content.getIndirectBuffer();
836 if (buffer!=null)
837 buffer.writeTo(out);
838 else
839 resource.writeTo(out,0,content_length);
840 }
841 }
842 }
843 else
844 {
845
846 List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length);
847
848
849 if (ranges==null || ranges.size()==0)
850 {
851 writeHeaders(response, content, content_length);
852 response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
853 response.setHeader(HttpHeaders.CONTENT_RANGE,
854 InclusiveByteRange.to416HeaderRangeString(content_length));
855 resource.writeTo(out,0,content_length);
856 return;
857 }
858
859
860
861 if ( ranges.size()== 1)
862 {
863 InclusiveByteRange singleSatisfiableRange =
864 (InclusiveByteRange)ranges.get(0);
865 long singleLength = singleSatisfiableRange.getSize(content_length);
866 writeHeaders(response,content,singleLength );
867 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
868 response.setHeader(HttpHeaders.CONTENT_RANGE,
869 singleSatisfiableRange.toHeaderRangeString(content_length));
870 resource.writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
871 return;
872 }
873
874
875
876
877
878 writeHeaders(response,content,-1);
879 String mimetype=content.getContentType().toString();
880 MultiPartOutputStream multi = new MultiPartOutputStream(out);
881 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
882
883
884
885
886 String ctp;
887 if (request.getHeader(HttpHeaders.REQUEST_RANGE)!=null)
888 ctp = "multipart/x-byteranges; boundary=";
889 else
890 ctp = "multipart/byteranges; boundary=";
891 response.setContentType(ctp+multi.getBoundary());
892
893 InputStream in=resource.getInputStream();
894 long pos=0;
895
896
897 int length=0;
898 String[] header = new String[ranges.size()];
899 for (int i=0;i<ranges.size();i++)
900 {
901 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
902 header[i]=ibr.toHeaderRangeString(content_length);
903 length+=
904 ((i>0)?2:0)+
905 2+multi.getBoundary().length()+2+
906 HttpHeaders.CONTENT_TYPE.length()+2+mimetype.length()+2+
907 HttpHeaders.CONTENT_RANGE.length()+2+header[i].length()+2+
908 2+
909 (ibr.getLast(content_length)-ibr.getFirst(content_length))+1;
910 }
911 length+=2+2+multi.getBoundary().length()+2+2;
912 response.setContentLength(length);
913
914 for (int i=0;i<ranges.size();i++)
915 {
916 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
917 multi.startPart(mimetype,new String[]{HttpHeaders.CONTENT_RANGE+": "+header[i]});
918
919 long start=ibr.getFirst(content_length);
920 long size=ibr.getSize(content_length);
921 if (in!=null)
922 {
923
924 if (start<pos)
925 {
926 in.close();
927 in=resource.getInputStream();
928 pos=0;
929 }
930 if (pos<start)
931 {
932 in.skip(start-pos);
933 pos=start;
934 }
935 IO.copy(in,multi,size);
936 pos+=size;
937 }
938 else
939
940 (resource).writeTo(multi,start,size);
941
942 }
943 if (in!=null)
944 in.close();
945 multi.close();
946 }
947 return;
948 }
949
950
951 protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
952 throws IOException
953 {
954 if (content.getContentType()!=null && response.getContentType()==null)
955 response.setContentType(content.getContentType().toString());
956
957 if (response instanceof Response)
958 {
959 Response r=(Response)response;
960 HttpFields fields = r.getHttpFields();
961
962 if (content.getLastModified()!=null)
963 fields.put(HttpHeaders.LAST_MODIFIED_BUFFER,content.getLastModified());
964 else if (content.getResource()!=null)
965 {
966 long lml=content.getResource().lastModified();
967 if (lml!=-1)
968 fields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
969 }
970
971 if (count != -1)
972 r.setLongContentLength(count);
973
974 writeOptionHeaders(fields);
975 }
976 else
977 {
978 long lml=content.getResource().lastModified();
979 if (lml>=0)
980 response.setDateHeader(HttpHeaders.LAST_MODIFIED,lml);
981
982 if (count != -1)
983 {
984 if (count<Integer.MAX_VALUE)
985 response.setContentLength((int)count);
986 else
987 response.setHeader(HttpHeaders.CONTENT_LENGTH,Long.toString(count));
988 }
989
990 writeOptionHeaders(response);
991 }
992 }
993
994
995 protected void writeOptionHeaders(HttpFields fields) throws IOException
996 {
997 if (_acceptRanges)
998 fields.put(HttpHeaders.ACCEPT_RANGES_BUFFER,HttpHeaderValues.BYTES_BUFFER);
999
1000 if (_cacheControl!=null)
1001 fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl);
1002 }
1003
1004
1005 protected void writeOptionHeaders(HttpServletResponse response) throws IOException
1006 {
1007 if (_acceptRanges)
1008 response.setHeader(HttpHeaders.ACCEPT_RANGES,"bytes");
1009
1010 if (_cacheControl!=null)
1011 response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString());
1012 }
1013
1014
1015
1016
1017
1018 @Override
1019 public void destroy()
1020 {
1021 if (_cache!=null)
1022 _cache.flushCache();
1023 super.destroy();
1024 }
1025
1026 }