AbstractProcessorLight.java

  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. package org.apache.coyote;

  18. import java.io.IOException;
  19. import java.util.Iterator;
  20. import java.util.Set;
  21. import java.util.concurrent.CopyOnWriteArraySet;

  22. import org.apache.juli.logging.Log;
  23. import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
  24. import org.apache.tomcat.util.net.DispatchType;
  25. import org.apache.tomcat.util.net.SocketEvent;
  26. import org.apache.tomcat.util.net.SocketWrapperBase;

  27. /**
  28.  * This is a light-weight abstract processor implementation that is intended as a basis for all Processor
  29.  * implementations from the light-weight upgrade processors to the HTTP/AJP processors.
  30.  */
  31. public abstract class AbstractProcessorLight implements Processor {

  32.     private Set<DispatchType> dispatches = new CopyOnWriteArraySet<>();


  33.     @Override
  34.     public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) throws IOException {

  35.         SocketState state = SocketState.CLOSED;
  36.         Iterator<DispatchType> dispatches = null;
  37.         do {
  38.             if (dispatches != null) {
  39.                 DispatchType nextDispatch = dispatches.next();
  40.                 if (getLog().isTraceEnabled()) {
  41.                     getLog().trace("Processing dispatch type: [" + nextDispatch + "]");
  42.                 }
  43.                 state = dispatch(nextDispatch.getSocketStatus());
  44.                 if (!dispatches.hasNext()) {
  45.                     state = checkForPipelinedData(state, socketWrapper);
  46.                 }
  47.             } else if (status == SocketEvent.DISCONNECT) {
  48.                 // Do nothing here, just wait for it to get recycled
  49.             } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
  50.                 state = dispatch(status);
  51.                 state = checkForPipelinedData(state, socketWrapper);
  52.             } else if (status == SocketEvent.OPEN_WRITE) {
  53.                 // Extra write event likely after async, ignore
  54.                 state = SocketState.LONG;
  55.             } else if (status == SocketEvent.OPEN_READ) {
  56.                 state = service(socketWrapper);
  57.             } else if (status == SocketEvent.CONNECT_FAIL) {
  58.                 logAccess(socketWrapper);
  59.             } else {
  60.                 // Default to closing the socket if the SocketEvent passed in
  61.                 // is not consistent with the current state of the Processor
  62.                 state = SocketState.CLOSED;
  63.             }

  64.             if (getLog().isTraceEnabled()) {
  65.                 getLog().trace(
  66.                         "Socket: [" + socketWrapper + "], Status in: [" + status + "], State out: [" + state + "]");
  67.             }

  68.             /*
  69.              * If state is already CLOSED don't call asyncPostProcess() as that will likely change the the state to some
  70.              * other value causing processing to continue when it should cease. The AsyncStateMachine will be recycled
  71.              * as part of the Processor clean-up on CLOSED so it doesn't matter what state it is left in at this point.
  72.              */
  73.             if (isAsync() && state != SocketState.CLOSED) {
  74.                 state = asyncPostProcess();
  75.                 if (getLog().isTraceEnabled()) {
  76.                     getLog().trace(
  77.                             "Socket: [" + socketWrapper + "], State after async post processing: [" + state + "]");
  78.                 }
  79.             }

  80.             if (dispatches == null || !dispatches.hasNext()) {
  81.                 // Only returns non-null iterator if there are
  82.                 // dispatches to process.
  83.                 dispatches = getIteratorAndClearDispatches();
  84.             }
  85.         } while (state == SocketState.ASYNC_END || dispatches != null && state != SocketState.CLOSED);

  86.         return state;
  87.     }


  88.     private SocketState checkForPipelinedData(SocketState inState, SocketWrapperBase<?> socketWrapper)
  89.             throws IOException {
  90.         if (inState == SocketState.OPEN) {
  91.             // There may be pipe-lined data to read. If the data isn't
  92.             // processed now, execution will exit this loop and call
  93.             // release() which will recycle the processor (and input
  94.             // buffer) deleting any pipe-lined data. To avoid this,
  95.             // process it now.
  96.             return service(socketWrapper);
  97.         } else {
  98.             return inState;
  99.         }
  100.     }


  101.     public void addDispatch(DispatchType dispatchType) {
  102.         synchronized (dispatches) {
  103.             dispatches.add(dispatchType);
  104.         }
  105.     }


  106.     public Iterator<DispatchType> getIteratorAndClearDispatches() {
  107.         // Note: Logic in AbstractProtocol depends on this method only returning
  108.         // a non-null value if the iterator is non-empty. i.e. it should never
  109.         // return an empty iterator.
  110.         Iterator<DispatchType> result;
  111.         synchronized (dispatches) {
  112.             // Synchronized as the generation of the iterator and the clearing
  113.             // of dispatches needs to be an atomic operation.
  114.             result = dispatches.iterator();
  115.             if (result.hasNext()) {
  116.                 dispatches.clear();
  117.             } else {
  118.                 result = null;
  119.             }
  120.         }
  121.         return result;
  122.     }


  123.     protected void clearDispatches() {
  124.         synchronized (dispatches) {
  125.             dispatches.clear();
  126.         }
  127.     }


  128.     /**
  129.      * Add an entry to the access log for a failed connection attempt.
  130.      *
  131.      * @param socketWrapper The connection to process
  132.      *
  133.      * @throws IOException If an I/O error occurs during the processing of the request
  134.      */
  135.     protected void logAccess(SocketWrapperBase<?> socketWrapper) throws IOException {
  136.         // NO-OP by default
  137.     }


  138.     /**
  139.      * Service a 'standard' HTTP request. This method is called for both new requests and for requests that have
  140.      * partially read the HTTP request line or HTTP headers. Once the headers have been fully read this method is not
  141.      * called again until there is a new HTTP request to process. Note that the request type may change during
  142.      * processing which may result in one or more calls to {@link #dispatch(SocketEvent)}. Requests may be pipe-lined.
  143.      *
  144.      * @param socketWrapper The connection to process
  145.      *
  146.      * @return The state the caller should put the socket in when this method returns
  147.      *
  148.      * @throws IOException If an I/O error occurs during the processing of the request
  149.      */
  150.     protected abstract SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException;

  151.     /**
  152.      * Process an in-progress request that is not longer in standard HTTP mode. Uses currently include Servlet 3.0 Async
  153.      * and HTTP upgrade connections. Further uses may be added in the future. These will typically start as HTTP
  154.      * requests.
  155.      *
  156.      * @param status The event to process
  157.      *
  158.      * @return The state the caller should put the socket in when this method returns
  159.      *
  160.      * @throws IOException If an I/O error occurs during the processing of the request
  161.      */
  162.     protected abstract SocketState dispatch(SocketEvent status) throws IOException;

  163.     /**
  164.      * Calls the post process of the async state machine.
  165.      *
  166.      * @return The state the caller should put the socket in when this method returns
  167.      */
  168.     protected abstract SocketState asyncPostProcess();

  169.     /**
  170.      * @return the logger associated with this processor type
  171.      */
  172.     protected abstract Log getLog();
  173. }