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