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