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