Save This Page
Home » apache-tomcat-6.0.16-src » org.apache.jk » common » [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   package org.apache.jk.common;
   19   
   20   import java.io.File;
   21   import java.io.FileOutputStream;
   22   import java.io.IOException;
   23   import java.io.CharConversionException;
   24   import java.net.InetAddress;
   25   import java.util.Properties;
   26   
   27   import org.apache.coyote.Request;
   28   import org.apache.coyote.RequestInfo;
   29   import org.apache.coyote.Response;
   30   import org.apache.coyote.Constants;
   31   import org.apache.jk.core.JkHandler;
   32   import org.apache.jk.core.Msg;
   33   import org.apache.jk.core.MsgContext;
   34   import org.apache.jk.core.WorkerEnv;
   35   import org.apache.jk.core.JkChannel;
   36   import org.apache.tomcat.util.buf.ByteChunk;
   37   import org.apache.tomcat.util.buf.CharChunk;
   38   import org.apache.tomcat.util.buf.HexUtils;
   39   import org.apache.tomcat.util.buf.MessageBytes;
   40   import org.apache.tomcat.util.http.MimeHeaders;
   41   import org.apache.tomcat.util.net.SSLSupport;
   42   import org.apache.tomcat.util.threads.ThreadWithAttributes;
   43   
   44   /**
   45    * Handle messages related with basic request information.
   46    *
   47    * This object can handle the following incoming messages:
   48    * - "FORWARD_REQUEST" input message ( sent when a request is passed from the
   49    *   web server )
   50    * - "RECEIVE_BODY_CHUNK" input ( sent by container to pass more body, in
   51    *   response to GET_BODY_CHUNK )
   52    *
   53    * It can handle the following outgoing messages:
   54    * - SEND_HEADERS. Pass the status code and headers.
   55    * - SEND_BODY_CHUNK. Send a chunk of body
   56    * - GET_BODY_CHUNK. Request a chunk of body data
   57    * - END_RESPONSE. Notify the end of a request processing.
   58    *
   59    * @author Henri Gomez [hgomez@apache.org]
   60    * @author Dan Milstein [danmil@shore.net]
   61    * @author Keith Wannamaker [Keith@Wannamaker.org]
   62    * @author Costin Manolache
   63    */
   64   public class HandlerRequest extends JkHandler
   65   {
   66       private static org.apache.juli.logging.Log log=
   67           org.apache.juli.logging.LogFactory.getLog( HandlerRequest.class );
   68   
   69       /*
   70        * Note for Host parsing.
   71        */
   72       public static final int HOSTBUFFER = 10;
   73   
   74       /**
   75        * Thread lock.
   76        */
   77       private static Object lock = new Object();
   78   
   79       private HandlerDispatch dispatch;
   80       private String ajpidDir="conf";
   81       
   82   
   83       public HandlerRequest() {
   84       }
   85   
   86       public void init() {
   87           dispatch=(HandlerDispatch)wEnv.getHandler( "dispatch" );
   88           if( dispatch != null ) {
   89               // register incoming message handlers
   90               dispatch.registerMessageType( AjpConstants.JK_AJP13_FORWARD_REQUEST,
   91                                             "JK_AJP13_FORWARD_REQUEST",
   92                                             this, null); // 2
   93               
   94               dispatch.registerMessageType( AjpConstants.JK_AJP13_SHUTDOWN,
   95                                             "JK_AJP13_SHUTDOWN",
   96                                             this, null); // 7
   97               
   98               dispatch.registerMessageType( AjpConstants.JK_AJP13_CPING_REQUEST,
   99                                             "JK_AJP13_CPING_REQUEST",
  100                                              this, null); // 10
  101               dispatch.registerMessageType( HANDLE_THREAD_END,
  102                                            "HANDLE_THREAD_END",
  103                                            this, null);
  104               // register outgoing messages handler
  105               dispatch.registerMessageType( AjpConstants.JK_AJP13_SEND_BODY_CHUNK, // 3
  106                                             "JK_AJP13_SEND_BODY_CHUNK",
  107                                             this,null );
  108           }
  109   
  110           tmpBufNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "tmpBuf" );
  111           secretNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "secret" );
  112           
  113           if( next==null )
  114               next=wEnv.getHandler( "container" );
  115           if( log.isDebugEnabled() )
  116               log.debug( "Container handler " + next + " " + next.getName() +
  117                          " " + next.getClass().getName());
  118   
  119           // should happen on start()
  120           generateAjp13Id();
  121       }
  122   
  123       public void setSecret( String s ) {
  124           requiredSecret=s;
  125       }
  126   
  127       public void setUseSecret( boolean b ) {
  128           if(b) {
  129               requiredSecret=Double.toString(Math.random());
  130           }
  131       }
  132   
  133       public void setDecodedUri( boolean b ) {
  134           decoded=b;
  135       }
  136   
  137       public boolean isTomcatAuthentication() {
  138           return tomcatAuthentication;
  139       }
  140   
  141       public void setShutdownEnabled(boolean se) {
  142           shutdownEnabled = se;
  143       }
  144   
  145       public boolean getShutdownEnabled() {
  146           return shutdownEnabled;
  147       }
  148   
  149       public void setTomcatAuthentication(boolean newTomcatAuthentication) {
  150           tomcatAuthentication = newTomcatAuthentication;
  151       }
  152       
  153       public void setAjpidDir( String path ) {
  154           if( "".equals( path ) ) path=null;
  155           ajpidDir=path;
  156       }
  157   
  158       /**
  159        * Set the flag to tell if we JMX register requests.
  160        */
  161       public void setRegisterRequests(boolean srr) {
  162           registerRequests = srr;
  163       }
  164   
  165       /**
  166        * Get the flag to tell if we JMX register requests.
  167        */
  168       public boolean getRegisterRequests() {
  169           return registerRequests;
  170       }
  171   
  172       /**
  173        * Set the flag to delay the initial body read
  174        */
  175       public void setDelayInitialRead(boolean dir) {
  176   	delayInitialRead = dir;
  177       }
  178   
  179       /**
  180        * Get the flag to tell if we delay the initial body read
  181        */
  182       public boolean getDelayInitialRead() {
  183   	return delayInitialRead;
  184       }
  185   
  186       // -------------------- Ajp13.id --------------------
  187   
  188       private void generateAjp13Id() {
  189           int portInt=8009; // tcpCon.getPort();
  190           InetAddress address=null; // tcpCon.getAddress();
  191   
  192           if( requiredSecret == null || !shutdownEnabled )
  193               return;
  194           
  195           File f1=new File( wEnv.getJkHome() );
  196           File f2=new File( f1, "conf" );
  197           
  198           if( ! f2.exists() ) {
  199               log.error( "No conf dir for ajp13.id " + f2 );
  200               return;
  201           }
  202           
  203           File sf=new File( f2, "ajp13.id");
  204           
  205           if( log.isDebugEnabled())
  206               log.debug( "Using stop file: "+sf);
  207   
  208           try {
  209               Properties props=new Properties();
  210   
  211               props.put( "port", Integer.toString( portInt ));
  212               if( address!=null ) {
  213                   props.put( "address", address.getHostAddress() );
  214               }
  215               if( requiredSecret !=null ) {
  216                   props.put( "secret", requiredSecret );
  217               }
  218   
  219               FileOutputStream stopF=new FileOutputStream( sf );
  220               props.store( stopF, "Automatically generated, don't edit" );
  221           } catch( IOException ex ) {
  222               if(log.isDebugEnabled())
  223                   log.debug( "Can't create stop file: "+sf,ex );
  224           }
  225       }
  226       
  227       // -------------------- Incoming message --------------------
  228       private String requiredSecret=null;
  229       private int secretNote;
  230       private int tmpBufNote;
  231   
  232       private boolean decoded=true;
  233       private boolean tomcatAuthentication=true;
  234       private boolean registerRequests=true;
  235       private boolean shutdownEnabled=false;
  236       private boolean delayInitialRead = true;
  237       
  238       public int invoke(Msg msg, MsgContext ep ) 
  239           throws IOException    {
  240           int type=msg.getByte();
  241           ThreadWithAttributes twa = null;
  242           if (Thread.currentThread() instanceof ThreadWithAttributes) {
  243               twa = (ThreadWithAttributes) Thread.currentThread();
  244           }
  245           Object control=ep.getControl();
  246           MessageBytes tmpMB=(MessageBytes)ep.getNote( tmpBufNote );
  247           if( tmpMB==null ) {
  248               tmpMB= MessageBytes.newInstance();
  249               ep.setNote( tmpBufNote, tmpMB);
  250           }
  251   
  252           if( log.isDebugEnabled() )
  253               log.debug( "Handling " + type );
  254           
  255           switch( type ) {
  256           case AjpConstants.JK_AJP13_FORWARD_REQUEST:
  257               try {
  258                   if (twa != null) {
  259                       twa.setCurrentStage(control, "JkDecode");
  260                   }
  261                   decodeRequest( msg, ep, tmpMB );
  262                   if (twa != null) {
  263                       twa.setCurrentStage(control, "JkService");
  264                       twa.setParam(control,
  265                                    ((Request)ep.getRequest()).unparsedURI());
  266                   }
  267               } catch( Exception ex ) {
  268                   log.error( "Error decoding request ", ex );
  269                   msg.dump( "Incomming message");
  270                   return ERROR;
  271               }
  272   
  273               if( requiredSecret != null ) {
  274                   String epSecret=(String)ep.getNote( secretNote );
  275                   if( epSecret==null || ! requiredSecret.equals( epSecret ) )
  276                       return ERROR;
  277               }
  278               /* XXX it should be computed from request, by workerEnv */
  279               if(log.isDebugEnabled() )
  280                   log.debug("Calling next " + next.getName() + " " +
  281                     next.getClass().getName());
  282   
  283               int err= next.invoke( msg, ep );
  284               if (twa != null) {
  285                   twa.setCurrentStage(control, "JkDone");
  286               }
  287   
  288               if( log.isDebugEnabled() )
  289                   log.debug( "Invoke returned " + err );
  290               return err;
  291           case AjpConstants.JK_AJP13_SHUTDOWN:
  292               String epSecret=null;
  293               if( msg.getLen() > 3 ) {
  294                   // we have a secret
  295                   msg.getBytes( tmpMB );
  296                   epSecret=tmpMB.toString();
  297               }
  298               
  299               if( requiredSecret != null &&
  300                   requiredSecret.equals( epSecret ) ) {
  301                   if( log.isDebugEnabled() )
  302                       log.debug("Received wrong secret, no shutdown ");
  303                   return ERROR;
  304               }
  305   
  306               // XXX add isSameAddress check
  307               JkChannel ch=ep.getSource();
  308               if( !ch.isSameAddress(ep) ) {
  309                   log.error("Shutdown request not from 'same address' ");
  310                   return ERROR;
  311               }
  312   
  313               if( !shutdownEnabled ) {
  314                   log.warn("Ignoring shutdown request: shutdown not enabled");
  315                   return ERROR;
  316               }
  317               // forward to the default handler - it'll do the shutdown
  318               checkRequest(ep);
  319               next.invoke( msg, ep );
  320   
  321               if(log.isInfoEnabled())
  322                   log.info("Exiting");
  323               System.exit(0);
  324               
  325               return OK;
  326   
  327               // We got a PING REQUEST, quickly respond with a PONG
  328           case AjpConstants.JK_AJP13_CPING_REQUEST:
  329               msg.reset();
  330               msg.appendByte(AjpConstants.JK_AJP13_CPONG_REPLY);
  331               ep.getSource().send( msg, ep );
  332               ep.getSource().flush( msg, ep ); // Server needs to get it
  333               return OK;
  334   
  335           case HANDLE_THREAD_END:
  336               return OK;
  337   
  338           default:
  339               if(log.isInfoEnabled())
  340                   log.info("Unknown message " + type);
  341           }
  342   
  343           return OK;
  344       }
  345   
  346       static int count = 0;
  347   
  348       private Request checkRequest(MsgContext ep) {
  349           Request req=ep.getRequest();
  350           if( req==null ) {
  351               req=new Request();
  352               Response res=new Response();
  353               req.setResponse(res);
  354               ep.setRequest( req );
  355               if( registerRequests ) {
  356                   synchronized(lock) {
  357                       ep.getSource().registerRequest(req, ep, count++);
  358                   }
  359               }
  360           }
  361           return req;
  362       }
  363   
  364       private int decodeRequest( Msg msg, MsgContext ep, MessageBytes tmpMB )
  365           throws IOException    {
  366           // FORWARD_REQUEST handler
  367           Request req = checkRequest(ep);
  368   
  369           RequestInfo rp = req.getRequestProcessor();
  370           rp.setStage(Constants.STAGE_PARSE);
  371           MessageBytes tmpMB2 = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE);
  372           if(tmpMB2 != null) {
  373               tmpMB2.recycle();
  374           }
  375           req.setStartTime(System.currentTimeMillis());
  376           
  377           // Translate the HTTP method code to a String.
  378           byte methodCode = msg.getByte();
  379           if (methodCode != AjpConstants.SC_M_JK_STORED) {
  380               String mName=AjpConstants.methodTransArray[(int)methodCode - 1];
  381               req.method().setString(mName);
  382           }
  383   
  384           msg.getBytes(req.protocol()); 
  385           msg.getBytes(req.requestURI());
  386   
  387           msg.getBytes(req.remoteAddr());
  388           msg.getBytes(req.remoteHost());
  389           msg.getBytes(req.localName());
  390           req.setLocalPort(msg.getInt());
  391   
  392           boolean isSSL = msg.getByte() != 0;
  393           if( isSSL ) {
  394               // XXX req.setSecure( true );
  395               req.scheme().setString("https");
  396           }
  397   
  398           decodeHeaders( ep, msg, req, tmpMB );
  399   
  400           decodeAttributes( ep, msg, req, tmpMB );
  401   
  402           rp.setStage(Constants.STAGE_PREPARE);
  403           MessageBytes valueMB = req.getMimeHeaders().getValue("host");
  404           parseHost(valueMB, req);
  405           // set cookies on request now that we have all headers
  406           req.getCookies().setHeaders(req.getMimeHeaders());
  407   
  408           // Check to see if there should be a body packet coming along
  409           // immediately after
  410           long cl=req.getContentLengthLong();
  411           if(cl > 0) {
  412               JkInputStream jkIS = ep.getInputStream();
  413               jkIS.setIsReadRequired(true);
  414               if(!delayInitialRead) {
  415                   jkIS.receive();
  416               }
  417           }
  418       
  419           if (log.isTraceEnabled()) {
  420               log.trace(req.toString());
  421            }
  422   
  423           return OK;
  424       }
  425           
  426       private int decodeAttributes( MsgContext ep, Msg msg, Request req,
  427                                     MessageBytes tmpMB) {
  428           boolean moreAttr=true;
  429   
  430           while( moreAttr ) {
  431               byte attributeCode=msg.getByte();
  432               if( attributeCode == AjpConstants.SC_A_ARE_DONE )
  433                   return 200;
  434   
  435               /* Special case ( XXX in future API make it separate type !)
  436                */
  437               if( attributeCode == AjpConstants.SC_A_SSL_KEY_SIZE ) {
  438                   // Bug 1326: it's an Integer.
  439                   req.setAttribute(SSLSupport.KEY_SIZE_KEY,
  440                                    new Integer( msg.getInt()));
  441                  //Integer.toString(msg.getInt()));
  442               }
  443   
  444               if( attributeCode == AjpConstants.SC_A_REQ_ATTRIBUTE ) {
  445                   // 2 strings ???...
  446                   msg.getBytes( tmpMB );
  447                   String n=tmpMB.toString();
  448                   msg.getBytes( tmpMB );
  449                   String v=tmpMB.toString();
  450                   req.setAttribute(n, v );
  451                   if(log.isTraceEnabled())
  452                       log.trace("jk Attribute set " + n + "=" + v);
  453               }
  454   
  455   
  456               // 1 string attributes
  457               switch(attributeCode) {
  458               case AjpConstants.SC_A_CONTEXT      :
  459                   msg.getBytes( tmpMB );
  460                   // nothing
  461                   break;
  462                   
  463               case AjpConstants.SC_A_SERVLET_PATH :
  464                   msg.getBytes( tmpMB );
  465                   // nothing 
  466                   break;
  467                   
  468               case AjpConstants.SC_A_REMOTE_USER  :
  469                   if( tomcatAuthentication ) {
  470                       // ignore server
  471                       msg.getBytes( tmpMB );
  472                   } else {
  473                       msg.getBytes(req.getRemoteUser());
  474                   }
  475                   break;
  476                   
  477               case AjpConstants.SC_A_AUTH_TYPE    :
  478                   if( tomcatAuthentication ) {
  479                       // ignore server
  480                       msg.getBytes( tmpMB );
  481                   } else {
  482                       msg.getBytes(req.getAuthType());
  483                   }
  484                   break;
  485                   
  486               case AjpConstants.SC_A_QUERY_STRING :
  487                   msg.getBytes(req.queryString());
  488                   break;
  489                   
  490               case AjpConstants.SC_A_JVM_ROUTE    :
  491                   msg.getBytes(req.instanceId());
  492                   break;
  493                   
  494               case AjpConstants.SC_A_SSL_CERT     :
  495                   req.scheme().setString( "https" );
  496                   // Transform the string into certificate.
  497                   MessageBytes tmpMB2 = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE);
  498                   if(tmpMB2 == null) {
  499                       tmpMB2 = MessageBytes.newInstance();
  500                       req.setNote(WorkerEnv.SSL_CERT_NOTE, tmpMB2);
  501                   }
  502                   // SSL certificate extraction is costy, moved to JkCoyoteHandler
  503                   msg.getBytes(tmpMB2);
  504                   break;
  505                   
  506               case AjpConstants.SC_A_SSL_CIPHER   :
  507                   req.scheme().setString( "https" );
  508                   msg.getBytes(tmpMB);
  509                   req.setAttribute(SSLSupport.CIPHER_SUITE_KEY,
  510                                    tmpMB.toString());
  511                   break;
  512                   
  513               case AjpConstants.SC_A_SSL_SESSION  :
  514                   req.scheme().setString( "https" );
  515                   msg.getBytes(tmpMB);
  516                   req.setAttribute(SSLSupport.SESSION_ID_KEY, 
  517                                     tmpMB.toString());
  518                   break;
  519                   
  520               case AjpConstants.SC_A_SECRET  :
  521                   msg.getBytes(tmpMB);
  522                   String secret=tmpMB.toString();
  523                   if(log.isTraceEnabled())
  524                       log.trace("Secret: " + secret );
  525                   // endpoint note
  526                   ep.setNote( secretNote, secret );
  527                   break;
  528                   
  529               case AjpConstants.SC_A_STORED_METHOD:
  530                   msg.getBytes(req.method()); 
  531                   break;
  532                   
  533               default:
  534                   break; // ignore, we don't know about it - backward compat
  535               }
  536           }
  537           return 200;
  538       }
  539       
  540       private void decodeHeaders( MsgContext ep, Msg msg, Request req,
  541                                   MessageBytes tmpMB ) {
  542           // Decode headers
  543           MimeHeaders headers = req.getMimeHeaders();
  544   
  545           int hCount = msg.getInt();
  546           for(int i = 0 ; i < hCount ; i++) {
  547               String hName = null;
  548   
  549               // Header names are encoded as either an integer code starting
  550               // with 0xA0, or as a normal string (in which case the first
  551               // two bytes are the length).
  552               int isc = msg.peekInt();
  553               int hId = isc & 0xFF;
  554   
  555               MessageBytes vMB=null;
  556               isc &= 0xFF00;
  557               if(0xA000 == isc) {
  558                   msg.getInt(); // To advance the read position
  559                   hName = AjpConstants.headerTransArray[hId - 1];
  560                   vMB=headers.addValue( hName );
  561               } else {
  562                   // reset hId -- if the header currently being read
  563                   // happens to be 7 or 8 bytes long, the code below
  564                   // will think it's the content-type header or the
  565                   // content-length header - SC_REQ_CONTENT_TYPE=7,
  566                   // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
  567                   // behaviour.  see bug 5861 for more information.
  568                   hId = -1;
  569                   msg.getBytes( tmpMB );
  570                   ByteChunk bc=tmpMB.getByteChunk();
  571                   vMB=headers.addValue( bc.getBuffer(),
  572                                         bc.getStart(), bc.getLength() );
  573               }
  574   
  575               msg.getBytes(vMB);
  576   
  577               if (hId == AjpConstants.SC_REQ_CONTENT_LENGTH ||
  578                   (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
  579                   // just read the content-length header, so set it
  580                   long cl = vMB.getLong();
  581                   if(cl < Integer.MAX_VALUE) 
  582                       req.setContentLength( (int)cl );
  583               } else if (hId == AjpConstants.SC_REQ_CONTENT_TYPE ||
  584                   (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
  585                   // just read the content-type header, so set it
  586                   ByteChunk bchunk = vMB.getByteChunk();
  587                   req.contentType().setBytes(bchunk.getBytes(),
  588                                              bchunk.getOffset(),
  589                                              bchunk.getLength());
  590               }
  591           }
  592       }
  593   
  594       /**
  595        * Parse host.
  596        */
  597       private void parseHost(MessageBytes valueMB, Request request) 
  598           throws IOException {
  599   
  600           if (valueMB == null || valueMB.isNull()) {
  601               // HTTP/1.0
  602               // Default is what the socket tells us. Overriden if a host is 
  603               // found/parsed
  604               request.setServerPort(request.getLocalPort());
  605               request.serverName().duplicate(request.localName());
  606               return;
  607           }
  608   
  609           ByteChunk valueBC = valueMB.getByteChunk();
  610           byte[] valueB = valueBC.getBytes();
  611           int valueL = valueBC.getLength();
  612           int valueS = valueBC.getStart();
  613           int colonPos = -1;
  614           CharChunk hostNameC = (CharChunk)request.getNote(HOSTBUFFER);
  615           if(hostNameC == null) {
  616               hostNameC = new CharChunk(valueL);
  617               request.setNote(HOSTBUFFER, hostNameC);
  618           }
  619           hostNameC.recycle();
  620   
  621           boolean ipv6 = (valueB[valueS] == '[');
  622           boolean bracketClosed = false;
  623           for (int i = 0; i < valueL; i++) {
  624               char b = (char) valueB[i + valueS];
  625               hostNameC.append(b);
  626               if (b == ']') {
  627                   bracketClosed = true;
  628               } else if (b == ':') {
  629                   if (!ipv6 || bracketClosed) {
  630                       colonPos = i;
  631                       break;
  632                   }
  633               }
  634           }
  635   
  636           if (colonPos < 0) {
  637               if (request.scheme().equalsIgnoreCase("https")) {
  638                   // 80 - Default HTTTP port
  639                   request.setServerPort(443);
  640               } else {
  641                   // 443 - Default HTTPS port
  642                   request.setServerPort(80);
  643               }
  644               request.serverName().setChars(hostNameC.getChars(), 
  645                                             hostNameC.getStart(), 
  646                                             hostNameC.getLength());
  647           } else {
  648   
  649               request.serverName().setChars(hostNameC.getChars(), 
  650                                             hostNameC.getStart(), colonPos);
  651   
  652               int port = 0;
  653               int mult = 1;
  654               for (int i = valueL - 1; i > colonPos; i--) {
  655                   int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
  656                   if (charValue == -1) {
  657                       // Invalid character
  658                       throw new CharConversionException("Invalid char in port: " + valueB[i + valueS]); 
  659                   }
  660                   port = port + (charValue * mult);
  661                   mult = 10 * mult;
  662               }
  663               request.setServerPort(port);
  664   
  665           }
  666   
  667       }
  668   
  669   }

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