/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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; import java.io.IOException; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import org.apache.juli.logging.Log; import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; import org.apache.tomcat.util.net.DispatchType; import org.apache.tomcat.util.net.SocketEvent; import org.apache.tomcat.util.net.SocketWrapperBase; /** * This is a light-weight abstract processor implementation that is intended as * a basis for all Processor implementations from the light-weight upgrade * processors to the HTTP/AJP processors. */ public abstract class AbstractProcessorLight implements Processor { private Set dispatches = new CopyOnWriteArraySet<>(); @Override public SocketState process(SocketWrapperBase socketWrapper, SocketEvent status) throws IOException { SocketState state = SocketState.CLOSED; Iterator dispatches = null; do { if (dispatches != null) { DispatchType nextDispatch = dispatches.next(); if (getLog().isDebugEnabled()) { getLog().debug("Processing dispatch type: [" + nextDispatch + "]"); } state = dispatch(nextDispatch.getSocketStatus()); if (!dispatches.hasNext()) { state = checkForPipelinedData(state, socketWrapper); } } else if (status == SocketEvent.DISCONNECT) { // Do nothing here, just wait for it to get recycled } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) { state = dispatch(status); state = checkForPipelinedData(state, socketWrapper); } else if (status == SocketEvent.OPEN_WRITE) { // Extra write event likely after async, ignore state = SocketState.LONG; } else if (status == SocketEvent.OPEN_READ) { state = service(socketWrapper); } else if (status == SocketEvent.CONNECT_FAIL) { logAccess(socketWrapper); } else { // Default to closing the socket if the SocketEvent passed in // is not consistent with the current state of the Processor state = SocketState.CLOSED; } if (getLog().isDebugEnabled()) { getLog().debug("Socket: [" + socketWrapper + "], Status in: [" + status + "], State out: [" + state + "]"); } if (state != SocketState.CLOSED && isAsync()) { state = asyncPostProcess(); if (getLog().isDebugEnabled()) { getLog().debug("Socket: [" + socketWrapper + "], State after async post processing: [" + state + "]"); } } if (dispatches == null || !dispatches.hasNext()) { // Only returns non-null iterator if there are // dispatches to process. dispatches = getIteratorAndClearDispatches(); } } while (state == SocketState.ASYNC_END || dispatches != null && state != SocketState.CLOSED); return state; } private SocketState checkForPipelinedData(SocketState inState, SocketWrapperBase socketWrapper) throws IOException { if (inState == SocketState.OPEN) { // There may be pipe-lined data to read. If the data isn't // processed now, execution will exit this loop and call // release() which will recycle the processor (and input // buffer) deleting any pipe-lined data. To avoid this, // process it now. return service(socketWrapper); } else { return inState; } } public void addDispatch(DispatchType dispatchType) { synchronized (dispatches) { dispatches.add(dispatchType); } } public Iterator getIteratorAndClearDispatches() { // Note: Logic in AbstractProtocol depends on this method only returning // a non-null value if the iterator is non-empty. i.e. it should never // return an empty iterator. Iterator result; synchronized (dispatches) { // Synchronized as the generation of the iterator and the clearing // of dispatches needs to be an atomic operation. result = dispatches.iterator(); if (result.hasNext()) { dispatches.clear(); } else { result = null; } } return result; } protected void clearDispatches() { synchronized (dispatches) { dispatches.clear(); } } /** * Add an entry to the access log for a failed connection attempt. * * @param socketWrapper The connection to process * * @throws IOException If an I/O error occurs during the processing of the * request */ protected void logAccess(SocketWrapperBase socketWrapper) throws IOException { // NO-OP by default } /** * Service a 'standard' HTTP request. This method is called for both new * requests and for requests that have partially read the HTTP request line * or HTTP headers. Once the headers have been fully read this method is not * called again until there is a new HTTP request to process. Note that the * request type may change during processing which may result in one or more * calls to {@link #dispatch(SocketEvent)}. Requests may be pipe-lined. * * @param socketWrapper The connection to process * * @return The state the caller should put the socket in when this method * returns * * @throws IOException If an I/O error occurs during the processing of the * request */ protected abstract SocketState service(SocketWrapperBase socketWrapper) throws IOException; /** * Process an in-progress request that is not longer in standard HTTP mode. * Uses currently include Servlet 3.0 Async and HTTP upgrade connections. * Further uses may be added in the future. These will typically start as * HTTP requests. * * @param status The event to process * * @return The state the caller should put the socket in when this method * returns * * @throws IOException If an I/O error occurs during the processing of the * request */ protected abstract SocketState dispatch(SocketEvent status) throws IOException; protected abstract SocketState asyncPostProcess(); protected abstract Log getLog(); }