Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » coyote » tomcat5 » [javadoc | source]
    1   /*
    2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    3    * 
    4    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    5    * 
    6    * Portions Copyright Apache Software Foundation.
    7    * 
    8    * The contents of this file are subject to the terms of either the GNU
    9    * General Public License Version 2 only ("GPL") or the Common Development
   10    * and Distribution License("CDDL") (collectively, the "License").  You
   11    * may not use this file except in compliance with the License. You can obtain
   12    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   13    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   14    * language governing permissions and limitations under the License.
   15    * 
   16    * When distributing the software, include this License Header Notice in each
   17    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   18    * Sun designates this particular file as subject to the "Classpath" exception
   19    * as provided by Sun in the GPL Version 2 section of the License file that
   20    * accompanied this code.  If applicable, add the following below the License
   21    * Header, with the fields enclosed by brackets [] replaced by your own
   22    * identifying information: "Portions Copyrighted [year]
   23    * [name of copyright owner]"
   24    * 
   25    * Contributor(s):
   26    * 
   27    * If you wish your version of this file to be governed by only the CDDL or
   28    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   29    * elects to include this software in this distribution under the [CDDL or GPL
   30    * Version 2] license."  If you don't indicate a single choice of license, a
   31    * recipient has the option to distribute your version of this file under
   32    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   33    * its licensees as provided above.  However, if you add GPL Version 2 code
   34    * and therefore, elected the GPL Version 2 license, then the option applies
   35    * only if the new code is made subject to such option by the copyright
   36    * holder.
   37    */
   38   
   39   
   40   package org.apache.coyote.tomcat5;
   41   
   42   import java.io.IOException;
   43   
   44   // START S1AS 6188932
   45   import java.security.cert.X509Certificate;
   46   import java.security.cert.CertificateException;
   47   // END S1AS 6188932
   48   
   49   import javax.servlet.http.Cookie;
   50   import javax.servlet.http.HttpServletRequest;
   51   import javax.servlet.http.HttpServletResponse;
   52   import org.apache.catalina.ContainerEvent;
   53   import org.apache.catalina.ContainerListener;
   54   
   55   import org.apache.catalina.Context;
   56   import org.apache.catalina.Globals;
   57   import org.apache.catalina.Wrapper;
   58   import org.apache.catalina.core.ContainerBase;
   59   import org.apache.catalina.util.StringManager;
   60   import com.sun.org.apache.commons.logging.Log;
   61   import com.sun.org.apache.commons.logging.LogFactory;
   62   import org.apache.coyote.ActionCode;
   63   import org.apache.coyote.Adapter;
   64   import org.apache.coyote.Request;
   65   import org.apache.coyote.Response;
   66   /* CR 6309511
   67   import org.apache.tomcat.util.buf.B2CConverter;
   68    */
   69   import org.apache.tomcat.util.buf.ByteChunk;
   70   import org.apache.tomcat.util.buf.CharChunk;
   71   import org.apache.tomcat.util.buf.MessageBytes;
   72   // START GlassFish 936
   73   import org.apache.tomcat.util.buf.UEncoder;
   74   // END GlassFish 936
   75   /* CR 6309511
   76   import org.apache.tomcat.util.http.Cookies;
   77   import org.apache.tomcat.util.http.ServerCookie;
   78    */
   79   // START S1AS 6188932
   80   import com.sun.appserv.ProxyHandler;
   81   // END S1AS 6188932
   82   
   83   
   84   /**
   85    * Implementation of a request processor which delegates the processing to a
   86    * Coyote processor.
   87    *
   88    * @author Craig R. McClanahan
   89    * @author Remy Maucherat
   90    * @version $Revision: 1.32.2.1.2.2 $ $Date: 2007/11/27 01:53:48 $
   91    */
   92   
   93   public class CoyoteAdapter
   94       implements Adapter 
   95    {
   96       private static Log log = LogFactory.getLog(CoyoteAdapter.class);
   97   
   98       // -------------------------------------------------------------- Constants
   99   
  100   
  101       public static final int ADAPTER_NOTES = 1;
  102   
  103       static final String JVM_ROUTE = System.getProperty("jvmRoute");
  104   
  105       protected static final boolean ALLOW_BACKSLASH = 
  106           Boolean.valueOf(System.getProperty("org.apache.coyote.tomcat5.CoyoteAdapter.ALLOW_BACKSLASH", "false")).booleanValue();
  107   
  108       private static final boolean COLLAPSE_ADJACENT_SLASHES =
  109           Boolean.valueOf(System.getProperty(
  110               "com.sun.enterprise.web.collapseAdjacentSlashes", "true")).booleanValue();
  111   
  112       /**
  113        * When mod_jk is used, the adapter must be invoked the same way 
  114        * Tomcat does by invoking service(...) and the afterService(...). This
  115        * is a hack to make it compatible with Tomcat 5|6.
  116        */
  117       private boolean compatWithTomcat = false;
  118       
  119       private String serverName = System.getProperty("product.name");
  120       
  121       // ----------------------------------------------------------- Constructors
  122   
  123   
  124       /**
  125        * Construct a new CoyoteProcessor associated with the specified connector.
  126        *
  127        * @param connector CoyoteConnector that owns this processor
  128        * @param id Identifier of this CoyoteProcessor (unique per connector)
  129        */
  130       public CoyoteAdapter(CoyoteConnector connector) {
  131   
  132           super();
  133           this.connector = connector;
  134           this.debug = connector.getDebug();
  135           // START GlassFish 936
  136           urlEncoder.addSafeCharacter('/');
  137           // END GlassFish 936
  138       }
  139   
  140   
  141       // ----------------------------------------------------- Instance Variables
  142   
  143   
  144       /**
  145        * The CoyoteConnector with which this processor is associated.
  146        */
  147       private CoyoteConnector connector = null;
  148   
  149   
  150       /**
  151        * The debugging detail level for this component.
  152        */
  153       private int debug = 0;
  154   
  155       // START GlassFish 936
  156       private UEncoder urlEncoder = new UEncoder();
  157       // END GlassFish 936
  158   
  159       /**
  160        * The match string for identifying a session ID parameter.
  161        */
  162       /* CR 6309511
  163       private static final String match =
  164           ";" + Globals.SESSION_PARAMETER_NAME + "=";
  165        */
  166   
  167   
  168       /**
  169        * The match string for identifying a session ID parameter.
  170        */
  171       /* CR 6309511
  172       private static final char[] SESSION_ID = match.toCharArray();
  173        */
  174   
  175   
  176       /**
  177        * The string manager for this package.
  178        */
  179       protected StringManager sm =
  180           StringManager.getManager(Constants.Package);
  181   
  182   
  183       // -------------------------------------------------------- Adapter Methods
  184   
  185   
  186       /**
  187        * Service method.
  188        */
  189       public void service(Request req, Response res)
  190           throws Exception {
  191   
  192           CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
  193           CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
  194   
  195           if (request == null) {
  196   
  197               // Create objects
  198               request = (CoyoteRequest) connector.createRequest();
  199               request.setCoyoteRequest(req);
  200               response = (CoyoteResponse) connector.createResponse();
  201               response.setCoyoteResponse(res);
  202   
  203               // Link objects
  204               request.setResponse(response);
  205               response.setRequest(request);
  206   
  207               // Set as notes
  208               req.setNote(ADAPTER_NOTES, request);
  209               res.setNote(ADAPTER_NOTES, response);
  210   
  211               // Set query string encoding
  212               req.getParameters().setQueryStringEncoding
  213                   (connector.getURIEncoding());
  214           }
  215   
  216           // START SJSAS 6331392
  217           // Check connector for disabled state
  218           if (!connector.isEnabled()) {
  219               String msg = sm.getString("coyoteAdapter.listenerOff",
  220                                         String.valueOf(connector.getPort()));
  221               if (log.isDebugEnabled()) {
  222                   log.debug(msg);            
  223               }
  224               response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
  225               return;
  226           }
  227           // END SJSAS 6331392
  228   
  229           if (connector.isXpoweredBy()) {
  230               response.addHeader("X-Powered-By", "Servlet/2.5");
  231           }
  232   
  233           try {
  234   
  235               // Parse and set Catalina and configuration specific 
  236               // request parameters
  237               if ( postParseRequest(req, request, res, response) ) {
  238   
  239                   // START S1AS 6188932
  240                   boolean authPassthroughEnabled = 
  241                       connector.getAuthPassthroughEnabled();
  242                   ProxyHandler proxyHandler = connector.getProxyHandler();
  243                   if (authPassthroughEnabled && proxyHandler != null) {
  244   
  245                       // START SJSAS 6397218
  246                       if (proxyHandler.getSSLKeysize(
  247                               (HttpServletRequest)request.getRequest()) > 0) {
  248                           request.setSecure(true);
  249                       }
  250                       // END SJSAS 6397218
  251   
  252                       X509Certificate[] certs = null;
  253                       try {
  254                           certs = proxyHandler.getSSLClientCertificateChain(
  255                                       request.getRequest());
  256                       } catch (CertificateException ce) {
  257                           log.error(sm.getString(
  258                               "coyoteAdapter.proxyAuthCertError"),
  259                               ce);
  260                       }
  261                       if (certs != null) {
  262                           request.setAttribute(Globals.CERTIFICATES_ATTR,
  263                                                certs);
  264                       }
  265                       
  266                   }
  267                   // END S1AS 6188932
  268                   
  269                   response.addHeader("Server",serverName);
  270                   
  271                   // Calling the container
  272                   connector.getContainer().invoke(request, response);
  273               }
  274            /* GlassFish Issue 79    
  275               response.finishResponse();
  276               req.action( ActionCode.ACTION_POST_REQUEST , null);
  277   
  278            } catch (IOException e) {
  279               ;
  280            } catch (Throwable t) {
  281                log.error(sm.getString("coyoteAdapter.service"), t);
  282            } finally {
  283                // Recycle the wrapper request and response
  284                request.recycle();
  285                response.recycle();
  286            }*/
  287           // START GlassFish Issue 798
  288           } catch (IOException e) {
  289               // Recycle the wrapper request and response
  290               request.recycle();
  291               response.recycle();
  292           } catch (Throwable t) {
  293               log.error(sm.getString("coyoteAdapter.service"), t);
  294                           // Recycle the wrapper request and response
  295               request.recycle();
  296               response.recycle();
  297           } 
  298           // END GlassFish Issue 798
  299           
  300           if ( compatWithTomcat ) {
  301               afterService(req,res);
  302           }
  303           
  304       }
  305   
  306       // START GlassFish Issue 798
  307       /**
  308        * Finish the response and close the connection based on the connection
  309        * header.
  310        */
  311       public void afterService(Request req,Response res) throws Exception{
  312           CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
  313           CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
  314           
  315           if ( request == null || response == null) return;
  316           
  317           try{
  318               response.finishResponse();
  319               req.action( ActionCode.ACTION_POST_REQUEST , null);
  320           }catch (Throwable t) {
  321               log.error(sm.getString("coyoteAdapter.service"), t);
  322           } finally {
  323               // Recycle the wrapper request and response
  324               request.recycle();
  325               response.recycle();
  326           }
  327       }
  328       // END GlassFish Issue 798
  329       // ------------------------------------------------------ Protected Methods
  330   
  331   
  332       /**
  333        * Parse additional request parameters.
  334        */
  335       protected boolean postParseRequest(Request req, CoyoteRequest request,
  336                                          Response res, CoyoteResponse response)
  337           throws Exception {
  338           // XXX the processor needs to set a correct scheme and port prior to this point, 
  339           // in ajp13 protocols dont make sense to get the port from the connector..
  340           // XXX the processor may have set a correct scheme and port prior to this point, 
  341           // in ajp13 protocols dont make sense to get the port from the connector...
  342           // otherwise, use connector configuration
  343           if (! req.scheme().isNull()) {
  344               // use processor specified scheme to determine secure state
  345               request.setSecure(req.scheme().equals("https"));
  346           } else {
  347               // use connector scheme and secure configuration, (defaults to
  348               // "http" and false respectively)
  349               req.scheme().setString(connector.getScheme());
  350               request.setSecure(connector.getSecure());
  351           }
  352   
  353           // FIXME: the code below doesnt belongs to here, 
  354           // this is only have sense 
  355           // in Http11, not in ajp13..
  356           // At this point the Host header has been processed.
  357           // Override if the proxyPort/proxyHost are set 
  358           String proxyName = connector.getProxyName();
  359           int proxyPort = connector.getProxyPort();
  360           if (proxyPort != 0) {
  361               req.setServerPort(proxyPort);
  362           }
  363           if (proxyName != null) {
  364               req.serverName().setString(proxyName);
  365           }
  366   
  367           // URI decoding
  368           MessageBytes decodedURI = req.decodedURI();
  369           decodedURI.duplicate(req.requestURI());
  370           try {
  371             req.getURLDecoder().convert(decodedURI, false);
  372           } catch (IOException ioe) {
  373             res.setStatus(400);
  374             res.setMessage("Invalid URI: " + ioe.getMessage());
  375             return false;
  376           }
  377   
  378           /* GlassFish Issue 2339
  379           // Normalize decoded URI
  380           if (!normalize(req.decodedURI())) {
  381               res.setStatus(400);
  382               res.setMessage("Invalid URI");
  383               return false;
  384           }
  385           */
  386   
  387           // Set the remote principal
  388           String principal = req.getRemoteUser().toString();
  389           if (principal != null) {
  390               request.setUserPrincipal(new CoyotePrincipal(principal));
  391           }
  392   
  393           // Set the authorization type
  394           String authtype = req.getAuthType().toString();
  395           if (authtype != null) {
  396               request.setAuthType(authtype);
  397           }
  398   
  399           /* CR 6309511
  400           // URI character decoding
  401           convertURI(decodedURI, request);
  402   
  403           // Parse session Id
  404           parseSessionId(req, request);
  405            */
  406           // START CR 6309511
  407           // URI character decoding
  408           request.convertURI(decodedURI);
  409   
  410           // START GlassFish Issue 2339
  411           // Normalize decoded URI
  412           if (!normalize(decodedURI)) {
  413               res.setStatus(400);
  414               res.setMessage("Invalid URI");
  415               return false;
  416           }
  417           // END GlassFish Issue 2339
  418   
  419           // Parse session Id
  420           request.parseSessionId();
  421           // END CR 6309511
  422   
  423           // Remove any remaining parameters (other than session id, which has
  424           // already been removed in parseSessionId()) from the URI, so they
  425           // won't be considered by the mapping algorithm.
  426           CharChunk uriCC = decodedURI.getCharChunk();
  427           int semicolon = uriCC.indexOf(';');
  428           String sessionVersionString = null;
  429           if (semicolon > 0) {
  430               sessionVersionString = request.parseSessionVersion();
  431               decodedURI.setChars
  432                   (uriCC.getBuffer(), uriCC.getStart(), semicolon);
  433           }
  434   
  435           // Request mapping.
  436           connector.getMapper().map(req.serverName(), decodedURI, 
  437                                     request.getMappingData());
  438           // START GlassFish 1024
  439           request.setDefaultContext(request.getMappingData().isDefaultContext);
  440           // END GlassFish 1024
  441   
  442           // START SJSAS 6253524
  443           // request.setContext((Context) request.getMappingData().context);
  444           // END SJSAS 6253524
  445           // START SJSAS 6253524
  446           Context ctx = (Context) request.getMappingData().context;
  447           request.setContext(ctx);
  448           // END SJSAS 6253524
  449   
  450           request.setWrapper((Wrapper) request.getMappingData().wrapper);
  451   
  452           // Filter trace method
  453           if (!connector.getAllowTrace() 
  454                   && req.method().equalsIgnoreCase("TRACE")) {
  455               Wrapper wrapper = request.getWrapper();
  456               String header = null;
  457               if (wrapper != null) {
  458                   String[] methods = wrapper.getServletMethods();
  459                   if (methods != null) {
  460                       for (int i=0; i<methods.length; i++) {
  461                           // Exclude TRACE from methods returned in Allow header
  462                           if ("TRACE".equals(methods[i])) {
  463                               continue;
  464                           }
  465                           if (header == null) {
  466                               header = methods[i];
  467                           } else {
  468                               header += ", " + methods[i];
  469                           }
  470                       }
  471                   }
  472               }                               
  473               res.setStatus(405);
  474               res.addHeader("Allow", header);
  475               res.setMessage("TRACE method is not allowed");
  476               return false;
  477           }
  478   
  479           // Possible redirect
  480           MessageBytes redirectPathMB = request.getMappingData().redirectPath;
  481           // START SJSAS 6253524
  482           // if (!redirectPathMB.isNull()) {
  483           // END SJSAS 6253524
  484           // START SJSAS 6253524
  485           if (!redirectPathMB.isNull()
  486               && (!ctx.hasAdHocPaths()
  487                   || (ctx.getAdHocServletName(((HttpServletRequest)
  488                           request.getRequest()).getServletPath()) == null))) {
  489           // END SJSAS 6253524
  490               String redirectPath = redirectPathMB.toString();
  491               String query = request.getQueryString();
  492               if (request.isRequestedSessionIdFromURL()) {
  493                   // This is not optimal, but as this is not very common, it
  494                   // shouldn't matter
  495                   redirectPath = redirectPath + ";jsessionid=" 
  496                       + request.getRequestedSessionId();
  497               }            
  498               // START GlassFish 936
  499               redirectPath = urlEncoder.encodeURL(redirectPath);
  500               // END GlassFish 936
  501               if (query != null) {
  502                   // This is not optimal, but as this is not very common, it
  503                   // shouldn't matter
  504                   redirectPath = redirectPath + "?" + query;
  505               }
  506   
  507               // START CR 6590921
  508               boolean authPassthroughEnabled = 
  509                   connector.getAuthPassthroughEnabled();
  510               ProxyHandler proxyHandler = connector.getProxyHandler();
  511               if (authPassthroughEnabled && proxyHandler != null) {
  512   
  513                   if (proxyHandler.getSSLKeysize(
  514                           (HttpServletRequest)request.getRequest()) > 0) {
  515                       request.setSecure(true);
  516                   }
  517               }
  518               // END CR 6590921
  519   
  520               response.sendRedirect(redirectPath);
  521               return false;
  522           }
  523   
  524           // Parse session Id
  525           /* CR 6309511
  526           parseSessionCookiesId(req, request);
  527            */
  528           // START CR 6309511
  529           request.parseSessionCookiesId();
  530           // END CR 6309511
  531   
  532           // START SJSAS 6346226
  533           request.parseJrouteCookie();
  534           // END SJSAS 6346226
  535   
  536           if (sessionVersionString != null) {
  537               request.parseSessionVersionString(sessionVersionString);
  538           }
  539   
  540           return true;
  541       }
  542   
  543   
  544       /**
  545        * Parse session id in URL.
  546        */
  547       /* CR 6309511
  548       protected void parseSessionId(Request req, CoyoteRequest request) {
  549   
  550           CharChunk uriCC = req.decodedURI().getCharChunk();
  551           int semicolon = uriCC.indexOf(match, 0, match.length(), 0);
  552   
  553           if (semicolon > 0) {
  554   
  555               // Parse session ID, and extract it from the decoded request URI
  556               int start = uriCC.getStart();
  557               int end = uriCC.getEnd();
  558   
  559               int sessionIdStart = start + semicolon + match.length();
  560               int semicolon2 = uriCC.indexOf(';', sessionIdStart);
  561               if (semicolon2 >= 0) {
  562                   request.setRequestedSessionId
  563                       (new String(uriCC.getBuffer(), sessionIdStart, 
  564                                   semicolon2 - semicolon - match.length()));
  565               } else {
  566                   request.setRequestedSessionId
  567                       (new String(uriCC.getBuffer(), sessionIdStart, 
  568                                   end - sessionIdStart));
  569               }
  570               request.setRequestedSessionURL(true);
  571   
  572               // Extract session ID from request URI
  573               ByteChunk uriBC = req.requestURI().getByteChunk();
  574               start = uriBC.getStart();
  575               end = uriBC.getEnd();
  576               semicolon = uriBC.indexOf(match, 0, match.length(), 0);
  577   
  578               if (semicolon > 0) {
  579                   sessionIdStart = start + semicolon;
  580                   semicolon2 = uriCC.indexOf
  581                       (';', start + semicolon + match.length());
  582                   uriBC.setEnd(start + semicolon);
  583                   byte[] buf = uriBC.getBuffer();
  584                   if (semicolon2 >= 0) {
  585                       for (int i = 0; i < end - start - semicolon2; i++) {
  586                           buf[start + semicolon + i] 
  587                               = buf[start + i + semicolon2];
  588                       }
  589                       uriBC.setBytes(buf, start, semicolon 
  590                                      + (end - start - semicolon2));
  591                   }
  592               }
  593   
  594           } else {
  595               request.setRequestedSessionId(null);
  596               request.setRequestedSessionURL(false);
  597           }
  598   
  599       }
  600        */
  601   
  602   
  603       /**
  604        * Parse session id in URL.
  605        */
  606       /* CR 6309511
  607       protected void parseSessionCookiesId(Request req, CoyoteRequest request) {
  608   
  609           // Parse session id from cookies
  610           Cookies serverCookies = req.getCookies();
  611           int count = serverCookies.getCookieCount();
  612           if (count <= 0)
  613               return;
  614   
  615           for (int i = 0; i < count; i++) {
  616               ServerCookie scookie = serverCookies.getCookie(i);
  617               if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
  618                   // Override anything requested in the URL
  619                   if (!request.isRequestedSessionIdFromCookie()) {
  620                       // Accept only the first session id cookie
  621                       convertMB(scookie.getValue());
  622                       request.setRequestedSessionId
  623                           (scookie.getValue().toString());
  624                       request.setRequestedSessionCookie(true);
  625                       request.setRequestedSessionURL(false);
  626                       if (log.isDebugEnabled())
  627                           log.debug(" Requested cookie session id is " +
  628                               ((HttpServletRequest) request.getRequest())
  629                               .getRequestedSessionId());
  630                   } else {
  631                       if (!request.isRequestedSessionIdValid()) {
  632                           // Replace the session id until one is valid
  633                           convertMB(scookie.getValue());
  634                           request.setRequestedSessionId
  635                               (scookie.getValue().toString());
  636                       }
  637                   }
  638               }
  639           }
  640   
  641       }
  642        */
  643   
  644   
  645       /**
  646        * Character conversion of the URI.
  647        */
  648       /* CR 6309511
  649       protected void convertURI(MessageBytes uri, CoyoteRequest request) 
  650           throws Exception {
  651   
  652           ByteChunk bc = uri.getByteChunk();
  653           CharChunk cc = uri.getCharChunk();
  654           cc.allocate(bc.getLength(), -1);
  655   
  656           String enc = connector.getURIEncoding();
  657           if (enc != null) {
  658               B2CConverter conv = request.getURIConverter();
  659               try {
  660                   if (conv == null) {
  661                       conv = new B2CConverter(enc);
  662                       request.setURIConverter(conv);
  663                   } else {
  664                       conv.recycle();
  665                   }
  666               } catch (IOException e) {
  667                   // Ignore
  668                   log.error("Invalid URI encoding; using HTTP default");
  669                   connector.setURIEncoding(null);
  670               }
  671               if (conv != null) {
  672                   try {
  673                       conv.convert(bc, cc);
  674                       uri.setChars(cc.getBuffer(), cc.getStart(), 
  675                                    cc.getLength());
  676                       return;
  677                   } catch (IOException e) {
  678                       log.error("Invalid URI character encoding; trying ascii");
  679                       cc.recycle();
  680                   }
  681               }
  682           }
  683   
  684           // Default encoding: fast conversion
  685           byte[] bbuf = bc.getBuffer();
  686           char[] cbuf = cc.getBuffer();
  687           int start = bc.getStart();
  688           for (int i = 0; i < bc.getLength(); i++) {
  689               cbuf[i] = (char) (bbuf[i + start] & 0xff);
  690           }
  691           uri.setChars(cbuf, 0, bc.getLength());
  692   
  693       }
  694        */
  695   
  696   
  697       /**
  698        * Normalize URI.
  699        * <p>
  700        * This method normalizes "\", "//", "/./" and "/../". This method will
  701        * return false when trying to go above the root, or if the URI contains
  702        * a null byte.
  703        * 
  704        * @param uriMB URI to be normalized
  705        */
  706       public static boolean normalize(MessageBytes uriMB) {
  707   
  708           int type = uriMB.getType();
  709           if (type == MessageBytes.T_CHARS) {
  710               return normalizeChars(uriMB);
  711           } else {
  712               return normalizeBytes(uriMB);
  713           }
  714       }
  715   
  716   
  717       private static boolean normalizeBytes(MessageBytes uriMB) {
  718   
  719           ByteChunk uriBC = uriMB.getByteChunk();
  720           byte[] b = uriBC.getBytes();
  721           int start = uriBC.getStart();
  722           int end = uriBC.getEnd();
  723   
  724           // URL * is acceptable
  725           if ((end - start == 1) && b[start] == (byte) '*')
  726             return true;
  727   
  728           int pos = 0;
  729           int index = 0;
  730   
  731           // Replace '\' with '/'
  732           // Check for null byte
  733           for (pos = start; pos < end; pos++) {
  734               if (b[pos] == (byte) '\\') {
  735                   if (ALLOW_BACKSLASH) {
  736                       b[pos] = (byte) '/';
  737                   } else {
  738                       return false;
  739                   }
  740               }
  741               if (b[pos] == (byte) 0) {
  742                   return false;
  743               }
  744           }
  745   
  746           // The URL must start with '/'
  747           if (b[start] != (byte) '/') {
  748               return false;
  749           }
  750   
  751           // Replace "//" with "/"
  752           if (COLLAPSE_ADJACENT_SLASHES) {
  753               for (pos = start; pos < (end - 1); pos++) {
  754                   if (b[pos] == (byte) '/') {
  755                       while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
  756                           copyBytes(b, pos, pos + 1, end - pos - 1);
  757                           end--;
  758                       }
  759                   }
  760               }
  761           }
  762   
  763           // If the URI ends with "/." or "/..", then we append an extra "/"
  764           // Note: It is possible to extend the URI by 1 without any side effect
  765           // as the next character is a non-significant WS.
  766           if (((end - start) > 2) && (b[end - 1] == (byte) '.')) {
  767               if ((b[end - 2] == (byte) '/') 
  768                   || ((b[end - 2] == (byte) '.') 
  769                       && (b[end - 3] == (byte) '/'))) {
  770                   b[end] = (byte) '/';
  771                   end++;
  772               }
  773           }
  774   
  775           uriBC.setEnd(end);
  776   
  777           index = 0;
  778   
  779           // Resolve occurrences of "/./" in the normalized path
  780           while (true) {
  781               index = uriBC.indexOf("/./", 0, 3, index);
  782               if (index < 0)
  783                   break;
  784               copyBytes(b, start + index, start + index + 2, 
  785                         end - start - index - 2);
  786               end = end - 2;
  787               uriBC.setEnd(end);
  788           }
  789   
  790           index = 0;
  791   
  792           // Resolve occurrences of "/../" in the normalized path
  793           while (true) {
  794               index = uriBC.indexOf("/../", 0, 4, index);
  795               if (index < 0)
  796                   break;
  797               // Prevent from going outside our context
  798               if (index == 0)
  799                   return false;
  800               int index2 = -1;
  801               for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
  802                   if (b[pos] == (byte) '/') {
  803                       index2 = pos;
  804                   }
  805               }
  806               copyBytes(b, start + index2, start + index + 3,
  807                         end - start - index - 3);
  808               end = end + index2 - index - 3;
  809               uriBC.setEnd(end);
  810               index = index2;
  811           }
  812   
  813           uriBC.setBytes(b, start, end);
  814   
  815           return true;
  816   
  817       }
  818   
  819   
  820       private static boolean normalizeChars(MessageBytes uriMB) {
  821   
  822           CharChunk uriCC = uriMB.getCharChunk();
  823           char[] c = uriCC.getChars();
  824           int start = uriCC.getStart();
  825           int end = uriCC.getEnd();
  826   
  827           // URL * is acceptable
  828           if ((end - start == 1) && c[start] == (char) '*')
  829             return true;
  830   
  831           int pos = 0;
  832           int index = 0;
  833   
  834           // Replace '\' with '/'
  835           // Check for null char
  836           for (pos = start; pos < end; pos++) {
  837               if (c[pos] == (char) '\\') {
  838                   if (ALLOW_BACKSLASH) {
  839                       c[pos] = (char) '/';
  840                   } else {
  841                       return false;
  842                   }
  843               }
  844               if (c[pos] == (char) 0) {
  845                   return false;
  846               }
  847           }
  848   
  849           // The URL must start with '/'
  850           if (c[start] != (char) '/') {
  851               return false;
  852           }
  853   
  854           // Replace "//" with "/"
  855           if (COLLAPSE_ADJACENT_SLASHES) {
  856               for (pos = start; pos < (end - 1); pos++) {
  857                   if (c[pos] == (char) '/') {
  858                       while ((pos + 1 < end) && (c[pos + 1] == (char) '/')) {
  859                           copyChars(c, pos, pos + 1, end - pos - 1);
  860                           end--;
  861                       }
  862                   }
  863               }
  864           }	
  865   
  866           // If the URI ends with "/." or "/..", then we append an extra "/"
  867           // Note: It is possible to extend the URI by 1 without any side effect
  868           // as the next character is a non-significant WS.
  869           if (((end - start) > 2) && (c[end - 1] == (char) '.')) {
  870               if ((c[end - 2] == (char) '/') 
  871                   || ((c[end - 2] == (char) '.') 
  872                       && (c[end - 3] == (char) '/'))) {
  873                   c[end] = (char) '/';
  874                   end++;
  875               }
  876           }
  877   
  878           uriCC.setEnd(end);
  879   
  880           index = 0;
  881   
  882           // Resolve occurrences of "/./" in the normalized path
  883           while (true) {
  884               index = uriCC.indexOf("/./", 0, 3, index);
  885               if (index < 0)
  886                   break;
  887               copyChars(c, start + index, start + index + 2, 
  888                         end - start - index - 2);
  889               end = end - 2;
  890               uriCC.setEnd(end);
  891           }
  892   
  893           index = 0;
  894   
  895           // Resolve occurrences of "/../" in the normalized path
  896           while (true) {
  897               index = uriCC.indexOf("/../", 0, 4, index);
  898               if (index < 0)
  899                   break;
  900               // Prevent from going outside our context
  901               if (index == 0)
  902                   return false;
  903               int index2 = -1;
  904               for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
  905                   if (c[pos] == (char) '/') {
  906                       index2 = pos;
  907                   }
  908               }
  909               copyChars(c, start + index2, start + index + 3,
  910                         end - start - index - 3);
  911               end = end + index2 - index - 3;
  912               uriCC.setEnd(end);
  913               index = index2;
  914           }
  915   
  916           uriCC.setChars(c, start, end);
  917   
  918           return true;
  919   
  920       }
  921   
  922   
  923       // ------------------------------------------------------ Protected Methods
  924   
  925   
  926       /**
  927        * Copy an array of bytes to a different position. Used during 
  928        * normalization.
  929        */
  930       protected static void copyBytes(byte[] b, int dest, int src, int len) {
  931           for (int pos = 0; pos < len; pos++) {
  932               b[pos + dest] = b[pos + src];
  933           }
  934       }
  935   
  936   
  937       /**
  938        * Copy an array of chars to a different position. Used during 
  939        * normalization.
  940        */
  941       private static void copyChars(char[] c, int dest, int src, int len) {
  942           for (int pos = 0; pos < len; pos++) {
  943               c[pos + dest] = c[pos + src];
  944           }
  945       }
  946   
  947   
  948       /**
  949        * Log a message on the Logger associated with our Container (if any)
  950        *
  951        * @param message Message to be logged
  952        */
  953       protected void log(String message) {
  954           log.info( message );
  955       }
  956   
  957   
  958       /**
  959        * Log a message on the Logger associated with our Container (if any)
  960        *
  961        * @param message Message to be logged
  962        * @param throwable Associated exception
  963        */
  964       protected void log(String message, Throwable throwable) {
  965           log.error( message, throwable);
  966       }
  967   
  968   
  969        /**
  970         * Character conversion of the a US-ASCII MessageBytes.
  971         */
  972       /* CR 6309511
  973        protected void convertMB(MessageBytes mb) {
  974    
  975           // This is of course only meaningful for bytes
  976           if (mb.getType() != MessageBytes.T_BYTES)
  977               return;
  978           
  979           ByteChunk bc = mb.getByteChunk();
  980           CharChunk cc = mb.getCharChunk();
  981           cc.allocate(bc.getLength(), -1);
  982   
  983           // Default encoding: fast conversion
  984           byte[] bbuf = bc.getBuffer();
  985           char[] cbuf = cc.getBuffer();
  986           int start = bc.getStart();
  987           for (int i = 0; i < bc.getLength(); i++) {
  988               cbuf[i] = (char) (bbuf[i + start] & 0xff);
  989           }
  990           mb.setChars(cbuf, 0, bc.getLength());
  991      
  992        }
  993        */
  994   
  995       
  996       // START SJSAS 6349248
  997       /**
  998        * Notify all container event listeners that a particular event has
  999        * occurred for this Adapter.  The default implementation performs
 1000        * this notification synchronously using the calling thread.
 1001        *
 1002        * @param type Event type
 1003        * @param data Event data
 1004        */
 1005       public void fireAdapterEvent(String type, Object data) {
 1006           if ( connector != null && connector.getContainer() != null) {
 1007               try{
 1008                   ((ContainerBase)connector.getContainer())
 1009                       .fireContainerEvent(type,data);
 1010               } catch (Throwable t){
 1011                   log.error(sm.getString("coyoteAdapter.service"), t);
 1012               }
 1013           }
 1014       }
 1015       // END SJSAS 6349248
 1016   
 1017       
 1018       /**
 1019        * Return true when an instance is executed the same way it does in Tomcat.
 1020        */
 1021       public boolean isCompatWithTomcat() {
 1022           return compatWithTomcat;
 1023       }
 1024   
 1025       
 1026       /**
 1027        * <tt>true</tt> if this class needs to be compatible with Tomcat
 1028        * Adapter class. Since Tomcat Adapter implementation doesn't support 
 1029        * the afterService method, the afterService method must be invoked 
 1030        * inside the service method.
 1031        */
 1032       public void setCompatWithTomcat(boolean compatWithTomcat) {
 1033           this.compatWithTomcat = compatWithTomcat;
 1034   
 1035           // Add server header
 1036           if (compatWithTomcat){
 1037               serverName = "Apache/" + serverName;
 1038           } else {
 1039               // Recalculate.
 1040               serverName = System.getProperty("product.name");
 1041           }
 1042       }
 1043          
 1044   }

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