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