Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » connector » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.connector;
   20   
   21   import java.io.IOException;
   22   
   23   import org.apache.catalina.CometEvent;
   24   import org.apache.catalina.Context;
   25   import org.apache.catalina.Globals;
   26   import org.apache.catalina.Wrapper;
   27   import org.apache.catalina.util.StringManager;
   28   import org.apache.catalina.util.URLEncoder;
   29   import org.apache.coyote.ActionCode;
   30   import org.apache.coyote.Adapter;
   31   import org.apache.juli.logging.Log;
   32   import org.apache.juli.logging.LogFactory;
   33   import org.apache.tomcat.util.buf.B2CConverter;
   34   import org.apache.tomcat.util.buf.ByteChunk;
   35   import org.apache.tomcat.util.buf.CharChunk;
   36   import org.apache.tomcat.util.buf.MessageBytes;
   37   import org.apache.tomcat.util.http.Cookies;
   38   import org.apache.tomcat.util.http.ServerCookie;
   39   import org.apache.tomcat.util.net.SocketStatus;
   40   
   41   
   42   /**
   43    * Implementation of a request processor which delegates the processing to a
   44    * Coyote processor.
   45    *
   46    * @author Craig R. McClanahan
   47    * @author Remy Maucherat
   48    * @version $Revision: 610940 $ $Date: 2008-01-10 22:40:21 +0100 (jeu., 10 janv. 2008) $
   49    */
   50   
   51   public class CoyoteAdapter
   52       implements Adapter 
   53    {
   54       private static Log log = LogFactory.getLog(CoyoteAdapter.class);
   55   
   56       // -------------------------------------------------------------- Constants
   57   
   58   
   59       public static final int ADAPTER_NOTES = 1;
   60   
   61   
   62       protected static final boolean ALLOW_BACKSLASH = 
   63           Boolean.valueOf(System.getProperty("org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH", "false")).booleanValue();
   64   
   65   
   66       // ----------------------------------------------------------- Constructors
   67   
   68   
   69       /**
   70        * Construct a new CoyoteProcessor associated with the specified connector.
   71        *
   72        * @param connector CoyoteConnector that owns this processor
   73        */
   74       public CoyoteAdapter(Connector connector) {
   75   
   76           super();
   77           this.connector = connector;
   78   
   79       }
   80   
   81   
   82       // ----------------------------------------------------- Instance Variables
   83   
   84   
   85       /**
   86        * The CoyoteConnector with which this processor is associated.
   87        */
   88       private Connector connector = null;
   89   
   90   
   91       /**
   92        * The match string for identifying a session ID parameter.
   93        */
   94       private static final String match =
   95           ";" + Globals.SESSION_PARAMETER_NAME + "=";
   96   
   97   
   98       /**
   99        * The string manager for this package.
  100        */
  101       protected StringManager sm =
  102           StringManager.getManager(Constants.Package);
  103   
  104   
  105       /**
  106        * Encoder for the Location URL in HTTP redirects.
  107        */
  108       protected static URLEncoder urlEncoder;
  109   
  110   
  111       // ----------------------------------------------------- Static Initializer
  112   
  113   
  114       /**
  115        * The safe character set.
  116        */
  117       static {
  118           urlEncoder = new URLEncoder();
  119           urlEncoder.addSafeCharacter('-');
  120           urlEncoder.addSafeCharacter('_');
  121           urlEncoder.addSafeCharacter('.');
  122           urlEncoder.addSafeCharacter('*');
  123           urlEncoder.addSafeCharacter('/');
  124       }
  125   
  126   
  127       // -------------------------------------------------------- Adapter Methods
  128   
  129       
  130       /**
  131        * Event method.
  132        * 
  133        * @return false to indicate an error, expected or not
  134        */
  135       public boolean event(org.apache.coyote.Request req, 
  136               org.apache.coyote.Response res, SocketStatus status) {
  137   
  138           Request request = (Request) req.getNote(ADAPTER_NOTES);
  139           Response response = (Response) res.getNote(ADAPTER_NOTES);
  140   
  141           if (request.getWrapper() != null) {
  142               
  143               boolean error = false;
  144               boolean read = false;
  145               try {
  146                   if (status == SocketStatus.OPEN) {
  147                       if (response.isClosed()) {
  148                           // The event has been closed asynchronously, so call end instead of
  149                           // read to cleanup the pipeline
  150                           request.getEvent().setEventType(CometEvent.EventType.END);
  151                           request.getEvent().setEventSubType(null);
  152                       } else {
  153                           try {
  154                               // Fill the read buffer of the servlet layer
  155                               if (request.read()) {
  156                                   read = true;
  157                               }
  158                           } catch (IOException e) {
  159                               error = true;
  160                           }
  161                           if (read) {
  162                               request.getEvent().setEventType(CometEvent.EventType.READ);
  163                               request.getEvent().setEventSubType(null);
  164                           } else if (error) {
  165                               request.getEvent().setEventType(CometEvent.EventType.ERROR);
  166                               request.getEvent().setEventSubType(CometEvent.EventSubType.CLIENT_DISCONNECT);
  167                           } else {
  168                               request.getEvent().setEventType(CometEvent.EventType.END);
  169                               request.getEvent().setEventSubType(null);
  170                           }
  171                       }
  172                   } else if (status == SocketStatus.DISCONNECT) {
  173                       request.getEvent().setEventType(CometEvent.EventType.ERROR);
  174                       request.getEvent().setEventSubType(CometEvent.EventSubType.CLIENT_DISCONNECT);
  175                       error = true;
  176                   } else if (status == SocketStatus.ERROR) {
  177                       request.getEvent().setEventType(CometEvent.EventType.ERROR);
  178                       request.getEvent().setEventSubType(CometEvent.EventSubType.IOEXCEPTION);
  179                       error = true;
  180                   } else if (status == SocketStatus.STOP) {
  181                       request.getEvent().setEventType(CometEvent.EventType.END);
  182                       request.getEvent().setEventSubType(CometEvent.EventSubType.SERVER_SHUTDOWN);
  183                   } else if (status == SocketStatus.TIMEOUT) {
  184                       if (response.isClosed()) {
  185                           // The event has been closed asynchronously, so call end instead of
  186                           // read to cleanup the pipeline
  187                           request.getEvent().setEventType(CometEvent.EventType.END);
  188                           request.getEvent().setEventSubType(null);
  189                       } else {
  190                           request.getEvent().setEventType(CometEvent.EventType.ERROR);
  191                           request.getEvent().setEventSubType(CometEvent.EventSubType.TIMEOUT);
  192                       }
  193                   }
  194   
  195                   req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
  196                   
  197                   // Calling the container
  198                   connector.getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
  199   
  200                   if (!error && !response.isClosed() && (request.getAttribute(Globals.EXCEPTION_ATTR) != null)) {
  201                       // An unexpected exception occurred while processing the event, so
  202                       // error should be called
  203                       request.getEvent().setEventType(CometEvent.EventType.ERROR);
  204                       request.getEvent().setEventSubType(null);
  205                       error = true;
  206                       connector.getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
  207                   }
  208                   if (response.isClosed() || !request.isComet()) {
  209                       res.action(ActionCode.ACTION_COMET_END, null);
  210                   } else if (!error && read && request.getAvailable()) {
  211                       // If this was a read and not all bytes have been read, or if no data
  212                       // was read from the connector, then it is an error
  213                       request.getEvent().setEventType(CometEvent.EventType.ERROR);
  214                       request.getEvent().setEventSubType(CometEvent.EventSubType.IOEXCEPTION);
  215                       error = true;
  216                       connector.getContainer().getPipeline().getFirst().event(request, response, request.getEvent());
  217                   }
  218                   return (!error);
  219               } catch (Throwable t) {
  220                   if (!(t instanceof IOException)) {
  221                       log.error(sm.getString("coyoteAdapter.service"), t);
  222                   }
  223                   error = true;
  224                   return false;
  225               } finally {
  226                   req.getRequestProcessor().setWorkerThreadName(null);
  227                   // Recycle the wrapper request and response
  228                   if (error || response.isClosed() || !request.isComet()) {
  229                       request.recycle();
  230                       request.setFilterChain(null);
  231                       response.recycle();
  232                   }
  233               }
  234               
  235           } else {
  236               return false;
  237           }
  238       }
  239       
  240   
  241       /**
  242        * Service method.
  243        */
  244       public void service(org.apache.coyote.Request req, 
  245       	                org.apache.coyote.Response res)
  246           throws Exception {
  247   
  248           Request request = (Request) req.getNote(ADAPTER_NOTES);
  249           Response response = (Response) res.getNote(ADAPTER_NOTES);
  250   
  251           if (request == null) {
  252   
  253               // Create objects
  254               request = (Request) connector.createRequest();
  255               request.setCoyoteRequest(req);
  256               response = (Response) connector.createResponse();
  257               response.setCoyoteResponse(res);
  258   
  259               // Link objects
  260               request.setResponse(response);
  261               response.setRequest(request);
  262   
  263               // Set as notes
  264               req.setNote(ADAPTER_NOTES, request);
  265               res.setNote(ADAPTER_NOTES, response);
  266   
  267               // Set query string encoding
  268               req.getParameters().setQueryStringEncoding
  269                   (connector.getURIEncoding());
  270   
  271           }
  272   
  273           if (connector.getXpoweredBy()) {
  274               response.addHeader("X-Powered-By", "Servlet/2.5");
  275           }
  276   
  277           boolean comet = false;
  278           
  279           try {
  280   
  281               // Parse and set Catalina and configuration specific 
  282               // request parameters
  283               req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
  284               if (postParseRequest(req, request, res, response)) {
  285                   // Calling the container
  286                   connector.getContainer().getPipeline().getFirst().invoke(request, response);
  287   
  288                   if (request.isComet()) {
  289                       if (!response.isClosed() && !response.isError()) {
  290                           if (request.getAvailable()) {
  291                               // Invoke a read event right away if there are available bytes
  292                               if (event(req, res, SocketStatus.OPEN)) {
  293                                   comet = true;
  294                                   res.action(ActionCode.ACTION_COMET_BEGIN, null);
  295                               }
  296                           } else {
  297                               comet = true;
  298                               res.action(ActionCode.ACTION_COMET_BEGIN, null);
  299                           }
  300                       } else {
  301                           // Clear the filter chain, as otherwise it will not be reset elsewhere
  302                           // since this is a Comet request
  303                           request.setFilterChain(null);
  304                       }
  305                   }
  306   
  307               }
  308   
  309               if (!comet) {
  310                   response.finishResponse();
  311                   req.action(ActionCode.ACTION_POST_REQUEST , null);
  312               }
  313   
  314           } catch (IOException e) {
  315               ;
  316           } catch (Throwable t) {
  317               log.error(sm.getString("coyoteAdapter.service"), t);
  318           } finally {
  319               req.getRequestProcessor().setWorkerThreadName(null);
  320               // Recycle the wrapper request and response
  321               if (!comet) {
  322                   request.recycle();
  323                   response.recycle();
  324               } else {
  325                   // Clear converters so that the minimum amount of memory 
  326                   // is used by this processor
  327                   request.clearEncoders();
  328                   response.clearEncoders();
  329               }
  330           }
  331   
  332       }
  333   
  334   
  335       // ------------------------------------------------------ Protected Methods
  336   
  337   
  338       /**
  339        * Parse additional request parameters.
  340        */
  341       protected boolean postParseRequest(org.apache.coyote.Request req, 
  342                                          Request request,
  343       		                       org.apache.coyote.Response res, 
  344                                          Response response)
  345               throws Exception {
  346   
  347           // XXX the processor needs to set a correct scheme and port prior to this point, 
  348           // in ajp13 protocols dont make sense to get the port from the connector..
  349           // XXX the processor may have set a correct scheme and port prior to this point, 
  350           // in ajp13 protocols dont make sense to get the port from the connector...
  351           // otherwise, use connector configuration
  352           if (! req.scheme().isNull()) {
  353               // use processor specified scheme to determine secure state
  354               request.setSecure(req.scheme().equals("https"));
  355           } else {
  356               // use connector scheme and secure configuration, (defaults to
  357               // "http" and false respectively)
  358               req.scheme().setString(connector.getScheme());
  359               request.setSecure(connector.getSecure());
  360           }
  361   
  362           // FIXME: the code below doesnt belongs to here, 
  363           // this is only have sense 
  364           // in Http11, not in ajp13..
  365           // At this point the Host header has been processed.
  366           // Override if the proxyPort/proxyHost are set 
  367           String proxyName = connector.getProxyName();
  368           int proxyPort = connector.getProxyPort();
  369           if (proxyPort != 0) {
  370               req.setServerPort(proxyPort);
  371           }
  372           if (proxyName != null) {
  373               req.serverName().setString(proxyName);
  374           }
  375   
  376           // Parse session Id
  377           parseSessionId(req, request);
  378   
  379           // URI decoding
  380           MessageBytes decodedURI = req.decodedURI();
  381           decodedURI.duplicate(req.requestURI());
  382   
  383           if (decodedURI.getType() == MessageBytes.T_BYTES) {
  384               // Remove any path parameters
  385               ByteChunk uriBB = decodedURI.getByteChunk();
  386               int semicolon = uriBB.indexOf(';', 0);
  387               if (semicolon > 0) {
  388                   decodedURI.setBytes
  389                       (uriBB.getBuffer(), uriBB.getStart(), semicolon);
  390               }
  391               // %xx decoding of the URL
  392               try {
  393                   req.getURLDecoder().convert(decodedURI, false);
  394               } catch (IOException ioe) {
  395                   res.setStatus(400);
  396                   res.setMessage("Invalid URI: " + ioe.getMessage());
  397                   return false;
  398               }
  399               // Normalization
  400               if (!normalize(req.decodedURI())) {
  401                   res.setStatus(400);
  402                   res.setMessage("Invalid URI");
  403                   return false;
  404               }
  405               // Character decoding
  406               convertURI(decodedURI, request);
  407           } else {
  408               // The URL is chars or String, and has been sent using an in-memory
  409               // protocol handler, we have to assume the URL has been properly
  410               // decoded already
  411               decodedURI.toChars();
  412               // Remove any path parameters
  413               CharChunk uriCC = decodedURI.getCharChunk();
  414               int semicolon = uriCC.indexOf(';');
  415               if (semicolon > 0) {
  416                   decodedURI.setChars
  417                       (uriCC.getBuffer(), uriCC.getStart(), semicolon);
  418               }
  419           }
  420   
  421           // Set the remote principal
  422           String principal = req.getRemoteUser().toString();
  423           if (principal != null) {
  424               request.setUserPrincipal(new CoyotePrincipal(principal));
  425           }
  426   
  427           // Set the authorization type
  428           String authtype = req.getAuthType().toString();
  429           if (authtype != null) {
  430               request.setAuthType(authtype);
  431           }
  432   
  433           // Request mapping.
  434           MessageBytes serverName;
  435           if (connector.getUseIPVHosts()) {
  436               serverName = req.localName();
  437               if (serverName.isNull()) {
  438                   // well, they did ask for it
  439                   res.action(ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE, null);
  440               }
  441           } else {
  442               serverName = req.serverName();
  443           }
  444           connector.getMapper().map(serverName, decodedURI, 
  445                                     request.getMappingData());
  446           request.setContext((Context) request.getMappingData().context);
  447           request.setWrapper((Wrapper) request.getMappingData().wrapper);
  448   
  449           // Filter trace method
  450           if (!connector.getAllowTrace() 
  451                   && req.method().equalsIgnoreCase("TRACE")) {
  452               Wrapper wrapper = request.getWrapper();
  453               String header = null;
  454               if (wrapper != null) {
  455                   String[] methods = wrapper.getServletMethods();
  456                   if (methods != null) {
  457                       for (int i=0; i<methods.length; i++) {
  458                           if ("TRACE".equals(methods[i])) {
  459                               continue;
  460                           }
  461                           if (header == null) {
  462                               header = methods[i];
  463                           } else {
  464                               header += ", " + methods[i];
  465                           }
  466                       }
  467                   }
  468               }                               
  469               res.setStatus(405);
  470               res.addHeader("Allow", header);
  471               res.setMessage("TRACE method is not allowed");
  472               return false;
  473           }
  474   
  475           // Possible redirect
  476           MessageBytes redirectPathMB = request.getMappingData().redirectPath;
  477           if (!redirectPathMB.isNull()) {
  478               String redirectPath = urlEncoder.encode(redirectPathMB.toString());
  479               String query = request.getQueryString();
  480               if (request.isRequestedSessionIdFromURL()) {
  481                   // This is not optimal, but as this is not very common, it
  482                   // shouldn't matter
  483                   redirectPath = redirectPath + ";" + Globals.SESSION_PARAMETER_NAME + "=" 
  484                       + request.getRequestedSessionId();
  485               }
  486               if (query != null) {
  487                   // This is not optimal, but as this is not very common, it
  488                   // shouldn't matter
  489                   redirectPath = redirectPath + "?" + query;
  490               }
  491               response.sendRedirect(redirectPath);
  492               return false;
  493           }
  494   
  495           // Parse session Id
  496           parseSessionCookiesId(req, request);
  497   
  498           return true;
  499       }
  500   
  501   
  502       /**
  503        * Parse session id in URL.
  504        */
  505       protected void parseSessionId(org.apache.coyote.Request req, Request request) {
  506   
  507           ByteChunk uriBC = req.requestURI().getByteChunk();
  508           int semicolon = uriBC.indexOf(match, 0, match.length(), 0);
  509   
  510           if (semicolon > 0) {
  511   
  512               // Parse session ID, and extract it from the decoded request URI
  513               int start = uriBC.getStart();
  514               int end = uriBC.getEnd();
  515   
  516               int sessionIdStart = semicolon + match.length();
  517               int semicolon2 = uriBC.indexOf(';', sessionIdStart);
  518               if (semicolon2 >= 0) {
  519                   request.setRequestedSessionId
  520                       (new String(uriBC.getBuffer(), start + sessionIdStart, 
  521                               semicolon2 - sessionIdStart));
  522                   // Extract session ID from request URI
  523                   byte[] buf = uriBC.getBuffer();
  524                   for (int i = 0; i < end - start - semicolon2; i++) {
  525                       buf[start + semicolon + i] 
  526                           = buf[start + i + semicolon2];
  527                   }
  528                   uriBC.setBytes(buf, start, end - start - semicolon2 + semicolon);
  529               } else {
  530                   request.setRequestedSessionId
  531                       (new String(uriBC.getBuffer(), start + sessionIdStart, 
  532                               (end - start) - sessionIdStart));
  533                   uriBC.setEnd(start + semicolon);
  534               }
  535               request.setRequestedSessionURL(true);
  536   
  537           } else {
  538               request.setRequestedSessionId(null);
  539               request.setRequestedSessionURL(false);
  540           }
  541   
  542       }
  543   
  544   
  545       /**
  546        * Parse session id in URL.
  547        */
  548       protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
  549   
  550           // If session tracking via cookies has been disabled for the current
  551           // context, don't go looking for a session ID in a cookie as a cookie
  552           // from a parent context with a session ID may be present which would
  553           // overwrite the valid session ID encoded in the URL
  554           Context context = (Context) request.getMappingData().context;
  555           if (context != null && !context.getCookies())
  556               return;
  557   
  558           // Parse session id from cookies
  559           Cookies serverCookies = req.getCookies();
  560           int count = serverCookies.getCookieCount();
  561           if (count <= 0)
  562               return;
  563   
  564           for (int i = 0; i < count; i++) {
  565               ServerCookie scookie = serverCookies.getCookie(i);
  566               if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
  567                   // Override anything requested in the URL
  568                   if (!request.isRequestedSessionIdFromCookie()) {
  569                       // Accept only the first session id cookie
  570                       convertMB(scookie.getValue());
  571                       request.setRequestedSessionId
  572                           (scookie.getValue().toString());
  573                       request.setRequestedSessionCookie(true);
  574                       request.setRequestedSessionURL(false);
  575                       if (log.isDebugEnabled())
  576                           log.debug(" Requested cookie session id is " +
  577                               request.getRequestedSessionId());
  578                   } else {
  579                       if (!request.isRequestedSessionIdValid()) {
  580                           // Replace the session id until one is valid
  581                           convertMB(scookie.getValue());
  582                           request.setRequestedSessionId
  583                               (scookie.getValue().toString());
  584                       }
  585                   }
  586               }
  587           }
  588   
  589       }
  590   
  591   
  592       /**
  593        * Character conversion of the URI.
  594        */
  595       protected void convertURI(MessageBytes uri, Request request) 
  596           throws Exception {
  597   
  598           ByteChunk bc = uri.getByteChunk();
  599           int length = bc.getLength();
  600           CharChunk cc = uri.getCharChunk();
  601           cc.allocate(length, -1);
  602   
  603           String enc = connector.getURIEncoding();
  604           if (enc != null) {
  605               B2CConverter conv = request.getURIConverter();
  606               try {
  607                   if (conv == null) {
  608                       conv = new B2CConverter(enc);
  609                       request.setURIConverter(conv);
  610                   } else {
  611                       conv.recycle();
  612                   }
  613               } catch (IOException e) {
  614                   // Ignore
  615                   log.error("Invalid URI encoding; using HTTP default");
  616                   connector.setURIEncoding(null);
  617               }
  618               if (conv != null) {
  619                   try {
  620                       conv.convert(bc, cc);
  621                       uri.setChars(cc.getBuffer(), cc.getStart(), 
  622                                    cc.getLength());
  623                       return;
  624                   } catch (IOException e) {
  625                       log.error("Invalid URI character encoding; trying ascii");
  626                       cc.recycle();
  627                   }
  628               }
  629           }
  630   
  631           // Default encoding: fast conversion
  632           byte[] bbuf = bc.getBuffer();
  633           char[] cbuf = cc.getBuffer();
  634           int start = bc.getStart();
  635           for (int i = 0; i < length; i++) {
  636               cbuf[i] = (char) (bbuf[i + start] & 0xff);
  637           }
  638           uri.setChars(cbuf, 0, length);
  639   
  640       }
  641   
  642   
  643       /**
  644        * Character conversion of the a US-ASCII MessageBytes.
  645        */
  646       protected void convertMB(MessageBytes mb) {
  647   
  648           // This is of course only meaningful for bytes
  649           if (mb.getType() != MessageBytes.T_BYTES)
  650               return;
  651           
  652           ByteChunk bc = mb.getByteChunk();
  653           CharChunk cc = mb.getCharChunk();
  654           int length = bc.getLength();
  655           cc.allocate(length, -1);
  656   
  657           // Default encoding: fast conversion
  658           byte[] bbuf = bc.getBuffer();
  659           char[] cbuf = cc.getBuffer();
  660           int start = bc.getStart();
  661           for (int i = 0; i < length; i++) {
  662               cbuf[i] = (char) (bbuf[i + start] & 0xff);
  663           }
  664           mb.setChars(cbuf, 0, length);
  665   
  666       }
  667   
  668   
  669       /**
  670        * Normalize URI.
  671        * <p>
  672        * This method normalizes "\", "//", "/./" and "/../". This method will
  673        * return false when trying to go above the root, or if the URI contains
  674        * a null byte.
  675        * 
  676        * @param uriMB URI to be normalized
  677        */
  678       public static boolean normalize(MessageBytes uriMB) {
  679   
  680           ByteChunk uriBC = uriMB.getByteChunk();
  681           byte[] b = uriBC.getBytes();
  682           int start = uriBC.getStart();
  683           int end = uriBC.getEnd();
  684   
  685           // URL * is acceptable
  686           if ((end - start == 1) && b[start] == (byte) '*')
  687             return true;
  688   
  689           int pos = 0;
  690           int index = 0;
  691   
  692           // Replace '\' with '/'
  693           // Check for null byte
  694           for (pos = start; pos < end; pos++) {
  695               if (b[pos] == (byte) '\\') {
  696                   if (ALLOW_BACKSLASH) {
  697                       b[pos] = (byte) '/';
  698                   } else {
  699                       return false;
  700                   }
  701               }
  702               if (b[pos] == (byte) 0) {
  703                   return false;
  704               }
  705           }
  706   
  707           // The URL must start with '/'
  708           if (b[start] != (byte) '/') {
  709               return false;
  710           }
  711   
  712           // Replace "//" with "/"
  713           for (pos = start; pos < (end - 1); pos++) {
  714               if (b[pos] == (byte) '/') {
  715                   while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
  716                       copyBytes(b, pos, pos + 1, end - pos - 1);
  717                       end--;
  718                   }
  719               }
  720           }
  721   
  722           // If the URI ends with "/." or "/..", then we append an extra "/"
  723           // Note: It is possible to extend the URI by 1 without any side effect
  724           // as the next character is a non-significant WS.
  725           if (((end - start) >= 2) && (b[end - 1] == (byte) '.')) {
  726               if ((b[end - 2] == (byte) '/') 
  727                   || ((b[end - 2] == (byte) '.') 
  728                       && (b[end - 3] == (byte) '/'))) {
  729                   b[end] = (byte) '/';
  730                   end++;
  731               }
  732           }
  733   
  734           uriBC.setEnd(end);
  735   
  736           index = 0;
  737   
  738           // Resolve occurrences of "/./" in the normalized path
  739           while (true) {
  740               index = uriBC.indexOf("/./", 0, 3, index);
  741               if (index < 0)
  742                   break;
  743               copyBytes(b, start + index, start + index + 2, 
  744                         end - start - index - 2);
  745               end = end - 2;
  746               uriBC.setEnd(end);
  747           }
  748   
  749           index = 0;
  750   
  751           // Resolve occurrences of "/../" in the normalized path
  752           while (true) {
  753               index = uriBC.indexOf("/../", 0, 4, index);
  754               if (index < 0)
  755                   break;
  756               // Prevent from going outside our context
  757               if (index == 0)
  758                   return false;
  759               int index2 = -1;
  760               for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
  761                   if (b[pos] == (byte) '/') {
  762                       index2 = pos;
  763                   }
  764               }
  765               copyBytes(b, start + index2, start + index + 3,
  766                         end - start - index - 3);
  767               end = end + index2 - index - 3;
  768               uriBC.setEnd(end);
  769               index = index2;
  770           }
  771   
  772           uriBC.setBytes(b, start, end);
  773   
  774           return true;
  775   
  776       }
  777   
  778   
  779       // ------------------------------------------------------ Protected Methods
  780   
  781   
  782       /**
  783        * Copy an array of bytes to a different position. Used during 
  784        * normalization.
  785        */
  786       protected static void copyBytes(byte[] b, int dest, int src, int len) {
  787           for (int pos = 0; pos < len; pos++) {
  788               b[pos + dest] = b[pos + src];
  789           }
  790       }
  791   
  792   
  793   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » connector » [javadoc | source]