View Javadoc

    1   // ========================================================================
    2   // $Id: ProxyServlet.java 800 2006-08-20 00:01:46Z gregw $
    3   // Copyright 2004-2004 Mort Bay Consulting Pty. Ltd.
    4   // ------------------------------------------------------------------------
    5   // Licensed under the Apache License, Version 2.0 (the "License");
    6   // you may not use this file except in compliance with the License.
    7   // You may obtain a copy of the License at
    8   // http://www.apache.org/licenses/LICENSE-2.0
    9   // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  // ========================================================================
    15
    16  package org.mortbay.servlet;
    17
    18  import java.io.File;
    19  import java.io.FileOutputStream;
    20  import java.io.IOException;
    21  import java.io.InputStream;
    22  import java.io.OutputStream;
    23  import java.net.URI;
    24  import java.net.URISyntaxException;
    25  import java.util.HashSet;
    26  import java.util.Set;
    27  import java.util.concurrent.ConcurrentHashMap;
    28  import java.util.concurrent.ConcurrentMap;
    29
    30  import javax.servlet.Filter;
    31  import javax.servlet.FilterChain;
    32  import javax.servlet.FilterConfig;
    33  import javax.servlet.ServletContext;
    34  import javax.servlet.ServletException;
    35  import javax.servlet.ServletRequest;
    36  import javax.servlet.ServletResponse;
    37  import javax.servlet.UnavailableException;
    38  import javax.servlet.http.HttpServletRequest;
    39  import javax.servlet.http.HttpServletResponse;
    40
    41  import org.mortbay.util.IO;
    42  import org.mortbay.util.URIUtil;
    43
    44  /**
    45   * PutFilter
    46   *
    47   * A Filter that handles PUT, DELETE and MOVE methods.
    48   * Files are hidden during PUT operations, so that 404's result.
    49   *
    50   * The following init paramters pay be used:<ul>
    51   * <li><b>baseURI</b> - The file URI of the document root for put content.
    52   * <li><b>delAllowed</b> - boolean, if true DELETE and MOVE methods are supported.
    53   * </ul>
    54   *
    55   */
    56  public class PutFilter implements Filter
    57  {
    58      public final static String __PUT="PUT";
    59      public final static String __DELETE="DELETE";
    60      public final static String __MOVE="MOVE";
    61      public final static String __OPTIONS="OPTIONS";
    62
    63      Set _operations = new HashSet();
    64      protected ConcurrentMap _hidden = new ConcurrentHashMap();
    65
    66      protected ServletContext _context;
    67      protected String _baseURI;
    68      protected boolean _delAllowed;
    69
    70      /* ------------------------------------------------------------ */
    71      public void init(FilterConfig config) throws ServletException
    72      {
    73          _context=config.getServletContext();
    74          if (_context.getRealPath("/")==null)
    75             throw new UnavailableException("Packed war");
    76
    77          String b = config.getInitParameter("baseURI");
    78          if (b != null)
    79          {
    80              _baseURI=b;
    81          }
    82          else
    83          {
    84              File base=new File(_context.getRealPath("/"));
    85              _baseURI=base.toURI().toString();
    86          }
    87
    88          _delAllowed = getInitBoolean(config,"delAllowed");
    89
    90          _operations.add(__OPTIONS);
    91          _operations.add(__PUT);
    92          if (_delAllowed)
    93          {
    94              _operations.add(__DELETE);
    95              _operations.add(__MOVE);
    96          }
    97      }
    98
    99      /* ------------------------------------------------------------ */
    100     private boolean getInitBoolean(FilterConfig config,String name)
    101     {
    102         String value = config.getInitParameter(name);
    103         return value != null && value.length() > 0 && (value.startsWith("t") || value.startsWith("T") || value.startsWith("y") || value.startsWith("Y") || value.startsWith("1"));
    104     }
    105
    106     /* ------------------------------------------------------------ */
    107     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
    108     {
    109
    110         HttpServletRequest request=(HttpServletRequest)req;
    111         HttpServletResponse response=(HttpServletResponse)res;
    112
    113         String servletPath =request.getServletPath();
    114         String pathInfo = request.getPathInfo();
    115         String pathInContext = URIUtil.addPaths(servletPath, pathInfo);
    116
    117         String resource = URIUtil.addPaths(_baseURI,pathInContext);
    118
    119         String method = request.getMethod();
    120         boolean op = _operations.contains(method);
    121
    122         if (op)
    123         {
    124             File file = null;
    125             try
    126             {
    127                 if (method.equals(__OPTIONS))
    128                     handleOptions(request, response);
    129                 else
    130                 {
    131                     file=new File(new URI(resource));
    132                     boolean exists = file.exists();
    133                     if (exists && !passConditionalHeaders(request, response, file))
    134                         return;
    135
    136                     if (method.equals(__PUT))
    137                         handlePut(request, response,pathInContext, file);
    138                     else if (method.equals(__DELETE))
    139                         handleDelete(request, response, pathInContext, file);
    140                     else if (method.equals(__MOVE))
    141                         handleMove(request, response, pathInContext, file);
    142                     else
    143                         throw new IllegalStateException();
    144                 }
    145             }
    146             catch(Exception e)
    147             {
    148                 _context.log(e.toString(),e);
    149                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    150             }
    151         }
    152         else
    153         {
    154             if (isHidden(pathInContext))
    155                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
    156             else
    157                 chain.doFilter(request,response);
    158             return;
    159         }
    160     }
    161
    162     /* ------------------------------------------------------------ */
    163     private boolean isHidden(String pathInContext)
    164     {
    165         return _hidden.containsKey(pathInContext);
    166     }
    167
    168     /* ------------------------------------------------------------ */
    169     public void destroy()
    170     {
    171     }
    172
    173     /* ------------------------------------------------------------------- */
    174     public void handlePut(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file) throws ServletException, IOException
    175     {
    176         boolean exists = file.exists();
    177         if (pathInContext.endsWith("/"))
    178         {
    179             if (!exists)
    180             {
    181                 if (!file.mkdirs())
    182                     response.sendError(HttpServletResponse.SC_FORBIDDEN);
    183                 else
    184                 {
    185                     response.setStatus(HttpServletResponse.SC_CREATED);
    186                     response.flushBuffer();
    187                 }
    188             }
    189             else
    190             {
    191                 response.setStatus(HttpServletResponse.SC_OK);
    192                 response.flushBuffer();
    193             }
    194         }
    195         else
    196         {
    197             boolean ok=false;
    198             try
    199             {
    200                 _hidden.put(pathInContext,pathInContext);
    201                 File parent = file.getParentFile();
    202                 parent.mkdirs();
    203                 int toRead = request.getContentLength();
    204                 InputStream in = request.getInputStream();
    205                 OutputStream out = new FileOutputStream(file,false);
    206                 if (toRead >= 0)
    207                     IO.copy(in, out, toRead);
    208                 else
    209                     IO.copy(in, out);
    210                 out.close();
    211
    212                 response.setStatus(exists ? HttpServletResponse.SC_OK : HttpServletResponse.SC_CREATED);
    213                 response.flushBuffer();
    214                 ok=true;
    215             }
    216             catch (Exception ex)
    217             {
    218                 _context.log(ex.toString(),ex);
    219                 response.sendError(HttpServletResponse.SC_FORBIDDEN);
    220             }
    221             finally
    222             {
    223                 if (!ok)
    224                 {
    225                     try
    226                     {
    227                         if (file.exists())
    228                             file.delete();
    229                     }
    230                     catch(Exception e)
    231                     {
    232                         _context.log(e.toString(),e);
    233                     }
    234                 }
    235                 _hidden.remove(pathInContext);
    236             }
    237         }
    238     }
    239
    240     /* ------------------------------------------------------------------- */
    241     public void handleDelete(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file) throws ServletException, IOException
    242     {
    243         try
    244         {
    245             // delete the file
    246             if (file.delete())
    247             {
    248                 response.setStatus(HttpServletResponse.SC_NO_CONTENT);
    249                 response.flushBuffer();
    250             }
    251             else
    252                 response.sendError(HttpServletResponse.SC_FORBIDDEN);
    253         }
    254         catch (SecurityException sex)
    255         {
    256             _context.log(sex.toString(),sex);
    257             response.sendError(HttpServletResponse.SC_FORBIDDEN);
    258         }
    259     }
    260
    261     /* ------------------------------------------------------------------- */
    262     public void handleMove(HttpServletRequest request, HttpServletResponse response, String pathInContext, File file)
    263         throws ServletException, IOException, URISyntaxException
    264     {
    265         String newPath = URIUtil.canonicalPath(request.getHeader("new-uri"));
    266         if (newPath == null)
    267         {
    268             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
    269             return;
    270         }
    271
    272         String contextPath = request.getContextPath();
    273         if (contextPath != null && !newPath.startsWith(contextPath))
    274         {
    275             response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
    276             return;
    277         }
    278         String newInfo = newPath;
    279         if (contextPath != null)
    280             newInfo = newInfo.substring(contextPath.length());
    281
    282         String new_resource = URIUtil.addPaths(_baseURI,newInfo);
    283         File new_file=new File(new URI(new_resource));
    284
    285         file.renameTo(new_file);
    286
    287         response.setStatus(HttpServletResponse.SC_NO_CONTENT);
    288         response.flushBuffer();
    289
    290
    291     }
    292
    293     /* ------------------------------------------------------------ */
    294     public void handleOptions(HttpServletRequest request, HttpServletResponse response) throws IOException
    295     {
    296         // TODO implement
    297         throw new UnsupportedOperationException("Not Implemented");
    298     }
    299
    300     /* ------------------------------------------------------------ */
    301     /*
    302      * Check modification date headers.
    303      */
    304     protected boolean passConditionalHeaders(HttpServletRequest request, HttpServletResponse response, File file) throws IOException
    305     {
    306         long date = 0;
    307
    308         if ((date = request.getDateHeader("if-unmodified-since")) > 0)
    309         {
    310             if (file.lastModified() / 1000 > date / 1000)
    311             {
    312                 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
    313                 return false;
    314             }
    315         }
    316
    317         if ((date = request.getDateHeader("if-modified-since")) > 0)
    318         {
    319             if (file.lastModified() / 1000 <= date / 1000)
    320             {
    321                 response.reset();
    322                 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
    323                 response.flushBuffer();
    324                 return false;
    325             }
    326         }
    327         return true;
    328     }
    329 }