View Javadoc

1   // ========================================================================
2   // Copyright 1999-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.jetty.handler;
16  
17  import java.io.IOException;
18  import java.io.OutputStream;
19  import java.net.MalformedURLException;
20  
21  import javax.servlet.ServletException;
22  import javax.servlet.http.HttpServletRequest;
23  import javax.servlet.http.HttpServletResponse;
24  
25  import org.mortbay.io.Buffer;
26  import org.mortbay.io.ByteArrayBuffer;
27  import org.mortbay.io.WriterOutputStream;
28  import org.mortbay.jetty.HttpConnection;
29  import org.mortbay.jetty.HttpFields;
30  import org.mortbay.jetty.HttpHeaders;
31  import org.mortbay.jetty.HttpMethods;
32  import org.mortbay.jetty.MimeTypes;
33  import org.mortbay.jetty.Request;
34  import org.mortbay.jetty.Response;
35  import org.mortbay.jetty.handler.ContextHandler.SContext;
36  import org.mortbay.log.Log;
37  import org.mortbay.resource.Resource;
38  import org.mortbay.util.TypeUtil;
39  import org.mortbay.util.URIUtil;
40  
41  
42  /* ------------------------------------------------------------ */
43  /** Resource Handler.
44   * 
45   * This handle will serve static content and handle If-Modified-Since headers.
46   * No caching is done.
47   * Requests that cannot be handled are let pass (Eg no 404's)
48   * 
49   * @author Greg Wilkins (gregw)
50   * @org.apache.xbean.XBean
51   */
52  public class ResourceHandler extends AbstractHandler
53  {
54      ContextHandler _context;
55      Resource _baseResource;
56      String[] _welcomeFiles={"index.html"};
57      MimeTypes _mimeTypes = new MimeTypes();
58      ByteArrayBuffer _cacheControl;
59  
60      /* ------------------------------------------------------------ */
61      public ResourceHandler()
62      {
63      }
64  
65      /* ------------------------------------------------------------ */
66      public MimeTypes getMimeTypes()
67      {
68          return _mimeTypes;
69      }
70  
71      /* ------------------------------------------------------------ */
72      public void setMimeTypes(MimeTypes mimeTypes)
73      {
74          _mimeTypes = mimeTypes;
75      }
76      
77      /* ------------------------------------------------------------ */
78      public void doStart()
79      throws Exception
80      {
81          SContext scontext = ContextHandler.getCurrentContext();
82          _context = (scontext==null?null:scontext.getContextHandler());
83          super.doStart();
84      }
85  
86      /* ------------------------------------------------------------ */
87      /**
88       * @return Returns the resourceBase.
89       */
90      public Resource getBaseResource()
91      {
92          if (_baseResource==null)
93              return null;
94          return _baseResource;
95      }
96  
97      /* ------------------------------------------------------------ */
98      /**
99       * @return Returns the base resource as a string.
100      */
101     public String getResourceBase()
102     {
103         if (_baseResource==null)
104             return null;
105         return _baseResource.toString();
106     }
107 
108     
109     /* ------------------------------------------------------------ */
110     /**
111      * @param base The resourceBase to set.
112      */
113     public void setBaseResource(Resource base) 
114     {
115         _baseResource=base;
116     }
117 
118     /* ------------------------------------------------------------ */
119     /**
120      * @param resourceBase The base resource as a string.
121      */
122     public void setResourceBase(String resourceBase) 
123     {
124         try
125         {
126             setBaseResource(Resource.newResource(resourceBase));
127         }
128         catch (Exception e)
129         {
130             Log.warn(e);
131             throw new IllegalArgumentException(resourceBase);
132         }
133     }
134 
135     /* ------------------------------------------------------------ */
136     /**
137      * @return the cacheControl header to set on all static content.
138      */
139     public String getCacheControl()
140     {
141         return _cacheControl.toString();
142     }
143 
144     /* ------------------------------------------------------------ */
145     /**
146      * @param cacheControl the cacheControl header to set on all static content.
147      */
148     public void setCacheControl(String cacheControl)
149     {
150         _cacheControl=cacheControl==null?null:new ByteArrayBuffer(cacheControl);
151     }
152 
153     /* ------------------------------------------------------------ */
154     /* 
155      */
156     public Resource getResource(String path) throws MalformedURLException
157     {
158         if (path==null || !path.startsWith("/"))
159             throw new MalformedURLException(path);
160         
161         Resource base = _baseResource;
162         if (base==null)
163         {
164             if (_context==null)
165                 return null;            
166             base=_context.getBaseResource();
167             if (base==null)
168                 return null;
169         }
170 
171         try
172         {
173             path=URIUtil.canonicalPath(path);
174             Resource resource=base.addPath(path);
175             return resource;
176         }
177         catch(Exception e)
178         {
179             Log.ignore(e);
180         }
181                     
182         return null;
183     }
184 
185     /* ------------------------------------------------------------ */
186     protected Resource getResource(HttpServletRequest request) throws MalformedURLException
187     {
188         String path_info=request.getPathInfo();
189         if (path_info==null)
190             return null;
191         return getResource(path_info);
192     }
193 
194 
195     /* ------------------------------------------------------------ */
196     public String[] getWelcomeFiles()
197     {
198         return _welcomeFiles;
199     }
200 
201     /* ------------------------------------------------------------ */
202     public void setWelcomeFiles(String[] welcomeFiles)
203     {
204         _welcomeFiles=welcomeFiles;
205     }
206     
207     /* ------------------------------------------------------------ */
208     protected Resource getWelcome(Resource directory) throws MalformedURLException, IOException
209     {
210         for (int i=0;i<_welcomeFiles.length;i++)
211         {
212             Resource welcome=directory.addPath(_welcomeFiles[i]);
213             if (welcome.exists() && !welcome.isDirectory())
214                 return welcome;
215         }
216 
217         return null;
218     }
219 
220     /* ------------------------------------------------------------ */
221     /* 
222      * @see org.mortbay.jetty.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
223      */
224     public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException
225     {
226         Request base_request = request instanceof Request?(Request)request:HttpConnection.getCurrentConnection().getRequest();
227         if (base_request.isHandled())
228             return;
229         
230         boolean skipContentBody = false;
231         if(!HttpMethods.GET.equals(request.getMethod()))
232         {
233             if(!HttpMethods.HEAD.equals(request.getMethod()))
234                 return;
235             skipContentBody = true;
236         }
237      
238         Resource resource=getResource(request);
239         
240         if (resource==null || !resource.exists())
241             return;
242 
243         // We are going to server something
244         base_request.setHandled(true);
245         
246         if (resource.isDirectory())
247         {
248             if (!request.getPathInfo().endsWith(URIUtil.SLASH))
249             {
250                 response.sendRedirect(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH));
251                 return;
252             }
253             resource=getWelcome(resource);
254 
255             if (resource==null || !resource.exists() || resource.isDirectory())
256             {
257                 response.sendError(HttpServletResponse.SC_FORBIDDEN);
258                 return;
259             }
260         }
261         
262         // set some headers
263         long last_modified=resource.lastModified();
264         if (last_modified>0)
265         {
266             long if_modified=request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
267             if (if_modified>0 && last_modified/1000<=if_modified/1000)
268             {
269                 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
270                 return;
271             }
272         }
273         
274         Buffer mime=_mimeTypes.getMimeByExtension(resource.toString());
275         if (mime==null)
276             mime=_mimeTypes.getMimeByExtension(request.getPathInfo());
277         
278         // set the headers
279         doResponseHeaders(response,resource,mime!=null?mime.toString():null);
280         response.setDateHeader(HttpHeaders.LAST_MODIFIED,last_modified);
281         if(skipContentBody)
282             return;
283         // Send the content
284         OutputStream out =null;
285         try {out = response.getOutputStream();}
286         catch(IllegalStateException e) {out = new WriterOutputStream(response.getWriter());}
287         
288         // See if a short direct method can be used?
289         if (out instanceof HttpConnection.Output)
290         {
291             // TODO file mapped buffers
292             ((HttpConnection.Output)out).sendContent(resource.getInputStream());
293         }
294         else
295         {
296             // Write content normally
297             resource.writeTo(out,0,resource.length());
298         }
299     }
300 
301     /* ------------------------------------------------------------ */
302     /** Set the response headers.
303      * This method is called to set the response headers such as content type and content length.
304      * May be extended to add additional headers.
305      * @param response
306      * @param resource
307      * @param mimeType
308      */
309     protected void doResponseHeaders(HttpServletResponse response, Resource resource, String mimeType)
310     {
311         if (mimeType!=null)
312             response.setContentType(mimeType);
313 
314         long length=resource.length();
315         
316         if (response instanceof Response)
317         {
318             HttpFields fields = ((Response)response).getHttpFields();
319 
320             if (length>0)
321                 fields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,length);
322                 
323             if (_cacheControl!=null)
324                 fields.put(HttpHeaders.CACHE_CONTROL_BUFFER,_cacheControl);
325         }
326         else
327         {
328             if (length>0)
329                 response.setHeader(HttpHeaders.CONTENT_LENGTH,TypeUtil.toString(length));
330                 
331             if (_cacheControl!=null)
332                 response.setHeader(HttpHeaders.CACHE_CONTROL,_cacheControl.toString());
333         }
334         
335     }
336 }