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