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.addHeader(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 if (_etags)
760 r.getHttpFields().add(HttpHeaders.ETAG_BUFFER,content.getETag());
761 r.flushBuffer();
762 return false;
763 }
764 }
765 }
766
767 long ifmsl=request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
768 if (ifmsl!=-1)
769 {
770 if (resource.lastModified()/1000 <= ifmsl/1000)
771 {
772 r.reset(true);
773 r.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
774 if (_etags)
775 r.getHttpFields().add(HttpHeaders.ETAG_BUFFER,content.getETag());
776 r.flushBuffer();
777 return false;
778 }
779 }
780 }
781
782
783 long date=request.getDateHeader(HttpHeaders.IF_UNMODIFIED_SINCE);
784
785 if (date!=-1)
786 {
787 if (resource.lastModified()/1000 > date/1000)
788 {
789 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
790 return false;
791 }
792 }
793
794 }
795 }
796 catch(IllegalArgumentException iae)
797 {
798 if(!response.isCommitted())
799 response.sendError(400, iae.getMessage());
800 throw iae;
801 }
802 return true;
803 }
804
805
806
807 protected void sendDirectory(HttpServletRequest request,
808 HttpServletResponse response,
809 Resource resource,
810 String pathInContext)
811 throws IOException
812 {
813 if (!_dirAllowed)
814 {
815 response.sendError(HttpServletResponse.SC_FORBIDDEN);
816 return;
817 }
818
819 byte[] data=null;
820 String base = URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH);
821
822
823 if (_resourceBase != null)
824 {
825
826 if (_resourceBase instanceof ResourceCollection)
827 resource=_resourceBase.addPath(pathInContext);
828 }
829
830 else if (_contextHandler.getBaseResource() instanceof ResourceCollection)
831 resource=_contextHandler.getBaseResource().addPath(pathInContext);
832
833 String dir = resource.getListHTML(base,pathInContext.length()>1);
834 if (dir==null)
835 {
836 response.sendError(HttpServletResponse.SC_FORBIDDEN,
837 "No directory");
838 return;
839 }
840
841 data=dir.getBytes("UTF-8");
842 response.setContentType("text/html; charset=UTF-8");
843 response.setContentLength(data.length);
844 response.getOutputStream().write(data);
845 }
846
847
848 protected void sendData(HttpServletRequest request,
849 HttpServletResponse response,
850 boolean include,
851 Resource resource,
852 HttpContent content,
853 Enumeration reqRanges)
854 throws IOException
855 {
856 boolean direct;
857 long content_length;
858 if (content==null)
859 {
860 direct=false;
861 content_length=resource.length();
862 }
863 else
864 {
865 Connector connector = AbstractHttpConnection.getCurrentConnection().getConnector();
866 direct=connector instanceof NIOConnector && ((NIOConnector)connector).getUseDirectBuffers() && !(connector instanceof SslConnector);
867 content_length=content.getContentLength();
868 }
869
870
871
872 OutputStream out =null;
873 boolean written;
874 try
875 {
876 out = response.getOutputStream();
877
878
879 written = out instanceof HttpOutput
880 ? ((HttpOutput)out).isWritten()
881 : AbstractHttpConnection.getCurrentConnection().getGenerator().isWritten();
882 }
883 catch(IllegalStateException e)
884 {
885 out = new WriterOutputStream(response.getWriter());
886 written=true;
887 }
888
889 if ( reqRanges == null || !reqRanges.hasMoreElements() || content_length<0)
890 {
891
892 if (include)
893 {
894 resource.writeTo(out,0,content_length);
895 }
896 else
897 {
898
899 if (content!=null && !written && out instanceof HttpOutput)
900 {
901 if (response instanceof Response)
902 {
903 writeOptionHeaders(((Response)response).getHttpFields());
904 ((AbstractHttpConnection.Output)out).sendContent(content);
905 }
906 else
907 {
908 Buffer buffer = direct?content.getDirectBuffer():content.getIndirectBuffer();
909 if (buffer!=null)
910 {
911 writeHeaders(response,content,content_length);
912 ((AbstractHttpConnection.Output)out).sendContent(buffer);
913 }
914 else
915 {
916 writeHeaders(response,content,content_length);
917 resource.writeTo(out,0,content_length);
918 }
919 }
920 }
921 else
922 {
923
924 writeHeaders(response,content,written?-1:content_length);
925
926
927 Buffer buffer = (content==null)?null:content.getIndirectBuffer();
928 if (buffer!=null)
929 buffer.writeTo(out);
930 else
931 resource.writeTo(out,0,content_length);
932 }
933 }
934 }
935 else
936 {
937
938 List ranges =InclusiveByteRange.satisfiableRanges(reqRanges,content_length);
939
940
941 if (ranges==null || ranges.size()==0)
942 {
943 writeHeaders(response, content, content_length);
944 response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
945 response.setHeader(HttpHeaders.CONTENT_RANGE,
946 InclusiveByteRange.to416HeaderRangeString(content_length));
947 resource.writeTo(out,0,content_length);
948 return;
949 }
950
951
952
953 if ( ranges.size()== 1)
954 {
955 InclusiveByteRange singleSatisfiableRange =
956 (InclusiveByteRange)ranges.get(0);
957 long singleLength = singleSatisfiableRange.getSize(content_length);
958 writeHeaders(response,content,singleLength );
959 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
960 response.setHeader(HttpHeaders.CONTENT_RANGE,
961 singleSatisfiableRange.toHeaderRangeString(content_length));
962 resource.writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
963 return;
964 }
965
966
967
968
969
970 writeHeaders(response,content,-1);
971 String mimetype=(content.getContentType()==null?null:content.getContentType().toString());
972 if (mimetype==null)
973 LOG.warn("Unknown mimetype for "+request.getRequestURI());
974 MultiPartOutputStream multi = new MultiPartOutputStream(out);
975 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
976
977
978
979
980 String ctp;
981 if (request.getHeader(HttpHeaders.REQUEST_RANGE)!=null)
982 ctp = "multipart/x-byteranges; boundary=";
983 else
984 ctp = "multipart/byteranges; boundary=";
985 response.setContentType(ctp+multi.getBoundary());
986
987 InputStream in=resource.getInputStream();
988 long pos=0;
989
990
991 int length=0;
992 String[] header = new String[ranges.size()];
993 for (int i=0;i<ranges.size();i++)
994 {
995 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
996 header[i]=ibr.toHeaderRangeString(content_length);
997 length+=
998 ((i>0)?2:0)+
999 2+multi.getBoundary().length()+2+
1000 (mimetype==null?0:HttpHeaders.CONTENT_TYPE.length()+2+mimetype.length())+2+
1001 HttpHeaders.CONTENT_RANGE.length()+2+header[i].length()+2+
1002 2+
1003 (ibr.getLast(content_length)-ibr.getFirst(content_length))+1;
1004 }
1005 length+=2+2+multi.getBoundary().length()+2+2;
1006 response.setContentLength(length);
1007
1008 for (int i=0;i<ranges.size();i++)
1009 {
1010 InclusiveByteRange ibr = (InclusiveByteRange) ranges.get(i);
1011 multi.startPart(mimetype,new String[]{HttpHeaders.CONTENT_RANGE+": "+header[i]});
1012
1013 long start=ibr.getFirst(content_length);
1014 long size=ibr.getSize(content_length);
1015 if (in!=null)
1016 {
1017
1018 if (start<pos)
1019 {
1020 in.close();
1021 in=resource.getInputStream();
1022 pos=0;
1023 }
1024 if (pos<start)
1025 {
1026 in.skip(start-pos);
1027 pos=start;
1028 }
1029 IO.copy(in,multi,size);
1030 pos+=size;
1031 }
1032 else
1033
1034 (resource).writeTo(multi,start,size);
1035
1036 }
1037 if (in!=null)
1038 in.close();
1039 multi.close();
1040 }
1041 return;
1042 }
1043
1044
1045 protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
1046 throws IOException
1047 {
1048 if (content.getContentType()!=null && response.getContentType()==null)
1049 response.setContentType(content.getContentType().toString());
1050
1051 if (response instanceof Response)
1052 {
1053 Response r=(Response)response;
1054 HttpFields fields = r.getHttpFields();
1055
1056 if (content.getLastModified()!=null)
1057 fields.put(HttpHeaders.LAST_MODIFIED_BUFFER,content.getLastModified());
1058 else if (content.getResource()!=null)
1059 {
1060 long lml=content.getResource().lastModified();
1061 if (lml!=-1)
1062 fields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
1063 }
1064
1065 if (count != -1)
1066 r.setLongContentLength(count);
1067
1068 writeOptionHeaders(fields);
1069
1070 if (_etags)
1071 fields.put(HttpHeaders.ETAG_BUFFER,content.getETag());
1072 }
1073 else
1074 {
1075 long lml=content.getResource().lastModified();
1076 if (lml>=0)
1077 response.setDateHeader(HttpHeaders.LAST_MODIFIED,lml);
1078
1079 if (count != -1)
1080 {
1081 if (count<Integer.MAX_VALUE)
1082 response.setContentLength((int)count);
1083 else
1084 response.setHeader(HttpHeaders.CONTENT_LENGTH,Long.toString(count));
1085 }
1086
1087 writeOptionHeaders(response);
1088
1089 if (_etags)
1090 response.setHeader(HttpHeaders.ETAG,content.getETag().toString());
1091 }
1092 }
1093
1094
1095 protected void writeOptionHeaders(HttpFields fields) throws IOException
1096 {
1097 if (_acceptRanges)
1098 fields.put(HttpHeaders.ACCEPT_RANGES_BUFFER,HttpHeaderValues.BYTES_BUFFER);
1099
1100 if (_cacheControl!=null)
1101 fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl);
1102 }
1103
1104
1105 protected void writeOptionHeaders(HttpServletResponse response) throws IOException
1106 {
1107 if (_acceptRanges)
1108 response.setHeader(HttpHeaders.ACCEPT_RANGES,"bytes");
1109
1110 if (_cacheControl!=null)
1111 response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString());
1112 }
1113
1114
1115
1116
1117
1118
1119
1120 @Override
1121 public void destroy()
1122 {
1123 if (_cache!=null)
1124 _cache.flushCache();
1125 super.destroy();
1126 }
1127
1128 }