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 }