Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » coyote » tomcat4 » [javadoc | source]
    1   /*
    2    *  Copyright 1999-2004 The Apache Software Foundation
    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    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    *  Unless required by applicable law or agreed to in writing, software
   11    *  distributed under the License is distributed on an "AS IS" BASIS,
   12    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    *  See the License for the specific language governing permissions and
   14    *  limitations under the License.
   15    */
   16   
   17   package org.apache.coyote.tomcat4;
   18   
   19   
   20   import java.io.IOException;
   21   import javax.servlet.http.Cookie;
   22   import javax.servlet.http.HttpServletRequest;
   23   
   24   import org.apache.tomcat.util.buf.B2CConverter;
   25   import org.apache.tomcat.util.buf.ByteChunk;
   26   import org.apache.tomcat.util.buf.CharChunk;
   27   import org.apache.tomcat.util.buf.MessageBytes;
   28   import org.apache.tomcat.util.http.Cookies;
   29   import org.apache.tomcat.util.http.ServerCookie;
   30   
   31   import org.apache.coyote.ActionCode;
   32   import org.apache.coyote.Adapter;
   33   import org.apache.coyote.Request;
   34   import org.apache.coyote.Response;
   35   
   36   import org.apache.catalina.Globals;
   37   import org.apache.catalina.Logger;
   38   import org.apache.catalina.util.StringManager;
   39   
   40   
   41   /**
   42    * Implementation of a request processor which delegates the processing to a
   43    * Coyote processor.
   44    *
   45    * @author Craig R. McClanahan
   46    * @author Remy Maucherat
   47    * @version $Revision: 299381 $ $Date: 2004-04-04 15:09:38 -0400 (Sun, 04 Apr 2004) $
   48    */
   49   
   50   final class CoyoteAdapter
   51       implements Adapter {
   52   
   53   
   54       // -------------------------------------------------------------- Constants
   55   
   56   
   57       public static final int ADAPTER_NOTES = 1;
   58   
   59   
   60       // ----------------------------------------------------------- Constructors
   61   
   62   
   63       /**
   64        * Construct a new CoyoteProcessor associated with the specified connector.
   65        *
   66        * @param connector CoyoteConnector that owns this processor
   67        * @param id Identifier of this CoyoteProcessor (unique per connector)
   68        */
   69       public CoyoteAdapter(CoyoteConnector connector) {
   70   
   71           super();
   72           this.connector = connector;
   73           this.debug = connector.getDebug();
   74   
   75       }
   76   
   77   
   78       // ----------------------------------------------------- Instance Variables
   79   
   80   
   81       /**
   82        * The CoyoteConnector with which this processor is associated.
   83        */
   84       private CoyoteConnector connector = null;
   85   
   86   
   87       /**
   88        * The debugging detail level for this component.
   89        */
   90       private int debug = 0;
   91   
   92   
   93       /**
   94        * The match string for identifying a session ID parameter.
   95        */
   96       private static final String match =
   97           ";" + Globals.SESSION_PARAMETER_NAME + "=";
   98   
   99   
  100       /**
  101        * The match string for identifying a session ID parameter.
  102        */
  103       private static final char[] SESSION_ID = match.toCharArray();
  104   
  105   
  106       /**
  107        * The string manager for this package.
  108        */
  109       protected StringManager sm =
  110           StringManager.getManager(Constants.Package);
  111   
  112   
  113       // -------------------------------------------------------- Adapter Methods
  114   
  115   
  116       /**
  117        * Service method.
  118        */
  119       public void service(Request req, Response res)
  120           throws Exception {
  121   
  122           CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
  123           CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
  124   
  125           if (request == null) {
  126   
  127               // Create objects
  128               request = (CoyoteRequest) connector.createRequest();
  129               request.setCoyoteRequest(req);
  130               response = (CoyoteResponse) connector.createResponse();
  131               response.setCoyoteResponse(res);
  132   
  133               // Link objects
  134               request.setResponse(response);
  135               response.setRequest(request);
  136   
  137               // Set as notes
  138               req.setNote(ADAPTER_NOTES, request);
  139               res.setNote(ADAPTER_NOTES, response);
  140   
  141               // Set query string encoding
  142               req.getParameters().setQueryStringEncoding
  143                   (connector.getURIEncoding());
  144   
  145           }
  146   
  147           try {
  148               // Parse and set Catalina and configuration specific 
  149               // request parameters
  150               postParseRequest(req, request, res, response);
  151               // Calling the container
  152               connector.getContainer().invoke(request, response);
  153               response.finishResponse();
  154   
  155               req.action( ActionCode.ACTION_POST_REQUEST , null);
  156           } catch (IOException e) {
  157               ;
  158           } catch (Throwable t) {
  159               log(sm.getString("coyoteAdapter.service"), t);
  160           } finally {
  161               // Recycle the wrapper request and response
  162               request.recycle();
  163               response.recycle();
  164           }
  165   
  166       }
  167   
  168   
  169       // ------------------------------------------------------ Protected Methods
  170   
  171   
  172       /**
  173        * Parse additional request parameters.
  174        */
  175       protected void postParseRequest(Request req, CoyoteRequest request,
  176                                       Response res, CoyoteResponse response)
  177           throws Exception {
  178           // XXX the processor needs to set a correct scheme and port prior to this point, 
  179           // in ajp13 protocols dont make sense to get the port from the connector..
  180           // XXX the processor may have set a correct scheme and port prior to this point, 
  181           // in ajp13 protocols dont make sense to get the port from the connector...
  182           // otherwise, use connector configuration
  183           if (! req.scheme().isNull()) {
  184               // use processor specified scheme to determine secure state
  185               request.setSecure(req.scheme().equals("https"));
  186           } else {
  187               // use connector scheme and secure configuration, (defaults to
  188               // "http" and false respectively)
  189               req.scheme().setString(connector.getScheme());
  190               request.setSecure(connector.getSecure());
  191           }
  192    
  193           // Filter trace method
  194           if (!connector.getAllowTrace() 
  195               && req.method().equalsIgnoreCase("TRACE")) {
  196               res.setStatus(403);
  197               res.setMessage("TRACE method is not allowed");
  198               throw new IOException("TRACE method is not allowed");
  199           }
  200   
  201           request.setAuthorization
  202               (req.getHeader(Constants.AUTHORIZATION_HEADER));
  203           // FIXME: the code below doesnt belongs to here, this is only  have sense 
  204           // in Http11, not in ajp13..
  205           // At this point the Host header has been processed.
  206           // Override if the proxyPort/proxyHost are set 
  207           String proxyName = connector.getProxyName();
  208           int proxyPort = connector.getProxyPort();
  209           if (proxyPort != 0) {
  210               request.setServerPort(proxyPort);
  211               req.setServerPort(proxyPort);
  212           } else {
  213               request.setServerPort(req.getServerPort());
  214           }
  215           if (proxyName != null) {
  216               request.setServerName(proxyName);
  217               req.serverName().setString(proxyName);
  218           } else {
  219               request.setServerName(req.serverName().toString());
  220           }
  221   
  222           // URI decoding
  223           req.decodedURI().duplicate(req.requestURI());
  224           try {
  225             req.getURLDecoder().convert(req.decodedURI(), false);
  226           } catch (IOException ioe) {
  227               res.setStatus(400);
  228               res.setMessage("Invalid URI");
  229               throw ioe;
  230           }
  231   
  232           // Normalize decoded URI
  233           if (!normalize(req.decodedURI())) {
  234               res.setStatus(400);
  235               res.setMessage("Invalid URI");
  236               throw new IOException("Invalid URI");
  237           }
  238   
  239           // URI character decoding
  240           convertURI(req.decodedURI(), request);
  241   
  242           // Parse session Id
  243           parseSessionId(req, request);
  244   
  245           // Additional URI normalization and validation is needed for security 
  246           // reasons on Tomcat 4.0.x
  247           if (connector.getUseURIValidationHack()) {
  248               String uri = validate(request.getRequestURI());
  249               if (uri == null) {
  250                   res.setStatus(400);
  251                   res.setMessage("Invalid URI");
  252                   throw new IOException("Invalid URI");
  253               } else {
  254                   req.requestURI().setString(uri);
  255                   // Redoing the URI decoding
  256                   req.decodedURI().duplicate(req.requestURI());
  257                   req.getURLDecoder().convert(req.decodedURI(), true);
  258                   convertURI(req.decodedURI(), request);
  259               }
  260           }
  261   
  262           // Parse cookies
  263           parseCookies(req, request);
  264   
  265           // Set the SSL properties
  266           if( request.isSecure() ) {
  267               res.action(ActionCode.ACTION_REQ_SSL_ATTRIBUTE,
  268                          request.getCoyoteRequest());
  269               //Set up for getAttributeNames
  270               request.getAttribute(Globals.CERTIFICATES_ATTR);
  271               request.getAttribute(Globals.CIPHER_SUITE_ATTR);
  272               request.getAttribute(Globals.KEY_SIZE_ATTR);
  273           }
  274   
  275           // Set the remote principal
  276           String principal = req.getRemoteUser().toString();
  277           if (principal != null) {
  278               request.setUserPrincipal(new CoyotePrincipal(principal));
  279           }
  280   
  281           // Set the authorization type
  282           String authtype = req.getAuthType().toString();
  283           if (authtype != null) {
  284               request.setAuthType(authtype);
  285           }
  286   
  287       }
  288   
  289       /**
  290        * Parse session id in URL.
  291        * FIXME: Optimize this.
  292        */
  293       protected void parseSessionId(Request req, CoyoteRequest request) {
  294   
  295           req.decodedURI().toChars();
  296           CharChunk uriCC = req.decodedURI().getCharChunk();
  297           int semicolon = uriCC.indexOf(match, 0, match.length(), 0);
  298   
  299           if (semicolon > 0) {
  300   
  301               // Parse session ID, and extract it from the decoded request URI
  302               int start = uriCC.getStart();
  303               int end = uriCC.getEnd();
  304   
  305               int sessionIdStart = start + semicolon + match.length();
  306               int semicolon2 = uriCC.indexOf(';', sessionIdStart);
  307               if (semicolon2 >= 0) {
  308                   request.setRequestedSessionId
  309                       (new String(uriCC.getBuffer(), sessionIdStart, 
  310                                   semicolon2 - semicolon - match.length()));
  311                   req.decodedURI().setString
  312                       (new String(uriCC.getBuffer(), start, semicolon) + 
  313                               new String(uriCC.getBuffer(),
  314                                           semicolon2,
  315                                           end-semicolon2));
  316               } else {
  317                   request.setRequestedSessionId
  318                       (new String(uriCC.getBuffer(), sessionIdStart, 
  319                                   end - sessionIdStart));
  320                   req.decodedURI().setString
  321                       (new String(uriCC.getBuffer(), start, semicolon));
  322               }
  323               request.setRequestedSessionURL(true);
  324   
  325               // Extract session ID from request URI
  326               String uri = req.requestURI().toString();
  327               semicolon = uri.indexOf(match);
  328   
  329               if (semicolon > 0) {
  330                   String rest = uri.substring(semicolon + match.length());
  331                   semicolon2 = rest.indexOf(';');
  332                   if (semicolon2 >= 0) {
  333                       rest = rest.substring(semicolon2);
  334                   } else {
  335                       rest = "";
  336                   }
  337                   req.requestURI().setString(uri.substring(0, semicolon) + rest);
  338               }
  339   
  340           } else {
  341               request.setRequestedSessionId(null);
  342               request.setRequestedSessionURL(false);
  343           }
  344   
  345       }
  346   
  347   
  348       /**
  349        * Parse cookies.
  350        */
  351       protected void parseCookies(Request req, CoyoteRequest request) {
  352   
  353           Cookies serverCookies = req.getCookies();
  354           int count = serverCookies.getCookieCount();
  355           if (count <= 0)
  356               return;
  357   
  358           Cookie[] cookies = new Cookie[count];
  359   
  360           int idx=0;
  361           for (int i = 0; i < count; i++) {
  362               ServerCookie scookie = serverCookies.getCookie(i);
  363               if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
  364                   // Override anything requested in the URL
  365                   if (!request.isRequestedSessionIdFromCookie()) {
  366                       // Accept only the first session id cookie
  367                       request.setRequestedSessionId
  368                           (scookie.getValue().toString());
  369                       request.setRequestedSessionCookie(true);
  370                       request.setRequestedSessionURL(false);
  371                       if (debug >= 1)
  372                           log(" Requested cookie session id is " +
  373                               ((HttpServletRequest) request.getRequest())
  374                               .getRequestedSessionId());
  375                   }
  376               }
  377               try {
  378                   Cookie cookie = new Cookie(scookie.getName().toString(),
  379                                              scookie.getValue().toString());
  380                   cookie.setPath(scookie.getPath().toString());
  381                   cookie.setVersion(scookie.getVersion());
  382                   String domain = scookie.getDomain().toString();
  383                   if (domain != null) {
  384                       cookie.setDomain(scookie.getDomain().toString());
  385                   }
  386                   cookies[idx++] = cookie;
  387               } catch(Exception ex) {
  388                   log("Bad Cookie Name: " + scookie.getName() + 
  389                       " /Value: " + scookie.getValue(),ex);
  390               }
  391           }
  392           if( idx < count ) {
  393               Cookie [] ncookies = new Cookie[idx];
  394               System.arraycopy(cookies, 0, ncookies, 0, idx);
  395               cookies = ncookies;
  396           }
  397   
  398           request.setCookies(cookies);
  399   
  400       }
  401   
  402   
  403       /**
  404        * Return a context-relative path, beginning with a "/", that represents
  405        * the canonical version of the specified path after ".." and "." elements
  406        * are resolved out.  If the specified path attempts to go outside the
  407        * boundaries of the current context (i.e. too many ".." path elements
  408        * are present), return <code>null</code> instead.
  409        * This code is not optimized, and is only needed for Tomcat 4.0.x.
  410        *
  411        * @param path Path to be validated
  412        */
  413       protected static String validate(String path) {
  414   
  415           if (path == null)
  416               return null;
  417   
  418           // Create a place for the normalized path
  419           String normalized = path;
  420   
  421           // Normalize "/%7E" and "/%7e" at the beginning to "/~"
  422           if (normalized.startsWith("/%7E") ||
  423               normalized.startsWith("/%7e"))
  424               normalized = "/~" + normalized.substring(4);
  425   
  426           // Prevent encoding '%', '/', '.' and '\', which are special reserved
  427           // characters
  428           if ((normalized.indexOf("%25") >= 0)
  429               || (normalized.indexOf("%2F") >= 0)
  430               || (normalized.indexOf("%2E") >= 0)
  431               || (normalized.indexOf("%5C") >= 0)
  432               || (normalized.indexOf("%2f") >= 0)
  433               || (normalized.indexOf("%2e") >= 0)
  434               || (normalized.indexOf("%5c") >= 0)) {
  435               return null;
  436           }
  437   
  438           if (normalized.equals("/."))
  439               return "/";
  440   
  441           // Normalize the slashes and add leading slash if necessary
  442           if (normalized.indexOf('\\') >= 0)
  443               normalized = normalized.replace('\\', '/');
  444           if (!normalized.startsWith("/"))
  445               normalized = "/" + normalized;
  446   
  447           // Resolve occurrences of "//" in the normalized path
  448           while (true) {
  449               int index = normalized.indexOf("//");
  450               if (index < 0)
  451                   break;
  452               normalized = normalized.substring(0, index) +
  453                   normalized.substring(index + 1);
  454           }
  455   
  456           // Resolve occurrences of "/./" in the normalized path
  457           while (true) {
  458               int index = normalized.indexOf("/./");
  459               if (index < 0)
  460                   break;
  461               normalized = normalized.substring(0, index) +
  462                   normalized.substring(index + 2);
  463           }
  464   
  465           // Resolve occurrences of "/../" in the normalized path
  466           while (true) {
  467               int index = normalized.indexOf("/../");
  468               if (index < 0)
  469                   break;
  470               if (index == 0)
  471                   return (null);  // Trying to go outside our context
  472               int index2 = normalized.lastIndexOf('/', index - 1);
  473               normalized = normalized.substring(0, index2) +
  474                   normalized.substring(index + 3);
  475           }
  476   
  477           // Declare occurrences of "/..." (three or more dots) to be invalid
  478           // (on some Windows platforms this walks the directory tree!!!)
  479           if (normalized.indexOf("/...") >= 0)
  480               return (null);
  481   
  482           // Return the normalized path that we have completed
  483           return (normalized);
  484   
  485       }
  486   
  487   
  488       /**
  489        * Character conversion of the URI.
  490        */
  491       protected void convertURI(MessageBytes uri, CoyoteRequest request) 
  492           throws Exception {
  493   
  494           ByteChunk bc = uri.getByteChunk();
  495           CharChunk cc = uri.getCharChunk();
  496           cc.allocate(bc.getLength(), -1);
  497   
  498           String enc = connector.getURIEncoding();
  499           if (enc != null) {
  500               B2CConverter conv = request.getURIConverter();
  501               try {
  502                   if (conv == null) {
  503                       conv = new B2CConverter(enc);
  504                       request.setURIConverter(conv);
  505                   } else {
  506                       conv.recycle();
  507                   }
  508               } catch (IOException e) {
  509                   // Ignore
  510                   log("Invalid URI encoding; using HTTP default", e);
  511                   connector.setURIEncoding(null);
  512               }
  513               if (conv != null) {
  514                   try {
  515                       conv.convert(bc, cc);
  516                       uri.setChars(cc.getBuffer(), cc.getStart(), 
  517                                    cc.getLength());
  518                       return;
  519                   } catch (IOException e) {
  520                       if (debug >= 1) {
  521                           log("Invalid URI character encoding; trying ascii", e);
  522                       }
  523                       cc.recycle();
  524                   }
  525               }
  526           }
  527   
  528           // Default encoding: fast conversion
  529           byte[] bbuf = bc.getBuffer();
  530           char[] cbuf = cc.getBuffer();
  531           int start = bc.getStart();
  532           for (int i = 0; i < bc.getLength(); i++) {
  533               cbuf[i] = (char) (bbuf[i + start] & 0xff);
  534           }
  535           uri.setChars(cbuf, 0, bc.getLength());
  536   
  537       }
  538   
  539   
  540       /**
  541        * Normalize URI.
  542        * <p>
  543        * This method normalizes "\", "//", "/./" and "/../". This method will
  544        * return false when trying to go above the root, or if the URI contains
  545        * a null byte.
  546        * 
  547        * @param uriMB URI to be normalized
  548        */
  549       public static boolean normalize(MessageBytes uriMB) {
  550   
  551           ByteChunk uriBC = uriMB.getByteChunk();
  552           byte[] b = uriBC.getBytes();
  553           int start = uriBC.getStart();
  554           int end = uriBC.getEnd();
  555   
  556           // URL * is acceptable
  557           if ((end - start == 1) && b[start] == (byte) '*')
  558             return true;
  559   
  560           int pos = 0;
  561           int index = 0;
  562   
  563           // Replace '\' with '/'
  564           // Check for null byte
  565           for (pos = start; pos < end; pos++) {
  566               if (b[pos] == (byte) '\\')
  567                   b[pos] = (byte) '/';
  568               if (b[pos] == (byte) 0)
  569                   return false;
  570           }
  571   
  572           // The URL must start with '/'
  573           if (b[start] != (byte) '/') {
  574               return false;
  575           }
  576   
  577           // Replace "//" with "/"
  578           for (pos = start; pos < (end - 1); pos++) {
  579               if (b[pos] == (byte) '/') {
  580                   while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
  581                       copyBytes(b, pos, pos + 1, end - pos - 1);
  582                       end--;
  583                   }
  584               }
  585           }
  586   
  587           // If the URI ends with "/." or "/..", then we append an extra "/"
  588           // Note: It is possible to extend the URI by 1 without any side effect
  589           // as the next character is a non-significant WS.
  590           if (((end - start) > 2) && (b[end - 1] == (byte) '.')) {
  591               if ((b[end - 2] == (byte) '/') 
  592                   || ((b[end - 2] == (byte) '.') 
  593                       && (b[end - 3] == (byte) '/'))) {
  594                   b[end] = (byte) '/';
  595                   end++;
  596               }
  597           }
  598   
  599           uriBC.setEnd(end);
  600   
  601           index = 0;
  602   
  603           // Resolve occurrences of "/./" in the normalized path
  604           while (true) {
  605               index = uriBC.indexOf("/./", 0, 3, index);
  606               if (index < 0)
  607                   break;
  608               copyBytes(b, start + index, start + index + 2, 
  609                         end - start - index - 2);
  610               end = end - 2;
  611               uriBC.setEnd(end);
  612           }
  613   
  614           index = 0;
  615   
  616           // Resolve occurrences of "/../" in the normalized path
  617           while (true) {
  618               index = uriBC.indexOf("/../", 0, 4, index);
  619               if (index < 0)
  620                   break;
  621               // Prevent from going outside our context
  622               if (index == 0)
  623                   return false;
  624               int index2 = -1;
  625               for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
  626                   if (b[pos] == (byte) '/') {
  627                       index2 = pos;
  628                   }
  629               }
  630               copyBytes(b, start + index2, start + index + 3,
  631                         end - start - index - 3);
  632               end = end + index2 - index - 3;
  633               uriBC.setEnd(end);
  634               index = index2;
  635           }
  636   
  637           uriBC.setBytes(b, start, end);
  638   
  639           return true;
  640   
  641       }
  642   
  643   
  644       // ------------------------------------------------------ Protected Methods
  645   
  646   
  647       /**
  648        * Copy an array of bytes to a different position. Used during 
  649        * normalization.
  650        */
  651       protected static void copyBytes(byte[] b, int dest, int src, int len) {
  652           for (int pos = 0; pos < len; pos++) {
  653               b[pos + dest] = b[pos + src];
  654           }
  655       }
  656   
  657   
  658       /**
  659        * Log a message on the Logger associated with our Container (if any)
  660        *
  661        * @param message Message to be logged
  662        */
  663       protected void log(String message) {
  664   
  665           Logger logger = connector.getContainer().getLogger();
  666           if (logger != null)
  667               logger.log("CoyoteAdapter " + message);
  668   
  669       }
  670   
  671   
  672       /**
  673        * Log a message on the Logger associated with our Container (if any)
  674        *
  675        * @param message Message to be logged
  676        * @param throwable Associated exception
  677        */
  678       protected void log(String message, Throwable throwable) {
  679   
  680           Logger logger = connector.getContainer().getLogger();
  681           if (logger != null)
  682               logger.log("CoyoteAdapter " + message, throwable);
  683   
  684       }
  685   
  686   
  687   }

Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » coyote » tomcat4 » [javadoc | source]