/*
 *  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.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import jakarta.servlet.RequestDispatcher;

import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.parser.Host;
import org.apache.tomcat.util.log.UserDataHelper;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.DispatchType;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.SocketEvent;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;

Provides functionality and attributes common to all supported protocols (currently HTTP and AJP) for processing a single request/response.
/** * Provides functionality and attributes common to all supported protocols * (currently HTTP and AJP) for processing a single request/response. */
public abstract class AbstractProcessor extends AbstractProcessorLight implements ActionHook { private static final StringManager sm = StringManager.getManager(AbstractProcessor.class); // Used to avoid useless B2C conversion on the host name. private char[] hostNameC = new char[0]; protected final Adapter adapter; protected final AsyncStateMachine asyncStateMachine; private volatile long asyncTimeout = -1; /* * Tracks the current async generation when a timeout is dispatched. In the * time it takes for a container thread to be allocated and the timeout * processing to start, it is possible that the application completes this * generation of async processing and starts a new one. If the timeout is * then processed against the new generation, response mix-up can occur. * This field is used to ensure that any timeout event processed is for the * current async generation. This prevents the response mix-up. */ private volatile long asyncTimeoutGeneration = 0; protected final Request request; protected final Response response; protected volatile SocketWrapperBase<?> socketWrapper = null; protected volatile SSLSupport sslSupport;
Error state for the request/response currently being processed.
/** * Error state for the request/response currently being processed. */
private ErrorState errorState = ErrorState.NONE; protected final UserDataHelper userDataHelper; public AbstractProcessor(Adapter adapter) { this(adapter, new Request(), new Response()); } protected AbstractProcessor(Adapter adapter, Request coyoteRequest, Response coyoteResponse) { this.adapter = adapter; asyncStateMachine = new AsyncStateMachine(this); request = coyoteRequest; response = coyoteResponse; response.setHook(this); request.setResponse(response); request.setHook(this); userDataHelper = new UserDataHelper(getLog()); }
Update the current error state to the new error state if the new error state is more severe than the current error state.
Params:
  • errorState – The error status details
  • t – The error which occurred
/** * Update the current error state to the new error state if the new error * state is more severe than the current error state. * @param errorState The error status details * @param t The error which occurred */
protected void setErrorState(ErrorState errorState, Throwable t) { if (getLog().isDebugEnabled()) { getLog().debug(sm.getString("abstractProcessor.setErrorState", errorState), t); } // Use the return value to avoid processing more than one async error // in a single async cycle. boolean setError = response.setError(); boolean blockIo = this.errorState.isIoAllowed() && !errorState.isIoAllowed(); this.errorState = this.errorState.getMostSevere(errorState); // Don't change the status code for IOException since that is almost // certainly a client disconnect in which case it is preferable to keep // the original status code http://markmail.org/message/4cxpwmxhtgnrwh7n if (response.getStatus() < 400 && !(t instanceof IOException)) { response.setStatus(500); } if (t != null) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); } if (blockIo && isAsync() && setError) { if (asyncStateMachine.asyncError()) { processSocketEvent(SocketEvent.ERROR, true); } } } protected ErrorState getErrorState() { return errorState; } @Override public Request getRequest() { return request; }
Get the associated adapter.
Returns:the associated adapter
/** * Get the associated adapter. * * @return the associated adapter */
public Adapter getAdapter() { return adapter; }
Set the socket wrapper being used.
Params:
  • socketWrapper – The socket wrapper
/** * Set the socket wrapper being used. * @param socketWrapper The socket wrapper */
protected void setSocketWrapper(SocketWrapperBase<?> socketWrapper) { this.socketWrapper = socketWrapper; }
Returns:the socket wrapper being used.
/** * @return the socket wrapper being used. */
protected final SocketWrapperBase<?> getSocketWrapper() { return socketWrapper; } @Override public final void setSslSupport(SSLSupport sslSupport) { this.sslSupport = sslSupport; }
Provides a mechanism to trigger processing on a container thread.
Params:
  • runnable – The task representing the processing that needs to take place on a container thread
/** * Provides a mechanism to trigger processing on a container thread. * * @param runnable The task representing the processing that needs to take * place on a container thread */
protected void execute(Runnable runnable) { SocketWrapperBase<?> socketWrapper = this.socketWrapper; if (socketWrapper == null) { throw new RejectedExecutionException(sm.getString("abstractProcessor.noExecute")); } else { socketWrapper.execute(runnable); } } @Override public boolean isAsync() { return asyncStateMachine.isAsync(); } @Override public SocketState asyncPostProcess() { return asyncStateMachine.asyncPostProcess(); } @Override public final SocketState dispatch(SocketEvent status) throws IOException { if (status == SocketEvent.OPEN_WRITE && response.getWriteListener() != null) { asyncStateMachine.asyncOperation(); try { if (flushBufferedWrite()) { return SocketState.LONG; } } catch (IOException ioe) { if (getLog().isDebugEnabled()) { getLog().debug("Unable to write async data.", ioe); } status = SocketEvent.ERROR; request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe); } } else if (status == SocketEvent.OPEN_READ && request.getReadListener() != null) { dispatchNonBlockingRead(); } else if (status == SocketEvent.ERROR) { // An I/O error occurred on a non-container thread. This includes: // - read/write timeouts fired by the Poller (NIO & APR) // - completion handler failures in NIO2 if (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) == null) { // Because the error did not occur on a container thread the // request's error attribute has not been set. If an exception // is available from the socketWrapper, use it to set the // request's error attribute here so it is visible to the error // handling. request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, socketWrapper.getError()); } if (request.getReadListener() != null || response.getWriteListener() != null) { // The error occurred during non-blocking I/O. Set the correct // state else the error handling will trigger an ISE. asyncStateMachine.asyncOperation(); } } RequestInfo rp = request.getRequestProcessor(); try { rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE); if (!getAdapter().asyncDispatch(request, response, status)) { setErrorState(ErrorState.CLOSE_NOW, null); } } catch (InterruptedIOException e) { setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); setErrorState(ErrorState.CLOSE_NOW, t); getLog().error(sm.getString("http11processor.request.process"), t); } rp.setStage(org.apache.coyote.Constants.STAGE_ENDED); SocketState state; if (getErrorState().isError()) { request.updateCounters(); state = SocketState.CLOSED; } else if (isAsync()) { state = SocketState.LONG; } else { request.updateCounters(); state = dispatchEndRequest(); } if (getLog().isDebugEnabled()) { getLog().debug("Socket: [" + socketWrapper + "], Status in: [" + status + "], State out: [" + state + "]"); } return state; } protected void parseHost(MessageBytes valueMB) { if (valueMB == null || valueMB.isNull()) { populateHost(); populatePort(); return; } else if (valueMB.getLength() == 0) { // Empty Host header so set sever name to empty string request.serverName().setString(""); populatePort(); return; } ByteChunk valueBC = valueMB.getByteChunk(); byte[] valueB = valueBC.getBytes(); int valueL = valueBC.getLength(); int valueS = valueBC.getStart(); if (hostNameC.length < valueL) { hostNameC = new char[valueL]; } try { // Validates the host name int colonPos = Host.parse(valueMB); // Extract the port information first, if any if (colonPos != -1) { int port = 0; for (int i = colonPos + 1; i < valueL; i++) { char c = (char) valueB[i + valueS]; if (c < '0' || c > '9') { response.setStatus(400); setErrorState(ErrorState.CLOSE_CLEAN, null); return; } port = port * 10 + c - '0'; } request.setServerPort(port); // Only need to copy the host name up to the : valueL = colonPos; } // Extract the host name for (int i = 0; i < valueL; i++) { hostNameC[i] = (char) valueB[i + valueS]; } request.serverName().setChars(hostNameC, 0, valueL); } catch (IllegalArgumentException e) { // IllegalArgumentException indicates that the host name is invalid UserDataHelper.Mode logMode = userDataHelper.getNextMode(); if (logMode != null) { String message = sm.getString("abstractProcessor.hostInvalid", valueMB.toString()); switch (logMode) { case INFO_THEN_DEBUG: message += sm.getString("abstractProcessor.fallToDebug"); //$FALL-THROUGH$ case INFO: getLog().info(message, e); break; case DEBUG: getLog().debug(message, e); } } response.setStatus(400); setErrorState(ErrorState.CLOSE_CLEAN, e); } }
Called when a host header is not present in the request (e.g. HTTP/1.0). It populates the server name with appropriate information. The source is expected to vary by protocol.

The default implementation is a NO-OP.

/** * Called when a host header is not present in the request (e.g. HTTP/1.0). * It populates the server name with appropriate information. The source is * expected to vary by protocol. * <p> * The default implementation is a NO-OP. */
protected void populateHost() { // NO-OP }
Called when a host header is not present or is empty in the request (e.g. HTTP/1.0). It populates the server port with appropriate information. The source is expected to vary by protocol.

The default implementation is a NO-OP.

/** * Called when a host header is not present or is empty in the request (e.g. * HTTP/1.0). It populates the server port with appropriate information. The * source is expected to vary by protocol. * <p> * The default implementation is a NO-OP. */
protected void populatePort() { // NO-OP } @Override public final void action(ActionCode actionCode, Object param) { switch (actionCode) { // 'Normal' servlet support case COMMIT: { if (!response.isCommitted()) { try { // Validate and write response headers prepareResponse(); } catch (IOException e) { handleIOException(e); } } break; } case CLOSE: { action(ActionCode.COMMIT, null); try { finishResponse(); } catch (IOException e) { handleIOException(e); } break; } case ACK: { ack((ContinueResponseTiming) param); break; } case CLIENT_FLUSH: { action(ActionCode.COMMIT, null); try { flush(); } catch (IOException e) { handleIOException(e); response.setErrorException(e); } break; } case AVAILABLE: { request.setAvailable(available(Boolean.TRUE.equals(param))); break; } case REQ_SET_BODY_REPLAY: { ByteChunk body = (ByteChunk) param; setRequestBody(body); break; } // Error handling case IS_ERROR: { ((AtomicBoolean) param).set(getErrorState().isError()); break; } case IS_IO_ALLOWED: { ((AtomicBoolean) param).set(getErrorState().isIoAllowed()); break; } case CLOSE_NOW: { // Prevent further writes to the response setSwallowResponse(); if (param instanceof Throwable) { setErrorState(ErrorState.CLOSE_NOW, (Throwable) param); } else { setErrorState(ErrorState.CLOSE_NOW, null); } break; } case DISABLE_SWALLOW_INPUT: { // Cancelled upload or similar. // No point reading the remainder of the request. disableSwallowRequest(); // This is an error state. Make sure it is marked as such. setErrorState(ErrorState.CLOSE_CLEAN, null); break; } // Request attribute support case REQ_HOST_ADDR_ATTRIBUTE: { if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) { request.remoteAddr().setString(socketWrapper.getRemoteAddr()); } break; } case REQ_HOST_ATTRIBUTE: { populateRequestAttributeRemoteHost(); break; } case REQ_LOCALPORT_ATTRIBUTE: { if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) { request.setLocalPort(socketWrapper.getLocalPort()); } break; } case REQ_LOCAL_ADDR_ATTRIBUTE: { if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) { request.localAddr().setString(socketWrapper.getLocalAddr()); } break; } case REQ_LOCAL_NAME_ATTRIBUTE: { if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) { request.localName().setString(socketWrapper.getLocalName()); } break; } case REQ_REMOTEPORT_ATTRIBUTE: { if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) { request.setRemotePort(socketWrapper.getRemotePort()); } break; } // SSL request attribute support case REQ_SSL_ATTRIBUTE: { populateSslRequestAttributes(); break; } case REQ_SSL_CERTIFICATE: { try { sslReHandShake(); } catch (IOException ioe) { setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); } break; } // Servlet 3.0 asynchronous support case ASYNC_START: { asyncStateMachine.asyncStart((AsyncContextCallback) param); break; } case ASYNC_COMPLETE: { clearDispatches(); if (asyncStateMachine.asyncComplete()) { processSocketEvent(SocketEvent.OPEN_READ, true); } break; } case ASYNC_DISPATCH: { if (asyncStateMachine.asyncDispatch()) { processSocketEvent(SocketEvent.OPEN_READ, true); } break; } case ASYNC_DISPATCHED: { asyncStateMachine.asyncDispatched(); break; } case ASYNC_ERROR: { asyncStateMachine.asyncError(); break; } case ASYNC_IS_ASYNC: { ((AtomicBoolean) param).set(asyncStateMachine.isAsync()); break; } case ASYNC_IS_COMPLETING: { ((AtomicBoolean) param).set(asyncStateMachine.isCompleting()); break; } case ASYNC_IS_DISPATCHING: { ((AtomicBoolean) param).set(asyncStateMachine.isAsyncDispatching()); break; } case ASYNC_IS_ERROR: { ((AtomicBoolean) param).set(asyncStateMachine.isAsyncError()); break; } case ASYNC_IS_STARTED: { ((AtomicBoolean) param).set(asyncStateMachine.isAsyncStarted()); break; } case ASYNC_IS_TIMINGOUT: { ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut()); break; } case ASYNC_RUN: { asyncStateMachine.asyncRun((Runnable) param); break; } case ASYNC_SETTIMEOUT: { if (param == null) { return; } long timeout = ((Long) param).longValue(); setAsyncTimeout(timeout); break; } case ASYNC_TIMEOUT: { AtomicBoolean result = (AtomicBoolean) param; result.set(asyncStateMachine.asyncTimeout()); break; } case ASYNC_POST_PROCESS: { asyncStateMachine.asyncPostProcess(); break; } // Servlet 3.1 non-blocking I/O case REQUEST_BODY_FULLY_READ: { AtomicBoolean result = (AtomicBoolean) param; result.set(isRequestBodyFullyRead()); break; } case NB_READ_INTEREST: { AtomicBoolean isReady = (AtomicBoolean)param; isReady.set(isReadyForRead()); break; } case NB_WRITE_INTEREST: { AtomicBoolean isReady = (AtomicBoolean)param; isReady.set(isReadyForWrite()); break; } case DISPATCH_READ: { addDispatch(DispatchType.NON_BLOCKING_READ); break; } case DISPATCH_WRITE: { addDispatch(DispatchType.NON_BLOCKING_WRITE); break; } case DISPATCH_EXECUTE: { executeDispatches(); break; } // Servlet 3.1 HTTP Upgrade case UPGRADE: { doHttpUpgrade((UpgradeToken) param); break; } // Servlet 4.0 Push requests case IS_PUSH_SUPPORTED: { AtomicBoolean result = (AtomicBoolean) param; result.set(isPushSupported()); break; } case PUSH_REQUEST: { doPush((Request) param); break; } // Servlet 4.0 Trailers case IS_TRAILER_FIELDS_READY: { AtomicBoolean result = (AtomicBoolean) param; result.set(isTrailerFieldsReady()); break; } case IS_TRAILER_FIELDS_SUPPORTED: { AtomicBoolean result = (AtomicBoolean) param; result.set(isTrailerFieldsSupported()); break; } // Identifiers associated with multiplexing protocols like HTTP/2 case CONNECTION_ID: { @SuppressWarnings("unchecked") AtomicReference<Object> result = (AtomicReference<Object>) param; result.set(getConnectionID()); break; } case STREAM_ID: { @SuppressWarnings("unchecked") AtomicReference<Object> result = (AtomicReference<Object>) param; result.set(getStreamID()); break; } } } private void handleIOException (IOException ioe) { if (ioe instanceof CloseNowException) { // Close the channel but keep the connection open setErrorState(ErrorState.CLOSE_NOW, ioe); } else { // Close the connection and all channels within that connection setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); } }
Perform any necessary processing for a non-blocking read before dispatching to the adapter.
/** * Perform any necessary processing for a non-blocking read before * dispatching to the adapter. */
protected void dispatchNonBlockingRead() { asyncStateMachine.asyncOperation(); }
{@inheritDoc}

Sub-classes of this base class represent a single request/response pair. The timeout to be processed is, therefore, the Servlet asynchronous processing timeout.

/** * {@inheritDoc} * <p> * Sub-classes of this base class represent a single request/response pair. * The timeout to be processed is, therefore, the Servlet asynchronous * processing timeout. */
@Override public void timeoutAsync(long now) { if (now < 0) { doTimeoutAsync(); } else { long asyncTimeout = getAsyncTimeout(); if (asyncTimeout > 0) { long asyncStart = asyncStateMachine.getLastAsyncStart(); if ((now - asyncStart) > asyncTimeout) { doTimeoutAsync(); } } else if (!asyncStateMachine.isAvailable()) { // Timeout the async process if the associated web application // is no longer running. doTimeoutAsync(); } } } private void doTimeoutAsync() { // Avoid multiple timeouts setAsyncTimeout(-1); asyncTimeoutGeneration = asyncStateMachine.getCurrentGeneration(); processSocketEvent(SocketEvent.TIMEOUT, true); } @Override public boolean checkAsyncTimeoutGeneration() { return asyncTimeoutGeneration == asyncStateMachine.getCurrentGeneration(); } public void setAsyncTimeout(long timeout) { asyncTimeout = timeout; } public long getAsyncTimeout() { return asyncTimeout; } @Override public void recycle() { errorState = ErrorState.NONE; asyncStateMachine.recycle(); } protected abstract void prepareResponse() throws IOException; protected abstract void finishResponse() throws IOException; protected abstract void ack(ContinueResponseTiming continueResponseTiming); protected abstract void flush() throws IOException; protected abstract int available(boolean doRead); protected abstract void setRequestBody(ByteChunk body); protected abstract void setSwallowResponse(); protected abstract void disableSwallowRequest();
Processors that populate request attributes directly (e.g. AJP) should over-ride this method and return false.
Returns:true if the SocketWrapper should be used to populate the request attributes, otherwise false.
/** * Processors that populate request attributes directly (e.g. AJP) should * over-ride this method and return {@code false}. * * @return {@code true} if the SocketWrapper should be used to populate the * request attributes, otherwise {@code false}. */
protected boolean getPopulateRequestAttributesFromSocket() { return true; }
Populate the remote host request attribute. Processors (e.g. AJP) that populate this from an alternative source should override this method.
/** * Populate the remote host request attribute. Processors (e.g. AJP) that * populate this from an alternative source should override this method. */
protected void populateRequestAttributeRemoteHost() { if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) { request.remoteHost().setString(socketWrapper.getRemoteHost()); } }
Populate the TLS related request attributes from the SSLSupport instance associated with this processor. Protocols that populate TLS attributes from a different source (e.g. AJP) should override this method.
/** * Populate the TLS related request attributes from the {@link SSLSupport} * instance associated with this processor. Protocols that populate TLS * attributes from a different source (e.g. AJP) should override this * method. */
protected void populateSslRequestAttributes() { try { if (sslSupport != null) { Object sslO = sslSupport.getCipherSuite(); if (sslO != null) { request.setAttribute(SSLSupport.CIPHER_SUITE_KEY, sslO); } sslO = sslSupport.getPeerCertificateChain(); if (sslO != null) { request.setAttribute(SSLSupport.CERTIFICATE_KEY, sslO); } sslO = sslSupport.getKeySize(); if (sslO != null) { request.setAttribute (SSLSupport.KEY_SIZE_KEY, sslO); } sslO = sslSupport.getSessionId(); if (sslO != null) { request.setAttribute(SSLSupport.SESSION_ID_KEY, sslO); } sslO = sslSupport.getProtocol(); if (sslO != null) { request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, sslO); } request.setAttribute(SSLSupport.SESSION_MGR, sslSupport); } } catch (Exception e) { getLog().warn(sm.getString("abstractProcessor.socket.ssl"), e); } }
Processors that can perform a TLS re-handshake (e.g. HTTP/1.1) should override this method and implement the re-handshake.
Throws:
  • IOException – If authentication is required then there will be I/O with the client and this exception will be thrown if that goes wrong
/** * Processors that can perform a TLS re-handshake (e.g. HTTP/1.1) should * override this method and implement the re-handshake. * * @throws IOException If authentication is required then there will be I/O * with the client and this exception will be thrown if * that goes wrong */
protected void sslReHandShake() throws IOException { // NO-OP } protected void processSocketEvent(SocketEvent event, boolean dispatch) { SocketWrapperBase<?> socketWrapper = getSocketWrapper(); if (socketWrapper != null) { socketWrapper.processSocket(event, dispatch); } } protected boolean isReadyForRead() { if (available(true) > 0) { return true; } if (!isRequestBodyFullyRead()) { registerReadInterest(); } return false; } protected abstract boolean isRequestBodyFullyRead(); protected abstract void registerReadInterest(); protected abstract boolean isReadyForWrite(); protected void executeDispatches() { SocketWrapperBase<?> socketWrapper = getSocketWrapper(); Iterator<DispatchType> dispatches = getIteratorAndClearDispatches(); if (socketWrapper != null) { synchronized (socketWrapper) { /* * This method is called when non-blocking IO is initiated by defining * a read and/or write listener in a non-container thread. It is called * once the non-container thread completes so that the first calls to * onWritePossible() and/or onDataAvailable() as appropriate are made by * the container. * * Processing the dispatches requires (for APR/native at least) * that the socket has been added to the waitingRequests queue. This may * not have occurred by the time that the non-container thread completes * triggering the call to this method. Therefore, the coded syncs on the * SocketWrapper as the container thread that initiated this * non-container thread holds a lock on the SocketWrapper. The container * thread will add the socket to the waitingRequests queue before * releasing the lock on the socketWrapper. Therefore, by obtaining the * lock on socketWrapper before processing the dispatches, we can be * sure that the socket has been added to the waitingRequests queue. */ while (dispatches != null && dispatches.hasNext()) { DispatchType dispatchType = dispatches.next(); socketWrapper.processSocket(dispatchType.getSocketStatus(), false); } } } }
{@inheritDoc} Processors that implement HTTP upgrade must override this method and provide the necessary token.
/** * {@inheritDoc} * Processors that implement HTTP upgrade must override this method and * provide the necessary token. */
@Override public UpgradeToken getUpgradeToken() { // Should never reach this code but in case we do... throw new IllegalStateException( sm.getString("abstractProcessor.httpupgrade.notsupported")); }
Process an HTTP upgrade. Processors that support HTTP upgrade should override this method and process the provided token.
Params:
  • upgradeToken – Contains all the information necessary for the Processor to process the upgrade
Throws:
/** * Process an HTTP upgrade. Processors that support HTTP upgrade should * override this method and process the provided token. * * @param upgradeToken Contains all the information necessary for the * Processor to process the upgrade * * @throws UnsupportedOperationException if the protocol does not support * HTTP upgrade */
protected void doHttpUpgrade(UpgradeToken upgradeToken) { // Should never happen throw new UnsupportedOperationException( sm.getString("abstractProcessor.httpupgrade.notsupported")); }
{@inheritDoc} Processors that implement HTTP upgrade must override this method.
/** * {@inheritDoc} * Processors that implement HTTP upgrade must override this method. */
@Override public ByteBuffer getLeftoverInput() { // Should never reach this code but in case we do... throw new IllegalStateException(sm.getString("abstractProcessor.httpupgrade.notsupported")); }
{@inheritDoc} Processors that implement HTTP upgrade must override this method.
/** * {@inheritDoc} * Processors that implement HTTP upgrade must override this method. */
@Override public boolean isUpgrade() { return false; }
Protocols that support push should override this method and return true.
Returns:true if push is supported by this processor, otherwise false.
/** * Protocols that support push should override this method and return {@code * true}. * * @return {@code true} if push is supported by this processor, otherwise * {@code false}. */
protected boolean isPushSupported() { return false; }
Process a push. Processors that support push should override this method and process the provided token.
Params:
  • pushTarget – Contains all the information necessary for the Processor to process the push request
Throws:
/** * Process a push. Processors that support push should override this method * and process the provided token. * * @param pushTarget Contains all the information necessary for the Processor * to process the push request * * @throws UnsupportedOperationException if the protocol does not support * push */
protected void doPush(Request pushTarget) { throw new UnsupportedOperationException( sm.getString("abstractProcessor.pushrequest.notsupported")); } protected abstract boolean isTrailerFieldsReady();
Protocols that support trailer fields should override this method and return true.
Returns:true if trailer fields are supported by this processor, otherwise false.
/** * Protocols that support trailer fields should override this method and * return {@code true}. * * @return {@code true} if trailer fields are supported by this processor, * otherwise {@code false}. */
protected boolean isTrailerFieldsSupported() { return false; }
Protocols that support multiplexing (e.g. HTTP/2) should override this method and return the appropriate ID.
Returns:The stream ID associated with this request or null if a multiplexing protocol is not being used
/** * Protocols that support multiplexing (e.g. HTTP/2) should override this * method and return the appropriate ID. * * @return The stream ID associated with this request or {@code null} if a * multiplexing protocol is not being used */
protected Object getConnectionID() { return null; }
Protocols that support multiplexing (e.g. HTTP/2) should override this method and return the appropriate ID.
Returns:The stream ID associated with this request or null if a multiplexing protocol is not being used
/** * Protocols that support multiplexing (e.g. HTTP/2) should override this * method and return the appropriate ID. * * @return The stream ID associated with this request or {@code null} if a * multiplexing protocol is not being used */
protected Object getStreamID() { return null; }
Flush any pending writes. Used during non-blocking writes to flush any remaining data from a previous incomplete write.
Throws:
  • IOException – If an I/O error occurs while attempting to flush the data
Returns:true if data remains to be flushed at the end of method
/** * Flush any pending writes. Used during non-blocking writes to flush any * remaining data from a previous incomplete write. * * @return <code>true</code> if data remains to be flushed at the end of * method * * @throws IOException If an I/O error occurs while attempting to flush the * data */
protected abstract boolean flushBufferedWrite() throws IOException ;
Perform any necessary clean-up processing if the dispatch resulted in the completion of processing for the current request.
Throws:
  • IOException – If an I/O error occurs while attempting to end the request
Returns:The state to return for the socket once the clean-up for the current request has completed
/** * Perform any necessary clean-up processing if the dispatch resulted in the * completion of processing for the current request. * * @return The state to return for the socket once the clean-up for the * current request has completed * * @throws IOException If an I/O error occurs while attempting to end the * request */
protected abstract SocketState dispatchEndRequest() throws IOException; @Override protected final void logAccess(SocketWrapperBase<?> socketWrapper) throws IOException { // Set the socket wrapper so the access log can read the socket related // information (e.g. client IP) setSocketWrapper(socketWrapper); // Setup the minimal request information request.setStartTimeNanos(System.nanoTime()); // Setup the minimal response information response.setStatus(400); response.setError(); getAdapter().log(request, response, 0); } }