Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * JBoss, Home of Professional Open Source.
    * Copyright 2012 Red Hat, Inc., and individual contributors
    * as indicated by the @author tags.
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.coyote.ajp;
  
  import static org.jboss.web.CoyoteMessages.MESSAGES;
  
  import java.net.Socket;
  
  
Processes HTTP requests.

Author(s):
Remy Maucherat
Henri Gomez
Dan Milstein
Keith Wannamaker
Kevin Seguin
Costin Manolache
Bill Barker
  
  public class AjpProcessor implements ActionHook {
  
  
      // ----------------------------------------------------------- Constructors
  
  
      public AjpProcessor(int packetSizeJIoEndpoint endpoint) {
  
          this. = endpoint;
  
           = new Request();
          .setInputBuffer(new SocketInputBuffer());
  
           = new Response();
          .setHook(this);
          .setOutputBuffer(new SocketOutputBuffer());
          .setResponse();
  
           = new AjpMessage(packetSize);
           = new AjpMessage(packetSize);
           = new AjpMessage(packetSize);
          
          // Set the get body message buffer
          AjpMessage getBodyMessage = new AjpMessage(16);
          getBodyMessage.reset();
          getBodyMessage.appendByte(.);
          getBodyMessage.appendInt(packetSize - .);
          getBodyMessage.end();
           = new byte[getBodyMessage.getLen()];
          System.arraycopy(getBodyMessage.getBuffer(), 0, 
                  0, getBodyMessage.getLen());
  
          // Cause loading of HexUtils
          int foo = .[0];
  
         // Cause loading of HttpMessages
         HttpMessages.getMessage(200);
 
     }
 
 
     // ----------------------------------------------------- Instance Variables
 

    
Associated adapter.
 
     protected Adapter adapter = null;


    
Request object.
 
     protected Request request = null;


    
Response object.
 
     protected Response response = null;


    
Header message. Note that this header is merely the one used during the processing of the first message of a "request", so it might not be a request header. It will stay unchanged during the processing of the whole request.
 
     protected AjpMessage requestHeaderMessage = null;


    
Message used for response header composition.
 
     protected AjpMessage responseHeaderMessage = null;


    
Body message.
 
     protected AjpMessage bodyMessage = null;


    
Body message.
 
     protected MessageBytes bodyBytes = MessageBytes.newInstance();


    
Error flag.
 
     protected boolean error = false;


    
Socket associated with the current connection.
 
     protected Socket socket;

    
    
Input stream.
 
     protected InputStream input;
    
    
    
Output stream.
 
     protected OutputStream output;
    

    
Host name (used to avoid useless B2C conversion on the host name).
 
     protected char[] hostNameC = new char[0];


    
Associated endpoint.
 
     protected JIoEndpoint endpoint;


    
The socket timeout used when reading the first block of the request header.
 
     protected long readTimeout;


    
Temp message bytes used for processing.
 
     protected MessageBytes tmpMB = MessageBytes.newInstance();


    
Byte chunk for certs.
 
     protected MessageBytes certificates = MessageBytes.newInstance();


    
End of stream flag.
 
     protected boolean endOfStream = false;


    
Body empty flag.
 
     protected boolean empty = true;


    
First read.
 
     protected boolean first = true;


    
Replay read.
 
     protected boolean replay = false;


    
Finished response.
 
     protected boolean finished = false;


    
Direct buffer used for sending right away a get body message.
 
     protected final byte[] getBodyMessageArray;


    
Direct buffer used for sending right away a pong message.
 
     protected static final byte[] pongMessageArray;


    
End message array.
 
     protected static final byte[] endMessageArray;

    
Flush message array.
 
     protected static final byte[] flushMessageArray;


    
Event used.
 
     protected boolean event = false;
    

    
Event processing.
 
     protected boolean eventProcessing = true;
     public void startProcessing() {  = true; }
     public void endProcessing() {  = false; }
     
 
     // ----------------------------------------------------- Static Initializer
 
 
     static {
 
         // Set the read body message buffer
         AjpMessage pongMessage = new AjpMessage(16);
         pongMessage.reset();
         pongMessage.appendByte(.);
         pongMessage.end();
          = new byte[pongMessage.getLen()];
         System.arraycopy(pongMessage.getBuffer(), 0, 
                 0, pongMessage.getLen());
 
         // Allocate the end message array
         AjpMessage endMessage = new AjpMessage(16);
         endMessage.reset();
         endMessage.appendByte(.);
         endMessage.appendByte(1);
         endMessage.end();
          = new byte[endMessage.getLen()];
         System.arraycopy(endMessage.getBuffer(), 0, , 0,
                 endMessage.getLen());
 
         // Allocate the flush message array
         AjpMessage flushMessage = new AjpMessage(16);
         flushMessage.reset();
         flushMessage.appendByte(.);
         flushMessage.appendInt(0);
         flushMessage.appendByte(0);
         flushMessage.end();
          = new byte[flushMessage.getLen()];
         System.arraycopy(flushMessage.getBuffer(), 0, , 0,
                 flushMessage.getLen());
 
     }
 
 
     // ------------------------------------------------------------- Properties
 

    
Use Tomcat authentication ?
 
     protected boolean tomcatAuthentication = true;
     public boolean getTomcatAuthentication() { return ; }
     public void setTomcatAuthentication(boolean tomcatAuthentication) { this. = tomcatAuthentication; }


    
Required secret.
 
     protected String requiredSecret = null;
     public void setRequiredSecret(String requiredSecret) { this. = requiredSecret; }


    
The number of milliseconds Tomcat will wait for a subsequent request before closing the connection. The default is the same as for Apache HTTP Server (15 000 milliseconds).
 
     protected int keepAliveTimeout = -1;
     public int getKeepAliveTimeout() { return ; }
     public void setKeepAliveTimeout(int timeout) {  = timeout; }


    
Timeout.
 
     protected int timeout = -1;
     public void setTimeout(int timeout) { this. = timeout; }
     public int getTimeout() { return ; }


    
A resume has been requested.
 
     protected boolean resumeNotification = false;
     public boolean getResumeNotification() { return ; }
 
 
     // --------------------------------------------------------- Public Methods
 

    
Get the request associated with this processor.

Returns:
The request
 
     public Request getRequest() {
         return ;
     }
 
 
     public SocketState event(SocketStatus status)
     throws IOException {
 
         RequestInfo rp = .getRequestProcessor();
         try {
             if (status == .) {
                 // The resume notification is now done
                  = false;
             } else if (status == .) {
                 // Set error flag right away
                  = true;
             }
             rp.setStage(....);
              = !.event(status);
         } catch (InterruptedIOException e) {
              = true;
         } catch (Throwable t) {
             ..errorProcessingRequest(t);
             // 500 - Internal Server Error
             .setStatus(500);
              = true;
         }
 
 
         if () {
             recycle();
             return .;
         } else if (!) {
             finish();
             recycle();
             return .;
         } else {
             return .;
         }
     }


    
Process pipelined HTTP requests using the specified input and output streams.

Throws:
java.io.IOException error during an I/O operation
 
     public SocketState process(Socket socket)
         throws IOException {
         RequestInfo rp = .getRequestProcessor();
 
         // Setting up the socket
         this. = socket;
          = socket.getInputStream();
          = socket.getOutputStream();
         int soTimeout = -1;
         if ( > 0) {
             soTimeout = socket.getSoTimeout();
         }
 
         // Error flag
          = false;
 
         while (! && !) {
 
             // Parsing the request header
             try {
                 // Set keep alive timeout if enabled
                 if ( > 0) {
                     socket.setSoTimeout();
                 }
                 // Get first message of the request
                 if (!readMessage()) {
                     // This means a connection timeout
                     rp.setStage(....);
                     break;
                 }
                 // Set back timeout if keep alive timeout is enabled
                 if ( > 0) {
                     socket.setSoTimeout(soTimeout);
                 }
                 // Check message type, process right away and break if
                 // not regular request processing
                 int type = .getByte();
                 if (type == .) {
                     try {
                         .write();
                     } catch (IOException e) {
                          = true;
                     }
                     continue;
                 } else if(type != .) {
                     // Usually the servlet didn't read the previous request body
                     ..unexpectedAjpMessage(type);
                      = true;
                     break;
                 }
 
                 .setStartTime(System.currentTimeMillis());
             } catch (IOException e) {
                  = true;
                 break;
             } catch (Throwable t) {
                 ..errorParsingAjpHeaderMessage(t);
                 // 400 - Bad Request
                 .setStatus(400);
                  = true;
             }
 
             // Setting up filters, and parse some request headers
             rp.setStage(....);
             try {
                 prepareRequest();
             } catch (Throwable t) {
                 ..errorPreparingAjpRequest(t);
                 // 400 - Internal Server Error
                 .setStatus(400);
                  = true;
             }
 
             // Process the request in the adapter
             if (!) {
                 try {
                     rp.setStage(....);
                     .service();
                 } catch (InterruptedIOException e) {
                      = true;
                 } catch (Throwable t) {
                     ..errorProcessingRequest(t);
                     // 500 - Internal Server Error
                     .setStatus(500);
                      = true;
                 }
             }
 
             // Finish the response if not done yet
             if (! && !) {
                 try {
                     finish();
                 } catch (Throwable t) {
                      = true;
                 }
             }
 
             // If there was an error, make sure the request is counted as
             // and error, and update the statistics counter
             if () {
                 .setStatus(500);
             }
             .updateCounters();
 
             if (!) {
                 recycle();
             }
 
             rp.setStage(....);
 
         }
 
         
         if () {
             if () {
                  = null;
                  = null;
                 recycle();
                 return .;
             } else {
                  = false;
                 return .;
             }
         } else {
              = null;
              = null;
             recycle();
             return .;
         }
     }
 
 
     // ----------------------------------------------------- ActionHook Methods
 

    
Send an action to the connector.

Parameters:
actionCode Type of the action
param Action parameter
 
     public void action(ActionCode actionCodeObject param) {
 
         if (actionCode == .) {
 
             if (.isCommitted())
                 return;
 
             // Validate and write response headers
             try {
                 prepareResponse();
             } catch (IOException e) {
                 // Set error flag
                  = true;
             }
 
         } else if (actionCode == .) {
 
             if (!.isCommitted()) {
                 // Validate and write response headers
                 try {
                     prepareResponse();
                 } catch (IOException e) {
                     // Set error flag
                      = true;
                     return;
                 }
             }
 
             try {
                 flush();
             } catch (IOException e) {
                 // Set error flag
                  = true;
             }
 
         } else if (actionCode == .) {
             // Close
 
             // End the processing of the current request, and stop any further
             // transactions with the client
 
             try {
                 finish();
             } catch (IOException e) {
                 // Set error flag
                  = true;
             }
 
         } else if (actionCode == . ) {
 
             if (!.isNull()) {
                 ByteChunk certData = .getByteChunk();
                 X509Certificate jsseCerts[] = null;
                 ByteArrayInputStream bais =
                     new ByteArrayInputStream(certData.getBytes(),
                             certData.getStart(),
                             certData.getLength());
                 // Fill the  elements.
                 try {
                     CertificateFactory cf =
                         CertificateFactory.getInstance("X.509");
                     while(bais.available() > 0) {
                         X509Certificate cert = (X509Certificate)
                             cf.generateCertificate(bais);
                         if(jsseCerts == null) {
                             jsseCerts = new X509Certificate[1];
                             jsseCerts[0] = cert;
                         } else {
                             X509Certificate [] temp = new X509Certificate[jsseCerts.length+1];
                             System.arraycopy(jsseCerts,0,temp,0,jsseCerts.length);
                             temp[jsseCerts.length] = cert;
                             jsseCerts = temp;
                         }
                     }
                 } catch (java.security.cert.CertificateException e) {
                     ..errorProcessingCertificates(e);
                     return;
                 }
                 .setAttribute(......jsseCerts);
             }
 
         } else if (actionCode == .) {
 
             // Get remote host name using a DNS resolution
             if (.remoteHost().isNull()) {
                 try {
                     .remoteHost().setString(InetAddress.getByName
                             (.remoteAddr().toString()).getHostName());
                 } catch (IOException iex) {
                     // Ignore
                 }
             }
 
         } else if (actionCode == .) {
 
             // Copy from local name for now, which should simply be an address
             .localAddr().setString(.localName().toString());
 
         } else if (actionCode == .) {
 
             // Set the given bytes as the content
             ByteChunk bc = (ByteChunkparam;
             int length = bc.getLength();
             .setBytes(bc.getBytes(), bc.getStart(), length);
             .setContentLength(length);
              = false;
              = false;
              = true;
              = false;
 
         } else if (actionCode == .) {
              = true;
         } else if (actionCode == .) {
              = false;
         } else if (actionCode == .) {
             // No action needed
         } else if (actionCode == .) {
             // An event is being processed already: adding for resume will be done
             // when the socket gets back to the poller
             if (! && !) {
                 .getEventPoller().add(truetrue);
             }
              = true;
         } else if (actionCode == .) {
              = ((Integerparam).intValue();
         }
 
 
     }
 
 
     // ------------------------------------------------------ Connector Methods
 

    
Set the associated adapter.

Parameters:
adapter the new adapter
 
     public void setAdapter(Adapter adapter) {
         this. = adapter;
     }


    
Get the associated adapter.

Returns:
the associated adapter
 
     public Adapter getAdapter() {
         return ;
     }
 
 
     // ------------------------------------------------------ Protected Methods
 

    
After reading the request headers, we have to setup the request filters.
 
     protected void prepareRequest() {
 
         // Translate the HTTP method code to a String.
         byte methodCode = .getByte();
         if (methodCode != .) {
             String methodName = .[(int)methodCode - 1];
             .method().setString(methodName);
         }
 
 
 
         boolean isSSL = .getByte() != 0;
         if (isSSL) {
             .scheme().setString("https");
         }
 
         // Decode headers
         MimeHeaders headers = .getMimeHeaders();
 
         boolean contentLengthSet = false;
         int hCount = .getInt();
         for(int i = 0 ; i < hCount ; i++) {
             String hName = null;
 
             // Header names are encoded as either an integer code starting
             // with 0xA0, or as a normal string (in which case the first
             // two bytes are the length).
             int isc = .peekInt();
             int hId = isc & 0xFF;
 
             MessageBytes vMB = null;
             isc &= 0xFF00;
             if(0xA000 == isc) {
                 .getInt(); // To advance the read position
                 hName = .[hId - 1];
                 vMB = headers.addValue(hName);
             } else {
                 // reset hId -- if the header currently being read
                 // happens to be 7 or 8 bytes long, the code below
                 // will think it's the content-type header or the
                 // content-length header - SC_REQ_CONTENT_TYPE=7,
                 // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
                 // behaviour.  see bug 5861 for more information.
                 hId = -1;
                 .getBytes();
                 ByteChunk bc = .getByteChunk();
                 vMB = headers.addValue(bc.getBuffer(),
                         bc.getStart(), bc.getLength());
             }
 
             .getBytes(vMB);
 
             if (hId == . ||
                     (hId == -1 && .equalsIgnoreCase("Content-Length"))) {
                 long cl = vMB.getLong();
                 if (contentLengthSet) {
                     .setStatus(.);
                      = true;
                 } else {
                     contentLengthSet = true;
                     // Set the content-length header for the request
                     .setContentLength(cl);
                 }
             } else if (hId == . ||
                     (hId == -1 && .equalsIgnoreCase("Content-Type"))) {
                 // just read the content-type header, so set it
                 ByteChunk bchunk = vMB.getByteChunk();
                 .contentType().setBytes(bchunk.getBytes(),
                         bchunk.getOffset(),
                         bchunk.getLength());
             }
         }
 
         // Decode extra attributes
         boolean secret = false;
         byte attributeCode;
         while ((attributeCode = .getByte())
                 != .) {
 
             switch (attributeCode) {
 
             case . :
                 .getBytes();
                 String n = .toString();
                 .getBytes();
                 String v = .toString();
                 .setAttribute(nv);
                 break;
 
             case . :
                 .getBytes();
                 // nothing
                 break;
 
             case . :
                 .getBytes();
                 // nothing
                 break;
 
             case . :
                 if () {
                     // ignore server
                     .getBytes();
                 } else {
                     .getBytes(.getRemoteUser());
                 }
                 break;
 
             case . :
                 if () {
                     // ignore server
                     .getBytes();
                 } else {
                     .getBytes(.getAuthType());
                 }
                 break;
 
             case . :
                 .getBytes(.queryString());
                 break;
 
             case . :
                 .getBytes(.instanceId());
                 break;
 
             case . :
                 .scheme().setString("https");
                 // SSL certificate extraction is lazy, moved to JkCoyoteHandler
                 .getBytes();
                 break;
 
             case . :
                 .scheme().setString("https");
                 .getBytes();
                                      .toString());
                 break;
 
             case . :
                 .scheme().setString("https");
                 .getBytes();
                                      .toString());
                 break;
 
             case . :
                                      new Integer(.getInt()));
                 break;
 
             case .:
                 .getBytes(.method());
                 break;
 
             case .:
                 .getBytes();
                 if ( != null) {
                     secret = true;
                     if (!.equals()) {
                         .setStatus(403);
                          = true;
                     }
                 }
                 break;
 
             default:
                 // Ignore unknown attribute for backward compatibility
                 break;
 
             }
 
         }
 
         // Check if secret was submitted if required
         if (( != null) && !secret) {
             .setStatus(403);
              = true;
         }
 
         // Check for a full URI (including protocol://host:port/)
         ByteChunk uriBC = .requestURI().getByteChunk();
         if (uriBC.startsWithIgnoreCase("http", 0)) {
 
             int pos = uriBC.indexOf("://", 0, 3, 4);
             int uriBCStart = uriBC.getStart();
             int slashPos = -1;
             if (pos != -1) {
                 byte[] uriB = uriBC.getBytes();
                 slashPos = uriBC.indexOf('/'pos + 3);
                 if (slashPos == -1) {
                     slashPos = uriBC.getLength();
                     // Set URI as "/"
                     .requestURI().setBytes
                         (uriBuriBCStart + pos + 1, 1);
                 } else {
                     .requestURI().setBytes
                         (uriBuriBCStart + slashPos,
                          uriBC.getLength() - slashPos);
                 }
                 MessageBytes hostMB = headers.setValue("host");
                 hostMB.setBytes(uriBuriBCStart + pos + 3,
                                 slashPos - pos - 3);
             }
 
         }
 
         MessageBytes valueMB = .getMimeHeaders().getValue("host");
         parseHost(valueMB);
 
     }


    
Parse host.
 
     public void parseHost(MessageBytes valueMB) {
 
         if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
             // HTTP/1.0
             .setServerPort(.getLocalPort());
             try {
                 .serverName().duplicate(.localName());
             } catch (IOException e) {
                 .setStatus(400);
                  = true;
             }
             return;
         }
 
         ByteChunk valueBC = valueMB.getByteChunk();
         byte[] valueB = valueBC.getBytes();
         int valueL = valueBC.getLength();
         int valueS = valueBC.getStart();
         int colonPos = -1;
         if (. < valueL) {
              = new char[valueL];
         }
 
         boolean ipv6 = (valueB[valueS] == '[');
         boolean bracketClosed = false;
         for (int i = 0; i < valueLi++) {
             char b = (charvalueB[i + valueS];
             [i] = b;
             if (b == ']') {
                 bracketClosed = true;
             } else if (b == ':') {
                 if (!ipv6 || bracketClosed) {
                     colonPos = i;
                     break;
                 }
             }
         }
 
         if (colonPos < 0) {
             if (.scheme().equalsIgnoreCase("https")) {
                 // 443 - Default HTTPS port
                 .setServerPort(443);
             } else {
                 // 80 - Default HTTTP port
                 .setServerPort(80);
             }
             .serverName().setChars(, 0, valueL);
         } else {
 
             .serverName().setChars(, 0, colonPos);
 
             int port = 0;
             int mult = 1;
             for (int i = valueL - 1; i > colonPosi--) {
                 int charValue = .[valueB[i + valueS] & 0xff];
                 if (charValue == -1) {
                     // Invalid character
                      = true;
                     // 400 - Bad request
                     .setStatus(400);
                     break;
                 }
                 port = port + (charValue * mult);
                 mult = 10 * mult;
            }
            .setServerPort(port);
        }
    }


    
When committing the response, we have to validate the set of headers, as well as setup the response filters.
    protected void prepareResponse()
        throws IOException {
        .setCommitted(true);
        .reset();
        // HTTP header contents
        String message = null;
            message = .getMessage();
        }
        if (message == null){
            message = HttpMessages.getMessage(.getStatus());
        } else {
            message = message.replace('\n'' ').replace('\r'' ');
        }
        if (message == null) {
            // Many httpd 2.x wants a non empty status message
            message = Integer.toString(.getStatus());
        }
        .setString(message);
        // Special headers
        MimeHeaders headers = .getMimeHeaders();
        String contentType = .getContentType();
        if (contentType != null) {
            headers.setValue("Content-Type").setString(contentType);
        }
        String contentLanguage = .getContentLanguage();
        if (contentLanguage != null) {
            headers.setValue("Content-Language").setString(contentLanguage);
        }
        int contentLength = .getContentLength();
        if (contentLength >= 0) {
            headers.setValue("Content-Length").setInt(contentLength);
        }
        // Other headers
        int numHeaders = headers.size();
        .appendInt(numHeaders);
        for (int i = 0; i < numHeadersi++) {
            MessageBytes hN = headers.getName(i);
            MessageBytes hV=headers.getValue(i);
            if (hN.getLength() > 0 && !hV.isNull()) {
                int hC = Constants.getResponseAjpIndex(hN.toString());
                if (hC > 0) {
                    .appendInt(hC);
                }
                else {
                    .appendBytes(hN);
                }
                .appendBytes(hV);
            }
        }
        // Write to buffer
        .end();
    }


    
Finish AJP response.
    protected void finish()
        throws IOException {
        if (!.isCommitted()) {
            // Validate and write response headers
            try {
                prepareResponse();
            } catch (IOException e) {
                // Set error flag
                 = true;
            }
        }
        if ()
            return;
         = true;
        // Add the end message
        .write();
        // read remaining data from the special first-body-chunk
        if ( && .getContentLengthLong() > 0) {
            try {
                receive();
            } catch (IOException e) {
            } 
        }
    }


    
Read at least the specified amount of bytes, and place them in the input buffer.
    protected boolean read(byte[] bufint posint n)
        throws IOException {
        int read = 0;
        int res = 0;
        while (read < n) {
            res = .read(bufread + posn - read);
            if (res > 0) {
                read += res;
            } else {
                throw new IOException(.failedRead());
            }
        }
        
        return true;
    }


    
Receive a chunk of data. Called to implement the 'special' packet in ajp13 and to receive the data after we send a GET_BODY packet
    public boolean receive() throws IOException {
         = false;
        .reset();
        readMessage();
        // No data received.
        if (.getLen() == 0) {
            // just the header
            // Don't mark 'end of stream' for the first chunk.
            return false;
        }
        int blen = .peekInt();
        if (blen == 0) {
            return false;
        }
         = false;
        return true;
    }

    
Get more request body data from the web server and store it in the internal buffer.

Returns:
true if there is more data, false if not.
    private boolean refillReadBuffer() throws IOException {
        // If the server returns an empty packet, assume that that end of
        // the stream has been reached (yuck -- fix protocol??).
        // FORM support
        if () {
             = true// we've read everything there is
        }
        if () {
            return false;
        }
        if () {
            return false;
        }
        // Request more data immediately
        boolean moreData = receive();
        if( !moreData ) {
             = true;
        }
        return moreData;
    }


    
Read an AJP message.

Returns:
true if the message has been read, false if the short read didn't return anything
Throws:
java.io.IOException any other failure, including incomplete reads
    protected boolean readMessage(AjpMessage message)
        throws IOException {
        byte[] buf = message.getBuffer();
        read(buf, 0, message.getHeaderLength());
        if (message.processHeader() < 0) {
            throw new IOException(.invalidAjpMessage());
        }
        read(bufmessage.getHeaderLength(), message.getLen());