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