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