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