This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,964 @@
/*
* 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.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.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;
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.
*/
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.
protected char[] hostNameC = new char[0];
protected 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 AbstractEndpoint<?> endpoint;
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.
*/
private ErrorState errorState = ErrorState.NONE;
protected final UserDataHelper userDataHelper;
public AbstractProcessor(AbstractEndpoint<?> endpoint) {
this(endpoint, new Request(), new Response());
}
protected AbstractProcessor(AbstractEndpoint<?> endpoint, Request coyoteRequest,
Response coyoteResponse) {
this.endpoint = endpoint;
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.
* @param errorState The error status details
* @param t The error which occurred
*/
protected void setErrorState(ErrorState errorState, Throwable 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;
}
/**
* Set the associated adapter.
*
* @param adapter the new adapter
*/
public void setAdapter(Adapter adapter) {
this.adapter = adapter;
}
/**
* Get the associated adapter.
*
* @return the associated adapter
*/
public Adapter getAdapter() {
return adapter;
}
/**
* Set the socket wrapper being used.
* @param socketWrapper The socket wrapper
*/
protected void setSocketWrapper(SocketWrapperBase<?> socketWrapper) {
this.socketWrapper = socketWrapper;
}
/**
* @return the socket wrapper being used.
*/
protected final SocketWrapperBase<?> getSocketWrapper() {
return socketWrapper;
}
@Override
public final void setSslSupport(SSLSupport sslSupport) {
this.sslSupport = sslSupport;
}
/**
* @return the Executor used by the underlying endpoint.
*/
protected Executor getExecutor() {
return endpoint.getExecutor();
}
@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.
* <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.
* <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) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
}
}
break;
}
case CLOSE: {
action(ActionCode.COMMIT, null);
try {
finishResponse();
} catch (CloseNowException cne) {
setErrorState(ErrorState.CLOSE_NOW, cne);
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
}
break;
}
case ACK: {
ack();
break;
}
case CLIENT_FLUSH: {
action(ActionCode.COMMIT, null);
try {
flush();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, 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: {
// Aborted 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;
}
}
}
/**
* Perform any necessary processing for a non-blocking read before
* dispatching to the adapter.
*/
protected void dispatchNonBlockingRead() {
asyncStateMachine.asyncOperation();
}
/**
* {@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();
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 {@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.
*/
protected void populateRequestAttributeRemoteHost() {
if (getPopulateRequestAttributesFromSocket() && socketWrapper != null) {
request.remoteHost().setString(socketWrapper.getRemoteHost());
}
}
/**
* 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
*/
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.
*/
@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.
*
* @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.
*/
@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.
*/
@Override
public boolean isUpgrade() {
return 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.
*
* @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"));
}
/**
* 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.
*
* @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.setStartTime(System.currentTimeMillis());
// Setup the minimal response information
response.setStatus(400);
response.setError();
getAdapter().log(request, response, 0);
}
}

View File

@@ -0,0 +1,198 @@
/*
* 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<DispatchType> dispatches = new CopyOnWriteArraySet<>();
@Override
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
throws IOException {
SocketState state = SocketState.CLOSED;
Iterator<DispatchType> 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<DispatchType> 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<DispatchType> 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();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,255 @@
/*
* 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;
/**
* ActionCodes represent callbacks from the servlet container to the coyote
* connector. Actions are implemented by ProtocolHandler, using the ActionHook
* interface.
*
* @see ProtocolHandler
* @see ActionHook
* @author Remy Maucherat
*/
public enum ActionCode {
ACK,
CLOSE,
COMMIT,
/**
* A serious error occurred from which it is not possible to recover safely.
* Further attempts to write to the response should be ignored and the
* connection needs to be closed as soon as possible. This can also be used
* to forcibly close a connection if an error occurs after the response has
* been committed.
*/
CLOSE_NOW,
/**
* A flush() operation originated by the client ( i.e. a flush() on the
* servlet output stream or writer, called by a servlet ). Argument is the
* Response.
*/
CLIENT_FLUSH,
/**
* Has the processor been placed into the error state? Note that the
* response may not have an appropriate error code set.
*/
IS_ERROR,
/**
* The processor may have been placed into an error state and some error
* states do not permit any further I/O. Is I/O currently allowed?
*/
IS_IO_ALLOWED,
/**
* Hook called if swallowing request input should be disabled.
* Example: Cancel a large file upload.
*
*/
DISABLE_SWALLOW_INPUT,
/**
* Callback for lazy evaluation - extract the remote host name and address.
*/
REQ_HOST_ATTRIBUTE,
/**
* Callback for lazy evaluation - extract the remote host address.
*/
REQ_HOST_ADDR_ATTRIBUTE,
/**
* Callback for lazy evaluation - extract the SSL-related attributes
* including the client certificate if present.
*/
REQ_SSL_ATTRIBUTE,
/**
* Force a TLS re-handshake and make the resulting client certificate (if
* any) available as a request attribute.
*/
REQ_SSL_CERTIFICATE,
/**
* Callback for lazy evaluation - socket remote port.
*/
REQ_REMOTEPORT_ATTRIBUTE,
/**
* Callback for lazy evaluation - socket local port.
*/
REQ_LOCALPORT_ATTRIBUTE,
/**
* Callback for lazy evaluation - local address.
*/
REQ_LOCAL_ADDR_ATTRIBUTE,
/**
* Callback for lazy evaluation - local address.
*/
REQ_LOCAL_NAME_ATTRIBUTE,
/**
* Callback for setting FORM auth body replay
*/
REQ_SET_BODY_REPLAY,
/**
* Callback for getting the amount of available bytes.
*/
AVAILABLE,
/**
* Callback for an async request.
*/
ASYNC_START,
/**
* Callback for an async call to
* {@link javax.servlet.AsyncContext#dispatch()}.
*/
ASYNC_DISPATCH,
/**
* Callback to indicate the the actual dispatch has started and that the
* async state needs change.
*/
ASYNC_DISPATCHED,
/**
* Callback for an async call to
* {@link javax.servlet.AsyncContext#start(Runnable)}.
*/
ASYNC_RUN,
/**
* Callback for an async call to
* {@link javax.servlet.AsyncContext#complete()}.
*/
ASYNC_COMPLETE,
/**
* Callback to trigger the processing of an async timeout.
*/
ASYNC_TIMEOUT,
/**
* Callback to trigger the error processing.
*/
ASYNC_ERROR,
/**
* Callback for an async call to
* {@link javax.servlet.AsyncContext#setTimeout(long)}
*/
ASYNC_SETTIMEOUT,
/**
* Callback to determine if async processing is in progress.
*/
ASYNC_IS_ASYNC,
/**
* Callback to determine if async dispatch is in progress.
*/
ASYNC_IS_STARTED,
/**
* Call back to determine if async complete is in progress.
*/
ASYNC_IS_COMPLETING,
/**
* Callback to determine if async dispatch is in progress.
*/
ASYNC_IS_DISPATCHING,
/**
* Callback to determine if async is timing out.
*/
ASYNC_IS_TIMINGOUT,
/**
* Callback to determine if async is in error.
*/
ASYNC_IS_ERROR,
/**
* Callback to trigger post processing. Typically only used during error
* handling to trigger essential processing that otherwise would be skipped.
*/
ASYNC_POST_PROCESS,
/**
* Callback to trigger the HTTP upgrade process.
*/
UPGRADE,
/**
* Indicator that Servlet is interested in being
* notified when data is available to be read.
*/
NB_READ_INTEREST,
/**
* Used with non-blocking writes to determine if a write is currently
* allowed (sets passed parameter to <code>true</code>) or not (sets passed
* parameter to <code>false</code>). If a write is not allowed then callback
* will be triggered at some future point when write becomes possible again.
*/
NB_WRITE_INTEREST,
/**
* Indicates if the request body has been fully read.
*/
REQUEST_BODY_FULLY_READ,
/**
* Indicates that the container needs to trigger a call to onDataAvailable()
* for the registered non-blocking read listener.
*/
DISPATCH_READ,
/**
* Indicates that the container needs to trigger a call to onWritePossible()
* for the registered non-blocking write listener.
*/
DISPATCH_WRITE,
/**
* Execute any non-blocking dispatches that have been registered via
* {@link #DISPATCH_READ} or {@link #DISPATCH_WRITE}. Typically required
* when the non-blocking listeners are configured on a thread where the
* processing wasn't triggered by a read or write event on the socket.
*/
DISPATCH_EXECUTE,
/**
* Is server push supported and allowed for the current request?
*/
IS_PUSH_SUPPORTED,
/**
* Push a request on behalf of the client of the current request.
*/
PUSH_REQUEST
}

View File

@@ -0,0 +1,45 @@
/*
* 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;
/**
* Action hook. Actions represent the callback mechanism used by
* coyote servlet containers to request operations on the coyote connectors.
* Some standard actions are defined in ActionCode, however custom
* actions are permitted.
*
* The param object can be used to pass and return informations related with the
* action.
*
*
* This interface is typically implemented by ProtocolHandlers, and the param
* is usually a Request or Response object.
*
* @author Remy Maucherat
*/
public interface ActionHook {
/**
* Send an action to the connector.
*
* @param actionCode Type of the action
* @param param Action parameter
*/
public void action(ActionCode actionCode, Object param);
}

View File

@@ -0,0 +1,91 @@
/*
* 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 org.apache.tomcat.util.net.SocketEvent;
/**
* Adapter. This represents the entry point in a coyote-based servlet container.
*
*
* @author Remy Maucherat
* @see ProtocolHandler
*/
public interface Adapter {
/**
* Call the service method, and notify all listeners
*
* @param req The request object
* @param res The response object
*
* @exception Exception if an error happens during handling of
* the request. Common errors are:
* <ul><li>IOException if an input/output error occurs and we are
* processing an included servlet (otherwise it is swallowed and
* handled by the top level error handler mechanism)
* <li>ServletException if a servlet throws an exception and
* we are processing an included servlet (otherwise it is swallowed
* and handled by the top level error handler mechanism)
* </ul>
* Tomcat should be able to handle and log any other exception ( including
* runtime exceptions )
*/
public void service(Request req, Response res) throws Exception;
/**
* Prepare the given request/response for processing. This method requires
* that the request object has been populated with the information available
* from the HTTP headers.
*
* @param req The request object
* @param res The response object
*
* @return <code>true</code> if processing can continue, otherwise
* <code>false</code> in which case an appropriate error will have
* been set on the response
*
* @throws Exception If the processing fails unexpectedly
*/
public boolean prepare(Request req, Response res) throws Exception;
public boolean asyncDispatch(Request req,Response res, SocketEvent status)
throws Exception;
public void log(Request req, Response res, long time);
/**
* Assert that request and response have been recycled. If they have not
* then log a warning and force a recycle. This method is called as a safety
* check when a processor is being recycled and may be returned to a pool
* for reuse.
*
* @param req
* Request
* @param res
* Response
*/
public void checkRecycled(Request req, Response res);
/**
* Provide the name of the domain to use to register MBeans for components
* associated with the connector.
*
* @return The MBean domain name
*/
public String getDomain();
}

View File

@@ -0,0 +1,36 @@
/*
* 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;
/**
* Provides a mechanism for the Coyote connectors to communicate with the
* {@link javax.servlet.AsyncContext}. It is implemented in this manner so that
* the org.apache.coyote package does not have a dependency on the
* org.apache.catalina package.
*/
public interface AsyncContextCallback {
public void fireOnComplete();
/**
* Reports if the web application associated with this async request is
* available.
*
* @return {@code true} if the associated web application is available,
* otherwise {@code false}
*/
public boolean isAvailable();
}

View File

@@ -0,0 +1,494 @@
/*
* 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.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.security.PrivilegedGetTccl;
import org.apache.tomcat.util.security.PrivilegedSetTccl;
/**
* Manages the state transitions for async requests.
*
* <pre>
* The internal states that are used are:
* DISPATCHED - Standard request. Not in Async mode.
* STARTING - ServletRequest.startAsync() has been called from
* Servlet.service() but service() has not exited.
* STARTED - ServletRequest.startAsync() has been called from
* Servlet.service() and service() has exited.
* READ_WRITE_OP - Performing an asynchronous read or write.
* MUST_COMPLETE - ServletRequest.startAsync() followed by complete() have
* been called during a single Servlet.service() method. The
* complete() will be processed as soon as Servlet.service()
* exits.
* COMPLETE_PENDING - ServletRequest.startAsync() has been called from
* Servlet.service() but, before service() exited, complete()
* was called from another thread. The complete() will
* be processed as soon as Servlet.service() exits.
* COMPLETING - The call to complete() was made once the request was in
* the STARTED state.
* TIMING_OUT - The async request has timed out and is waiting for a call
* to complete() or dispatch(). If that isn't made, the error
* state will be entered.
* MUST_DISPATCH - ServletRequest.startAsync() followed by dispatch() have
* been called during a single Servlet.service() method. The
* dispatch() will be processed as soon as Servlet.service()
* exits.
* DISPATCH_PENDING - ServletRequest.startAsync() has been called from
* Servlet.service() but, before service() exited, dispatch()
* was called from another thread. The dispatch() will
* be processed as soon as Servlet.service() exits.
* DISPATCHING - The dispatch is being processed.
* MUST_ERROR - ServletRequest.startAsync() has been called from
* Servlet.service() but, before service() exited, an I/O
* error occured on another thread. The container will
* perform the necessary error handling when
* Servlet.service() exits.
* ERROR - Something went wrong.
*
*
* The valid state transitions are:
*
* post() dispatched()
* |-------»------------------»---------| |-------«-----------------------«-----|
* | | | |
* | | | post() |
* | post() \|/ \|/ dispatched() |
* | |-----»----------------»DISPATCHED«-------------«-------------| |
* | | | /|\ | | |
* | | startAsync()| |--|timeout() | |
* ^ | | | |
* | | complete() | dispatch() ^ |
* | | |--«---------------«-- | ---«--MUST_ERROR--»-----| | |
* | | | | /|\ | | |
* | ^ | | | | | |
* | | | | /-----|error() | | |
* | | | | / | ^ |
* | | \|/ ST-complete() \|/ / ST-dispatch() \|/ | |
* | MUST_COMPLETE«--------«--------STARTING--------»---------»MUST_DISPATCH |
* | / | \ |
* | / | \ |
* | OT-complete() / | \ OT-dispatch() |
* | COMPLETE_PENDING«------«------/ | \-------»---------»DISPATCH_PENDING |
* | | | | |
* | post()| timeout() post()| post() post()| timeout() |
* | | |--| | |--| | |--| |
* | \|/ \|/ | complete() \|/\|/ | dispatch() \|/ \|/ | |
* |--«-----COMPLETING«--------«----------STARTED--------»---------»DISPATCHING----|
* /|\ /|\ /|\ | /|\ \ /|\ /|\ /|\
* | | | | \ \asyncOperation() | | |
* | | | timeout()| \ \ | | |
* | | | | \ \ | | |
* | | | | \ \ | | |
* | | | | \ \ | | |
* | | | | \ \ | | |
* | | | | post()\ \ dispatch()| | |
* | | | complete() | \ \|/ | | |
* | | |---«------------«-- | --«---READ_WRITE----»----| | |
* | | | | |
* | | complete() \|/ dispatch() | |
* | |------------«-------TIMING_OUT--------»----------------| |
* | |
* | complete() dispatch() |
* |---------------«-----------ERROR--------------»-----------------|
*
*
* Notes: * For clarity, the transitions to ERROR which are valid from every state apart from
* STARTING are not shown.
* * All transitions may happen on either the Servlet.service() thread (ST) or on any
* other thread (OT) unless explicitly marked.
* </pre>
*/
public class AsyncStateMachine {
/**
* The string manager for this package.
*/
private static final StringManager sm = StringManager.getManager(AsyncStateMachine.class);
private enum AsyncState {
DISPATCHED (false, false, false, false),
STARTING (true, true, false, false),
STARTED (true, true, false, false),
MUST_COMPLETE (true, true, true, false),
COMPLETE_PENDING(true, true, false, false),
COMPLETING (true, false, true, false),
TIMING_OUT (true, true, false, false),
MUST_DISPATCH (true, true, false, true),
DISPATCH_PENDING(true, true, false, false),
DISPATCHING (true, false, false, true),
READ_WRITE_OP (true, true, false, false),
MUST_ERROR (true, true, false, false),
ERROR (true, true, false, false);
private final boolean isAsync;
private final boolean isStarted;
private final boolean isCompleting;
private final boolean isDispatching;
private AsyncState(boolean isAsync, boolean isStarted, boolean isCompleting,
boolean isDispatching) {
this.isAsync = isAsync;
this.isStarted = isStarted;
this.isCompleting = isCompleting;
this.isDispatching = isDispatching;
}
public boolean isAsync() {
return isAsync;
}
public boolean isStarted() {
return isStarted;
}
public boolean isDispatching() {
return isDispatching;
}
public boolean isCompleting() {
return isCompleting;
}
}
private volatile AsyncState state = AsyncState.DISPATCHED;
private volatile long lastAsyncStart = 0;
/*
* Tracks the current generation of async processing for this state machine.
* The generation is incremented every time async processing is started. The
* primary purpose of this is to enable Tomcat to detect and prevent
* attempts to process an event for a previous generation with the current
* generation as processing such an event usually ends badly:
* e.g. CVE-2018-8037.
*/
private final AtomicLong generation = new AtomicLong(0);
// Need this to fire listener on complete
private AsyncContextCallback asyncCtxt = null;
private final AbstractProcessor processor;
public AsyncStateMachine(AbstractProcessor processor) {
this.processor = processor;
}
public boolean isAsync() {
return state.isAsync();
}
public boolean isAsyncDispatching() {
return state.isDispatching();
}
public boolean isAsyncStarted() {
return state.isStarted();
}
public boolean isAsyncTimingOut() {
return state == AsyncState.TIMING_OUT;
}
public boolean isAsyncError() {
return state == AsyncState.ERROR;
}
public boolean isCompleting() {
return state.isCompleting();
}
/**
* Obtain the time that this connection last transitioned to async
* processing.
*
* @return The time (as returned by {@link System#currentTimeMillis()}) that
* this connection last transitioned to async
*/
public long getLastAsyncStart() {
return lastAsyncStart;
}
long getCurrentGeneration() {
return generation.get();
}
public synchronized void asyncStart(AsyncContextCallback asyncCtxt) {
if (state == AsyncState.DISPATCHED) {
generation.incrementAndGet();
state = AsyncState.STARTING;
this.asyncCtxt = asyncCtxt;
lastAsyncStart = System.currentTimeMillis();
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncStart()", state));
}
}
public synchronized void asyncOperation() {
if (state==AsyncState.STARTED) {
state = AsyncState.READ_WRITE_OP;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncOperation()", state));
}
}
/*
* Async has been processed. Whether or not to enter a long poll depends on
* current state. For example, as per SRV.2.3.3.3 can now process calls to
* complete() or dispatch().
*/
public synchronized SocketState asyncPostProcess() {
if (state == AsyncState.COMPLETE_PENDING) {
clearNonBlockingListeners();
state = AsyncState.COMPLETING;
return SocketState.ASYNC_END;
} else if (state == AsyncState.DISPATCH_PENDING) {
clearNonBlockingListeners();
state = AsyncState.DISPATCHING;
return SocketState.ASYNC_END;
} else if (state == AsyncState.STARTING || state == AsyncState.READ_WRITE_OP) {
state = AsyncState.STARTED;
return SocketState.LONG;
} else if (state == AsyncState.MUST_COMPLETE || state == AsyncState.COMPLETING) {
asyncCtxt.fireOnComplete();
state = AsyncState.DISPATCHED;
return SocketState.ASYNC_END;
} else if (state == AsyncState.MUST_DISPATCH) {
state = AsyncState.DISPATCHING;
return SocketState.ASYNC_END;
} else if (state == AsyncState.DISPATCHING) {
state = AsyncState.DISPATCHED;
return SocketState.ASYNC_END;
} else if (state == AsyncState.STARTED) {
// This can occur if an async listener does a dispatch to an async
// servlet during onTimeout
return SocketState.LONG;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncPostProcess()", state));
}
}
public synchronized boolean asyncComplete() {
if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) {
state = AsyncState.COMPLETE_PENDING;
return false;
}
clearNonBlockingListeners();
boolean triggerDispatch = false;
if (state == AsyncState.STARTING || state == AsyncState.MUST_ERROR) {
// Processing is on a container thread so no need to transfer
// processing to a new container thread
state = AsyncState.MUST_COMPLETE;
} else if (state == AsyncState.STARTED) {
state = AsyncState.COMPLETING;
// A dispatch to a container thread is always required.
// If on a non-container thread, need to get back onto a container
// thread to complete the processing.
// If on a container thread the current request/response are not the
// request/response associated with the AsyncContext so need a new
// container thread to process the different request/response.
triggerDispatch = true;
} else if (state == AsyncState.READ_WRITE_OP || state == AsyncState.TIMING_OUT ||
state == AsyncState.ERROR) {
// Read/write operations can happen on or off a container thread but
// while in this state the call to listener that triggers the
// read/write will be in progress on a container thread.
// Processing of timeouts and errors can happen on or off a
// container thread (on is much more likely) but while in this state
// the call that triggers the timeout will be in progress on a
// container thread.
// The socket will be added to the poller when the container thread
// exits the AbstractConnectionHandler.process() method so don't do
// a dispatch here which would add it to the poller a second time.
state = AsyncState.COMPLETING;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncComplete()", state));
}
return triggerDispatch;
}
public synchronized boolean asyncTimeout() {
if (state == AsyncState.STARTED) {
state = AsyncState.TIMING_OUT;
return true;
} else if (state == AsyncState.COMPLETING ||
state == AsyncState.DISPATCHING ||
state == AsyncState.DISPATCHED) {
// NOOP - App called complete() or dispatch() between the the
// timeout firing and execution reaching this point
return false;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncTimeout()", state));
}
}
public synchronized boolean asyncDispatch() {
if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) {
state = AsyncState.DISPATCH_PENDING;
return false;
}
clearNonBlockingListeners();
boolean triggerDispatch = false;
if (state == AsyncState.STARTING || state == AsyncState.MUST_ERROR) {
// Processing is on a container thread so no need to transfer
// processing to a new container thread
state = AsyncState.MUST_DISPATCH;
} else if (state == AsyncState.STARTED) {
state = AsyncState.DISPATCHING;
// A dispatch to a container thread is always required.
// If on a non-container thread, need to get back onto a container
// thread to complete the processing.
// If on a container thread the current request/response are not the
// request/response associated with the AsyncContext so need a new
// container thread to process the different request/response.
triggerDispatch = true;
} else if (state == AsyncState.READ_WRITE_OP || state == AsyncState.TIMING_OUT ||
state == AsyncState.ERROR) {
// Read/write operations can happen on or off a container thread but
// while in this state the call to listener that triggers the
// read/write will be in progress on a container thread.
// Processing of timeouts and errors can happen on or off a
// container thread (on is much more likely) but while in this state
// the call that triggers the timeout will be in progress on a
// container thread.
// The socket will be added to the poller when the container thread
// exits the AbstractConnectionHandler.process() method so don't do
// a dispatch here which would add it to the poller a second time.
state = AsyncState.DISPATCHING;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncDispatch()", state));
}
return triggerDispatch;
}
public synchronized void asyncDispatched() {
if (state == AsyncState.DISPATCHING ||
state == AsyncState.MUST_DISPATCH) {
state = AsyncState.DISPATCHED;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncDispatched()", state));
}
}
public synchronized boolean asyncError() {
clearNonBlockingListeners();
if (state == AsyncState.STARTING) {
state = AsyncState.MUST_ERROR;
} else {
state = AsyncState.ERROR;
}
return !ContainerThreadMarker.isContainerThread();
}
public synchronized void asyncRun(Runnable runnable) {
if (state == AsyncState.STARTING || state == AsyncState.STARTED ||
state == AsyncState.READ_WRITE_OP) {
// Execute the runnable using a container thread from the
// Connector's thread pool. Use a wrapper to prevent a memory leak
ClassLoader oldCL;
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
oldCL = AccessController.doPrivileged(pa);
} else {
oldCL = Thread.currentThread().getContextClassLoader();
}
try {
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> pa = new PrivilegedSetTccl(
this.getClass().getClassLoader());
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader(
this.getClass().getClassLoader());
}
processor.getExecutor().execute(runnable);
} finally {
if (Constants.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> pa = new PrivilegedSetTccl(
oldCL);
AccessController.doPrivileged(pa);
} else {
Thread.currentThread().setContextClassLoader(oldCL);
}
}
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncRun()", state));
}
}
synchronized boolean isAvailable() {
if (asyncCtxt == null) {
// Async processing has probably been completed in another thread.
// Trigger a timeout to make sure the Processor is cleaned up.
return false;
}
return asyncCtxt.isAvailable();
}
public synchronized void recycle() {
// Use lastAsyncStart to determine if this instance has been used since
// it was last recycled. If it hasn't there is no need to recycle again
// which saves the relatively expensive call to notifyAll()
if (lastAsyncStart == 0) {
return;
}
// Ensure in case of error that any non-container threads that have been
// paused are unpaused.
notifyAll();
asyncCtxt = null;
state = AsyncState.DISPATCHED;
lastAsyncStart = 0;
}
private void clearNonBlockingListeners() {
processor.getRequest().listener = null;
processor.getRequest().getResponse().listener = null;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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;
/**
* This exception is thrown to signal to the Tomcat internals that an error has
* occurred that requires the connection to be closed. For multiplexed protocols
* such as HTTP/2, this means the channel must be closed but the connection can
* continue. For non-multiplexed protocols, the connection must be closed. It
* corresponds to {@link ErrorState#CLOSE_NOW}.
*/
public class CloseNowException extends IOException {
private static final long serialVersionUID = 1L;
public CloseNowException() {
super();
}
public CloseNowException(String message, Throwable cause) {
super(message, cause);
}
public CloseNowException(String message) {
super(message);
}
public CloseNowException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,351 @@
/*
* 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.StringReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.ResponseUtil;
import org.apache.tomcat.util.http.parser.AcceptEncoding;
import org.apache.tomcat.util.http.parser.TokenList;
import org.apache.tomcat.util.res.StringManager;
public class CompressionConfig {
private static final Log log = LogFactory.getLog(CompressionConfig.class);
private static final StringManager sm = StringManager.getManager(CompressionConfig.class);
private int compressionLevel = 0;
private Pattern noCompressionUserAgents = null;
private String compressibleMimeType = "text/html,text/xml,text/plain,text/css," +
"text/javascript,application/javascript,application/json,application/xml";
private String[] compressibleMimeTypes = null;
private int compressionMinSize = 2048;
private boolean noCompressionStrongETag = true;
/**
* Set compression level.
*
* @param compression One of <code>on</code>, <code>force</code>,
* <code>off</code> or the minimum compression size in
* bytes which implies <code>on</code>
*/
public void setCompression(String compression) {
if (compression.equals("on")) {
this.compressionLevel = 1;
} else if (compression.equals("force")) {
this.compressionLevel = 2;
} else if (compression.equals("off")) {
this.compressionLevel = 0;
} else {
try {
// Try to parse compression as an int, which would give the
// minimum compression size
setCompressionMinSize(Integer.parseInt(compression));
this.compressionLevel = 1;
} catch (Exception e) {
this.compressionLevel = 0;
}
}
}
/**
* Return compression level.
*
* @return The current compression level in string form (off/on/force)
*/
public String getCompression() {
switch (compressionLevel) {
case 0:
return "off";
case 1:
return "on";
case 2:
return "force";
}
return "off";
}
public int getCompressionLevel() {
return compressionLevel;
}
/**
* Obtain the String form of the regular expression that defines the user
* agents to not use gzip with.
*
* @return The regular expression as a String
*/
public String getNoCompressionUserAgents() {
if (noCompressionUserAgents == null) {
return null;
} else {
return noCompressionUserAgents.toString();
}
}
public Pattern getNoCompressionUserAgentsPattern() {
return noCompressionUserAgents;
}
/**
* Set no compression user agent pattern. Regular expression as supported
* by {@link Pattern}. e.g.: <code>gorilla|desesplorer|tigrus</code>.
*
* @param noCompressionUserAgents The regular expression for user agent
* strings for which compression should not
* be applied
*/
public void setNoCompressionUserAgents(String noCompressionUserAgents) {
if (noCompressionUserAgents == null || noCompressionUserAgents.length() == 0) {
this.noCompressionUserAgents = null;
} else {
this.noCompressionUserAgents =
Pattern.compile(noCompressionUserAgents);
}
}
public String getCompressibleMimeType() {
return compressibleMimeType;
}
public void setCompressibleMimeType(String valueS) {
compressibleMimeType = valueS;
compressibleMimeTypes = null;
}
public String[] getCompressibleMimeTypes() {
String[] result = compressibleMimeTypes;
if (result != null) {
return result;
}
List<String> values = new ArrayList<>();
StringTokenizer tokens = new StringTokenizer(compressibleMimeType, ",");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken().trim();
if (token.length() > 0) {
values.add(token);
}
}
result = values.toArray(new String[values.size()]);
compressibleMimeTypes = result;
return result;
}
public int getCompressionMinSize() {
return compressionMinSize;
}
/**
* Set Minimum size to trigger compression.
*
* @param compressionMinSize The minimum content length required for
* compression in bytes
*/
public void setCompressionMinSize(int compressionMinSize) {
this.compressionMinSize = compressionMinSize;
}
/**
* Determine if compression is disabled if the resource has a strong ETag.
*
* @return {@code true} if compression is disabled, otherwise {@code false}
*
* @deprecated Will be removed in Tomcat 10 where it will be hard-coded to
* {@code true}
*/
@Deprecated
public boolean getNoCompressionStrongETag() {
return noCompressionStrongETag;
}
/**
* Set whether compression is disabled for resources with a strong ETag.
*
* @param noCompressionStrongETag {@code true} if compression is disabled,
* otherwise {@code false}
*
* @deprecated Will be removed in Tomcat 10 where it will be hard-coded to
* {@code true}
*/
@Deprecated
public void setNoCompressionStrongETag(boolean noCompressionStrongETag) {
this.noCompressionStrongETag = noCompressionStrongETag;
}
/**
* Determines if compression should be enabled for the given response and if
* it is, sets any necessary headers to mark it as such.
*
* @param request The request that triggered the response
* @param response The response to consider compressing
*
* @return {@code true} if compression was enabled for the given response,
* otherwise {@code false}
*/
public boolean useCompression(Request request, Response response) {
// Check if compression is enabled
if (compressionLevel == 0) {
return false;
}
MimeHeaders responseHeaders = response.getMimeHeaders();
// Check if content is not already compressed
MessageBytes contentEncodingMB = responseHeaders.getValue("Content-Encoding");
if (contentEncodingMB != null) {
// Content-Encoding values are ordered but order is not important
// for this check so use a Set rather than a List
Set<String> tokens = new HashSet<>();
try {
TokenList.parseTokenList(responseHeaders.values("Content-Encoding"), tokens);
} catch (IOException e) {
// Because we are using StringReader, any exception here is a
// Tomcat bug.
log.warn(sm.getString("compressionConfig.ContentEncodingParseFail"), e);
return false;
}
if (tokens.contains("gzip") || tokens.contains("br")) {
return false;
}
}
// If force mode, the length and MIME type checks are skipped
if (compressionLevel != 2) {
// Check if the response is of sufficient length to trigger the compression
long contentLength = response.getContentLengthLong();
if (contentLength != -1 && contentLength < compressionMinSize) {
return false;
}
// Check for compatible MIME-TYPE
String[] compressibleMimeTypes = getCompressibleMimeTypes();
if (compressibleMimeTypes != null &&
!startsWithStringArray(compressibleMimeTypes, response.getContentType())) {
return false;
}
}
// Check if the resource has a strong ETag
if (noCompressionStrongETag) {
String eTag = responseHeaders.getHeader("ETag");
if (eTag != null && !eTag.trim().startsWith("W/")) {
// Has an ETag that doesn't start with "W/..." so it must be a
// strong ETag
return false;
}
}
// If processing reaches this far, the response might be compressed.
// Therefore, set the Vary header to keep proxies happy
ResponseUtil.addVaryFieldName(responseHeaders, "accept-encoding");
// Check if user-agent supports gzip encoding
// Only interested in whether gzip encoding is supported. Other
// encodings and weights can be ignored.
Enumeration<String> headerValues = request.getMimeHeaders().values("accept-encoding");
boolean foundGzip = false;
while (!foundGzip && headerValues.hasMoreElements()) {
List<AcceptEncoding> acceptEncodings = null;
try {
acceptEncodings = AcceptEncoding.parse(new StringReader(headerValues.nextElement()));
} catch (IOException ioe) {
// If there is a problem reading the header, disable compression
return false;
}
for (AcceptEncoding acceptEncoding : acceptEncodings) {
if ("gzip".equalsIgnoreCase(acceptEncoding.getEncoding())) {
foundGzip = true;
break;
}
}
}
if (!foundGzip) {
return false;
}
// If force mode, the browser checks are skipped
if (compressionLevel != 2) {
// Check for incompatible Browser
Pattern noCompressionUserAgents = this.noCompressionUserAgents;
if (noCompressionUserAgents != null) {
MessageBytes userAgentValueMB = request.getMimeHeaders().getValue("user-agent");
if(userAgentValueMB != null) {
String userAgentValue = userAgentValueMB.toString();
if (noCompressionUserAgents.matcher(userAgentValue).matches()) {
return false;
}
}
}
}
// All checks have passed. Compression is enabled.
// Compressed content length is unknown so mark it as such.
response.setContentLength(-1);
// Configure the content encoding for compressed content
responseHeaders.setValue("Content-Encoding").setString("gzip");
return true;
}
/**
* Checks if any entry in the string array starts with the specified value
*
* @param sArray the StringArray
* @param value string
*/
private static boolean startsWithStringArray(String sArray[], String value) {
if (value == null) {
return false;
}
for (int i = 0; i < sArray.length; i++) {
if (value.startsWith(sArray[i])) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Constants.
*
* @author Remy Maucherat
*/
public final class Constants {
/**
* @deprecated This will be removed in Tomcat 9.0.x onwards.
*/
@Deprecated
public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
public static final Charset DEFAULT_URI_CHARSET = StandardCharsets.ISO_8859_1;
public static final Charset DEFAULT_BODY_CHARSET = StandardCharsets.ISO_8859_1;
public static final int MAX_NOTES = 32;
// Request states
public static final int STAGE_NEW = 0;
public static final int STAGE_PARSE = 1;
public static final int STAGE_PREPARE = 2;
public static final int STAGE_SERVICE = 3;
public static final int STAGE_ENDINPUT = 4;
public static final int STAGE_ENDOUTPUT = 5;
public static final int STAGE_KEEPALIVE = 6;
public static final int STAGE_ENDED = 7;
// Default protocol settings
public static final int DEFAULT_CONNECTION_LINGER = -1;
public static final boolean DEFAULT_TCP_NO_DELAY = true;
/**
* Has security been turned on?
*/
public static final boolean IS_SECURITY_ENABLED = (System.getSecurityManager() != null);
/**
* If true, custom HTTP status messages will be used in headers.
* @deprecated This option will be removed in Tomcat 9. Reason phrase will
* not be sent.
*/
@Deprecated
public static final boolean USE_CUSTOM_STATUS_MSG_IN_HEADER =
Boolean.getBoolean("org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER");
/**
* The request attribute that is set to the value of {@code Boolean.TRUE}
* if connector processing this request supports use of sendfile.
*/
public static final String SENDFILE_SUPPORTED_ATTR = "org.apache.tomcat.sendfile.support";
/**
* The request attribute that can be used by a servlet to pass
* to the connector the name of the file that is to be served
* by sendfile. The value should be {@code java.lang.String}
* that is {@code File.getCanonicalPath()} of the file to be served.
*/
public static final String SENDFILE_FILENAME_ATTR = "org.apache.tomcat.sendfile.filename";
/**
* The request attribute that can be used by a servlet to pass
* to the connector the start offset of the part of a file
* that is to be served by sendfile. The value should be
* {@code java.lang.Long}. To serve complete file
* the value should be {@code Long.valueOf(0)}.
*/
public static final String SENDFILE_FILE_START_ATTR = "org.apache.tomcat.sendfile.start";
/**
* The request attribute that can be used by a servlet to pass
* to the connector the end offset (not including) of the part
* of a file that is to be served by sendfile. The value should be
* {@code java.lang.Long}. To serve complete file
* the value should be equal to the length of the file.
*/
public static final String SENDFILE_FILE_END_ATTR = "org.apache.tomcat.sendfile.end";
/**
* The request attribute set by the RemoteIpFilter, RemoteIpValve (and may
* be set by other similar components) that identifies for the connector the
* remote IP address claimed to be associated with this request when a
* request is received via one or more proxies. It is typically provided via
* the X-Forwarded-For HTTP header.
*/
public static final String REMOTE_ADDR_ATTRIBUTE = "org.apache.tomcat.remoteAddr";
}

View File

@@ -0,0 +1,38 @@
/*
* 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;
/**
* Used to mark threads that have been allocated by the container to process
* data from an incoming connection. Application created threads are not
* container threads and neither are threads taken from the container thread
* pool to execute AsyncContext.start(Runnable).
*/
public class ContainerThreadMarker {
public static boolean isContainerThread() {
return org.apache.tomcat.util.net.ContainerThreadMarker.isContainerThread();
}
public static void set() {
org.apache.tomcat.util.net.ContainerThreadMarker.set();
}
public static void clear() {
org.apache.tomcat.util.net.ContainerThreadMarker.clear();
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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;
public enum ErrorState {
/**
* Not in an error state.
*/
NONE(false, 0, true, true),
/**
* The current request/response is in an error state and while it is safe to
* complete the current response it is not safe to continue to use the
* existing connection which must be closed once the response has been
* completed. For multiplexed protocols, the channel must be closed when the
* current request/response completes but the connection may continue.
*/
CLOSE_CLEAN(true, 1, true, true),
/**
* The current request/response is in an error state and it is not safe to
* continue to use them. For multiplexed protocols (such as HTTP/2) the
* stream/channel must be closed immediately but the connection may
* continue. For non-multiplexed protocols (AJP, HTTP/1.x) the current
* connection must be closed.
*/
CLOSE_NOW(true, 2, false, true),
/**
* An error has been detected that impacts the underlying network
* connection. It is not safe to continue using the network connection which
* must be closed immediately. For multiplexed protocols (such as HTTP/2)
* this impacts all multiplexed channels.
*/
CLOSE_CONNECTION_NOW(true, 3, false, false);
private final boolean error;
private final int severity;
private final boolean ioAllowed;
private final boolean connectionIoAllowed;
private ErrorState(boolean error, int severity, boolean ioAllowed,
boolean connectionIoAllowed) {
this.error = error;
this.severity = severity;
this.ioAllowed = ioAllowed;
this.connectionIoAllowed = connectionIoAllowed;
}
public boolean isError() {
return error;
}
/**
* Compare this ErrorState with the provided ErrorState and return the most
* severe.
*
* @param input The error state to compare to this one
*
* @return The most severe error state from the the provided error state and
* this one
*/
public ErrorState getMostSevere(ErrorState input) {
if (input.severity > this.severity) {
return input;
} else {
return this;
}
}
public boolean isIoAllowed() {
return ioAllowed;
}
public boolean isConnectionIoAllowed() {
return connectionIoAllowed;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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 org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
* This class is only for internal use in the protocol implementation. All
* reading from Tomcat (or adapter) should be done using Request.doRead().
*/
public interface InputBuffer {
/**
* Read from the input stream into the given buffer.
* IMPORTANT: the current model assumes that the protocol will 'own' the
* buffer and return a pointer to it in ByteChunk (i.e. the param will
* have chunk.getBytes()==null before call, and the result after the call).
*
* @param chunk The buffer to read data into.
*
* @return The number of bytes that have been added to the buffer or -1 for
* end of stream
*
* @throws IOException If an I/O error occurs reading from the input stream
*
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doRead(ApplicationBufferHandler)}
*/
@Deprecated
public int doRead(ByteChunk chunk) throws IOException;
/**
* Read from the input stream into the ByteBuffer provided by the
* ApplicationBufferHandler.
* IMPORTANT: the current model assumes that the protocol will 'own' the
* ByteBuffer and return a pointer to it.
*
* @param handler ApplicationBufferHandler that provides the buffer to read
* data into.
*
* @return The number of bytes that have been added to the buffer or -1 for
* end of stream
*
* @throws IOException If an I/O error occurs reading from the input stream
*/
public int doRead(ApplicationBufferHandler handler) throws IOException;
}

View File

@@ -0,0 +1,67 @@
# 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.
abstractConnectionHandler.connectionsGet=Found processor [{0}] for socket [{1}]
abstractConnectionHandler.error=Error reading request, ignored
abstractConnectionHandler.ioexception.debug=IOExceptions are normal, ignored
abstractConnectionHandler.negotiatedProcessor.fail=Failed to create Processor for negotiated protocol [{0}]
abstractConnectionHandler.oome=Failed to complete processing of a request
abstractConnectionHandler.process=Processing socket [{0}] with status [{1}]
abstractConnectionHandler.processorCreate=Created new processor [{0}]
abstractConnectionHandler.processorPop=Popped processor [{0}] from cache
abstractConnectionHandler.protocolexception.debug=ProtocolExceptions are normal, ignored
abstractConnectionHandler.socketexception.debug=SocketExceptions are normal, ignored
abstractConnectionHandler.upgradeCreate=Created upgrade processor [{0}] for socket wrapper [{1}]
abstractProcessor.fallToDebug=\n\
\ Note: further occurrences of request parsing errors will be logged at DEBUG level.
abstractProcessor.hostInvalid=The host [{0}] is not valid
abstractProcessor.httpupgrade.notsupported=HTTP upgrade is not supported by this protocol
abstractProcessor.pushrequest.notsupported=Server push requests are not supported by this protocol
abstractProcessor.socket.ssl=Exception getting SSL attributes
abstractProtocol.mbeanDeregistrationFailed=Failed to deregister MBean named [{0}] from MBean server [{1}]
abstractProtocol.processorRegisterError=Error registering request processor
abstractProtocol.processorUnregisterError=Error unregistering request processor
abstractProcotol.waitingProcerssor.add=Added processor [{0}] to waiting processors
abstractProcotol.waitingProcerssor.remove=Removed processor [{0}] from waiting processors
abstractProtocolHandler.destroy=Destroying ProtocolHandler [{0}]
abstractProtocolHandler.destroyError=Failed to destroy end point associated with ProtocolHandler [{0}]
abstractProtocolHandler.getAttribute=Get attribute [{0}] with value [{1}]
abstractProtocolHandler.init=Initializing ProtocolHandler [{0}]
abstractProtocolHandler.pause=Pausing ProtocolHandler [{0}]
abstractProtocolHandler.pauseError=Failed to pause end point associated with ProtocolHandler [{0}]
abstractProtocolHandler.resume=Resuming ProtocolHandler [{0}]
abstractProtocolHandler.resumeError=Failed to resume end point associated with ProtocolHandler [{0}]
abstractProtocolHandler.setAttribute=Set attribute [{0}] with value [{1}]
abstractProtocolHandler.start=Starting ProtocolHandler [{0}]
abstractProtocolHandler.startError=Failed to start end point associated with ProtocolHandler [{0}]
abstractProtocolHandler.stop=Stopping ProtocolHandler [{0}]
abstractProtocolHandler.stopError=Failed to stop end point associated with ProtocolHandler [{0}]
asyncStateMachine.invalidAsyncState=Calling [{0}] is not valid for a request with Async state [{1}]
compressionConfig.ContentEncodingParseFail=Failed to parse Content-Encoding header when checking to see if compression was already in use
request.notAsync=It is only valid to switch to non-blocking IO within async processing or HTTP upgrade processing
request.nullReadListener=The listener passed to setReadListener() may not be null
request.readListenerSet=The non-blocking read listener has already been set
response.encoding.invalid=The encoding [{0}] is not recognised by the JRE
response.notAsync=It is only valid to switch to non-blocking IO within async processing or HTTP upgrade processing
response.notNonBlocking=It is invalid to call isReady() when the response has not been put into non-blocking mode
response.nullWriteListener=The listener passed to setWriteListener() may not be null
response.writeListenerSet=The non-blocking write listener has already been set

View File

@@ -0,0 +1,28 @@
# 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.
abstractConnectionHandler.oome=Verarbeitung der Anfrage fehlgeschlagen
abstractConnectionHandler.processorPop=Prozessor [{0}] wurde aus dem Cache genommen.
abstractConnectionHandler.socketexception.debug=SocketExceptions sind normal, werden ignoriert
abstractProcessor.fallToDebug=Info: Weitere Vorkommen von Fehlern beim Parsen der Anfragen werden mit DEBUG Level ausgegeben
abstractProcessor.hostInvalid=Der Host [{0}] ist nicht gültig.
abstractProcessor.httpupgrade.notsupported=HTTP-Upgrade wird von diesem Protokol nicht unterstützt
abstractProtocolHandler.init=Initialisiere ProtocolHandler[{0}]
asyncStateMachine.invalidAsyncState=Der Aufruf von [{0}] ist nicht erlaubt, während der Request im Async-Status [{1}] ist
response.writeListenerSet=Der Nicht-blockierende Schreib-Listener wurde bereits gesetzt

View File

@@ -0,0 +1,28 @@
# 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.
abstractConnectionHandler.error=Error leyendo requerimiento, ignorado
abstractConnectionHandler.processorPop=Sacando procesador [{0}] de la cache
abstractProcessor.fallToDebug=Nota: futuras ocurrencias de la solicitud the parseo de errores será logueada con nivel DEBUG.
abstractProcessor.hostInvalid=El host [{0}] no es válido\n
abstractProcessor.httpupgrade.notsupported=La actualización HTTP no esta soportada por esta protocol
abstractProcessor.socket.ssl=Excepción obteniendo atributos SSL
abstractProtocolHandler.init=Inicializando el manejador de protocolo [{0}]\n
asyncStateMachine.invalidAsyncState=Llamando [{0}] no es una solicitud válida en el estado [{1}]\n
response.writeListenerSet=El escuchador de escritura no bloqueable ya ha sido establecido

View File

@@ -0,0 +1,57 @@
# 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.
abstractConnectionHandler.connectionsGet=Trouvé le processeur [{0}] pour le socket [{1}]
abstractConnectionHandler.error=Erreur de lecture de la requête, ignorée
abstractConnectionHandler.ioexception.debug=Les IOException sont normales et sont ignorées
abstractConnectionHandler.negotiatedProcessor.fail=Impossible de créer un processeur pour le protocole négocié [{0}]
abstractConnectionHandler.oome=Echec de la fin de traitement d'une requête
abstractConnectionHandler.process=Traitement du socket [{0}] avec le statut [{1}]
abstractConnectionHandler.processorPop=Retrait du processeur [{0}] du cache
abstractConnectionHandler.protocolexception.debug=Les ProtocolExceptions sont normales et sont ignorées
abstractConnectionHandler.socketexception.debug=Les SocketException sont normales et sont ignorées
abstractConnectionHandler.upgradeCreate=Création du processeur pour l''upgrade [{0}] du wrapper du socket [{1}]
abstractProcessor.fallToDebug=\ Note : les occurrences suivantes d'erreur d'analyse de la requête seront enregistrées au niveau DEBUG
abstractProcessor.hostInvalid=L''hôte [{0}] n''est pas valide
abstractProcessor.httpupgrade.notsupported=La promotion (upgrade) HTTP n'est pas supporté par ce protocole
abstractProcessor.pushrequest.notsupported=Le requêtes push du serveur ne sont pas supportées par ce protocole
abstractProcessor.socket.ssl=Exception lors de l'obtention des attributs SSL
abstractProtocol.mbeanDeregistrationFailed=Erreur lors du désenregistrement du mbean [{0}] dans le serveur [{1}]
abstractProtocol.processorRegisterError=Erreur lors de l'enregistrement du processeur de requêtes
abstractProtocol.processorUnregisterError=Erreur lors du désenregistrement du processeur de requêtes
abstractProtocolHandler.destroy=Destruction du gestionnaire de protocole [{0}]
abstractProtocolHandler.init=Initialisation du gestionnaire de protocole [{0}]
abstractProtocolHandler.pause=Le gestionnaire de protocole [{0}] est mis en pause
abstractProtocolHandler.resume=Reprise du gestionnaire de protocole [{0}]
abstractProtocolHandler.setAttribute=Fixe l''attribut [{0}] avec la valeur [{1}]
abstractProtocolHandler.start=Démarrage du gestionnaire de protocole [{0}]
abstractProtocolHandler.stop=Arrêt du gestionnaire de protocole [{0}]
asyncStateMachine.invalidAsyncState=L''appel à [{0}] n''est pas valide pour une requête dans l''état Async [{1}]
compressionConfig.ContentEncodingParseFail=Echec du traitement de l'en-tête Content-Encoding en vérifiant si la compression était déjà utilisée
request.notAsync=Il n'est possible de passer en mode d'entrée-sorties non bloquantes que lors de traitements asynchrones ou après mise à niveau depuis HTTP
request.nullReadListener=L'écouteur passé à setReadListener() ne peut pas être null
request.readListenerSet=L'écouteur des lectures non bloquantes a déjà été défini
response.encoding.invalid=L''encodage [{0}] n''est pas reconnu par le JRE
response.notAsync=Il n'est possible de passer en mode d'entrée-sorties non bloquantes que lors de traitements asynchrones ou après mise à niveau depuis HTTP
response.notNonBlocking=Il n'est pas permis d'appeler isReady() quand la réponse n'a pas été mise en mode non-bloquant
response.nullWriteListener=L'écouteur passé à setWriteListener() ne peut pas être null
response.writeListenerSet=La cible des notifications en écriture ("write listener") non-bloquantes a déjà été spécifiée

View File

@@ -0,0 +1,52 @@
# 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.
abstractConnectionHandler.connectionsGet=ソケット[{1}]のプロセッサ[{0}]が見つかりました。
abstractConnectionHandler.error=リクエストの読み取り中にエラーが発生しました。無視します。
abstractConnectionHandler.ioexception.debug=IOExceptionは正常で無視されます。
abstractConnectionHandler.negotiatedProcessor.fail=ネゴシエートされたプロトコル[{0}]のプロセッサの作成に失敗しました。
abstractConnectionHandler.process=ステータス[{1}]のソケット[{0}]を処理しています。
abstractConnectionHandler.processorPop=キャッシュからプロセッサー [{0}] を取得しました。
abstractConnectionHandler.protocolexception.debug=ProtocolExceptionsは正常です。無視します。
abstractConnectionHandler.socketexception.debug=SocketExceptionsは正常です。無視します。
abstractConnectionHandler.upgradeCreate=SocketWrapper [{1}]のアップグレードプロセッサ[{0}]が作成されました。
abstractProcessor.fallToDebug=リクエスト構文解析エラーのさらなる発生はDEBUGレベルでログに記録されます。
abstractProcessor.hostInvalid=ホスト名 [{0}] は不正なホスト名です。
abstractProcessor.httpupgrade.notsupported=このプロトコルは HTTP アップグレードに対応していません。
abstractProcessor.pushrequest.notsupported=このプロトコルはサーバープッシュの要求に対応していません。
abstractProcessor.socket.ssl=SSL属性を取得する例外
abstractProtocol.mbeanDeregistrationFailed=MBeanサーバー[{1}]から[{0}]という名前のMBeanの登録を解除できませんでした。
abstractProtocolHandler.destroy=ProtocolHandler [{0}] を破棄します。
abstractProtocolHandler.init=プロトコルハンドラ [{0}] を初期化します。
abstractProtocolHandler.pause=ProtocolHandler [{0}] を一時停止します。
abstractProtocolHandler.resume=プロトコルハンドラー [{0}] を再開します。
abstractProtocolHandler.setAttribute=属性[{0}]に値[{1}]を設定する
abstractProtocolHandler.start=プロトコルハンドラー [{0}] を開始しました。
abstractProtocolHandler.stop=ProtocolHandler [{0}]の停止中
asyncStateMachine.invalidAsyncState=非同期状態[{1}]のリクエストに対して[{0}]を呼び出すことはできません
request.notAsync=非同期処理またはHTTPアップグレード処理内のンブロッキングIOに切り替えることは有効です。
request.nullReadListener=setReadListener() には null を指定できません。
request.readListenerSet=ノンブロッキングリードリスナーは既に設定されています。
response.encoding.invalid=JRE は文字エンコーディング [{0}] を認識しません。
response.notAsync=非同期処理、あるいは、HTTP アップグレード処理の途中でのみノンブロッキング IO へ切り替えることができます。
response.notNonBlocking=ノンブロッキングモードにしなかったレスポンスの isReady() を呼び出すことはできません。
response.nullWriteListener=setWriteListener() には null を指定できません。
response.writeListenerSet=ノンブロッキング書き込みリスナーが設定済みです

View File

@@ -0,0 +1,58 @@
# 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.
abstractConnectionHandler.connectionsGet=소켓 [{1}]을(를) 위한 프로세서 [{0}]을(를) 발견했습니다.
abstractConnectionHandler.error=요청을 읽는 중 오류 발생. 무시합니다.
abstractConnectionHandler.ioexception.debug=IOException들은 정상적이므로, 무시합니다.
abstractConnectionHandler.negotiatedProcessor.fail=Negotiate된 프로토콜 [{0}]을(를) 위한 프로세서를 생성하지 못했습니다.
abstractConnectionHandler.oome=요청 처리를 완료하지 못했습니다.
abstractConnectionHandler.process=상태가 [{1}]인 소켓 [{0}]을(를) 처리합니다.
abstractConnectionHandler.processorPop=캐시로부터 프로세서 [{0}]을(를) 추출했습니다.
abstractConnectionHandler.protocolexception.debug=ProtocolException들은 정상적이므로, 무시합니다.
abstractConnectionHandler.socketexception.debug=SocketException들은 정상적인 상태이므로 무시되었습니다.
abstractConnectionHandler.upgradeCreate=소켓 wrapper [{1}]을(를) 위한 업그레이드 프로세서 [{0}]을(를) 생성했습니다.
abstractProcessor.fallToDebug=\n\
비고: 요청에 대한 파싱 오류들이 더 발생하는 경우 DEBUG 레벨 로그로 기록될 것입니다.
abstractProcessor.hostInvalid=호스트 [{0}]은(는) 유효하지 않습니다.
abstractProcessor.httpupgrade.notsupported=HTTP 업그레이드는 이 프로토콜에 의해 지원되지 않습니다.
abstractProcessor.pushrequest.notsupported=이 프로토콜은 서버 push 요청들을 지원하지 않습니다.
abstractProcessor.socket.ssl=SSL 속성들을 얻으려는 중 예외 발생
abstractProtocol.mbeanDeregistrationFailed=MBean 서버 [{1}](으)로부터, [{0}](이)라는 이름의 MBean의 등록을 제거하지 못했습니다.
abstractProtocol.processorRegisterError=RequestProcessor 구성요소를 등록하는 중 오류 발생
abstractProtocol.processorUnregisterError=RequestProcessor 구성요소를 등록 해제하는 중 오류 발생
abstractProtocolHandler.destroy=프로토콜 핸들러 [{0}]을(를) 소멸시킵니다.
abstractProtocolHandler.init=프로토콜 핸들러 [{0}]을(를) 초기화합니다.
abstractProtocolHandler.pause=프로토콜 핸들러 [{0}]을(를) 일시 정지 중
abstractProtocolHandler.resume=프로토콜 핸들러 [{0}]을(를) 재개합니다.
abstractProtocolHandler.setAttribute=속성 [{0}]에 값 [{1}]을(를) 설정
abstractProtocolHandler.start=프로토콜 핸들러 [{0}]을(를) 시작합니다.
abstractProtocolHandler.stop=프로토콜 핸들러 [{0}]을(를) 중지시킵니다.
asyncStateMachine.invalidAsyncState=비동기 상태가 [{1}]인 요청에 대하여, [{0}]을(를) 호출하는 것은 유효하지 않습니다.
compressionConfig.ContentEncodingParseFail=압축이 이미 사용되는지 여부를 점검하는 중, Content-Encoding 헤더를 파싱하지 못했습니다.
request.notAsync=오직 비동기 처리 또는 HTTP 업그레이드 처리 시에만, Non-blocking IO로의 전환이 유효합니다.
request.nullReadListener=setReadListener()에 전달된 리스너는 널일 수 없습니다.
request.readListenerSet=Non-blocking 읽기 리스너가 이미 설정되어 있습니다.
response.encoding.invalid=인코딩 [{0}]은(는) JRE에 의해 인식되지 않습니다.
response.notAsync=오직 비동기 처리 중 또는 HTTP 업그레이드 처리 중일 때에만, non-blocking IO로 전환하려는 것이 유효합니다.
response.notNonBlocking=응답이 non-blocking 모드 내에 있지 않을 때, isReady()를 호출하는 것은 유효하지 않습니다.
response.nullWriteListener=setWriteListener()에 전달되는 리스너가 널이이서는 안됩니다.
response.writeListenerSet=Non-blocking 쓰기 리스너가 이미 설정되었습니다.

View File

@@ -0,0 +1,37 @@
# 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.
abstractConnectionHandler.ioexception.debug=正常的 IOException忽略
abstractConnectionHandler.processorPop=从缓存中弹出的处理器[{0}]
abstractConnectionHandler.socketexception.debug=(:SocketException是正常的忽略
abstractConnectionHandler.upgradeCreate=为套接字包装程序[{1}]创建了升级处理器[{0}]
abstractProcessor.fallToDebug=注意更多的请求解析错误将以DEBUG级别日志进行记录。
abstractProcessor.hostInvalid=[{0}] 是无效主机
abstractProcessor.httpupgrade.notsupported=此协议不支持HTTP升级upgrade
abstractProcessor.socket.ssl=获取SSL属性异常
abstractProtocol.processorRegisterError=注册请求处理器错误
abstractProtocol.processorUnregisterError=注销请求处理器错误
abstractProtocolHandler.destroy=正在摧毁协议处理器 [{0}]
abstractProtocolHandler.init=初始化协议处理器 [{0}]
abstractProtocolHandler.start=开始协议处理句柄[{0}]
abstractProtocolHandler.stop=正在停止ProtocolHandler [{0}]
asyncStateMachine.invalidAsyncState=调用[{0}]对于具有异步状态[{1}]的请求无效
response.notNonBlocking=当响应尚未进入非阻塞模式时,调用 isReady() 无效
response.writeListenerSet=非阻塞的写监听已经被设置.

View File

@@ -0,0 +1,71 @@
/*
* 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.nio.ByteBuffer;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Output buffer.
*
* This class is used internally by the protocol implementation. All writes from
* higher level code should happen via Response.doWrite().
*
* @author Remy Maucherat
*/
public interface OutputBuffer {
/**
* Write the given data to the response. The caller owns the chunks.
*
* @param chunk data to write
*
* @return The number of bytes written which may be less than available in
* the input chunk
*
* @throws IOException an underlying I/O error occurred
*
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
public int doWrite(ByteChunk chunk) throws IOException;
/**
* Write the given data to the response. The caller owns the chunks.
*
* @param chunk data to write
*
* @return The number of bytes written which may be less than available in
* the input chunk
*
* @throws IOException an underlying I/O error occurred
*/
public int doWrite(ByteBuffer chunk) throws IOException;
/**
* Bytes written to the underlying socket. This includes the effects of
* chunking, compression, etc.
*
* @return Bytes written for the current request
*/
public long getBytesWritten();
}

View File

@@ -0,0 +1,128 @@
/*
* 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.nio.ByteBuffer;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.SocketEvent;
import org.apache.tomcat.util.net.SocketWrapperBase;
/**
* Common interface for processors of all protocols.
*/
public interface Processor {
/**
* Process a connection. This is called whenever an event occurs (e.g. more
* data arrives) that allows processing to continue for a connection that is
* not currently being processed.
*
* @param socketWrapper The connection to process
* @param status The status of the connection that triggered this additional
* processing
*
* @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
*/
SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) throws IOException;
/**
* Generate an upgrade token.
*
* @return An upgrade token encapsulating the information required to
* process the upgrade request
*
* @throws IllegalStateException if this is called on a Processor that does
* not support upgrading
*/
UpgradeToken getUpgradeToken();
/**
* @return {@code true} if the Processor is currently processing an upgrade
* request, otherwise {@code false}
*/
boolean isUpgrade();
boolean isAsync();
/**
* Check this processor to see if the timeout has expired and process a
* timeout if that is that case.
* <p>
* Note: The name of this method originated with the Servlet 3.0
* asynchronous processing but evolved over time to represent a timeout that
* is triggered independently of the socket read/write timeouts.
*
* @param now The time (as returned by {@link System#currentTimeMillis()} to
* use as the current time to determine whether the timeout has
* expired. If negative, the timeout will always be treated as ifq
* it has expired.
*/
void timeoutAsync(long now);
/**
* @return The request associated with this processor.
*/
Request getRequest();
/**
* Recycle the processor, ready for the next request which may be on the
* same connection or a different connection.
*/
void recycle();
/**
* Set the SSL information for this HTTP connection.
*
* @param sslSupport The SSL support object to use for this connection
*/
void setSslSupport(SSLSupport sslSupport);
/**
* Allows retrieving additional input during the upgrade process.
*
* @return leftover bytes
*
* @throws IllegalStateException if this is called on a Processor that does
* not support upgrading
*/
ByteBuffer getLeftoverInput();
/**
* Informs the processor that the underlying I/O layer has stopped accepting
* new connections. This is primarily intended to enable processors that
* use multiplexed connections to prevent further 'streams' being added to
* an existing multiplexed connection.
*/
void pause();
/**
* Check to see if the async generation (each cycle of async increments the
* generation of the AsyncStateMachine) is the same as the generation when
* the most recent async timeout was triggered. This is intended to be used
* to avoid unnecessary processing.
*
* @return {@code true} If the async generation has not changed since the
* async timeout was triggered
*/
boolean checkAsyncTimeoutGeneration();
}

View File

@@ -0,0 +1,42 @@
/*
* 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;
/**
* Used when we need to indicate failure but the (Servlet) API doesn't declare
* any appropriate exceptions.
*/
public class ProtocolException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ProtocolException() {
super();
}
public ProtocolException(String message, Throwable cause) {
super(message, cause);
}
public ProtocolException(String message) {
super(message);
}
public ProtocolException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.util.concurrent.Executor;
import org.apache.tomcat.util.net.SSLHostConfig;
/**
* Abstract the protocol implementation, including threading, etc.
*
* This is the main interface to be implemented by a coyote protocol.
* Adapter is the main interface to be implemented by a coyote servlet
* container.
*
* @author Remy Maucherat
* @author Costin Manolache
* @see Adapter
*/
public interface ProtocolHandler {
/**
* Return the adapter associated with the protocol handler.
* @return the adapter
*/
public Adapter getAdapter();
/**
* The adapter, used to call the connector.
*
* @param adapter The adapter to associate
*/
public void setAdapter(Adapter adapter);
/**
* The executor, provide access to the underlying thread pool.
*
* @return The executor used to process requests
*/
public Executor getExecutor();
/**
* Initialise the protocol.
*
* @throws Exception If the protocol handler fails to initialise
*/
public void init() throws Exception;
/**
* Start the protocol.
*
* @throws Exception If the protocol handler fails to start
*/
public void start() throws Exception;
/**
* Pause the protocol (optional).
*
* @throws Exception If the protocol handler fails to pause
*/
public void pause() throws Exception;
/**
* Resume the protocol (optional).
*
* @throws Exception If the protocol handler fails to resume
*/
public void resume() throws Exception;
/**
* Stop the protocol.
*
* @throws Exception If the protocol handler fails to stop
*/
public void stop() throws Exception;
/**
* Destroy the protocol (optional).
*
* @throws Exception If the protocol handler fails to destroy
*/
public void destroy() throws Exception;
/**
* Close the server socket (to prevent further connections) if the server
* socket was bound on {@link #start()} (rather than on {@link #init()}
* but do not perform any further shutdown.
*/
public void closeServerSocketGraceful();
/**
* Requires APR/native library
*
* @return <code>true</code> if this Protocol Handler requires the
* APR/native library, otherwise <code>false</code>
*/
public boolean isAprRequired();
/**
* Does this ProtocolHandler support sendfile?
*
* @return <code>true</code> if this Protocol Handler supports sendfile,
* otherwise <code>false</code>
*/
public boolean isSendfileSupported();
public void addSslHostConfig(SSLHostConfig sslHostConfig);
public SSLHostConfig[] findSslHostConfigs();
public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol);
public UpgradeProtocol[] findUpgradeProtocols();
}

View File

@@ -0,0 +1,727 @@
/*
* 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.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ReadListener;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.Parameters;
import org.apache.tomcat.util.http.ServerCookies;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
* This is a low-level, efficient representation of a server request. Most
* fields are GC-free, expensive operations are delayed until the user code
* needs the information.
*
* Processing is delegated to modules, using a hook mechanism.
*
* This class is not intended for user code - it is used internally by tomcat
* for processing the request in the most efficient way. Users ( servlets ) can
* access the information using a facade, which provides the high-level view
* of the request.
*
* Tomcat defines a number of attributes:
* <ul>
* <li>"org.apache.tomcat.request" - allows access to the low-level
* request object in trusted applications
* </ul>
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
* @author Jason Hunter [jch@eng.sun.com]
* @author Harish Prabandham
* @author Alex Cruikshank [alex@epitonic.com]
* @author Hans Bergsten [hans@gefionsoftware.com]
* @author Costin Manolache
* @author Remy Maucherat
*/
public final class Request {
private static final StringManager sm = StringManager.getManager(Request.class);
// Expected maximum typical number of cookies per request.
private static final int INITIAL_COOKIE_SIZE = 4;
// ----------------------------------------------------------- Constructors
public Request() {
parameters.setQuery(queryMB);
parameters.setURLDecoder(urlDecoder);
}
// ----------------------------------------------------- Instance Variables
private int serverPort = -1;
private final MessageBytes serverNameMB = MessageBytes.newInstance();
private int remotePort;
private int localPort;
private final MessageBytes schemeMB = MessageBytes.newInstance();
private final MessageBytes methodMB = MessageBytes.newInstance();
private final MessageBytes uriMB = MessageBytes.newInstance();
private final MessageBytes decodedUriMB = MessageBytes.newInstance();
private final MessageBytes queryMB = MessageBytes.newInstance();
private final MessageBytes protoMB = MessageBytes.newInstance();
// remote address/host
private final MessageBytes remoteAddrMB = MessageBytes.newInstance();
private final MessageBytes localNameMB = MessageBytes.newInstance();
private final MessageBytes remoteHostMB = MessageBytes.newInstance();
private final MessageBytes localAddrMB = MessageBytes.newInstance();
private final MimeHeaders headers = new MimeHeaders();
/**
* Path parameters
*/
private final Map<String,String> pathParameters = new HashMap<>();
/**
* Notes.
*/
private final Object notes[] = new Object[Constants.MAX_NOTES];
/**
* Associated input buffer.
*/
private InputBuffer inputBuffer = null;
/**
* URL decoder.
*/
private final UDecoder urlDecoder = new UDecoder();
/**
* HTTP specific fields. (remove them ?)
*/
private long contentLength = -1;
private MessageBytes contentTypeMB = null;
private Charset charset = null;
// Retain the original, user specified character encoding so it can be
// returned even if it is invalid
private String characterEncoding = null;
/**
* Is there an expectation ?
*/
private boolean expectation = false;
private final ServerCookies serverCookies = new ServerCookies(INITIAL_COOKIE_SIZE);
private final Parameters parameters = new Parameters();
private final MessageBytes remoteUser = MessageBytes.newInstance();
private boolean remoteUserNeedsAuthorization = false;
private final MessageBytes authType = MessageBytes.newInstance();
private final HashMap<String,Object> attributes = new HashMap<>();
private Response response;
private volatile ActionHook hook;
private long bytesRead=0;
// Time of the request - useful to avoid repeated calls to System.currentTime
private long startTime = -1;
private int available = 0;
private final RequestInfo reqProcessorMX=new RequestInfo(this);
private boolean sendfile = true;
volatile ReadListener listener;
public ReadListener getReadListener() {
return listener;
}
public void setReadListener(ReadListener listener) {
if (listener == null) {
throw new NullPointerException(
sm.getString("request.nullReadListener"));
}
if (getReadListener() != null) {
throw new IllegalStateException(
sm.getString("request.readListenerSet"));
}
// Note: This class is not used for HTTP upgrade so only need to test
// for async
AtomicBoolean result = new AtomicBoolean(false);
action(ActionCode.ASYNC_IS_ASYNC, result);
if (!result.get()) {
throw new IllegalStateException(
sm.getString("request.notAsync"));
}
this.listener = listener;
}
private final AtomicBoolean allDataReadEventSent = new AtomicBoolean(false);
public boolean sendAllDataReadEvent() {
return allDataReadEventSent.compareAndSet(false, true);
}
// ------------------------------------------------------------- Properties
public MimeHeaders getMimeHeaders() {
return headers;
}
public UDecoder getURLDecoder() {
return urlDecoder;
}
// -------------------- Request data --------------------
public MessageBytes scheme() {
return schemeMB;
}
public MessageBytes method() {
return methodMB;
}
public MessageBytes requestURI() {
return uriMB;
}
public MessageBytes decodedURI() {
return decodedUriMB;
}
public MessageBytes queryString() {
return queryMB;
}
public MessageBytes protocol() {
return protoMB;
}
/**
* Get the "virtual host", derived from the Host: header associated with
* this request.
*
* @return The buffer holding the server name, if any. Use isNull() to check
* if there is no value set.
*/
public MessageBytes serverName() {
return serverNameMB;
}
public int getServerPort() {
return serverPort;
}
public void setServerPort(int serverPort ) {
this.serverPort=serverPort;
}
public MessageBytes remoteAddr() {
return remoteAddrMB;
}
public MessageBytes remoteHost() {
return remoteHostMB;
}
public MessageBytes localName() {
return localNameMB;
}
public MessageBytes localAddr() {
return localAddrMB;
}
public int getRemotePort(){
return remotePort;
}
public void setRemotePort(int port){
this.remotePort = port;
}
public int getLocalPort(){
return localPort;
}
public void setLocalPort(int port){
this.localPort = port;
}
// -------------------- encoding/type --------------------
/**
* Get the character encoding used for this request.
*
* @return The value set via {@link #setCharacterEncoding(String)} or if no
* call has been made to that method try to obtain if from the
* content type.
*/
public String getCharacterEncoding() {
if (characterEncoding == null) {
characterEncoding = getCharsetFromContentType(getContentType());
}
return characterEncoding;
}
/**
* Get the character encoding used for this request.
*
* @return The value set via {@link #setCharacterEncoding(String)} or if no
* call has been made to that method try to obtain if from the
* content type.
*
* @throws UnsupportedEncodingException If the user agent has specified an
* invalid character encoding
*/
public Charset getCharset() throws UnsupportedEncodingException {
if (charset == null) {
getCharacterEncoding();
if (characterEncoding != null) {
charset = B2CConverter.getCharset(characterEncoding);
}
}
return charset;
}
/**
* @param enc The new encoding
*
* @throws UnsupportedEncodingException If the encoding is invalid
*
* @deprecated This method will be removed in Tomcat 9.0.x
*/
@Deprecated
public void setCharacterEncoding(String enc) throws UnsupportedEncodingException {
setCharset(B2CConverter.getCharset(enc));
}
public void setCharset(Charset charset) {
this.charset = charset;
this.characterEncoding = charset.name();
}
public void setContentLength(long len) {
this.contentLength = len;
}
public int getContentLength() {
long length = getContentLengthLong();
if (length < Integer.MAX_VALUE) {
return (int) length;
}
return -1;
}
public long getContentLengthLong() {
if( contentLength > -1 ) {
return contentLength;
}
MessageBytes clB = headers.getUniqueValue("content-length");
contentLength = (clB == null || clB.isNull()) ? -1 : clB.getLong();
return contentLength;
}
public String getContentType() {
contentType();
if ((contentTypeMB == null) || contentTypeMB.isNull()) {
return null;
}
return contentTypeMB.toString();
}
public void setContentType(String type) {
contentTypeMB.setString(type);
}
public MessageBytes contentType() {
if (contentTypeMB == null) {
contentTypeMB = headers.getValue("content-type");
}
return contentTypeMB;
}
public void setContentType(MessageBytes mb) {
contentTypeMB=mb;
}
public String getHeader(String name) {
return headers.getHeader(name);
}
public void setExpectation(boolean expectation) {
this.expectation = expectation;
}
public boolean hasExpectation() {
return expectation;
}
// -------------------- Associated response --------------------
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
response.setRequest(this);
}
protected void setHook(ActionHook hook) {
this.hook = hook;
}
public void action(ActionCode actionCode, Object param) {
if (hook != null) {
if (param == null) {
hook.action(actionCode, this);
} else {
hook.action(actionCode, param);
}
}
}
// -------------------- Cookies --------------------
public ServerCookies getCookies() {
return serverCookies;
}
// -------------------- Parameters --------------------
public Parameters getParameters() {
return parameters;
}
public void addPathParameter(String name, String value) {
pathParameters.put(name, value);
}
public String getPathParameter(String name) {
return pathParameters.get(name);
}
// -------------------- Other attributes --------------------
// We can use notes for most - need to discuss what is of general interest
public void setAttribute( String name, Object o ) {
attributes.put( name, o );
}
public HashMap<String,Object> getAttributes() {
return attributes;
}
public Object getAttribute(String name ) {
return attributes.get(name);
}
public MessageBytes getRemoteUser() {
return remoteUser;
}
public boolean getRemoteUserNeedsAuthorization() {
return remoteUserNeedsAuthorization;
}
public void setRemoteUserNeedsAuthorization(boolean remoteUserNeedsAuthorization) {
this.remoteUserNeedsAuthorization = remoteUserNeedsAuthorization;
}
public MessageBytes getAuthType() {
return authType;
}
public int getAvailable() {
return available;
}
public void setAvailable(int available) {
this.available = available;
}
public boolean getSendfile() {
return sendfile;
}
public void setSendfile(boolean sendfile) {
this.sendfile = sendfile;
}
public boolean isFinished() {
AtomicBoolean result = new AtomicBoolean(false);
action(ActionCode.REQUEST_BODY_FULLY_READ, result);
return result.get();
}
public boolean getSupportsRelativeRedirects() {
if (protocol().equals("") || protocol().equals("HTTP/1.0")) {
return false;
}
return true;
}
// -------------------- Input Buffer --------------------
public InputBuffer getInputBuffer() {
return inputBuffer;
}
public void setInputBuffer(InputBuffer inputBuffer) {
this.inputBuffer = inputBuffer;
}
/**
* Read data from the input buffer and put it into a byte chunk.
*
* The buffer is owned by the protocol implementation - it will be reused on
* the next read. The Adapter must either process the data in place or copy
* it to a separate buffer if it needs to hold it. In most cases this is
* done during byte-&gt;char conversions or via InputStream. Unlike
* InputStream, this interface allows the app to process data in place,
* without copy.
*
* @param chunk The destination to which to copy the data
*
* @return The number of bytes copied
*
* @throws IOException If an I/O error occurs during the copy
*
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doRead(ApplicationBufferHandler)}
*/
@Deprecated
public int doRead(ByteChunk chunk) throws IOException {
int n = inputBuffer.doRead(chunk);
if (n > 0) {
bytesRead+=n;
}
return n;
}
/**
* Read data from the input buffer and put it into ApplicationBufferHandler.
*
* The buffer is owned by the protocol implementation - it will be reused on
* the next read. The Adapter must either process the data in place or copy
* it to a separate buffer if it needs to hold it. In most cases this is
* done during byte-&gt;char conversions or via InputStream. Unlike
* InputStream, this interface allows the app to process data in place,
* without copy.
*
* @param handler The destination to which to copy the data
*
* @return The number of bytes copied
*
* @throws IOException If an I/O error occurs during the copy
*/
public int doRead(ApplicationBufferHandler handler) throws IOException {
int n = inputBuffer.doRead(handler);
if (n > 0) {
bytesRead+=n;
}
return n;
}
// -------------------- debug --------------------
@Override
public String toString() {
return "R( " + requestURI().toString() + ")";
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
// -------------------- Per-Request "notes" --------------------
/**
* Used to store private data. Thread data could be used instead - but
* if you have the req, getting/setting a note is just a array access, may
* be faster than ThreadLocal for very frequent operations.
*
* Example use:
* Catalina CoyoteAdapter:
* ADAPTER_NOTES = 1 - stores the HttpServletRequest object ( req/res)
*
* To avoid conflicts, note in the range 0 - 8 are reserved for the
* servlet container ( catalina connector, etc ), and values in 9 - 16
* for connector use.
*
* 17-31 range is not allocated or used.
*
* @param pos Index to use to store the note
* @param value The value to store at that index
*/
public final void setNote(int pos, Object value) {
notes[pos] = value;
}
public final Object getNote(int pos) {
return notes[pos];
}
// -------------------- Recycling --------------------
public void recycle() {
bytesRead=0;
contentLength = -1;
contentTypeMB = null;
charset = null;
characterEncoding = null;
expectation = false;
headers.recycle();
serverNameMB.recycle();
serverPort=-1;
localAddrMB.recycle();
localNameMB.recycle();
localPort = -1;
remoteAddrMB.recycle();
remoteHostMB.recycle();
remotePort = -1;
available = 0;
sendfile = true;
serverCookies.recycle();
parameters.recycle();
pathParameters.clear();
uriMB.recycle();
decodedUriMB.recycle();
queryMB.recycle();
methodMB.recycle();
protoMB.recycle();
schemeMB.recycle();
remoteUser.recycle();
remoteUserNeedsAuthorization = false;
authType.recycle();
attributes.clear();
listener = null;
allDataReadEventSent.set(false);
startTime = -1;
}
// -------------------- Info --------------------
public void updateCounters() {
reqProcessorMX.updateCounters();
}
public RequestInfo getRequestProcessor() {
return reqProcessorMX;
}
public long getBytesRead() {
return bytesRead;
}
public boolean isProcessing() {
return reqProcessorMX.getStage()==org.apache.coyote.Constants.STAGE_SERVICE;
}
/**
* Parse the character encoding from the specified content type header.
* If the content type is null, or there is no explicit character encoding,
* <code>null</code> is returned.
*
* @param contentType a content type header
*/
private static String getCharsetFromContentType(String contentType) {
if (contentType == null) {
return null;
}
int start = contentType.indexOf("charset=");
if (start < 0) {
return null;
}
String encoding = contentType.substring(start + 8);
int end = encoding.indexOf(';');
if (end >= 0) {
encoding = encoding.substring(0, end);
}
encoding = encoding.trim();
if ((encoding.length() > 2) && (encoding.startsWith("\""))
&& (encoding.endsWith("\""))) {
encoding = encoding.substring(1, encoding.length() - 1);
}
return encoding.trim();
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.util.ArrayList;
/** This can be moved to top level ( eventually with a better name ).
* It is currently used only as a JMX artifact, to aggregate the data
* collected from each RequestProcessor thread.
*/
public class RequestGroupInfo {
private final ArrayList<RequestInfo> processors = new ArrayList<>();
private long deadMaxTime = 0;
private long deadProcessingTime = 0;
private int deadRequestCount = 0;
private int deadErrorCount = 0;
private long deadBytesReceived = 0;
private long deadBytesSent = 0;
public synchronized void addRequestProcessor( RequestInfo rp ) {
processors.add( rp );
}
public synchronized void removeRequestProcessor( RequestInfo rp ) {
if( rp != null ) {
if( deadMaxTime < rp.getMaxTime() )
deadMaxTime = rp.getMaxTime();
deadProcessingTime += rp.getProcessingTime();
deadRequestCount += rp.getRequestCount();
deadErrorCount += rp.getErrorCount();
deadBytesReceived += rp.getBytesReceived();
deadBytesSent += rp.getBytesSent();
processors.remove( rp );
}
}
public synchronized long getMaxTime() {
long maxTime = deadMaxTime;
for (RequestInfo rp : processors) {
if (maxTime < rp.getMaxTime()) {
maxTime=rp.getMaxTime();
}
}
return maxTime;
}
// Used to reset the times
public synchronized void setMaxTime(long maxTime) {
deadMaxTime = maxTime;
for (RequestInfo rp : processors) {
rp.setMaxTime(maxTime);
}
}
public synchronized long getProcessingTime() {
long time = deadProcessingTime;
for (RequestInfo rp : processors) {
time += rp.getProcessingTime();
}
return time;
}
public synchronized void setProcessingTime(long totalTime) {
deadProcessingTime = totalTime;
for (RequestInfo rp : processors) {
rp.setProcessingTime( totalTime );
}
}
public synchronized int getRequestCount() {
int requestCount = deadRequestCount;
for (RequestInfo rp : processors) {
requestCount += rp.getRequestCount();
}
return requestCount;
}
public synchronized void setRequestCount(int requestCount) {
deadRequestCount = requestCount;
for (RequestInfo rp : processors) {
rp.setRequestCount( requestCount );
}
}
public synchronized int getErrorCount() {
int requestCount = deadErrorCount;
for (RequestInfo rp : processors) {
requestCount += rp.getErrorCount();
}
return requestCount;
}
public synchronized void setErrorCount(int errorCount) {
deadErrorCount = errorCount;
for (RequestInfo rp : processors) {
rp.setErrorCount( errorCount);
}
}
public synchronized long getBytesReceived() {
long bytes = deadBytesReceived;
for (RequestInfo rp : processors) {
bytes += rp.getBytesReceived();
}
return bytes;
}
public synchronized void setBytesReceived(long bytesReceived) {
deadBytesReceived = bytesReceived;
for (RequestInfo rp : processors) {
rp.setBytesReceived( bytesReceived );
}
}
public synchronized long getBytesSent() {
long bytes=deadBytesSent;
for (RequestInfo rp : processors) {
bytes += rp.getBytesSent();
}
return bytes;
}
public synchronized void setBytesSent(long bytesSent) {
deadBytesSent = bytesSent;
for (RequestInfo rp : processors) {
rp.setBytesSent( bytesSent );
}
}
public void resetCounters() {
this.setBytesReceived(0);
this.setBytesSent(0);
this.setRequestCount(0);
this.setProcessingTime(0);
this.setMaxTime(0);
this.setErrorCount(0);
}
}

View File

@@ -0,0 +1,264 @@
/*
* 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 javax.management.ObjectName;
/**
* Structure holding the Request and Response objects. It also holds statistical
* informations about request processing and provide management informations
* about the requests being processed.
*
* Each thread uses a Request/Response pair that is recycled on each request.
* This object provides a place to collect global low-level statistics - without
* having to deal with synchronization ( since each thread will have it's own
* RequestProcessorMX ).
*
* @author Costin Manolache
*/
public class RequestInfo {
private RequestGroupInfo global=null;
// ----------------------------------------------------------- Constructors
public RequestInfo( Request req) {
this.req=req;
}
public RequestGroupInfo getGlobalProcessor() {
return global;
}
public void setGlobalProcessor(RequestGroupInfo global) {
if( global != null) {
this.global=global;
global.addRequestProcessor( this );
} else {
if (this.global != null) {
this.global.removeRequestProcessor( this );
this.global = null;
}
}
}
// ----------------------------------------------------- Instance Variables
private final Request req;
private int stage = Constants.STAGE_NEW;
private String workerThreadName;
private ObjectName rpName;
// -------------------- Information about the current request -----------
// This is useful for long-running requests only
public String getMethod() {
return req.method().toString();
}
public String getCurrentUri() {
return req.requestURI().toString();
}
public String getCurrentQueryString() {
return req.queryString().toString();
}
public String getProtocol() {
return req.protocol().toString();
}
public String getVirtualHost() {
return req.serverName().toString();
}
public int getServerPort() {
return req.getServerPort();
}
public String getRemoteAddr() {
req.action(ActionCode.REQ_HOST_ADDR_ATTRIBUTE, null);
return req.remoteAddr().toString();
}
/**
* Obtain the remote address for this connection as reported by an
* intermediate proxy (if any).
*
* @return The remote address for the this connection
*/
public String getRemoteAddrForwarded() {
String remoteAddrProxy = (String) req.getAttribute(Constants.REMOTE_ADDR_ATTRIBUTE);
if (remoteAddrProxy == null) {
return getRemoteAddr();
}
return remoteAddrProxy;
}
public int getContentLength() {
return req.getContentLength();
}
public long getRequestBytesReceived() {
return req.getBytesRead();
}
public long getRequestBytesSent() {
return req.getResponse().getContentWritten();
}
public long getRequestProcessingTime() {
// Not perfect, but good enough to avoid returning strange values due to
// concurrent updates.
long startTime = req.getStartTime();
if (getStage() == org.apache.coyote.Constants.STAGE_ENDED || startTime < 0) {
return 0;
} else {
return System.currentTimeMillis() - startTime;
}
}
// -------------------- Statistical data --------------------
// Collected at the end of each request.
private long bytesSent;
private long bytesReceived;
// Total time = divide by requestCount to get average.
private long processingTime;
// The longest response time for a request
private long maxTime;
// URI of the request that took maxTime
private String maxRequestUri;
private int requestCount;
// number of response codes >= 400
private int errorCount;
//the time of the last request
private long lastRequestProcessingTime = 0;
/** Called by the processor before recycling the request. It'll collect
* statistic information.
*/
void updateCounters() {
bytesReceived+=req.getBytesRead();
bytesSent+=req.getResponse().getContentWritten();
requestCount++;
if( req.getResponse().getStatus() >=400 )
errorCount++;
long t0=req.getStartTime();
long t1=System.currentTimeMillis();
long time=t1-t0;
this.lastRequestProcessingTime = time;
processingTime+=time;
if( maxTime < time ) {
maxTime=time;
maxRequestUri=req.requestURI().toString();
}
}
public int getStage() {
return stage;
}
public void setStage(int stage) {
this.stage = stage;
}
public long getBytesSent() {
return bytesSent;
}
public void setBytesSent(long bytesSent) {
this.bytesSent = bytesSent;
}
public long getBytesReceived() {
return bytesReceived;
}
public void setBytesReceived(long bytesReceived) {
this.bytesReceived = bytesReceived;
}
public long getProcessingTime() {
return processingTime;
}
public void setProcessingTime(long processingTime) {
this.processingTime = processingTime;
}
public long getMaxTime() {
return maxTime;
}
public void setMaxTime(long maxTime) {
this.maxTime = maxTime;
}
public String getMaxRequestUri() {
return maxRequestUri;
}
public void setMaxRequestUri(String maxRequestUri) {
this.maxRequestUri = maxRequestUri;
}
public int getRequestCount() {
return requestCount;
}
public void setRequestCount(int requestCount) {
this.requestCount = requestCount;
}
public int getErrorCount() {
return errorCount;
}
public void setErrorCount(int errorCount) {
this.errorCount = errorCount;
}
public String getWorkerThreadName() {
return workerThreadName;
}
public ObjectName getRpName() {
return rpName;
}
public long getLastRequestProcessingTime() {
return lastRequestProcessingTime;
}
public void setWorkerThreadName(String workerThreadName) {
this.workerThreadName = workerThreadName;
}
public void setRpName(ObjectName rpName) {
this.rpName = rpName;
}
public void setLastRequestProcessingTime(long lastRequestProcessingTime) {
this.lastRequestProcessingTime = lastRequestProcessingTime;
}
}

View File

@@ -0,0 +1,763 @@
/*
* 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.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.WriteListener;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.parser.MediaType;
import org.apache.tomcat.util.res.StringManager;
/**
* Response object.
*
* @author James Duncan Davidson [duncan@eng.sun.com]
* @author Jason Hunter [jch@eng.sun.com]
* @author James Todd [gonzo@eng.sun.com]
* @author Harish Prabandham
* @author Hans Bergsten [hans@gefionsoftware.com]
* @author Remy Maucherat
*/
public final class Response {
private static final StringManager sm = StringManager.getManager(Response.class);
private static final Log log = LogFactory.getLog(Response.class);
// ----------------------------------------------------- Class Variables
/**
* Default locale as mandated by the spec.
*/
private static final Locale DEFAULT_LOCALE = Locale.getDefault();
// ----------------------------------------------------- Instance Variables
/**
* Status code.
*/
int status = 200;
/**
* Status message.
*/
String message = null;
/**
* Response headers.
*/
final MimeHeaders headers = new MimeHeaders();
/**
* Associated output buffer.
*/
OutputBuffer outputBuffer;
/**
* Notes.
*/
final Object notes[] = new Object[Constants.MAX_NOTES];
/**
* Committed flag.
*/
volatile boolean committed = false;
/**
* Action hook.
*/
volatile ActionHook hook;
/**
* HTTP specific fields.
*/
String contentType = null;
String contentLanguage = null;
Charset charset = null;
// Retain the original name used to set the charset so exactly that name is
// used in the ContentType header. Some (arguably non-specification
// compliant) user agents are very particular
String characterEncoding = null;
long contentLength = -1;
private Locale locale = DEFAULT_LOCALE;
// General informations
private long contentWritten = 0;
private long commitTime = -1;
/**
* Holds request error exception.
*/
Exception errorException = null;
/**
* With the introduction of async processing and the possibility of
* non-container threads calling sendError() tracking the current error
* state and ensuring that the correct error page is called becomes more
* complicated. This state attribute helps by tracking the current error
* state and informing callers that attempt to change state if the change
* was successful or if another thread got there first.
*
* <pre>
* The state machine is very simple:
*
* 0 - NONE
* 1 - NOT_REPORTED
* 2 - REPORTED
*
*
* -->---->-- >NONE
* | | |
* | | | setError()
* ^ ^ |
* | | \|/
* | |-<-NOT_REPORTED
* | |
* ^ | report()
* | |
* | \|/
* |----<----REPORTED
* </pre>
*/
private final AtomicInteger errorState = new AtomicInteger(0);
Request req;
// ------------------------------------------------------------- Properties
public Request getRequest() {
return req;
}
public void setRequest( Request req ) {
this.req=req;
}
public void setOutputBuffer(OutputBuffer outputBuffer) {
this.outputBuffer = outputBuffer;
}
public MimeHeaders getMimeHeaders() {
return headers;
}
protected void setHook(ActionHook hook) {
this.hook = hook;
}
// -------------------- Per-Response "notes" --------------------
public final void setNote(int pos, Object value) {
notes[pos] = value;
}
public final Object getNote(int pos) {
return notes[pos];
}
// -------------------- Actions --------------------
public void action(ActionCode actionCode, Object param) {
if (hook != null) {
if (param == null) {
hook.action(actionCode, this);
} else {
hook.action(actionCode, param);
}
}
}
// -------------------- State --------------------
public int getStatus() {
return status;
}
/**
* Set the response status.
*
* @param status The status value to set
*/
public void setStatus(int status) {
this.status = status;
}
/**
* Get the status message.
*
* @return The message associated with the current status
*/
public String getMessage() {
return message;
}
/**
* Set the status message.
*
* @param message The status message to set
*/
public void setMessage(String message) {
this.message = message;
}
public boolean isCommitted() {
return committed;
}
public void setCommitted(boolean v) {
if (v && !this.committed) {
this.commitTime = System.currentTimeMillis();
}
this.committed = v;
}
/**
* Return the time the response was committed (based on System.currentTimeMillis).
*
* @return the time the response was committed
*/
public long getCommitTime() {
return commitTime;
}
// -----------------Error State --------------------
/**
* Set the error Exception that occurred during request processing.
*
* @param ex The exception that occurred
*/
public void setErrorException(Exception ex) {
errorException = ex;
}
/**
* Get the Exception that occurred during request processing.
*
* @return The exception that occurred
*/
public Exception getErrorException() {
return errorException;
}
public boolean isExceptionPresent() {
return ( errorException != null );
}
/**
* Set the error flag.
*
* @return <code>false</code> if the error flag was already set
*/
public boolean setError() {
return errorState.compareAndSet(0, 1);
}
/**
* Error flag accessor.
*
* @return <code>true</code> if the response has encountered an error
*/
public boolean isError() {
return errorState.get() > 0;
}
public boolean isErrorReportRequired() {
return errorState.get() == 1;
}
public boolean setErrorReported() {
return errorState.compareAndSet(1, 2);
}
// -------------------- Methods --------------------
public void reset() throws IllegalStateException {
if (committed) {
throw new IllegalStateException();
}
recycle();
}
// -------------------- Headers --------------------
/**
* Does the response contain the given header.
* <br>
* Warning: This method always returns <code>false</code> for Content-Type
* and Content-Length.
*
* @param name The name of the header of interest
*
* @return {@code true} if the response contains the header.
*/
public boolean containsHeader(String name) {
return headers.getHeader(name) != null;
}
public void setHeader(String name, String value) {
char cc=name.charAt(0);
if( cc=='C' || cc=='c' ) {
if( checkSpecialHeader(name, value) )
return;
}
headers.setValue(name).setString( value);
}
public void addHeader(String name, String value) {
addHeader(name, value, null);
}
public void addHeader(String name, String value, Charset charset) {
char cc=name.charAt(0);
if( cc=='C' || cc=='c' ) {
if( checkSpecialHeader(name, value) )
return;
}
MessageBytes mb = headers.addValue(name);
if (charset != null) {
mb.setCharset(charset);
}
mb.setString(value);
}
/**
* Set internal fields for special header names.
* Called from set/addHeader.
* Return true if the header is special, no need to set the header.
*/
private boolean checkSpecialHeader( String name, String value) {
// XXX Eliminate redundant fields !!!
// ( both header and in special fields )
if( name.equalsIgnoreCase( "Content-Type" ) ) {
setContentType( value );
return true;
}
if( name.equalsIgnoreCase( "Content-Length" ) ) {
try {
long cL=Long.parseLong( value );
setContentLength( cL );
return true;
} catch( NumberFormatException ex ) {
// Do nothing - the spec doesn't have any "throws"
// and the user might know what he's doing
return false;
}
}
return false;
}
/** Signal that we're done with the headers, and body will follow.
* Any implementation needs to notify ContextManager, to allow
* interceptors to fix headers.
*/
public void sendHeaders() {
action(ActionCode.COMMIT, this);
setCommitted(true);
}
// -------------------- I18N --------------------
public Locale getLocale() {
return locale;
}
/**
* Called explicitly by user to set the Content-Language and the default
* encoding.
*
* @param locale The locale to use for this response
*/
public void setLocale(Locale locale) {
if (locale == null) {
return; // throw an exception?
}
// Save the locale for use by getLocale()
this.locale = locale;
// Set the contentLanguage for header output
contentLanguage = locale.toLanguageTag();
}
/**
* Return the content language.
*
* @return The language code for the language currently associated with this
* response
*/
public String getContentLanguage() {
return contentLanguage;
}
/**
* Overrides the character encoding used in the body of the response. This
* method must be called prior to writing output using getWriter().
*
* @param characterEncoding The name of character encoding.
*/
public void setCharacterEncoding(String characterEncoding) {
if (isCommitted()) {
return;
}
if (characterEncoding == null) {
return;
}
try {
this.charset = B2CConverter.getCharset(characterEncoding);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e);
}
this.characterEncoding = characterEncoding;
}
/**
* @return The name of the current encoding
*/
public String getCharacterEncoding() {
return characterEncoding;
}
public Charset getCharset() {
return charset;
}
/**
* Sets the content type.
*
* This method must preserve any response charset that may already have
* been set via a call to response.setContentType(), response.setLocale(),
* or response.setCharacterEncoding().
*
* @param type the content type
*/
public void setContentType(String type) {
if (type == null) {
this.contentType = null;
return;
}
MediaType m = null;
try {
m = MediaType.parseMediaType(new StringReader(type));
} catch (IOException e) {
// Ignore - null test below handles this
}
if (m == null) {
// Invalid - Assume no charset and just pass through whatever
// the user provided.
this.contentType = type;
return;
}
this.contentType = m.toStringNoCharset();
String charsetValue = m.getCharset();
if (charsetValue != null) {
charsetValue = charsetValue.trim();
if (charsetValue.length() > 0) {
try {
charset = B2CConverter.getCharset(charsetValue);
} catch (UnsupportedEncodingException e) {
log.warn(sm.getString("response.encoding.invalid", charsetValue), e);
}
}
}
}
public void setContentTypeNoCharset(String type) {
this.contentType = type;
}
public String getContentType() {
String ret = contentType;
if (ret != null && charset != null) {
ret = ret + ";charset=" + characterEncoding;
}
return ret;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public int getContentLength() {
long length = getContentLengthLong();
if (length < Integer.MAX_VALUE) {
return (int) length;
}
return -1;
}
public long getContentLengthLong() {
return contentLength;
}
/**
* Write a chunk of bytes.
*
* @param chunk The bytes to write
*
* @throws IOException If an I/O error occurs during the write
*
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
public void doWrite(ByteChunk chunk) throws IOException {
outputBuffer.doWrite(chunk);
contentWritten+=chunk.getLength();
}
/**
* Write a chunk of bytes.
*
* @param chunk The ByteBuffer to write
*
* @throws IOException If an I/O error occurs during the write
*/
public void doWrite(ByteBuffer chunk) throws IOException {
int len = chunk.remaining();
outputBuffer.doWrite(chunk);
contentWritten += len - chunk.remaining();
}
// --------------------
public void recycle() {
contentType = null;
contentLanguage = null;
locale = DEFAULT_LOCALE;
charset = null;
characterEncoding = null;
contentLength = -1;
status = 200;
message = null;
committed = false;
commitTime = -1;
errorException = null;
errorState.set(0);
headers.clear();
// Servlet 3.1 non-blocking write listener
listener = null;
fireListener = false;
registeredForWrite = false;
// update counters
contentWritten=0;
}
/**
* Bytes written by application - i.e. before compression, chunking, etc.
*
* @return The total number of bytes written to the response by the
* application. This will not be the number of bytes written to the
* network which may be more or less than this value.
*/
public long getContentWritten() {
return contentWritten;
}
/**
* Bytes written to socket - i.e. after compression, chunking, etc.
*
* @param flush Should any remaining bytes be flushed before returning the
* total? If {@code false} bytes remaining in the buffer will
* not be included in the returned value
*
* @return The total number of bytes written to the socket for this response
*/
public long getBytesWritten(boolean flush) {
if (flush) {
action(ActionCode.CLIENT_FLUSH, this);
}
return outputBuffer.getBytesWritten();
}
/*
* State for non-blocking output is maintained here as it is the one point
* easily reachable from the CoyoteOutputStream and the Processor which both
* need access to state.
*/
volatile WriteListener listener;
private boolean fireListener = false;
private boolean registeredForWrite = false;
private final Object nonBlockingStateLock = new Object();
public WriteListener getWriteListener() {
return listener;
}
public void setWriteListener(WriteListener listener) {
if (listener == null) {
throw new NullPointerException(
sm.getString("response.nullWriteListener"));
}
if (getWriteListener() != null) {
throw new IllegalStateException(
sm.getString("response.writeListenerSet"));
}
// Note: This class is not used for HTTP upgrade so only need to test
// for async
AtomicBoolean result = new AtomicBoolean(false);
action(ActionCode.ASYNC_IS_ASYNC, result);
if (!result.get()) {
throw new IllegalStateException(
sm.getString("response.notAsync"));
}
this.listener = listener;
// The container is responsible for the first call to
// listener.onWritePossible(). If isReady() returns true, the container
// needs to call listener.onWritePossible() from a new thread. If
// isReady() returns false, the socket will be registered for write and
// the container will call listener.onWritePossible() once data can be
// written.
if (isReady()) {
synchronized (nonBlockingStateLock) {
// Ensure we don't get multiple write registrations if
// ServletOutputStream.isReady() returns false during a call to
// onDataAvailable()
registeredForWrite = true;
// Need to set the fireListener flag otherwise when the
// container tries to trigger onWritePossible, nothing will
// happen
fireListener = true;
}
action(ActionCode.DISPATCH_WRITE, null);
if (!ContainerThreadMarker.isContainerThread()) {
// Not on a container thread so need to execute the dispatch
action(ActionCode.DISPATCH_EXECUTE, null);
}
}
}
public boolean isReady() {
if (listener == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("response.notNonBlocking"));
}
return false;
}
// Assume write is not possible
boolean ready = false;
synchronized (nonBlockingStateLock) {
if (registeredForWrite) {
fireListener = true;
return false;
}
ready = checkRegisterForWrite();
fireListener = !ready;
}
return ready;
}
public boolean checkRegisterForWrite() {
AtomicBoolean ready = new AtomicBoolean(false);
synchronized (nonBlockingStateLock) {
if (!registeredForWrite) {
action(ActionCode.NB_WRITE_INTEREST, ready);
registeredForWrite = !ready.get();
}
}
return ready.get();
}
public void onWritePossible() throws IOException {
// Any buffered data left over from a previous non-blocking write is
// written in the Processor so if this point is reached the app is able
// to write data.
boolean fire = false;
synchronized (nonBlockingStateLock) {
registeredForWrite = false;
if (fireListener) {
fireListener = false;
fire = true;
}
}
if (fire) {
listener.onWritePossible();
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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 org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.tomcat.util.net.SocketWrapperBase;
public interface UpgradeProtocol {
/**
* @param isSSLEnabled Is this for a connector that is configured to support
* TLS. Some protocols (e.g. HTTP/2) only support HTTP
* upgrade over non-secure connections.
* @return The name that clients will use to request an upgrade to this
* protocol via an HTTP/1.1 upgrade request or <code>null</code> if
* upgrade via an HTTP/1.1 upgrade request is not supported.
*/
public String getHttpUpgradeName(boolean isSSLEnabled);
/**
* @return The byte sequence as listed in the IANA registry for this
* protocol or <code>null</code> if upgrade via ALPN is not
* supported.
*/
public byte[] getAlpnIdentifier();
/**
* @return The name of the protocol as listed in the IANA registry if and
* only if {@link #getAlpnIdentifier()} returns the UTF-8 encoding
* of this name. If {@link #getAlpnIdentifier()} returns some other
* byte sequence, then this method returns the empty string. If
* upgrade via ALPN is not supported then <code>null</code> is
* returned.
*/
/*
* Implementation note: If Tomcat ever supports ALPN for a protocol where
* the identifier is not the UTF-8 encoding of the name
* then some refactoring is going to be required.
*
* Implementation note: Tomcat assumes that the UTF-8 encoding of this name
* will not exceed 255 bytes. Tomcat's behaviour if
* longer names are used is undefined.
*/
public String getAlpnName();
/**
* @param socketWrapper The socketWrapper for the connection that requires
* a processor
* @param adapter The Adapter instance that provides access to the standard
* Engine/Host/Context/Wrapper processing chain
*
* @return A processor instance for processing a connection using this
* protocol.
*/
public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter);
/**
* @param adapter The Adapter to use to configure the new upgrade handler
* @param request A copy (may be incomplete) of the request that triggered
* the upgrade
*
* @return An instance of the HTTP upgrade handler for this protocol
*/
public InternalHttpUpgradeHandler getInternalUpgradeHandler(Adapter adapter, Request request);
/**
* Allows the implementation to examine the request and accept or reject it
* based on what it finds.
*
* @param request The request that included an upgrade header for this
* protocol
*
* @return <code>true</code> if the request is accepted, otherwise
* <code>false</code>
*/
public boolean accept(Request request);
}

View File

@@ -0,0 +1,53 @@
/*
* 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 javax.servlet.http.HttpUpgradeHandler;
import org.apache.tomcat.ContextBind;
import org.apache.tomcat.InstanceManager;
/**
* Token used during the upgrade process.
*/
public final class UpgradeToken {
private final ContextBind contextBind;
private final HttpUpgradeHandler httpUpgradeHandler;
private final InstanceManager instanceManager;
public UpgradeToken(HttpUpgradeHandler httpUpgradeHandler,
ContextBind contextBind, InstanceManager instanceManager) {
this.contextBind = contextBind;
this.httpUpgradeHandler = httpUpgradeHandler;
this.instanceManager = instanceManager;
}
public final ContextBind getContextBind() {
return contextBind;
}
public final HttpUpgradeHandler getHttpUpgradeHandler() {
return httpUpgradeHandler;
}
public final InstanceManager getInstanceManager() {
return instanceManager;
}
}

View File

@@ -0,0 +1,279 @@
/*
* 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.ajp;
import java.net.InetAddress;
import java.util.regex.Pattern;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.Processor;
import org.apache.coyote.UpgradeProtocol;
import org.apache.coyote.UpgradeToken;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
/**
* The is the base implementation for the AJP protocol handlers. Implementations
* typically extend this base class rather than implement {@link
* org.apache.coyote.ProtocolHandler}. All of the implementations that ship with
* Tomcat are implemented this way.
*
* @param <S> The type of socket used by the implementation
*/
public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(AbstractAjpProtocol.class);
public AbstractAjpProtocol(AbstractEndpoint<S> endpoint) {
super(endpoint);
setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
// AJP does not use Send File
getEndpoint().setUseSendfile(false);
// AJP listens on loopback by default
getEndpoint().setAddress(InetAddress.getLoopbackAddress());
ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
setHandler(cHandler);
getEndpoint().setHandler(cHandler);
}
@Override
protected String getProtocolName() {
return "Ajp";
}
/**
* {@inheritDoc}
*
* Overridden to make getter accessible to other classes in this package.
*/
@Override
protected AbstractEndpoint<S> getEndpoint() {
return super.getEndpoint();
}
/**
* {@inheritDoc}
*
* AJP does not support protocol negotiation so this always returns null.
*/
@Override
protected UpgradeProtocol getNegotiatedProtocol(String name) {
return null;
}
/**
* {@inheritDoc}
*
* AJP does not support protocol upgrade so this always returns null.
*/
@Override
protected UpgradeProtocol getUpgradeProtocol(String name) {
return null;
}
// ------------------------------------------------- AJP specific properties
// ------------------------------------------ managed in the ProtocolHandler
protected boolean ajpFlush = true;
public boolean getAjpFlush() { return ajpFlush; }
/**
* Configure whether to aend an AJP flush packet when flushing. A flush
* packet is a zero byte AJP13 SEND_BODY_CHUNK packet. mod_jk and
* mod_proxy_ajp interpret this as a request to flush data to the client.
* AJP always does flush at the and of the response, so if it is not
* important, that the packets get streamed up to the client, do not use
* extra flush packets. For compatibility and to stay on the safe side,
* flush packets are enabled by default.
*
* @param ajpFlush The new flush setting
*/
public void setAjpFlush(boolean ajpFlush) {
this.ajpFlush = ajpFlush;
}
private boolean tomcatAuthentication = true;
/**
* Should authentication be done in the native web server layer,
* or in the Servlet container ?
*
* @return {@code true} if authentication should be performed by Tomcat,
* otherwise {@code false}
*/
public boolean getTomcatAuthentication() { return tomcatAuthentication; }
public void setTomcatAuthentication(boolean tomcatAuthentication) {
this.tomcatAuthentication = tomcatAuthentication;
}
private boolean tomcatAuthorization = false;
/**
* Should authentication be done in the native web server layer and
* authorization in the Servlet container?
*
* @return {@code true} if authorization should be performed by Tomcat,
* otherwise {@code false}
*/
public boolean getTomcatAuthorization() { return tomcatAuthorization; }
public void setTomcatAuthorization(boolean tomcatAuthorization) {
this.tomcatAuthorization = tomcatAuthorization;
}
private String secret = null;
/**
* Set the secret that must be included with every request.
*
* @param secret The required secret
*/
public void setSecret(String secret) {
this.secret = secret;
}
protected String getSecret() {
return secret;
}
/**
* Set the required secret that must be included with every request.
*
* @param requiredSecret The required secret
*
* @deprecated Replaced by {@link #setSecret(String)}.
* Will be removed in Tomcat 11 onwards
*/
@Deprecated
public void setRequiredSecret(String requiredSecret) {
setSecret(requiredSecret);
}
/**
* @return The current secret
*
* @deprecated Replaced by {@link #getSecret()}.
* Will be removed in Tomcat 11 onwards
*/
@Deprecated
protected String getRequiredSecret() {
return getSecret();
}
private boolean secretRequired = true;
public void setSecretRequired(boolean secretRequired) {
this.secretRequired = secretRequired;
}
public boolean getSecretRequired() {
return secretRequired;
}
private Pattern allowedRequestAttributesPattern;
public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
this.allowedRequestAttributesPattern = Pattern.compile(allowedRequestAttributesPattern);
}
public String getAllowedRequestAttributesPattern() {
return allowedRequestAttributesPattern.pattern();
}
protected Pattern getAllowedRequestAttributesPatternInternal() {
return allowedRequestAttributesPattern;
}
/**
* AJP packet size.
*/
private int packetSize = Constants.MAX_PACKET_SIZE;
public int getPacketSize() { return packetSize; }
public void setPacketSize(int packetSize) {
if(packetSize < Constants.MAX_PACKET_SIZE) {
this.packetSize = Constants.MAX_PACKET_SIZE;
} else {
this.packetSize = packetSize;
}
}
// --------------------------------------------- SSL is not supported in AJP
@Override
public void addSslHostConfig(SSLHostConfig sslHostConfig) {
getLog().warn(sm.getString("ajpprotocol.noSSL", sslHostConfig.getHostName()));
}
@Override
public SSLHostConfig[] findSslHostConfigs() {
return new SSLHostConfig[0];
}
@Override
public void addUpgradeProtocol(UpgradeProtocol upgradeProtocol) {
getLog().warn(sm.getString("ajpprotocol.noUpgrade", upgradeProtocol.getClass().getName()));
}
@Override
public UpgradeProtocol[] findUpgradeProtocols() {
return new UpgradeProtocol[0];
}
@SuppressWarnings("deprecation")
@Override
protected Processor createProcessor() {
AjpProcessor processor = new AjpProcessor(getPacketSize(), getEndpoint());
processor.setAdapter(getAdapter());
processor.setAjpFlush(getAjpFlush());
processor.setTomcatAuthentication(getTomcatAuthentication());
processor.setTomcatAuthorization(getTomcatAuthorization());
processor.setSecret(secret);
processor.setKeepAliveTimeout(getKeepAliveTimeout());
processor.setClientCertProvider(getClientCertProvider());
processor.setSendReasonPhrase(getSendReasonPhrase());
processor.setAllowedRequestAttributesPattern(getAllowedRequestAttributesPatternInternal());
return processor;
}
@Override
protected Processor createUpgradeProcessor(SocketWrapperBase<?> socket,
UpgradeToken upgradeToken) {
throw new IllegalStateException(sm.getString("ajpprotocol.noUpgradeHandler",
upgradeToken.getHttpUpgradeHandler().getClass().getName()));
}
@Override
public void start() throws Exception {
if (getSecretRequired()) {
String secret = getSecret();
if (secret == null || secret.length() == 0) {
throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
}
}
super.start();
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.ajp;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.AprEndpoint;
/**
* This the APR/native based protocol handler implementation for AJP.
*/
public class AjpAprProtocol extends AbstractAjpProtocol<Long> {
private static final Log log = LogFactory.getLog(AjpAprProtocol.class);
@Override
protected Log getLog() { return log; }
@Override
public boolean isAprRequired() {
// Override since this protocol implementation requires the APR/native
// library
return true;
}
// ------------------------------------------------------------ Constructor
public AjpAprProtocol() {
super(new AprEndpoint());
}
// --------------------------------------------------------- Public Methods
public int getPollTime() { return ((AprEndpoint)getEndpoint()).getPollTime(); }
public void setPollTime(int pollTime) { ((AprEndpoint)getEndpoint()).setPollTime(pollTime); }
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
return "ajp-apr";
}
}

View File

@@ -0,0 +1,426 @@
/*
* 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.ajp;
import java.nio.ByteBuffer;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.res.StringManager;
/**
* A single packet for communication between the web server and the
* container. Designed to be reused many times with no creation of
* garbage. Understands the format of data types for these packets.
* Can be used (somewhat confusingly) for both incoming and outgoing
* packets.
*
* @author Henri Gomez
* @author Dan Milstein
* @author Keith Wannamaker
* @author Kevin Seguin
* @author Costin Manolache
*/
public class AjpMessage {
private static final Log log = LogFactory.getLog(AjpMessage.class);
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(AjpMessage.class);
// ------------------------------------------------------------ Constructor
public AjpMessage(int packetSize) {
buf = new byte[packetSize];
}
// ----------------------------------------------------- Instance Variables
/**
* Fixed size buffer.
*/
protected final byte buf[];
/**
* The current read or write position in the buffer.
*/
protected int pos;
/**
* This actually means different things depending on whether the
* packet is read or write. For read, it's the length of the
* payload (excluding the header). For write, it's the length of
* the packet as a whole (counting the header). Oh, well.
*/
protected int len;
// --------------------------------------------------------- Public Methods
/**
* Prepare this packet for accumulating a message from the container to
* the web server. Set the write position to just after the header
* (but leave the length unwritten, because it is as yet unknown).
*/
public void reset() {
len = 4;
pos = 4;
}
/**
* For a packet to be sent to the web server, finish the process of
* accumulating data and write the length of the data payload into
* the header.
*/
public void end() {
len = pos;
int dLen = len - 4;
buf[0] = (byte) 0x41;
buf[1] = (byte) 0x42;
buf[2] = (byte) ((dLen>>>8) & 0xFF);
buf[3] = (byte) (dLen & 0xFF);
}
/**
* Return the underlying byte buffer.
*
* @return The buffer
*/
public byte[] getBuffer() {
return buf;
}
/**
* Return the current message length.
*
* @return For read, it's the length of the payload (excluding the header).
* For write, it's the length of the packet as a whole (counting the
* header).
*/
public int getLen() {
return len;
}
/**
* Add a short integer (2 bytes) to the message.
*
* @param val The integer to append
*/
public void appendInt(int val) {
buf[pos++] = (byte) ((val >>> 8) & 0xFF);
buf[pos++] = (byte) (val & 0xFF);
}
/**
* Append a byte (1 byte) to the message.
*
* @param val The byte value to append
*/
public void appendByte(int val) {
buf[pos++] = (byte) val;
}
/**
* Write a MessageBytes out at the current write position. A null
* MessageBytes is encoded as a string with length 0.
*
* @param mb The data to write
*/
public void appendBytes(MessageBytes mb) {
if (mb == null) {
log.error(sm.getString("ajpmessage.null"),
new NullPointerException());
appendInt(0);
appendByte(0);
return;
}
if (mb.getType() != MessageBytes.T_BYTES) {
mb.toBytes();
ByteChunk bc = mb.getByteChunk();
// Need to filter out CTLs excluding TAB. ISO-8859-1 and UTF-8
// values will be OK. Strings using other encodings may be
// corrupted.
byte[] buffer = bc.getBuffer();
for (int i = bc.getOffset(); i < bc.getLength(); i++) {
// byte values are signed i.e. -128 to 127
// The values are used unsigned. 0 to 31 are CTLs so they are
// filtered (apart from TAB which is 9). 127 is a control (DEL).
// The values 128 to 255 are all OK. Converting those to signed
// gives -128 to -1.
if ((buffer[i] > -1 && buffer[i] <= 31 && buffer[i] != 9) ||
buffer[i] == 127) {
buffer[i] = ' ';
}
}
}
appendByteChunk(mb.getByteChunk());
}
/**
* Write a ByteChunk out at the current write position. A null ByteChunk is
* encoded as a string with length 0.
*
* @param bc The data to write
*/
public void appendByteChunk(ByteChunk bc) {
if (bc == null) {
log.error(sm.getString("ajpmessage.null"),
new NullPointerException());
appendInt(0);
appendByte(0);
return;
}
appendBytes(bc.getBytes(), bc.getStart(), bc.getLength());
}
/**
* Copy a chunk of bytes into the packet, starting at the current
* write position. The chunk of bytes is encoded with the length
* in two bytes first, then the data itself, and finally a
* terminating \0 (which is <B>not</B> included in the encoded
* length).
*
* @param b The array from which to copy bytes.
* @param off The offset into the array at which to start copying
* @param numBytes The number of bytes to copy.
*/
public void appendBytes(byte[] b, int off, int numBytes) {
if (checkOverflow(numBytes)) {
return;
}
appendInt(numBytes);
System.arraycopy(b, off, buf, pos, numBytes);
pos += numBytes;
appendByte(0);
}
/**
* Copy a chunk of bytes into the packet, starting at the current
* write position. The chunk of bytes is encoded with the length
* in two bytes first, then the data itself, and finally a
* terminating \0 (which is <B>not</B> included in the encoded
* length).
*
* @param b The ByteBuffer from which to copy bytes.
*/
public void appendBytes(ByteBuffer b) {
int numBytes = b.remaining();
if (checkOverflow(numBytes)) {
return;
}
appendInt(numBytes);
b.get(buf, pos, numBytes);
pos += numBytes;
appendByte(0);
}
private boolean checkOverflow(int numBytes) {
if (pos + numBytes + 3 > buf.length) {
log.error(sm.getString("ajpmessage.overflow", "" + numBytes, "" + pos),
new ArrayIndexOutOfBoundsException());
if (log.isDebugEnabled()) {
dump("Overflow/coBytes");
}
return true;
}
return false;
}
/**
* Read an integer from packet, and advance the read position past
* it. Integers are encoded as two unsigned bytes with the
* high-order byte first, and, as far as I can tell, in
* little-endian order within each byte.
*
* @return The integer value read from the message
*/
public int getInt() {
int b1 = buf[pos++] & 0xFF;
int b2 = buf[pos++] & 0xFF;
validatePos(pos);
return (b1<<8) + b2;
}
public int peekInt() {
validatePos(pos + 2);
int b1 = buf[pos] & 0xFF;
int b2 = buf[pos+1] & 0xFF;
return (b1<<8) + b2;
}
public byte getByte() {
byte res = buf[pos++];
validatePos(pos);
return res;
}
public void getBytes(MessageBytes mb) {
doGetBytes(mb, true);
}
public void getBodyBytes(MessageBytes mb) {
doGetBytes(mb, false);
}
private void doGetBytes(MessageBytes mb, boolean terminated) {
int length = getInt();
if ((length == 0xFFFF) || (length == -1)) {
mb.recycle();
return;
}
if (terminated) {
validatePos(pos + length + 1);
} else {
validatePos(pos + length);
}
mb.setBytes(buf, pos, length);
mb.getCharChunk().recycle(); // not valid anymore
pos += length;
if (terminated) {
pos++; // Skip the terminating \0
}
}
/**
* Read a 32 bits integer from packet, and advance the read position past
* it. Integers are encoded as four unsigned bytes with the
* high-order byte first, and, as far as I can tell, in
* little-endian order within each byte.
*
* @return The long value read from the message
*/
public int getLongInt() {
int b1 = buf[pos++] & 0xFF; // No swap, Java order
b1 <<= 8;
b1 |= (buf[pos++] & 0xFF);
b1 <<= 8;
b1 |= (buf[pos++] & 0xFF);
b1 <<=8;
b1 |= (buf[pos++] & 0xFF);
validatePos(pos);
return b1;
}
public int processHeader(boolean toContainer) {
pos = 0;
int mark = getInt();
len = getInt();
// Verify message signature
if ((toContainer && mark != 0x1234) ||
(!toContainer && mark != 0x4142)) {
log.error(sm.getString("ajpmessage.invalid", "" + mark));
if (log.isDebugEnabled()) {
dump("In");
}
return -1;
}
if (log.isDebugEnabled()) {
log.debug("Received " + len + " " + buf[0]);
}
return len;
}
private void dump(String prefix) {
if (log.isDebugEnabled()) {
log.debug(prefix + ": " + HexUtils.toHexString(buf) + " " + pos +"/" + (len + 4));
}
int max = pos;
if (len + 4 > pos)
max = len+4;
if (max > 1000)
max = 1000;
if (log.isDebugEnabled()) {
for (int j = 0; j < max; j += 16) {
log.debug(hexLine(buf, j, len));
}
}
}
private void validatePos(int posToTest) {
if (posToTest > len + 4) {
// Trying to read data beyond the end of the AJP message
throw new ArrayIndexOutOfBoundsException(sm.getString(
"ajpMessage.invalidPos", Integer.valueOf(posToTest)));
}
}
// ------------------------------------------------------ Protected Methods
protected static String hexLine(byte buf[], int start, int len) {
StringBuilder sb = new StringBuilder();
for (int i = start; i < start + 16 ; i++) {
if (i < len + 4) {
sb.append(hex(buf[i]) + " ");
} else {
sb.append(" ");
}
}
sb.append(" | ");
for (int i = start; i < start + 16 && i < len + 4; i++) {
if (!Character.isISOControl((char) buf[i])) {
sb.append(Character.valueOf((char) buf[i]));
} else {
sb.append(".");
}
}
return sb.toString();
}
protected static String hex(int x) {
String h = Integer.toHexString(x);
if (h.length() == 1) {
h = "0" + h;
}
return h.substring(h.length() - 2);
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.ajp;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.Nio2Channel;
import org.apache.tomcat.util.net.Nio2Endpoint;
/**
* This the NIO2 based protocol handler implementation for AJP.
*/
public class AjpNio2Protocol extends AbstractAjpProtocol<Nio2Channel> {
private static final Log log = LogFactory.getLog(AjpNio2Protocol.class);
@Override
protected Log getLog() { return log; }
// ------------------------------------------------------------ Constructor
public AjpNio2Protocol() {
super(new Nio2Endpoint());
}
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
return "ajp-nio2";
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.ajp;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
/**
* This the NIO based protocol handler implementation for AJP.
*/
public class AjpNioProtocol extends AbstractAjpProtocol<NioChannel> {
private static final Log log = LogFactory.getLog(AjpNioProtocol.class);
@Override
protected Log getLog() { return log; }
// ------------------------------------------------------------ Constructor
public AjpNioProtocol() {
super(new NioEndpoint());
}
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
return "ajp-nio";
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
/*
* 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.ajp;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* @deprecated This class will be removed in Tomcat 9.
*/
@Deprecated
public class AjpProtocol extends AjpNioProtocol {
private static final Log log = LogFactory.getLog(AjpProtocol.class);
private static final StringManager sm = StringManager.getManager(AjpProtocol.class);
public AjpProtocol() {
super();
log.warn(sm.getString("ajpprotocol.noBio"));
}
}

View File

@@ -0,0 +1,240 @@
/*
* 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.ajp;
import java.util.Hashtable;
/**
* Constants.
*
* @author Remy Maucherat
*/
public final class Constants {
public static final int DEFAULT_CONNECTION_TIMEOUT = -1;
// Prefix codes for message types from server to container
public static final byte JK_AJP13_FORWARD_REQUEST = 2;
public static final byte JK_AJP13_SHUTDOWN = 7; // XXX Unused
public static final byte JK_AJP13_PING_REQUEST = 8; // XXX Unused
public static final byte JK_AJP13_CPING_REQUEST = 10;
// Prefix codes for message types from container to server
public static final byte JK_AJP13_SEND_BODY_CHUNK = 3;
public static final byte JK_AJP13_SEND_HEADERS = 4;
public static final byte JK_AJP13_END_RESPONSE = 5;
public static final byte JK_AJP13_GET_BODY_CHUNK = 6;
public static final byte JK_AJP13_CPONG_REPLY = 9;
// Integer codes for common response header strings
public static final int SC_RESP_CONTENT_TYPE = 0xA001;
public static final int SC_RESP_CONTENT_LANGUAGE = 0xA002;
public static final int SC_RESP_CONTENT_LENGTH = 0xA003;
public static final int SC_RESP_DATE = 0xA004;
public static final int SC_RESP_LAST_MODIFIED = 0xA005;
public static final int SC_RESP_LOCATION = 0xA006;
public static final int SC_RESP_SET_COOKIE = 0xA007;
public static final int SC_RESP_SET_COOKIE2 = 0xA008;
public static final int SC_RESP_SERVLET_ENGINE = 0xA009;
public static final int SC_RESP_STATUS = 0xA00A;
public static final int SC_RESP_WWW_AUTHENTICATE = 0xA00B;
public static final int SC_RESP_AJP13_MAX = 11;
// Integer codes for common (optional) request attribute names
public static final byte SC_A_CONTEXT = 1; // XXX Unused
public static final byte SC_A_SERVLET_PATH = 2; // XXX Unused
public static final byte SC_A_REMOTE_USER = 3;
public static final byte SC_A_AUTH_TYPE = 4;
public static final byte SC_A_QUERY_STRING = 5;
public static final byte SC_A_JVM_ROUTE = 6;
public static final byte SC_A_SSL_CERT = 7;
public static final byte SC_A_SSL_CIPHER = 8;
public static final byte SC_A_SSL_SESSION = 9;
public static final byte SC_A_SSL_KEY_SIZE = 11;
public static final byte SC_A_SECRET = 12;
public static final byte SC_A_STORED_METHOD = 13;
// Used for attributes which are not in the list above
public static final byte SC_A_REQ_ATTRIBUTE = 10;
/**
* AJP private request attributes
*/
public static final String SC_A_REQ_LOCAL_ADDR = "AJP_LOCAL_ADDR";
public static final String SC_A_REQ_REMOTE_PORT = "AJP_REMOTE_PORT";
public static final String SC_A_SSL_PROTOCOL = "AJP_SSL_PROTOCOL";
// Terminates list of attributes
public static final byte SC_A_ARE_DONE = (byte)0xFF;
// Ajp13 specific - needs refactoring for the new model
/**
* Default maximum total byte size for an AJP packet
*/
public static final int MAX_PACKET_SIZE = 8192;
/**
* Size of basic packet header
*/
public static final int H_SIZE = 4;
/**
* Size of the header metadata
*/
public static final int READ_HEAD_LEN = 6;
public static final int SEND_HEAD_LEN = 8;
/**
* Default maximum size of data that can be sent in one packet
*/
public static final int MAX_READ_SIZE = MAX_PACKET_SIZE - READ_HEAD_LEN;
public static final int MAX_SEND_SIZE = MAX_PACKET_SIZE - SEND_HEAD_LEN;
// Translates integer codes to names of HTTP methods
private static final String [] methodTransArray = {
"OPTIONS",
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
"TRACE",
"PROPFIND",
"PROPPATCH",
"MKCOL",
"COPY",
"MOVE",
"LOCK",
"UNLOCK",
"ACL",
"REPORT",
"VERSION-CONTROL",
"CHECKIN",
"CHECKOUT",
"UNCHECKOUT",
"SEARCH",
"MKWORKSPACE",
"UPDATE",
"LABEL",
"MERGE",
"BASELINE-CONTROL",
"MKACTIVITY"
};
/**
* Converts an AJP coded HTTP method to the method name.
* @param code the coded value
* @return the string value of the method
*/
public static final String getMethodForCode(final int code) {
return methodTransArray[code];
}
public static final int SC_M_JK_STORED = (byte) 0xFF;
// id's for common request headers
public static final int SC_REQ_ACCEPT = 1;
public static final int SC_REQ_ACCEPT_CHARSET = 2;
public static final int SC_REQ_ACCEPT_ENCODING = 3;
public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
public static final int SC_REQ_AUTHORIZATION = 5;
public static final int SC_REQ_CONNECTION = 6;
public static final int SC_REQ_CONTENT_TYPE = 7;
public static final int SC_REQ_CONTENT_LENGTH = 8;
public static final int SC_REQ_COOKIE = 9;
public static final int SC_REQ_COOKIE2 = 10;
public static final int SC_REQ_HOST = 11;
public static final int SC_REQ_PRAGMA = 12;
public static final int SC_REQ_REFERER = 13;
public static final int SC_REQ_USER_AGENT = 14;
// Translates integer codes to request header names
private static final String [] headerTransArray = {
"accept",
"accept-charset",
"accept-encoding",
"accept-language",
"authorization",
"connection",
"content-type",
"content-length",
"cookie",
"cookie2",
"host",
"pragma",
"referer",
"user-agent"
};
/**
* Converts an AJP coded HTTP request header to the header name.
* @param code the coded value
* @return the string value of the header name
*/
public static final String getHeaderForCode(final int code) {
return headerTransArray[code];
}
// Translates integer codes to response header names
private static final String [] responseTransArray = {
"Content-Type",
"Content-Language",
"Content-Length",
"Date",
"Last-Modified",
"Location",
"Set-Cookie",
"Set-Cookie2",
"Servlet-Engine",
"Status",
"WWW-Authenticate"
};
/**
* Converts an AJP coded response header name to the HTTP response header name.
* @param code the coded value
* @return the string value of the header
*/
public static final String getResponseHeaderForCode(final int code) {
return responseTransArray[code];
}
private static final Hashtable<String,Integer> responseTransHash =
new Hashtable<>(20);
static {
try {
int i;
for (i = 0; i < SC_RESP_AJP13_MAX; i++) {
responseTransHash.put(getResponseHeaderForCode(i),
Integer.valueOf(0xA001 + i));
}
}
catch (Exception e) {
// Do nothing
}
}
public static final int getResponseAjpIndex(String header)
{
Integer i = responseTransHash.get(header);
if (i == null)
return 0;
else
return i.intValue();
}
}

View File

@@ -0,0 +1,37 @@
# 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.
ajpMessage.invalidPos=Requested read of bytes at position [{0}] which is beyond the end of the AJP message
ajpmessage.invalid=Invalid message received with signature [{0}]
ajpmessage.invalidLength=Invalid message received with length [{0}]
ajpmessage.null=Cannot append null value
ajpmessage.overflow=Overflow error for buffer adding [{0}] bytes at position [{1}]
ajpnioprotocol.releaseEnd=Done iterating through our connections to release a socket channel [{0}] released [{1}]
ajpnioprotocol.releaseStart=Iterating through our connections to release a socket channel [{0}]
ajpprocessor.certs.fail=Certificate conversion failed
ajpprocessor.header.error=Header message parsing failed
ajpprocessor.header.tooLong=Header message of length [{0}] received but the packetSize is only [{1}]
ajpprocessor.readtimeout=Timeout attempting to read data from the socket
ajpprocessor.request.prepare=Error preparing request
ajpprocessor.request.process=Error processing request
ajpprotocol.noBio=The AJP BIO connector has been removed in Tomcat 8.5.x onwards. The AJP BIO connector configuration has been automatically switched to use the AJP NIO connector instead.
ajpprotocol.noSSL=SSL is not supported with AJP. The SSL host configuration for [{0}] was ignored
ajpprotocol.nosecret=The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
ajpprotocol.noUpgrade=Upgrade is not supported with AJP. The UpgradeProtocol configuration for [{0}] was ignored
ajpprotocol.noUpgradeHandler=Upgrade is not supported with AJP. The HttpUpgradeHandler [{0}] can not be processed

View File

@@ -0,0 +1,18 @@
# 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.
ajpmessage.overflow=Überlauffehler im Puffer beim Hinzufügen von [{0}] Bytes an Position [{1}]
ajpprotocol.noUpgrade=Upgrade wird von AJP nicht unterstützt. Die Konfiguration UpgradeProtocol für [{0}] wurde ignoriert

View File

@@ -0,0 +1,25 @@
# 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.
ajpmessage.invalid=Mensaje inválido recibido con firma [{0}]
ajpmessage.null=No puedo añadir valor nulo
ajpmessage.overflow=Error de desbordamiento en búfer al añadir [{0}] bytes en posición [{1}]
ajpprocessor.certs.fail=Fallo en conversión de Certificado
ajpprocessor.header.error=Fallo en análisis de mensaje de cabecera
ajpprocessor.request.prepare=Error preparando requerimiento
ajpprocessor.request.process=Error procesando requerimiento
ajpprotocol.noUpgrade=Actualización (upgrade) no esta soportada para AJP. Se ha ignorado la configuración UpgradeProtocol para [{0}]

View File

@@ -0,0 +1,32 @@
# 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.
ajpMessage.invalidPos=Une lecture d''octets à la position [{0}] a été demandée ce qui est au-delà de la fin du message AJP
ajpmessage.invalid=Message invalide reçu avec la signature [{0}]
ajpmessage.invalidLength=Message invalide reçu avec une longueur [{0}]
ajpmessage.null=Impossible d'ajouter une valeur nulle.
ajpmessage.overflow=Débordement du tampon en ajoutant [{0}] octets à la position [{1}]
ajpprocessor.certs.fail=La conversion du certificat a échouée
ajpprocessor.header.error=Erreur de traitement du message d'en-tête
ajpprocessor.header.tooLong=Un en-tête de message de taille [{0}] a été reçu mais la packtSize est de seulement [{1}]
ajpprocessor.readtimeout=Timeout lors de la lecture de données sur le socket
ajpprocessor.request.prepare=Erreur lors de la préparation de la requête
ajpprocessor.request.process=Erreur de traitement de la requête
ajpprotocol.noSSL=SSL n''est pas supporté par AJP, la configuration de l''hôte SSL pour [{0}] a été ignorée
ajpprotocol.noUpgrade=L''upgrade n''est pas supporté par AJP. La configuration UpgradeProtocol pour [{0}] a été ignorée
ajpprotocol.noUpgradeHandler=AJP ne supporte pas la mise à niveau (upgrade) de HTTP/1.1, le HttpUpgradeHandler [{0}] ne peut pas être utilisé

View File

@@ -0,0 +1,32 @@
# 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.
ajpMessage.invalidPos=AJP メッセージの終端より先の位置 [{0}] のバイト読み取りを要求されました。
ajpmessage.invalid=署名[{0}]で無効なメッセージが受信されました。
ajpmessage.invalidLength=長さ[{0}]の無効なメッセージが受信されました
ajpmessage.null=null 値は追加できません。
ajpmessage.overflow=バッファーの位置 [{1}] へ [{0}] バイトのデータを追加しようとして、オーバーフローエラーが発生しました。
ajpprocessor.certs.fail=証明書変換に失敗しました。
ajpprocessor.header.error=ヘッダーメッセージの解析に失敗しました
ajpprocessor.header.tooLong=受信したヘッダーに指定されたメッセージ長は [{0}] ですがpacketSize は [{1}] しかありません。
ajpprocessor.readtimeout=ソケットからのデータの読み取りをタイムアウトにします。
ajpprocessor.request.prepare=リクエスト準備中エラー
ajpprocessor.request.process=リクエスト処理エラー
ajpprotocol.noSSL=AJP は SSL に対応していません。SSL ホスト構成 [{0}] を無視します。
ajpprotocol.noUpgrade=AJP はプロトコルアップグレードに対応していないため、[{0}] の設定を無視しました。
ajpprotocol.noUpgradeHandler=アップグレードはAJPではサポートされていません。 HttpUpgradeHandler [{0}]を処理できません。

View File

@@ -0,0 +1,32 @@
# 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.
ajpMessage.invalidPos=위치 [{0}](으)로부터 바이트들을 읽기를 요청 받았는데, 이는 해당 AJP 메시지의 끝 위치를 넘어섰습니다.
ajpmessage.invalid=서명 [{0}]와(과) 함께, 유효하지 않은 메시지를 받았습니다.
ajpmessage.invalidLength=길이가 [{0}]인 유효하지 않은 메시지를 받았습니다.
ajpmessage.null=널 값을 추가할 수 없습니다.
ajpmessage.overflow=버퍼에 [{0}] 바이트들을 위치 [{1}]에 추가하는 동안 오버플로우 오류 발생
ajpprocessor.certs.fail=인증서를 변환시키지 못했습니다.
ajpprocessor.header.error=헤더 메시지를 파싱하지 못했습니다.
ajpprocessor.header.tooLong=길이가 [{0}]인 헤더 메시지를 받았지만, 패킷 크기는 단지 [{1}]입니다.
ajpprocessor.readtimeout=소켓으로부터 데이터를 읽으려는 시도가 제한 시간 초과되었습니다.
ajpprocessor.request.prepare=요청을 준비하는 중 오류 발생
ajpprocessor.request.process=요청 처리 중 오류 발생
ajpprotocol.noSSL=AJP와 함께 SSL은 지원되지 않습니다. [{0}]을(를) 위한 SSL 호스트 설정은 무시되었습니다.
ajpprotocol.noUpgrade=AJP에서 프로토콜 업그레이드는 지원되지 않습니다. [{0}]을(를) 위한 UpgradeProtocol 설정은 무시됩니다.
ajpprotocol.noUpgradeHandler=AJP를 사용할 때, 업그레이드는 지원되지 않습니다. HttpUpgradeHandler [{0}]은(는) 처리될 수 없습니다.

View File

@@ -0,0 +1,23 @@
# 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.
ajpmessage.null=不能赋空值
ajpmessage.overflow=在缓冲区[{1}]位置添加[{0}]字节时发生溢出错误
ajpprocessor.certs.fail=):证书转换失败
ajpprocessor.header.error=头部信息解析失败
ajpprocessor.readtimeout=从Socket读取数据超时
ajpprotocol.noUpgrade=AJP 不支持升级。[{0}] 的升级协议配置被忽略。

View File

@@ -0,0 +1,52 @@
/*
* 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.http11;
import org.apache.tomcat.util.net.AbstractJsseEndpoint;
import org.apache.tomcat.util.net.openssl.OpenSSLImplementation;
public abstract class AbstractHttp11JsseProtocol<S>
extends AbstractHttp11Protocol<S> {
public AbstractHttp11JsseProtocol(AbstractJsseEndpoint<S> endpoint) {
super(endpoint);
}
@Override
protected AbstractJsseEndpoint<S> getEndpoint() {
// Over-ridden to add cast
return (AbstractJsseEndpoint<S>) super.getEndpoint();
}
protected String getSslImplementationShortName() {
if (OpenSSLImplementation.class.getName().equals(getSslImplementationName())) {
return "openssl";
}
return "jsse";
}
public String getSslImplementationName() { return getEndpoint().getSslImplementationName(); }
public void setSslImplementationName(String s) { getEndpoint().setSslImplementationName(s); }
public int getSniParseLimit() { return getEndpoint().getSniParseLimit(); }
public void setSniParseLimit(int sniParseLimit) {
getEndpoint().setSniParseLimit(sniParseLimit);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
/*
* 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.http11;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Constants.
*
* @author Remy Maucherat
*/
public final class Constants {
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
/**
* CRLF.
*/
public static final String CRLF = "\r\n";
/**
* CR.
*/
public static final byte CR = (byte) '\r';
/**
* LF.
*/
public static final byte LF = (byte) '\n';
/**
* SP.
*/
public static final byte SP = (byte) ' ';
/**
* HT.
*/
public static final byte HT = (byte) '\t';
/**
* COLON.
*/
public static final byte COLON = (byte) ':';
/**
* SEMI_COLON.
*/
public static final byte SEMI_COLON = (byte) ';';
/**
* 'A'.
*/
public static final byte A = (byte) 'A';
/**
* 'a'.
*/
public static final byte a = (byte) 'a';
/**
* 'Z'.
*/
public static final byte Z = (byte) 'Z';
/**
* '?'.
*/
public static final byte QUESTION = (byte) '?';
/**
* Lower case offset.
*/
public static final byte LC_OFFSET = A - a;
/* Various constant "strings" */
public static final String CONNECTION = "Connection";
public static final String CLOSE = "close";
/**
* @deprecated Unused. Will be removed in Tomcat 10.
*/
@Deprecated
public static final byte[] CLOSE_BYTES = ByteChunk.convertToBytes(CLOSE);
/**
* @deprecated Unused. Will be removed in Tomcat 10.
*/
@Deprecated
public static final String KEEPALIVE = "keep-alive";
public static final String KEEP_ALIVE_HEADER_VALUE_TOKEN = "keep-alive";
/**
* @deprecated Unused. Will be removed in Tomcat 10.
*/
@Deprecated
public static final byte[] KEEPALIVE_BYTES = ByteChunk.convertToBytes(KEEPALIVE);
public static final String CHUNKED = "chunked";
/**
* @deprecated This option will be removed in Tomcat 9. Reason phrase will
* not be sent.
*/
@Deprecated
public static final byte[] ACK_BYTES_REASON =
ByteChunk.convertToBytes("HTTP/1.1 100 Continue" + CRLF + CRLF);
public static final byte[] ACK_BYTES = ByteChunk.convertToBytes("HTTP/1.1 100 " + CRLF + CRLF);
public static final String TRANSFERENCODING = "Transfer-Encoding";
public static final String KEEP_ALIVE_HEADER_NAME = "Keep-Alive";
public static final byte[] _200_BYTES = ByteChunk.convertToBytes("200");
public static final byte[] _400_BYTES = ByteChunk.convertToBytes("400");
public static final byte[] _404_BYTES = ByteChunk.convertToBytes("404");
/**
* Identity filters (input and output).
*/
public static final int IDENTITY_FILTER = 0;
/**
* Chunked filters (input and output).
*/
public static final int CHUNKED_FILTER = 1;
/**
* Void filters (input and output).
*/
public static final int VOID_FILTER = 2;
/**
* GZIP filter (output).
*/
public static final int GZIP_FILTER = 3;
/**
* Buffered filter (input)
*/
public static final int BUFFERED_FILTER = 3;
/**
* HTTP/1.0.
*/
public static final String HTTP_10 = "HTTP/1.0";
/**
* HTTP/1.1.
*/
public static final String HTTP_11 = "HTTP/1.1";
public static final byte[] HTTP_11_BYTES = ByteChunk.convertToBytes(HTTP_11);
}

View File

@@ -0,0 +1,42 @@
/*
* 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.http11;
/**
* Exception used to mark the specific error condition of the HTTP headers
* exceeding the maximum permitted size.
*/
public class HeadersTooLargeException extends IllegalStateException {
private static final long serialVersionUID = 1L;
public HeadersTooLargeException() {
super();
}
public HeadersTooLargeException(String message, Throwable cause) {
super(message, cause);
}
public HeadersTooLargeException(String s) {
super(s);
}
public HeadersTooLargeException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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.http11;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.AprEndpoint;
/**
* Abstract the protocol implementation, including threading, etc.
* Processor is single threaded and specific to stream-based protocols,
* will not fit Jk protocols like JNI.
*
* @author Remy Maucherat
* @author Costin Manolache
*/
public class Http11AprProtocol extends AbstractHttp11Protocol<Long> {
private static final Log log = LogFactory.getLog(Http11AprProtocol.class);
public Http11AprProtocol() {
super(new AprEndpoint());
}
@Override
protected Log getLog() { return log; }
@Override
public boolean isAprRequired() {
// Override since this protocol implementation requires the APR/native
// library
return true;
}
public int getPollTime() { return ((AprEndpoint)getEndpoint()).getPollTime(); }
public void setPollTime(int pollTime) { ((AprEndpoint)getEndpoint()).setPollTime(pollTime); }
public int getSendfileSize() { return ((AprEndpoint)getEndpoint()).getSendfileSize(); }
public void setSendfileSize(int sendfileSize) { ((AprEndpoint)getEndpoint()).setSendfileSize(sendfileSize); }
public boolean getDeferAccept() { return ((AprEndpoint)getEndpoint()).getDeferAccept(); }
public void setDeferAccept(boolean deferAccept) { ((AprEndpoint)getEndpoint()).setDeferAccept(deferAccept); }
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
if (isSSLEnabled()) {
return ("https-openssl-apr");
} else {
return ("http-apr");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
/*
* 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.http11;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.Nio2Channel;
import org.apache.tomcat.util.net.Nio2Endpoint;
/**
* HTTP/1.1 protocol implementation using NIO2.
*/
public class Http11Nio2Protocol extends AbstractHttp11JsseProtocol<Nio2Channel> {
private static final Log log = LogFactory.getLog(Http11Nio2Protocol.class);
public Http11Nio2Protocol() {
super(new Nio2Endpoint());
}
@Override
protected Log getLog() { return log; }
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
if (isSSLEnabled()) {
return ("https-" + getSslImplementationShortName()+ "-nio2");
} else {
return ("http-nio2");
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.http11;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
/**
* Abstract the protocol implementation, including threading, etc.
* Processor is single threaded and specific to stream-based protocols,
* will not fit Jk protocols like JNI.
*
* @author Remy Maucherat
* @author Costin Manolache
*/
public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
private static final Log log = LogFactory.getLog(Http11NioProtocol.class);
public Http11NioProtocol() {
super(new NioEndpoint());
}
@Override
protected Log getLog() { return log; }
// -------------------- Pool setup --------------------
public void setPollerThreadCount(int count) {
((NioEndpoint)getEndpoint()).setPollerThreadCount(count);
}
public int getPollerThreadCount() {
return ((NioEndpoint)getEndpoint()).getPollerThreadCount();
}
public void setSelectorTimeout(long timeout) {
((NioEndpoint)getEndpoint()).setSelectorTimeout(timeout);
}
public long getSelectorTimeout() {
return ((NioEndpoint)getEndpoint()).getSelectorTimeout();
}
public void setPollerThreadPriority(int threadPriority) {
((NioEndpoint)getEndpoint()).setPollerThreadPriority(threadPriority);
}
public int getPollerThreadPriority() {
return ((NioEndpoint)getEndpoint()).getPollerThreadPriority();
}
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
if (isSSLEnabled()) {
return ("https-" + getSslImplementationShortName()+ "-nio");
} else {
return ("http-nio");
}
}
}

View File

@@ -0,0 +1,649 @@
/*
* 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.http11;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.coyote.ActionCode;
import org.apache.coyote.Response;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.HttpMessages;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
/**
* Provides buffering for the HTTP headers (allowing responses to be reset
* before they have been committed) and the link to the Socket for writing the
* headers (once committed) and the response body. Note that buffering of the
* response body happens at a higher level.
*/
public class Http11OutputBuffer implements HttpOutputBuffer {
// -------------------------------------------------------------- Variables
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(Http11OutputBuffer.class);
// ----------------------------------------------------- Instance Variables
/**
* Associated Coyote response.
*/
protected Response response;
/**
* Finished flag.
*/
protected boolean responseFinished;
/**
* The buffer used for header composition.
*/
protected final ByteBuffer headerBuffer;
/**
* Filter library for processing the response body.
*/
protected OutputFilter[] filterLibrary;
/**
* Active filters for the current request.
*/
protected OutputFilter[] activeFilters;
/**
* Index of the last active filter.
*/
protected int lastActiveFilter;
/**
* Underlying output buffer.
*/
protected HttpOutputBuffer outputStreamOutputBuffer;
/**
* Wrapper for socket where data will be written to.
*/
protected SocketWrapperBase<?> socketWrapper;
/**
* Bytes written to client for the current request
*/
protected long byteCount = 0;
@Deprecated
private boolean sendReasonPhrase = false;
protected Http11OutputBuffer(Response response, int headerBufferSize, boolean sendReasonPhrase) {
this.response = response;
this.sendReasonPhrase = sendReasonPhrase;
headerBuffer = ByteBuffer.allocate(headerBufferSize);
filterLibrary = new OutputFilter[0];
activeFilters = new OutputFilter[0];
lastActiveFilter = -1;
responseFinished = false;
outputStreamOutputBuffer = new SocketOutputBuffer();
if (sendReasonPhrase) {
// Cause loading of HttpMessages
HttpMessages.getInstance(response.getLocale()).getMessage(200);
}
}
// ------------------------------------------------------------- Properties
/**
* Add an output filter to the filter library. Note that calling this method
* resets the currently active filters to none.
*
* @param filter The filter to add
*/
public void addFilter(OutputFilter filter) {
OutputFilter[] newFilterLibrary = new OutputFilter[filterLibrary.length + 1];
for (int i = 0; i < filterLibrary.length; i++) {
newFilterLibrary[i] = filterLibrary[i];
}
newFilterLibrary[filterLibrary.length] = filter;
filterLibrary = newFilterLibrary;
activeFilters = new OutputFilter[filterLibrary.length];
}
/**
* Get filters.
*
* @return The current filter library containing all possible filters
*/
public OutputFilter[] getFilters() {
return filterLibrary;
}
/**
* Add an output filter to the active filters for the current response.
* <p>
* The filter does not have to be present in {@link #getFilters()}.
* <p>
* A filter can only be added to a response once. If the filter has already
* been added to this response then this method will be a NO-OP.
*
* @param filter The filter to add
*/
public void addActiveFilter(OutputFilter filter) {
if (lastActiveFilter == -1) {
filter.setBuffer(outputStreamOutputBuffer);
} else {
for (int i = 0; i <= lastActiveFilter; i++) {
if (activeFilters[i] == filter)
return;
}
filter.setBuffer(activeFilters[lastActiveFilter]);
}
activeFilters[++lastActiveFilter] = filter;
filter.setResponse(response);
}
// --------------------------------------------------- OutputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
@Override
public int doWrite(ByteChunk chunk) throws IOException {
if (!response.isCommitted()) {
// Send the connector a request for commit. The connector should
// then validate the headers, send them (using sendHeaders) and
// set the filters accordingly.
response.action(ActionCode.COMMIT, null);
}
if (lastActiveFilter == -1) {
return outputStreamOutputBuffer.doWrite(chunk);
} else {
return activeFilters[lastActiveFilter].doWrite(chunk);
}
}
@Override
public int doWrite(ByteBuffer chunk) throws IOException {
if (!response.isCommitted()) {
// Send the connector a request for commit. The connector should
// then validate the headers, send them (using sendHeaders) and
// set the filters accordingly.
response.action(ActionCode.COMMIT, null);
}
if (lastActiveFilter == -1) {
return outputStreamOutputBuffer.doWrite(chunk);
} else {
return activeFilters[lastActiveFilter].doWrite(chunk);
}
}
@Override
public long getBytesWritten() {
if (lastActiveFilter == -1) {
return outputStreamOutputBuffer.getBytesWritten();
} else {
return activeFilters[lastActiveFilter].getBytesWritten();
}
}
// ----------------------------------------------- HttpOutputBuffer Methods
/**
* Flush the response.
*
* @throws IOException an underlying I/O error occurred
*/
@Override
public void flush() throws IOException {
if (lastActiveFilter == -1) {
outputStreamOutputBuffer.flush();
} else {
activeFilters[lastActiveFilter].flush();
}
}
@Override
public void end() throws IOException {
if (responseFinished) {
return;
}
if (lastActiveFilter == -1) {
outputStreamOutputBuffer.end();
} else {
activeFilters[lastActiveFilter].end();
}
responseFinished = true;
}
// --------------------------------------------------------- Public Methods
/**
* Reset the header buffer if an error occurs during the writing of the
* headers so the error response can be written.
*/
void resetHeaderBuffer() {
headerBuffer.position(0).limit(headerBuffer.capacity());
}
/**
* Recycle the output buffer. This should be called when closing the
* connection.
*/
public void recycle() {
nextRequest();
socketWrapper = null;
}
/**
* End processing of current HTTP request.
* Note: All bytes of the current request should have been already
* consumed. This method only resets all the pointers so that we are ready
* to parse the next HTTP request.
*/
public void nextRequest() {
// Recycle filters
for (int i = 0; i <= lastActiveFilter; i++) {
activeFilters[i].recycle();
}
// Recycle response object
response.recycle();
// Reset pointers
headerBuffer.position(0).limit(headerBuffer.capacity());
lastActiveFilter = -1;
responseFinished = false;
byteCount = 0;
}
public void init(SocketWrapperBase<?> socketWrapper) {
this.socketWrapper = socketWrapper;
}
@SuppressWarnings("deprecation")
public void sendAck() throws IOException {
if (!response.isCommitted()) {
if (sendReasonPhrase) {
socketWrapper.write(isBlocking(), Constants.ACK_BYTES_REASON, 0, Constants.ACK_BYTES_REASON.length);
} else {
socketWrapper.write(isBlocking(), Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length);
}
if (flushBuffer(true)) {
throw new IOException(sm.getString("iob.failedwrite.ack"));
}
}
}
/**
* Commit the response.
*
* @throws IOException an underlying I/O error occurred
*/
protected void commit() throws IOException {
response.setCommitted(true);
if (headerBuffer.position() > 0) {
// Sending the response header buffer
headerBuffer.flip();
try {
socketWrapper.write(isBlocking(), headerBuffer);
} finally {
headerBuffer.position(0).limit(headerBuffer.capacity());
}
}
}
/**
* Send the response status line.
*/
@SuppressWarnings("deprecation")
public void sendStatus() {
// Write protocol name
write(Constants.HTTP_11_BYTES);
headerBuffer.put(Constants.SP);
// Write status code
int status = response.getStatus();
switch (status) {
case 200:
write(Constants._200_BYTES);
break;
case 400:
write(Constants._400_BYTES);
break;
case 404:
write(Constants._404_BYTES);
break;
default:
write(status);
}
headerBuffer.put(Constants.SP);
if (sendReasonPhrase) {
// Write message
String message = null;
if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
HttpMessages.isSafeInHttpHeader(response.getMessage())) {
message = response.getMessage();
}
if (message == null) {
write(HttpMessages.getInstance(
response.getLocale()).getMessage(status));
} else {
write(message);
}
} else {
// The reason phrase is optional but the space before it is not. Skip
// sending the reason phrase. Clients should ignore it (RFC 7230) and it
// just wastes bytes.
}
headerBuffer.put(Constants.CR).put(Constants.LF);
}
/**
* Send a header.
*
* @param name Header name
* @param value Header value
*/
public void sendHeader(MessageBytes name, MessageBytes value) {
write(name);
headerBuffer.put(Constants.COLON).put(Constants.SP);
write(value);
headerBuffer.put(Constants.CR).put(Constants.LF);
}
/**
* End the header block.
*/
public void endHeaders() {
headerBuffer.put(Constants.CR).put(Constants.LF);
}
/**
* This method will write the contents of the specified message bytes
* buffer to the output stream, without filtering. This method is meant to
* be used to write the response header.
*
* @param mb data to be written
*/
private void write(MessageBytes mb) {
if (mb.getType() != MessageBytes.T_BYTES) {
mb.toBytes();
ByteChunk bc = mb.getByteChunk();
// Need to filter out CTLs excluding TAB. ISO-8859-1 and UTF-8
// values will be OK. Strings using other encodings may be
// corrupted.
byte[] buffer = bc.getBuffer();
for (int i = bc.getOffset(); i < bc.getLength(); i++) {
// byte values are signed i.e. -128 to 127
// The values are used unsigned. 0 to 31 are CTLs so they are
// filtered (apart from TAB which is 9). 127 is a control (DEL).
// The values 128 to 255 are all OK. Converting those to signed
// gives -128 to -1.
if ((buffer[i] > -1 && buffer[i] <= 31 && buffer[i] != 9) ||
buffer[i] == 127) {
buffer[i] = ' ';
}
}
}
write(mb.getByteChunk());
}
/**
* This method will write the contents of the specified byte chunk to the
* output stream, without filtering. This method is meant to be used to
* write the response header.
*
* @param bc data to be written
*/
private void write(ByteChunk bc) {
// Writing the byte chunk to the output buffer
int length = bc.getLength();
checkLengthBeforeWrite(length);
headerBuffer.put(bc.getBytes(), bc.getStart(), length);
}
/**
* This method will write the contents of the specified byte
* buffer to the output stream, without filtering. This method is meant to
* be used to write the response header.
*
* @param b data to be written
*/
public void write(byte[] b) {
checkLengthBeforeWrite(b.length);
// Writing the byte chunk to the output buffer
headerBuffer.put(b);
}
/**
* This method will write the contents of the specified String to the
* output stream, without filtering. This method is meant to be used to
* write the response header.
*
* @param s data to be written
*/
private void write(String s) {
if (s == null) {
return;
}
// From the Tomcat 3.3 HTTP/1.0 connector
int len = s.length();
checkLengthBeforeWrite(len);
for (int i = 0; i < len; i++) {
char c = s.charAt (i);
// Note: This is clearly incorrect for many strings,
// but is the only consistent approach within the current
// servlet framework. It must suffice until servlet output
// streams properly encode their output.
if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
c = ' ';
}
headerBuffer.put((byte) c);
}
}
/**
* This method will write the specified integer to the output stream. This
* method is meant to be used to write the response header.
*
* @param value data to be written
*/
private void write(int value) {
// From the Tomcat 3.3 HTTP/1.0 connector
String s = Integer.toString(value);
int len = s.length();
checkLengthBeforeWrite(len);
for (int i = 0; i < len; i++) {
char c = s.charAt (i);
headerBuffer.put((byte) c);
}
}
/**
* Checks to see if there is enough space in the buffer to write the
* requested number of bytes.
*/
private void checkLengthBeforeWrite(int length) {
// "+ 4": BZ 57509. Reserve space for CR/LF/COLON/SP characters that
// are put directly into the buffer following this write operation.
if (headerBuffer.position() + length + 4 > headerBuffer.capacity()) {
throw new HeadersTooLargeException(
sm.getString("iob.responseheadertoolarge.error"));
}
}
//------------------------------------------------------ Non-blocking writes
/**
* Writes any remaining buffered data.
*
* @param block Should this method block until the buffer is empty
* @return <code>true</code> if data remains in the buffer (which can only
* happen in non-blocking mode) else <code>false</code>.
* @throws IOException Error writing data
*/
protected boolean flushBuffer(boolean block) throws IOException {
return socketWrapper.flush(block);
}
/**
* Is standard Servlet blocking IO being used for output?
* @return <code>true</code> if this is blocking IO
*/
protected final boolean isBlocking() {
return response.getWriteListener() == null;
}
protected final boolean isReady() {
boolean result = !hasDataToWrite();
if (!result) {
socketWrapper.registerWriteInterest();
}
return result;
}
public boolean hasDataToWrite() {
return socketWrapper.hasDataToWrite();
}
public void registerWriteInterest() {
socketWrapper.registerWriteInterest();
}
// ------------------------------------------ SocketOutputBuffer Inner Class
/**
* This class is an output buffer which will write data to a socket.
*/
protected class SocketOutputBuffer implements HttpOutputBuffer {
/**
* Write chunk.
*
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
@Override
public int doWrite(ByteChunk chunk) throws IOException {
int len = chunk.getLength();
int start = chunk.getStart();
byte[] b = chunk.getBuffer();
socketWrapper.write(isBlocking(), b, start, len);
byteCount += len;
return len;
}
/**
* Write chunk.
*/
@Override
public int doWrite(ByteBuffer chunk) throws IOException {
try {
int len = chunk.remaining();
socketWrapper.write(isBlocking(), chunk);
len -= chunk.remaining();
byteCount += len;
return len;
} catch (IOException ioe) {
response.action(ActionCode.CLOSE_NOW, ioe);
// Re-throw
throw ioe;
}
}
@Override
public long getBytesWritten() {
return byteCount;
}
@Override
public void end() throws IOException {
socketWrapper.flush(true);
}
@Override
public void flush() throws IOException {
socketWrapper.flush(isBlocking());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
/*
* 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.http11;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* @deprecated This class will be removed in Tomcat 9.
*/
@Deprecated
public class Http11Protocol extends Http11NioProtocol {
private static final Log log = LogFactory.getLog(Http11Protocol.class);
private static final StringManager sm = StringManager.getManager(Http11Protocol.class);
public Http11Protocol() {
super();
log.warn(sm.getString("http11protocol.noBio"));
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.http11;
import java.io.IOException;
import org.apache.coyote.OutputBuffer;
public interface HttpOutputBuffer extends OutputBuffer {
/**
* Finish writing the current response. It is acceptable to write extra
* bytes using {@link #doWrite(java.nio.ByteBuffer)} during the execution of
* this method.
*
* @throws IOException If an I/O error occurs while writing to the client
*/
public void end() throws IOException;
/**
* Flushes any unwritten data to the client.
*
* @throws IOException If an I/O error occurs while flushing
*/
public void flush() throws IOException;
}

View File

@@ -0,0 +1,93 @@
/*
* 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.http11;
import java.io.IOException;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Input filter interface.
*
* @author Remy Maucherat
*/
public interface InputFilter extends InputBuffer {
/**
* Some filters need additional parameters from the request.
*
* @param request The request to be associated with this filter
*/
public void setRequest(Request request);
/**
* Make the filter ready to process the next request.
*/
public void recycle();
/**
* Get the name of the encoding handled by this filter.
*
* @return The encoding name as a byte chunk to facilitate comparison with
* the value read from the HTTP headers which will also be a
* ByteChunk
*/
public ByteChunk getEncodingName();
/**
* Set the next buffer in the filter pipeline.
*
* @param buffer The next buffer
*/
public void setBuffer(InputBuffer buffer);
/**
* End the current request.
*
* @return 0 is the expected return value. A positive value indicates that
* too many bytes were read. This method is allowed to use buffer.doRead
* to consume extra bytes. The result of this method can't be negative (if
* an error happens, an IOException should be thrown instead).
*
* @throws IOException If an error happens
*/
public long end() throws IOException;
/**
* Amount of bytes still available in a buffer.
*
* @return The number of bytes in the buffer
*/
public int available();
/**
* Has the request body been read fully?
*
* @return {@code true} if the request body has been fully read, otherwise
* {@code false}
*/
public boolean isFinished();
}

View File

@@ -0,0 +1,55 @@
# 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.
abstractHttp11Protocol.alpnConfigured=The [{0}] connector has been configured to support negotiation to [{1}] via ALPN
abstractHttp11Protocol.alpnWithNoAlpn=The upgrade handler [{0}] for [{1}] only supports upgrade via ALPN but has been configured for the [{2}] connector that does not support ALPN.
abstractHttp11Protocol.httpUpgradeConfigured=The [{0}] connector has been configured to support HTTP upgrade to [{1}]
http11processor.fallToDebug=\n\
\ Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
http11processor.header.parse=Error parsing HTTP request header
http11processor.request.finish=Error finishing request
http11processor.request.inconsistentHosts=The host specified in the request line is not consistent with the host header
http11processor.request.invalidScheme=The HTTP request contained an absolute URI with an invalid scheme
http11processor.request.invalidTransferEncoding=The HTTP request contained an invalid Transfer-Encoding header
http11processor.request.invalidUri=The HTTP request contained an invalid URI
http11processor.request.invalidUserInfo=The HTTP request contained an absolute URI with an invalid userinfo
http11processor.request.multipleContentLength=The request contained multiple content-length headers
http11processor.request.multipleHosts=The request contained multiple host headers
http11processor.request.noHostHeader=The HTTP/1.1 request did not provide a host header
http11processor.request.nonNumericContentLength=The request contained a content-length header with a non-numeric value
http11processor.request.prepare=Error preparing request
http11processor.request.process=Error processing request
http11processor.response.finish=Error finishing response
http11processor.sendfile.error=Error sending data using sendfile. May be caused by invalid request attributes for start/end points
http11processor.socket.info=Exception getting socket information
http11protocol.noBio=The HTTP BIO connector has been removed in Tomcat 8.5.x onwards. The HTTP BIO connector configuration has been automatically switched to use the HTTP NIO connector instead.
iib.available.readFail=A non-blocking read failed while attempting to determine if data was available
iib.eof.error=Unexpected EOF read on the socket
iib.failedread.apr=Read failed with APR/native error code [{0}]
iib.filter.npe=You may not add a null filter
iib.invalidHttpProtocol=Invalid character found in the HTTP protocol
iib.invalidRequestTarget=Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
iib.invalidheader=The HTTP header line [{0}] does not conform to RFC 7230 and has been ignored.
iib.invalidmethod=Invalid character found in method name. HTTP method names must be tokens
iib.parseheaders.ise.error=Unexpected state: headers already parsed. Buffer not recycled?
iib.readtimeout=Timeout attempting to read data from the socket
iib.requestheadertoolarge.error=Request header is too large
iob.failedwrite=Failed write
iob.failedwrite.ack=Failed to send HTTP 100 continue response
iob.responseheadertoolarge.error=An attempt was made to write more data to the response headers than there was room available in the buffer. Increase maxHttpHeaderSize on the connector or write less data into the response headers.

View File

@@ -0,0 +1,22 @@
# 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.
http11processor.request.prepare=Fehler beim vorbereiten der Anfrage
http11processor.request.process=Fehler bei der Verarbeitung der Anfrage
http11processor.socket.info=Ausnahme beim Lesen der Informationen zum Socket
iib.invalidmethod=Ungültiges Zeichen in Methoden Namen gefunden. HTTP Methoden Namen müssen Token nach RFC-7230 sein
iob.failedwrite=Fehlgeschlagener Schreibvorgang

View File

@@ -0,0 +1,29 @@
# 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.
http11processor.header.parse=Error analizando cabecera de requerimiento HTTP
http11processor.request.finish=Error acabando requerimiento
http11processor.request.prepare=Error preparando solicitud
http11processor.request.process=Error procesando requerimiento
http11processor.response.finish=Error acabando respuesta
http11processor.socket.info=Excepción obteniendo información de conector
iib.eof.error=Inesperado Fin De Archivo (EOF) leído en el enchufe (socket)
iib.invalidmethod=Se encontró un carácter inválido en el nombre del método. Los nombres de métodos HTTP deben ser tokens
iib.parseheaders.ise.error=Estado inesperado: las cabeceras ya fueron parseadas. No se ha reciclado el buffer??
iib.requestheadertoolarge.error=La cabecera del requerimiento es demasido grande
iob.failedwrite=Fallo de escritura
iob.responseheadertoolarge.error=Un intento para escribir más datos en las cabeceras de respuesta escribir fue hecho, sin embargo no hay más espacio disponible en el bufer. Aumente el parámetro maxHttpHeaderSize en el conector o escribe menos datos en las respuestas de las cabeceras.\n

View File

@@ -0,0 +1,51 @@
# 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.
abstractHttp11Protocol.alpnConfigured=Le connecteur [{0}] a été configuré pour supporter la négociation de [{1}] avec ALPN
abstractHttp11Protocol.alpnWithNoAlpn=Le gestionnaire de mise à niveau [{0}] pour [{1}] ne supporte qu''une mise à niveau via ALPN mais il a été configuré pour le connecteur [{2}] qui ne supporte pas ALPN
abstractHttp11Protocol.httpUpgradeConfigured=Le connecteur [{0}] a été configuré pour supporter la mise à niveau de HTTP vers [{1}]
http11processor.fallToDebug=\ Note: toutes les occurrences suivantes d'erreurs d'analyse des requêtes HTTP seront enregistrées au niveau DEBUG
http11processor.header.parse=Erreur lors de l'analyse d'un en-tête de requête HTTP
http11processor.request.finish=Erreur en terminant la requête
http11processor.request.inconsistentHosts=L'hôte spécifié dans la ligne de requête ne correspond pas à celui de l'en-tête hôte
http11processor.request.invalidScheme=La requête HTTP contenait une URi absolue avec un schéma invalide
http11processor.request.invalidUri==La requête HTTP contenait une URI invalide
http11processor.request.invalidUserInfo=La requête HTTP contenait un URI absolu avec un composant "userinfo" invalide
http11processor.request.multipleContentLength=La requête contenait plusieurs en-têtes content-length
http11processor.request.multipleHosts=La requête contenait plusieurs en-têtes hôtes
http11processor.request.noHostHeader=La requ6ete HTTP/1.1 ne contient pas d'en-tête host
http11processor.request.nonNumericContentLength=La requête contenait un en-tête content-length avec une valeur non numérique
http11processor.request.prepare=Echec de préparation de la requête
http11processor.request.process=Erreur de traitement de la requête
http11processor.response.finish=Erreur en finissant la réponse
http11processor.sendfile.error=Erreur d'envoi des données avec sendfile, cela peut être causé par des attributs de démarrage ou de fin incorrects dans la requête
http11processor.socket.info=Exception pendant la requête d'information sur le socket.
iib.available.readFail=Une lecture non-bloquante a échoué lors de la détermination préalable de données disponibles
iib.eof.error=Fin de flux (EOF) inattendue à la lecture sur la socket
iib.failedread.apr=Echec de lecteur avec le code d''erreur APR [{0}]
iib.filter.npe=Impossible d'ajouter un filtre null
iib.invalidHttpProtocol=Un caractère invalide a été trouvé dans le protocole HTTP
iib.invalidRequestTarget=Un caractère invalide a été trouvé dans la cible de la requête, les caractères valides sont définis dans RFC 7230 et RFC 3986
iib.invalidheader=La ligne d''en-être HTTP [{0}] n''est pas conforme à la RFC 7230 et a été ignorée
iib.invalidmethod=Caractère invalide trouvé dans le nom de méthode. Les noms HTTP doivent être des "token".
iib.parseheaders.ise.error=Etat inattendu, les en-êtres ont déjà été traités, il est possible que le buffer n'ait pas été recyclé
iib.readtimeout=Délai d'attente dépassé en lisant des données du socket
iib.requestheadertoolarge.error=L'entête de requête est trop important
iob.failedwrite=Echec d'écriture
iob.failedwrite.ack=Echec d'envoi de la réponse HTTP 100 Continue
iob.responseheadertoolarge.error=Essai d'écriture de plus de données dans les en-t^tes de réponse, qu'il n'y a de place disponible dans le tampon. Augmentez maxHttpHeaderSize pour le connecteur, ou écrivez moins de données dans les en-têtes de réponse.

View File

@@ -0,0 +1,49 @@
# 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.
abstractHttp11Protocol.alpnConfigured=[{0}]コネクタは、ALPN経由で[{1}]へのネゴシエーションをサポートするように構成されています
abstractHttp11Protocol.alpnWithNoAlpn=[{1}]のアップグレードハンドラ[{0}]は、ALPNによるアップグレードのみをサポートしていますが、ALPNをサポートしていない[{2}]コネクタ用に構成されています。
abstractHttp11Protocol.httpUpgradeConfigured=コネクタ[{0}]は、[{1}]へのHTTPアップグレードをサポートするように構成されています。
http11processor.fallToDebug=HTTPリクエスト構文解析エラーがさらに発生すると、DEBUGレベルでログに記録されます。
http11processor.header.parse=HTTP リクエストヘッダーを解釈できませんでした。
http11processor.request.finish=リクエスト終了処理エラー
http11processor.request.inconsistentHosts=リクエストラインに指定されたホストが Host ヘッダーの値と矛盾しています。
http11processor.request.invalidScheme=HTTP リクエストに不正なスキーマを指定した完全 URI が含まれています。
http11processor.request.invalidUri== HTTPリクエストに無効なURIが含まれています
http11processor.request.invalidUserInfo=HTTP リクエストに不正な userinfo を含む絶対 URI が指定されました。
http11processor.request.multipleHosts=リクエストには複数のホストヘッダーが含まれていました。
http11processor.request.noHostHeader=HTTP / 1.1リクエストでhostヘッダーが提供されませんでした。
http11processor.request.prepare=リクエストの準備に失敗しました。
http11processor.request.process=リクエスト処理中にエラーが発生しました
http11processor.response.finish=レスポンス終了処理のエラー
http11processor.sendfile.error=sendfileを使ってデータを送信中にエラーが発生しました 開始点または終了点の無効なリクエスト属性によって引き起こされる可能性があります。
http11processor.socket.info=ソケット情報を取得する際の例外
iib.available.readFail=利用できるデータがあるか確かめている途中でノンブロッキング読み込みが失敗しました。
iib.eof.error=ソケットから予期しないEOFを読み込みました
iib.failedread.apr=APR/nativeエラーコード[{0}]で読み取りが失敗しました。
iib.filter.npe=Nullフィルタを追加することはできません。
iib.invalidHttpProtocol=HTTPプロトコルで無効な文字が見つかりました。
iib.invalidRequestTarget=リクエストの宛先に不正な文字が含まれています。利用可能な文字は RFC 7230 および RFC 3986 に定義されています。
iib.invalidheader=HTTP ヘッダー行 [{0}]は RFC 7230 に適合しないため無視します。
iib.invalidmethod=HTTP メソッド名に不正な文字が含まれています。HTTP メソッド名は決められたトークンでなければなりません。
iib.parseheaders.ise.error=予期しない状態:ヘッダがすでに解析されています。 バッファはリサイクルされていませんか?
iib.readtimeout=ソケットからデータを読み取ろうとしている際のタイムアウト
iib.requestheadertoolarge.error=リクエストヘッダが長すぎます
iob.failedwrite=書き込みが失敗しました。
iob.failedwrite.ack=HTTP 100 continue レスポンスの送信に失敗しました
iob.responseheadertoolarge.error=レスポンスヘッダーにバッファー領域より大きなデータの書き込みが発生しました。データを小さくするか Connector の maxHttpHeaderSize を大きくしてください。

View File

@@ -0,0 +1,52 @@
# 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.
abstractHttp11Protocol.alpnConfigured=[{0}] connector는 ALPN을 통해 [{1}](으)로 negotiation을 지원하도록 설정되었습니다.
abstractHttp11Protocol.alpnWithNoAlpn=[{1}]을(를) 위한 업그레이드 핸들러 [{0}]은(는) 오직 ALPN을 통한 업그레이드만 지원합니다만, 해당 업그레이드 핸들러가 ALPN을 지원하지 않는 [{2}] Connector를 위해 설정되어 있습니다.
abstractHttp11Protocol.httpUpgradeConfigured=[{0}] connector가, [{1}](으)로 HTTP 업그레이드를 지원하도록 설정되어 있습니다.
http11processor.fallToDebug=\n\
비고: HTTP 요청 파싱 오류들이 더 발생하는 경우 DEBUG 레벨 로그로 기록될 것입니다.
http11processor.header.parse=HTTP 요청 헤더를 파싱하는 중 오류 발생
http11processor.request.finish=요청을 완료하는 중 오류 발생
http11processor.request.inconsistentHosts=요청 행(request line)에 지정된 호스트가, 호스트 헤더와 일관되지 않습니다.
http11processor.request.invalidScheme=HTTP 요청이 유효하지 않은 스킴을 가진 절대 URI를 포함했습니다.
http11processor.request.invalidUri==HTTP 요청이 유효하지 않은 URI를 포함했습니다.
http11processor.request.invalidUserInfo=HTTP 요청이, 유효하지 않은 userinfo를 가진 절대 URI를 포함했습니다.
http11processor.request.multipleContentLength=해당 요청이 복수 개의 Content-Length 헤더들을 포함했습니다.
http11processor.request.multipleHosts=요청이 여러 개의 호스트 헤더들을 포함했습니다.
http11processor.request.noHostHeader=HTTP/1.1 요청이 호스트 헤더를 제공하지 않았습니다.
http11processor.request.nonNumericContentLength=해당 요청이 숫자가 아닌 Content-Length 헤더 값을 포함했습니다.
http11processor.request.prepare=요청을 준비하는 중 오류 발생
http11processor.request.process=요청 처리 중 오류 발생
http11processor.response.finish=응답을 완료하는 중 오류 발생
http11processor.sendfile.error=sendfile을 사용하여 데이터를 보내는 중 오류 발생. 시작 지점과 종료 지점을 위한 요청 속성들이 유효하지 않아 발생했을 수 있습니다.
http11processor.socket.info=소켓에 대한 정보를 얻는 중 예외 발생
iib.available.readFail=데이터가 가용한지 결정하려 시도하는 동안, non-blocking 읽기가 실패했습니다.
iib.eof.error=소켓에서 예기치 않은 EOF를 읽었습니다.
iib.failedread.apr=APR/native 오류 코드 [{0}]와(과) 함께, 읽기가 실패했습니다.
iib.filter.npe=널인 필터를 추가할 수 없습니다.
iib.invalidHttpProtocol=HTTP 프로토콜에서 유효하지 않은 문자가 발견되었습니다.
iib.invalidRequestTarget=요청 타겟에서 유효하지 않은 문자가 발견되었습니다. 유효한 문자들은 RFC 7230과 RFC 3986에 정의되어 있습니다.
iib.invalidheader=HTTP 헤더 행 [{0}]이(가) RFC 7230을 준수하지 않아, 무시되었습니다.
iib.invalidmethod=메소드 이름에 유효하지 않은 문자가 발견되었습니다. HTTP 메소드 이름은 유효한 토큰이어야 합니다.
iib.parseheaders.ise.error=예기치 않은 상태: 헤더들이 이미 파싱되었습니다. 버퍼가 참조 해제되지 않았었나요?
iib.readtimeout=소켓으로부터 데이터를 읽으려 시도하는 중 제한 시간 초과
iib.requestheadertoolarge.error=요청 헤더가 너무 큽니다.
iob.failedwrite=쓰기 실패
iob.failedwrite.ack=HTTP 100 continue 응답을 보내지 못했습니다.
iob.responseheadertoolarge.error=응답 헤더들에 가용한 버퍼 공간을 초과하는 데이터를 쓰려는 시도가 발생했습니다. 해당 Connector의 maxHttpHeaderSize를 증가시키거나, 응답 헤더들에 보다 적은 데이터를 쓰도록 하십시오.

View File

@@ -0,0 +1,16 @@
# 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.
http11processor.request.process=Ошибка при обработке запроса

View File

@@ -0,0 +1,39 @@
# 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.
abstractHttp11Protocol.alpnConfigured=[{0}]连接器已配置为支持通过ALPN与[{1}]协商。
http11processor.header.parse=解析 HTTP 请求 header 错误
http11processor.request.inconsistentHosts=请求行中指定的主机与主机头不一致。
http11processor.request.invalidScheme=HTTP请求包含具有无效方案的绝对URL
http11processor.request.invalidUserInfo=HTTP 请求包含带有无效 userinfo 的绝对 URI
http11processor.request.multipleContentLength=请求包含了多个content-length请求头参数
http11processor.request.noHostHeader=http/1.1请求没有提供主机头
http11processor.request.prepare=准备请求时出错
http11processor.request.process=错误的处理请求
http11processor.response.finish=错误完成相应
http11processor.socket.info=获取socket信息异常
iib.available.readFail=尝试确定数据是否可用时,非阻塞读取失败
iib.eof.error=套接字读取到意外的EOF
iib.filter.npe=你不能添加空过滤器(null)
iib.invalidheader=HTTP header行 [{0}] 不符合RFC 7230并且已被忽略。
iib.invalidmethod=在方法名称中发现无效的字符串, HTTP 方法名必须是有效的符号.
iib.parseheaders.ise.error=意外状态:已解析标头。 缓冲池不回收?
iib.readtimeout=从套接字读取数据超时
iob.failedwrite=写入.失败
iob.failedwrite.ack=无法发送HTTP 100继续响应
iob.responseheadertoolarge.error=尝试将更多数据写入响应标头,而不是缓冲区中有可用空间。 增加连接器上的maxHttpHeaderSize或将更少的数据写入响应头。

View File

@@ -0,0 +1,50 @@
/*
* 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.http11;
import org.apache.coyote.Response;
/**
* Output filter.
*
* @author Remy Maucherat
*/
public interface OutputFilter extends HttpOutputBuffer {
/**
* Some filters need additional parameters from the response. All the
* necessary reading can occur in that method, as this method is called
* after the response header processing is complete.
*
* @param response The response to associate with this OutputFilter
*/
public void setResponse(Response response);
/**
* Make the filter ready to process the next request.
*/
public void recycle();
/**
* Set the next buffer in the filter pipeline.
*
* @param buffer The next buffer instance
*/
public void setBuffer(HttpOutputBuffer buffer);
}

View File

@@ -0,0 +1,188 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
* Input filter responsible for reading and buffering the request body, so that
* it does not interfere with client SSL handshake messages.
*/
public class BufferedInputFilter implements InputFilter, ApplicationBufferHandler {
// -------------------------------------------------------------- Constants
private static final String ENCODING_NAME = "buffered";
private static final ByteChunk ENCODING = new ByteChunk();
// ----------------------------------------------------- Instance Variables
private ByteBuffer buffered;
private ByteBuffer tempRead;
private InputBuffer buffer;
private boolean hasRead = false;
// ----------------------------------------------------- Static Initializer
static {
ENCODING.setBytes(ENCODING_NAME.getBytes(StandardCharsets.ISO_8859_1),
0, ENCODING_NAME.length());
}
// --------------------------------------------------------- Public Methods
/**
* Set the buffering limit. This should be reset every time the buffer is
* used.
*
* @param limit The maximum number of bytes that will be buffered
*/
public void setLimit(int limit) {
if (buffered == null) {
buffered = ByteBuffer.allocate(limit);
buffered.flip();
}
}
// ---------------------------------------------------- InputBuffer Methods
/**
* Reads the request body and buffers it.
*/
@Override
public void setRequest(Request request) {
// save off the Request body
try {
while (buffer.doRead(this) >= 0) {
buffered.mark().position(buffered.limit()).limit(buffered.capacity());
buffered.put(tempRead);
buffered.limit(buffered.position()).reset();
tempRead = null;
}
} catch(IOException | BufferOverflowException ioe) {
// No need for i18n - this isn't going to get logged anywhere
throw new IllegalStateException(
"Request body too large for buffer");
}
}
/**
* Fills the given ByteChunk with the buffered request body.
*
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doRead(ApplicationBufferHandler)}
*/
@Deprecated
@Override
public int doRead(ByteChunk chunk) throws IOException {
if (isFinished()) {
return -1;
}
chunk.setBytes(buffered.array(), buffered.arrayOffset() + buffered.position(),
buffered.remaining());
hasRead = true;
return chunk.getLength();
}
/**
* Fills the given ByteBuffer with the buffered request body.
*/
@Override
public int doRead(ApplicationBufferHandler handler) throws IOException {
if (isFinished()) {
return -1;
}
handler.setByteBuffer(buffered);
hasRead = true;
return buffered.remaining();
}
@Override
public void setBuffer(InputBuffer buffer) {
this.buffer = buffer;
}
@Override
public void recycle() {
if (buffered != null) {
if (buffered.capacity() > 65536) {
buffered = null;
} else {
buffered.position(0).limit(0);
}
}
hasRead = false;
buffer = null;
}
@Override
public ByteChunk getEncodingName() {
return ENCODING;
}
@Override
public long end() throws IOException {
return 0;
}
@Override
public int available() {
return buffered.remaining();
}
@Override
public boolean isFinished() {
return hasRead || buffered.remaining() <= 0;
}
@Override
public void setByteBuffer(ByteBuffer buffer) {
tempRead = buffer;
}
@Override
public ByteBuffer getByteBuffer() {
return tempRead;
}
@Override
public void expand(int size) {
// no-op
}
}

View File

@@ -0,0 +1,688 @@
/*
* 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.http11.filters;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Set;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.http11.Constants;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
* Chunked input filter. Parses chunked data according to
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1">http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1</a><br>
*
* @author Remy Maucherat
*/
public class ChunkedInputFilter implements InputFilter, ApplicationBufferHandler {
private static final StringManager sm = StringManager.getManager(
ChunkedInputFilter.class.getPackage().getName());
// -------------------------------------------------------------- Constants
protected static final String ENCODING_NAME = "chunked";
protected static final ByteChunk ENCODING = new ByteChunk();
// ----------------------------------------------------- Static Initializer
static {
ENCODING.setBytes(ENCODING_NAME.getBytes(StandardCharsets.ISO_8859_1),
0, ENCODING_NAME.length());
}
// ----------------------------------------------------- Instance Variables
/**
* Next buffer in the pipeline.
*/
protected InputBuffer buffer;
/**
* Number of bytes remaining in the current chunk.
*/
protected int remaining = 0;
/**
* Byte chunk used to read bytes.
*/
protected ByteBuffer readChunk;
/**
* Flag set to true when the end chunk has been read.
*/
protected boolean endChunk = false;
/**
* Byte chunk used to store trailing headers.
*/
protected final ByteChunk trailingHeaders = new ByteChunk();
/**
* Flag set to true if the next call to doRead() must parse a CRLF pair
* before doing anything else.
*/
protected boolean needCRLFParse = false;
/**
* Request being parsed.
*/
private Request request;
/**
* Limit for extension size.
*/
private final long maxExtensionSize;
/**
* Limit for trailer size.
*/
private final int maxTrailerSize;
/**
* Size of extensions processed for this request.
*/
private long extensionSize;
private final int maxSwallowSize;
/**
* Flag that indicates if an error has occurred.
*/
private boolean error;
private final Set<String> allowedTrailerHeaders;
// ----------------------------------------------------------- Constructors
public ChunkedInputFilter(int maxTrailerSize, Set<String> allowedTrailerHeaders,
int maxExtensionSize, int maxSwallowSize) {
this.trailingHeaders.setLimit(maxTrailerSize);
this.allowedTrailerHeaders = allowedTrailerHeaders;
this.maxExtensionSize = maxExtensionSize;
this.maxTrailerSize = maxTrailerSize;
this.maxSwallowSize = maxSwallowSize;
}
// ---------------------------------------------------- InputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doRead(ApplicationBufferHandler)}
*/
@Deprecated
@Override
public int doRead(ByteChunk chunk) throws IOException {
if (endChunk) {
return -1;
}
checkError();
if(needCRLFParse) {
needCRLFParse = false;
parseCRLF(false);
}
if (remaining <= 0) {
if (!parseChunkHeader()) {
throwIOException(sm.getString("chunkedInputFilter.invalidHeader"));
}
if (endChunk) {
parseEndChunk();
return -1;
}
}
int result = 0;
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() < 0) {
throwIOException(sm.getString("chunkedInputFilter.eos"));
}
}
if (remaining > readChunk.remaining()) {
result = readChunk.remaining();
remaining = remaining - result;
chunk.setBytes(readChunk.array(), readChunk.arrayOffset() + readChunk.position(), result);
readChunk.position(readChunk.limit());
} else {
result = remaining;
chunk.setBytes(readChunk.array(), readChunk.arrayOffset() + readChunk.position(), remaining);
readChunk.position(readChunk.position() + remaining);
remaining = 0;
//we need a CRLF
if ((readChunk.position() + 1) >= readChunk.limit()) {
//if we call parseCRLF we overrun the buffer here
//so we defer it to the next call BZ 11117
needCRLFParse = true;
} else {
parseCRLF(false); //parse the CRLF immediately
}
}
return result;
}
@Override
public int doRead(ApplicationBufferHandler handler) throws IOException {
if (endChunk) {
return -1;
}
checkError();
if(needCRLFParse) {
needCRLFParse = false;
parseCRLF(false);
}
if (remaining <= 0) {
if (!parseChunkHeader()) {
throwIOException(sm.getString("chunkedInputFilter.invalidHeader"));
}
if (endChunk) {
parseEndChunk();
return -1;
}
}
int result = 0;
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() < 0) {
throwIOException(sm.getString("chunkedInputFilter.eos"));
}
}
if (remaining > readChunk.remaining()) {
result = readChunk.remaining();
remaining = remaining - result;
if (readChunk != handler.getByteBuffer()) {
handler.setByteBuffer(readChunk.duplicate());
}
readChunk.position(readChunk.limit());
} else {
result = remaining;
if (readChunk != handler.getByteBuffer()) {
handler.setByteBuffer(readChunk.duplicate());
handler.getByteBuffer().limit(readChunk.position() + remaining);
}
readChunk.position(readChunk.position() + remaining);
remaining = 0;
//we need a CRLF
if ((readChunk.position() + 1) >= readChunk.limit()) {
//if we call parseCRLF we overrun the buffer here
//so we defer it to the next call BZ 11117
needCRLFParse = true;
} else {
parseCRLF(false); //parse the CRLF immediately
}
}
return result;
}
// ---------------------------------------------------- InputFilter Methods
/**
* Read the content length from the request.
*/
@Override
public void setRequest(Request request) {
this.request = request;
}
/**
* End the current request.
*/
@Override
public long end() throws IOException {
long swallowed = 0;
int read = 0;
// Consume extra bytes : parse the stream until the end chunk is found
while ((read = doRead(this)) >= 0) {
swallowed += read;
if (maxSwallowSize > -1 && swallowed > maxSwallowSize) {
throwIOException(sm.getString("inputFilter.maxSwallow"));
}
}
// Return the number of extra bytes which were consumed
return readChunk.remaining();
}
/**
* Amount of bytes still available in a buffer.
*/
@Override
public int available() {
return readChunk != null ? readChunk.remaining() : 0;
}
/**
* Set the next buffer in the filter pipeline.
*/
@Override
public void setBuffer(InputBuffer buffer) {
this.buffer = buffer;
}
/**
* Make the filter ready to process the next request.
*/
@Override
public void recycle() {
remaining = 0;
if (readChunk != null) {
readChunk.position(0).limit(0);
}
endChunk = false;
needCRLFParse = false;
trailingHeaders.recycle();
trailingHeaders.setLimit(maxTrailerSize);
extensionSize = 0;
error = false;
}
/**
* Return the name of the associated encoding; Here, the value is
* "identity".
*/
@Override
public ByteChunk getEncodingName() {
return ENCODING;
}
@Override
public boolean isFinished() {
return endChunk;
}
// ------------------------------------------------------ Protected Methods
/**
* Read bytes from the previous buffer.
* @return The byte count which has been read
* @throws IOException Read error
*/
protected int readBytes() throws IOException {
return buffer.doRead(this);
}
/**
* Parse the header of a chunk.
* A chunk header can look like one of the following:<br>
* A10CRLF<br>
* F23;chunk-extension to be ignoredCRLF
*
* <p>
* The letters before CRLF or ';' (whatever comes first) must be valid hex
* digits. We should not parse F23IAMGONNAMESSTHISUP34CRLF as a valid
* header according to the spec.
* @return <code>true</code> if the chunk header has been
* successfully parsed
* @throws IOException Read error
*/
protected boolean parseChunkHeader() throws IOException {
int result = 0;
boolean eol = false;
int readDigit = 0;
boolean extension = false;
while (!eol) {
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <= 0)
return false;
}
byte chr = readChunk.get(readChunk.position());
if (chr == Constants.CR || chr == Constants.LF) {
parseCRLF(false);
eol = true;
} else if (chr == Constants.SEMI_COLON && !extension) {
// First semi-colon marks the start of the extension. Further
// semi-colons may appear to separate multiple chunk-extensions.
// These need to be processed as part of parsing the extensions.
extension = true;
extensionSize++;
} else if (!extension) {
//don't read data after the trailer
int charValue = HexUtils.getDec(chr);
if (charValue != -1 && readDigit < 8) {
readDigit++;
result = (result << 4) | charValue;
} else {
//we shouldn't allow invalid, non hex characters
//in the chunked header
return false;
}
} else {
// Extension 'parsing'
// Note that the chunk-extension is neither parsed nor
// validated. Currently it is simply ignored.
extensionSize++;
if (maxExtensionSize > -1 && extensionSize > maxExtensionSize) {
throwIOException(sm.getString("chunkedInputFilter.maxExtension"));
}
}
// Parsing the CRLF increments pos
if (!eol) {
readChunk.position(readChunk.position() + 1);
}
}
if (readDigit == 0 || result < 0) {
return false;
}
if (result == 0) {
endChunk = true;
}
remaining = result;
return true;
}
/**
* Parse CRLF at end of chunk.
*
* @param tolerant Should tolerant parsing (LF and CRLF) be used? This
* is recommended (RFC2616, section 19.3) for message
* headers.
* @throws IOException An error occurred parsing CRLF
*/
protected void parseCRLF(boolean tolerant) throws IOException {
boolean eol = false;
boolean crfound = false;
while (!eol) {
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <= 0) {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlfNoData"));
}
}
byte chr = readChunk.get(readChunk.position());
if (chr == Constants.CR) {
if (crfound) {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlfCRCR"));
}
crfound = true;
} else if (chr == Constants.LF) {
if (!tolerant && !crfound) {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlfNoCR"));
}
eol = true;
} else {
throwIOException(sm.getString("chunkedInputFilter.invalidCrlf"));
}
readChunk.position(readChunk.position() + 1);
}
}
/**
* Parse end chunk data.
* @throws IOException Error propagation
*/
protected void parseEndChunk() throws IOException {
// Handle optional trailer headers
while (parseHeader()) {
// Loop until we run out of headers
}
}
private boolean parseHeader() throws IOException {
MimeHeaders headers = request.getMimeHeaders();
byte chr = 0;
// Read new bytes if needed
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
// readBytes() above will set readChunk unless it returns a value < 0
chr = readChunk.get(readChunk.position());
// CRLF terminates the request
if (chr == Constants.CR || chr == Constants.LF) {
parseCRLF(false);
return false;
}
// Mark the current buffer position
int startPos = trailingHeaders.getEnd();
//
// Reading the header name
// Header name is always US-ASCII
//
boolean colon = false;
while (!colon) {
// Read new bytes if needed
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
// readBytes() above will set readChunk unless it returns a value < 0
chr = readChunk.get(readChunk.position());
if ((chr >= Constants.A) && (chr <= Constants.Z)) {
chr = (byte) (chr - Constants.LC_OFFSET);
}
if (chr == Constants.COLON) {
colon = true;
} else {
trailingHeaders.append(chr);
}
readChunk.position(readChunk.position() + 1);
}
int colonPos = trailingHeaders.getEnd();
//
// Reading the header value (which can be spanned over multiple lines)
//
boolean eol = false;
boolean validLine = true;
int lastSignificantChar = 0;
while (validLine) {
boolean space = true;
// Skipping spaces
while (space) {
// Read new bytes if needed
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
chr = readChunk.get(readChunk.position());
if ((chr == Constants.SP) || (chr == Constants.HT)) {
readChunk.position(readChunk.position() + 1);
// If we swallow whitespace, make sure it counts towards the
// limit placed on trailing header size
int newlimit = trailingHeaders.getLimit() -1;
if (trailingHeaders.getEnd() > newlimit) {
throwIOException(sm.getString("chunkedInputFilter.maxTrailer"));
}
trailingHeaders.setLimit(newlimit);
} else {
space = false;
}
}
// Reading bytes until the end of the line
while (!eol) {
// Read new bytes if needed
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
chr = readChunk.get(readChunk.position());
if (chr == Constants.CR || chr == Constants.LF) {
parseCRLF(true);
eol = true;
} else if (chr == Constants.SP) {
trailingHeaders.append(chr);
} else {
trailingHeaders.append(chr);
lastSignificantChar = trailingHeaders.getEnd();
}
if (!eol) {
readChunk.position(readChunk.position() + 1);
}
}
// Checking the first character of the new line. If the character
// is a LWS, then it's a multiline header
// Read new bytes if needed
if (readChunk == null || readChunk.position() >= readChunk.limit()) {
if (readBytes() <0) {
throwEOFException(sm.getString("chunkedInputFilter.eosTrailer"));
}
}
chr = readChunk.get(readChunk.position());
if ((chr != Constants.SP) && (chr != Constants.HT)) {
validLine = false;
} else {
eol = false;
// Copying one extra space in the buffer (since there must
// be at least one space inserted between the lines)
trailingHeaders.append(chr);
}
}
String headerName = new String(trailingHeaders.getBytes(), startPos,
colonPos - startPos, StandardCharsets.ISO_8859_1);
if (allowedTrailerHeaders.contains(headerName.toLowerCase(Locale.ENGLISH))) {
MessageBytes headerValue = headers.addValue(headerName);
// Set the header value
headerValue.setBytes(trailingHeaders.getBytes(), colonPos,
lastSignificantChar - colonPos);
}
return true;
}
private void throwIOException(String msg) throws IOException {
error = true;
throw new IOException(msg);
}
private void throwEOFException(String msg) throws IOException {
error = true;
throw new EOFException(msg);
}
private void checkError() throws IOException {
if (error) {
throw new IOException(sm.getString("chunkedInputFilter.error"));
}
}
@Override
public void setByteBuffer(ByteBuffer buffer) {
readChunk = buffer;
}
@Override
public ByteBuffer getByteBuffer() {
return readChunk;
}
@Override
public void expand(int size) {
// no-op
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.coyote.Response;
import org.apache.coyote.http11.HttpOutputBuffer;
import org.apache.coyote.http11.OutputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.HexUtils;
/**
* Chunked output filter.
*
* @author Remy Maucherat
*/
public class ChunkedOutputFilter implements OutputFilter {
// -------------------------------------------------------------- Constants
private static final byte[] END_CHUNK_BYTES = {(byte) '0', (byte) '\r', (byte) '\n',
(byte) '\r', (byte) '\n'};
// ------------------------------------------------------------ Constructor
/**
* Default constructor.
*/
public ChunkedOutputFilter() {
chunkHeader.put(8, (byte) '\r');
chunkHeader.put(9, (byte) '\n');
}
// ----------------------------------------------------- Instance Variables
/**
* Next buffer in the pipeline.
*/
protected HttpOutputBuffer buffer;
/**
* Chunk header.
*/
protected final ByteBuffer chunkHeader = ByteBuffer.allocate(10);
/**
* End chunk.
*/
protected final ByteBuffer endChunk = ByteBuffer.wrap(END_CHUNK_BYTES);
// ------------------------------------------------------------- Properties
// --------------------------------------------------- OutputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
@Override
public int doWrite(ByteChunk chunk) throws IOException {
int result = chunk.getLength();
if (result <= 0) {
return 0;
}
int pos = calculateChunkHeader(result);
chunkHeader.position(pos).limit(10);
buffer.doWrite(chunkHeader);
buffer.doWrite(chunk);
chunkHeader.position(8).limit(10);
buffer.doWrite(chunkHeader);
return result;
}
@Override
public int doWrite(ByteBuffer chunk) throws IOException {
int result = chunk.remaining();
if (result <= 0) {
return 0;
}
int pos = calculateChunkHeader(result);
chunkHeader.position(pos).limit(10);
buffer.doWrite(chunkHeader);
buffer.doWrite(chunk);
chunkHeader.position(8).limit(10);
buffer.doWrite(chunkHeader);
return result;
}
private int calculateChunkHeader(int len) {
// Calculate chunk header
int pos = 8;
int current = len;
while (current > 0) {
int digit = current % 16;
current = current / 16;
chunkHeader.put(--pos, HexUtils.getHex(digit));
}
return pos;
}
@Override
public long getBytesWritten() {
return buffer.getBytesWritten();
}
// --------------------------------------------------- OutputFilter Methods
@Override
public void setResponse(Response response) {
// NOOP: No need for parameters from response in this filter
}
@Override
public void setBuffer(HttpOutputBuffer buffer) {
this.buffer = buffer;
}
@Override
public void flush() throws IOException {
// No data buffered in this filter. Flush next buffer.
buffer.flush();
}
@Override
public void end() throws IOException {
// Write end chunk
buffer.doWrite(endChunk);
endChunk.position(0).limit(endChunk.capacity());
buffer.end();
}
@Override
public void recycle() {
// NOOP: Nothing to recycle
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.zip.GZIPOutputStream;
import org.apache.coyote.Response;
import org.apache.coyote.http11.HttpOutputBuffer;
import org.apache.coyote.http11.OutputFilter;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Gzip output filter.
*
* @author Remy Maucherat
*/
public class GzipOutputFilter implements OutputFilter {
protected static final Log log = LogFactory.getLog(GzipOutputFilter.class);
// ----------------------------------------------------- Instance Variables
/**
* Next buffer in the pipeline.
*/
protected HttpOutputBuffer buffer;
/**
* Compression output stream.
*/
protected GZIPOutputStream compressionStream = null;
/**
* Fake internal output stream.
*/
protected final OutputStream fakeOutputStream = new FakeOutputStream();
// --------------------------------------------------- OutputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
@Override
public int doWrite(ByteChunk chunk) throws IOException {
if (compressionStream == null) {
compressionStream = new GZIPOutputStream(fakeOutputStream, true);
}
compressionStream.write(chunk.getBytes(), chunk.getStart(),
chunk.getLength());
return chunk.getLength();
}
@Override
public int doWrite(ByteBuffer chunk) throws IOException {
if (compressionStream == null) {
compressionStream = new GZIPOutputStream(fakeOutputStream, true);
}
int len = chunk.remaining();
if (chunk.hasArray()) {
compressionStream.write(chunk.array(), chunk.arrayOffset() + chunk.position(), len);
} else {
byte[] bytes = new byte[len];
chunk.put(bytes);
compressionStream.write(bytes, 0, len);
}
return len;
}
@Override
public long getBytesWritten() {
return buffer.getBytesWritten();
}
// --------------------------------------------------- OutputFilter Methods
/**
* Added to allow flushing to happen for the gzip'ed outputstream
*/
@Override
public void flush() throws IOException {
if (compressionStream != null) {
try {
if (log.isDebugEnabled()) {
log.debug("Flushing the compression stream!");
}
compressionStream.flush();
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug("Ignored exception while flushing gzip filter", e);
}
}
}
buffer.flush();
}
@Override
public void setResponse(Response response) {
// NOOP: No need for parameters from response in this filter
}
@Override
public void setBuffer(HttpOutputBuffer buffer) {
this.buffer = buffer;
}
@Override
public void end() throws IOException {
if (compressionStream == null) {
compressionStream = new GZIPOutputStream(fakeOutputStream, true);
}
compressionStream.finish();
compressionStream.close();
buffer.end();
}
/**
* Make the filter ready to process the next request.
*/
@Override
public void recycle() {
// Set compression stream to null
compressionStream = null;
}
// ------------------------------------------- FakeOutputStream Inner Class
protected class FakeOutputStream
extends OutputStream {
protected final ByteBuffer outputChunk = ByteBuffer.allocate(1);
@Override
public void write(int b)
throws IOException {
// Shouldn't get used for good performance, but is needed for
// compatibility with Sun JDK 1.4.0
outputChunk.put(0, (byte) (b & 0xff));
buffer.doWrite(outputChunk);
}
@Override
public void write(byte[] b, int off, int len)
throws IOException {
buffer.doWrite(ByteBuffer.wrap(b, off, len));
}
@Override
public void flush() throws IOException {/*NOOP*/}
@Override
public void close() throws IOException {/*NOOP*/}
}
}

View File

@@ -0,0 +1,274 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
import org.apache.tomcat.util.res.StringManager;
/**
* Identity input filter.
*
* @author Remy Maucherat
*/
public class IdentityInputFilter implements InputFilter, ApplicationBufferHandler {
private static final StringManager sm = StringManager.getManager(
IdentityInputFilter.class.getPackage().getName());
// -------------------------------------------------------------- Constants
protected static final String ENCODING_NAME = "identity";
protected static final ByteChunk ENCODING = new ByteChunk();
// ----------------------------------------------------- Static Initializer
static {
ENCODING.setBytes(ENCODING_NAME.getBytes(StandardCharsets.ISO_8859_1),
0, ENCODING_NAME.length());
}
// ----------------------------------------------------- Instance Variables
/**
* Content length.
*/
protected long contentLength = -1;
/**
* Remaining bytes.
*/
protected long remaining = 0;
/**
* Next buffer in the pipeline.
*/
protected InputBuffer buffer;
/**
* ByteBuffer used to read leftover bytes.
*/
protected ByteBuffer tempRead;
private final int maxSwallowSize;
public IdentityInputFilter(int maxSwallowSize) {
this.maxSwallowSize = maxSwallowSize;
}
// ---------------------------------------------------- InputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doRead(ApplicationBufferHandler)}
*/
@Deprecated
@Override
public int doRead(ByteChunk chunk) throws IOException {
int result = -1;
if (contentLength >= 0) {
if (remaining > 0) {
int nRead = buffer.doRead(chunk);
if (nRead > remaining) {
// The chunk is longer than the number of bytes remaining
// in the body; changing the chunk length to the number
// of bytes remaining
chunk.setBytes(chunk.getBytes(), chunk.getStart(),
(int) remaining);
result = (int) remaining;
} else {
result = nRead;
}
if (nRead > 0) {
remaining = remaining - nRead;
}
} else {
// No more bytes left to be read : return -1 and clear the
// buffer
chunk.recycle();
result = -1;
}
}
return result;
}
@Override
public int doRead(ApplicationBufferHandler handler) throws IOException {
int result = -1;
if (contentLength >= 0) {
if (remaining > 0) {
int nRead = buffer.doRead(handler);
if (nRead > remaining) {
// The chunk is longer than the number of bytes remaining
// in the body; changing the chunk length to the number
// of bytes remaining
handler.getByteBuffer().limit(handler.getByteBuffer().position() + (int) remaining);
result = (int) remaining;
} else {
result = nRead;
}
if (nRead > 0) {
remaining = remaining - nRead;
}
} else {
// No more bytes left to be read : return -1 and clear the
// buffer
if (handler.getByteBuffer() != null) {
handler.getByteBuffer().position(0).limit(0);
}
result = -1;
}
}
return result;
}
// ---------------------------------------------------- InputFilter Methods
/**
* Read the content length from the request.
*/
@Override
public void setRequest(Request request) {
contentLength = request.getContentLengthLong();
remaining = contentLength;
}
@Override
public long end() throws IOException {
final boolean maxSwallowSizeExceeded = (maxSwallowSize > -1 && remaining > maxSwallowSize);
long swallowed = 0;
// Consume extra bytes.
while (remaining > 0) {
int nread = buffer.doRead(this);
tempRead = null;
if (nread > 0 ) {
swallowed += nread;
remaining = remaining - nread;
if (maxSwallowSizeExceeded && swallowed > maxSwallowSize) {
// Note: We do not fail early so the client has a chance to
// read the response before the connection is closed. See:
// https://httpd.apache.org/docs/2.0/misc/fin_wait_2.html#appendix
throw new IOException(sm.getString("inputFilter.maxSwallow"));
}
} else { // errors are handled higher up.
remaining = 0;
}
}
// If too many bytes were read, return the amount.
return -remaining;
}
/**
* Amount of bytes still available in a buffer.
*/
@Override
public int available() {
return 0;
}
/**
* Set the next buffer in the filter pipeline.
*/
@Override
public void setBuffer(InputBuffer buffer) {
this.buffer = buffer;
}
/**
* Make the filter ready to process the next request.
*/
@Override
public void recycle() {
contentLength = -1;
remaining = 0;
}
/**
* Return the name of the associated encoding; Here, the value is
* "identity".
*/
@Override
public ByteChunk getEncodingName() {
return ENCODING;
}
@Override
public boolean isFinished() {
// Only finished if a content length is defined and there is no data
// remaining
return contentLength > -1 && remaining <= 0;
}
@Override
public void setByteBuffer(ByteBuffer buffer) {
tempRead = buffer;
}
@Override
public ByteBuffer getByteBuffer() {
return tempRead;
}
@Override
public void expand(int size) {
// no-op
}
}

View File

@@ -0,0 +1,175 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.coyote.Response;
import org.apache.coyote.http11.HttpOutputBuffer;
import org.apache.coyote.http11.OutputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Identity output filter.
*
* @author Remy Maucherat
*/
public class IdentityOutputFilter implements OutputFilter {
// ----------------------------------------------------- Instance Variables
/**
* Content length.
*/
protected long contentLength = -1;
/**
* Remaining bytes.
*/
protected long remaining = 0;
/**
* Next buffer in the pipeline.
*/
protected HttpOutputBuffer buffer;
// --------------------------------------------------- OutputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
@Override
public int doWrite(ByteChunk chunk) throws IOException {
int result = -1;
if (contentLength >= 0) {
if (remaining > 0) {
result = chunk.getLength();
if (result > remaining) {
// The chunk is longer than the number of bytes remaining
// in the body; changing the chunk length to the number
// of bytes remaining
chunk.setBytes(chunk.getBytes(), chunk.getStart(),
(int) remaining);
result = (int) remaining;
remaining = 0;
} else {
remaining = remaining - result;
}
buffer.doWrite(chunk);
} else {
// No more bytes left to be written : return -1 and clear the
// buffer
chunk.recycle();
result = -1;
}
} else {
// If no content length was set, just write the bytes
buffer.doWrite(chunk);
result = chunk.getLength();
}
return result;
}
@Override
public int doWrite(ByteBuffer chunk) throws IOException {
int result = -1;
if (contentLength >= 0) {
if (remaining > 0) {
result = chunk.remaining();
if (result > remaining) {
// The chunk is longer than the number of bytes remaining
// in the body; changing the chunk length to the number
// of bytes remaining
chunk.limit(chunk.position() + (int) remaining);
result = (int) remaining;
remaining = 0;
} else {
remaining = remaining - result;
}
buffer.doWrite(chunk);
} else {
// No more bytes left to be written : return -1 and clear the
// buffer
chunk.position(0);
chunk.limit(0);
result = -1;
}
} else {
// If no content length was set, just write the bytes
result = chunk.remaining();
buffer.doWrite(chunk);
result -= chunk.remaining();
}
return result;
}
@Override
public long getBytesWritten() {
return buffer.getBytesWritten();
}
// --------------------------------------------------- OutputFilter Methods
@Override
public void setResponse(Response response) {
contentLength = response.getContentLengthLong();
remaining = contentLength;
}
@Override
public void setBuffer(HttpOutputBuffer buffer) {
this.buffer = buffer;
}
@Override
public void flush() throws IOException {
// No data buffered in this filter. Flush next buffer.
buffer.flush();
}
@Override
public void end() throws IOException {
buffer.end();
}
@Override
public void recycle() {
contentLength = -1;
remaining = 0;
}
}

View File

@@ -0,0 +1,27 @@
# 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.
chunkedInputFilter.eos=Unexpected end of stream while reading request body
chunkedInputFilter.eosTrailer=Unexpected end of stream while reading trailer headers
chunkedInputFilter.error=No data available due to previous error
chunkedInputFilter.invalidCrlf=Invalid end of line sequence (character other than CR or LF found)
chunkedInputFilter.invalidCrlfCRCR=Invalid end of line sequence (CRCR)
chunkedInputFilter.invalidCrlfNoCR=Invalid end of line sequence (No CR before LF)
chunkedInputFilter.invalidCrlfNoData=Invalid end of line sequence (no data available to read)
chunkedInputFilter.invalidHeader=Invalid chunk header
chunkedInputFilter.maxExtension=maxExtensionSize exceeded
chunkedInputFilter.maxTrailer=maxTrailerSize exceeded
inputFilter.maxSwallow=maxSwallowSize exceeded

View File

@@ -0,0 +1,18 @@
# 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.
chunkedInputFilter.eos=Unerwartetes Ende des Streams beim lesen des Request Bodys.
chunkedInputFilter.eosTrailer=Unerwartetes Ende des Eingabestroms während die Trailer-Header gelesen wurden
chunkedInputFilter.invalidCrlfNoCR=Falsche Buchstabensequenz für Zeilenende (richtig wäre <CR><LF>)

View File

@@ -0,0 +1,18 @@
# 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.
chunkedInputFilter.eos=Fin del flujo inesperado mientras se leía la petición del cuerpo
chunkedInputFilter.eosTrailer=Fin inesperado de stream mintras se leían las cabeceras finales
chunkedInputFilter.invalidCrlfNoCR=Fin de linea incorrecto (CR no debe estar antes de LF)

View File

@@ -0,0 +1,27 @@
# 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.
chunkedInputFilter.eos=Fin du flux inattendue durant la lecture du corps de la requête
chunkedInputFilter.eosTrailer=Fin inattendue de flux lors de la lecture des en-têtes de fin (trailer headers)
chunkedInputFilter.error=Aucune donnée disponible suite à l'erreur précédente
chunkedInputFilter.invalidCrlf=Séquence de fin de ligne invalide, un caractère autre que CR ou LF a été trouvé
chunkedInputFilter.invalidCrlfCRCR=Séquence de fin de ligne invalide, CR CR
chunkedInputFilter.invalidCrlfNoCR=Terminateur de ligne incorrect (manque CR devant LF)
chunkedInputFilter.invalidCrlfNoData=Séquence de fin de ligne invalide (aucune donnée disponible en lecture)
chunkedInputFilter.invalidHeader=En-tête de morceau (chunk) invalide
chunkedInputFilter.maxExtension=maxExtensionSize a été dépassé
chunkedInputFilter.maxTrailer=maxTrailerSize a été dépassé
inputFilter.maxSwallow=maxSwallowSize a été dépassé

View File

@@ -0,0 +1,27 @@
# 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.
chunkedInputFilter.eos=リクエストボディの読み取り中に予期せぬストリームの終端が見つかりました。
chunkedInputFilter.eosTrailer=トレーラーヘッダーの読み込み中に突然ストリームが終了しました。
chunkedInputFilter.error=以前のエラーのため利用できるデータがありません。
chunkedInputFilter.invalidCrlf=無効なEOLシーケンスCRまたはLF以外の文字が見つかりました
chunkedInputFilter.invalidCrlfCRCR=無効なEOLシーケンスCRCR
chunkedInputFilter.invalidCrlfNoCR=不正な改行コードです (LFの前にCRがありません)。
chunkedInputFilter.invalidCrlfNoData=無効なEOL順序読み込み可能なデータがありません
chunkedInputFilter.invalidHeader=不正なチャンクヘッダーです。
chunkedInputFilter.maxExtension=maxExtensionSizeを超過しました
chunkedInputFilter.maxTrailer=maxTrailerSize を超過しています。
inputFilter.maxSwallow=maxShallowSize を超えました。

View File

@@ -0,0 +1,27 @@
# 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.
chunkedInputFilter.eos=요청의 body를 읽는 동안 예기치 않은 스트림의 끝
chunkedInputFilter.eosTrailer=Trailer 헤더들을 읽는 중 예기치 않은 스트림의 끝
chunkedInputFilter.error=이전 오류로 인하여 데이터가 가용하지 않습니다.
chunkedInputFilter.invalidCrlf=유효하지 않은 행의 끝 시퀀스입니다. (CR 또는 LF가 아닌 다른 문자가 발견됨)
chunkedInputFilter.invalidCrlfCRCR=유효하지 않은 행의 끝 시퀀스 (CRCR)
chunkedInputFilter.invalidCrlfNoCR=유효하지 않은 라인 끝 시퀀스 (CR 바이트가 LF 바이트 전에 존재하지 않음)
chunkedInputFilter.invalidCrlfNoData=유효하지 않은 행의 끝 시퀀스 (더 이상 읽을 데이터가 없음)
chunkedInputFilter.invalidHeader=유효하지 않은 chunk 헤더
chunkedInputFilter.maxExtension=maxExtensionSize 값을 초과했습니다.
chunkedInputFilter.maxTrailer=maxTrailerSize 값을 초과했습니다.
inputFilter.maxSwallow=maxSwallowSize 값을 초과했습니다.

View File

@@ -0,0 +1,25 @@
# 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.
chunkedInputFilter.eos=读取请求主体时流的意外结束
chunkedInputFilter.eosTrailer=读取 trailer 头时出现意外的流结束
chunkedInputFilter.error=没有数据可用由于先前的错误
chunkedInputFilter.invalidCrlfCRCR=无效的结束的行序列CRCR)
chunkedInputFilter.invalidCrlfNoCR=无效的行尾结束符序列(LF前缺少CR)
chunkedInputFilter.invalidCrlfNoData=无效的行尾序列(没有可读取的数据)
chunkedInputFilter.maxExtension=超过最大扩展数
chunkedInputFilter.maxTrailer=超过最大数
inputFilter.maxSwallow=最大吞咽数据大小超出异常

View File

@@ -0,0 +1,137 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
* Input filter responsible for replaying the request body when restoring the
* saved request after FORM authentication.
*/
public class SavedRequestInputFilter implements InputFilter {
/**
* The original request body.
*/
protected ByteChunk input = null;
/**
* Create a new SavedRequestInputFilter.
*
* @param input The saved request body to be replayed.
*/
public SavedRequestInputFilter(ByteChunk input) {
this.input = input;
}
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doRead(ApplicationBufferHandler)}
*/
@Deprecated
@Override
public int doRead(ByteChunk chunk) throws IOException {
if(input.getOffset()>= input.getEnd())
return -1;
int writeLength = 0;
if (chunk.getLimit() > 0 && chunk.getLimit() < input.getLength()) {
writeLength = chunk.getLimit();
} else {
writeLength = input.getLength();
}
input.substract(chunk.getBuffer(), 0, writeLength);
chunk.setOffset(0);
chunk.setEnd(writeLength);
return writeLength;
}
@Override
public int doRead(ApplicationBufferHandler handler) throws IOException {
if(input.getOffset()>= input.getEnd())
return -1;
ByteBuffer byteBuffer = handler.getByteBuffer();
byteBuffer.position(byteBuffer.limit()).limit(byteBuffer.capacity());
input.substract(byteBuffer);
return byteBuffer.remaining();
}
/**
* Set the content length on the request.
*/
@Override
public void setRequest(org.apache.coyote.Request request) {
request.setContentLength(input.getLength());
}
/**
* Make the filter ready to process the next request.
*/
@Override
public void recycle() {
input = null;
}
/**
* Return the name of the associated encoding; here, the value is null.
*/
@Override
public ByteChunk getEncodingName() {
return null;
}
/**
* Set the next buffer in the filter pipeline (has no effect).
*/
@Override
public void setBuffer(InputBuffer buffer) {
// NOOP since this filter will be providing the request body
}
/**
* Amount of bytes still available in a buffer.
*/
@Override
public int available() {
return input.getLength();
}
/**
* End the current request (has no effect).
*/
@Override
public long end() throws IOException {
return 0;
}
@Override
public boolean isFinished() {
return input.getOffset() >= input.getEnd();
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.Request;
import org.apache.coyote.http11.InputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
/**
* Void input filter, which returns -1 when attempting a read. Used with a GET,
* HEAD, or a similar request.
*
* @author Remy Maucherat
*/
public class VoidInputFilter implements InputFilter {
// -------------------------------------------------------------- Constants
protected static final String ENCODING_NAME = "void";
protected static final ByteChunk ENCODING = new ByteChunk();
// ----------------------------------------------------- Static Initializer
static {
ENCODING.setBytes(ENCODING_NAME.getBytes(StandardCharsets.ISO_8859_1),
0, ENCODING_NAME.length());
}
// ---------------------------------------------------- InputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doRead(ApplicationBufferHandler)}
*/
@Deprecated
@Override
public int doRead(ByteChunk chunk) throws IOException {
return -1;
}
@Override
public int doRead(ApplicationBufferHandler handler) throws IOException {
return -1;
}
// ---------------------------------------------------- InputFilter Methods
/**
* Set the associated request.
*/
@Override
public void setRequest(Request request) {
// NOOP: Request isn't used so ignore it
}
/**
* Set the next buffer in the filter pipeline.
*/
@Override
public void setBuffer(InputBuffer buffer) {
// NOOP: No body to read
}
/**
* Make the filter ready to process the next request.
*/
@Override
public void recycle() {
// NOOP
}
/**
* Return the name of the associated encoding; Here, the value is
* "void".
*/
@Override
public ByteChunk getEncodingName() {
return ENCODING;
}
/**
* End the current request. It is acceptable to write extra bytes using
* buffer.doWrite during the execution of this method.
*
* @return Should return 0 unless the filter does some content length
* delimitation, in which case the number is the amount of extra bytes or
* missing bytes, which would indicate an error.
* Note: It is recommended that extra bytes be swallowed by the filter.
*/
@Override
public long end() throws IOException {
return 0;
}
@Override
public int available() {
return 0;
}
@Override
public boolean isFinished() {
return true;
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.http11.filters;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.coyote.Response;
import org.apache.coyote.http11.HttpOutputBuffer;
import org.apache.coyote.http11.OutputFilter;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Void output filter, which silently swallows bytes written. Used with a 204
* status (no content) or a HEAD request.
*
* @author Remy Maucherat
*/
public class VoidOutputFilter implements OutputFilter {
private HttpOutputBuffer buffer = null;
// --------------------------------------------------- OutputBuffer Methods
/**
* @deprecated Unused. Will be removed in Tomcat 9. Use
* {@link #doWrite(ByteBuffer)}
*/
@Deprecated
@Override
public int doWrite(ByteChunk chunk) throws IOException {
return chunk.getLength();
}
@Override
public int doWrite(ByteBuffer chunk) throws IOException {
return chunk.remaining();
}
@Override
public long getBytesWritten() {
return 0;
}
// --------------------------------------------------- OutputFilter Methods
@Override
public void setResponse(Response response) {
// NOOP: No need for parameters from response in this filter
}
@Override
public void setBuffer(HttpOutputBuffer buffer) {
this.buffer = buffer;
}
@Override
public void flush() throws IOException {
this.buffer.flush();
}
@Override
public void recycle() {
buffer = null;
}
@Override
public void end() throws IOException {
buffer.end();
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.http11.upgrade;
import javax.servlet.http.HttpUpgradeHandler;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.SocketEvent;
import org.apache.tomcat.util.net.SocketWrapperBase;
/**
* This Tomcat specific interface is implemented by handlers that require direct
* access to Tomcat's I/O layer rather than going through the Servlet API.
*/
public interface InternalHttpUpgradeHandler extends HttpUpgradeHandler {
SocketState upgradeDispatch(SocketEvent status);
void timeoutAsync(long now);
void setSocketWrapper(SocketWrapperBase<?> wrapper);
void setSslSupport(SSLSupport sslSupport);
void pause();
}

View File

@@ -0,0 +1,36 @@
# 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.
upgrade.sis.errorCloseFail=Failed to close InputStream cleanly after a previous error
upgrade.sis.isFinished.ise=It is illegal to call isFinished() when the ServletInputStream is not in non-blocking mode (i.e. setReadListener() must be called first)
upgrade.sis.isReady.ise=It is illegal to call isReady() when the ServletInputStream is not in non-blocking mode (i.e. setReadListener() must be called first)
upgrade.sis.onErrorFail=onError processing for the registered ReadListener triggered this further error which was swallowed
upgrade.sis.read.closed=The InputStream has been closed
upgrade.sis.read.ise=It is illegal to call any of the read() methods in non-blocking mode without first checking that there is data available by calling isReady()
upgrade.sis.readListener.null=It is illegal to pass null to setReadListener()
upgrade.sis.readListener.set=It is illegal to call setReadListener() more than once for the same upgraded connection
upgrade.sos.canWrite.ise=It is illegal to call canWrite() when the ServletOutputStream is not in non-blocking mode (i.e. setWriteListener() must be called first)
upgrade.sos.errorCloseFail=Failed to close OutputStream cleanly after a previous error
upgrade.sos.onErrorFail=onError processing for the registered WriteListener triggered this further error which was swallowed
upgrade.sos.write.closed=The OutputStream has been closed
upgrade.sos.write.ise=It is illegal to call any of the write() methods in non-blocking mode without first checking that there is space available by calling isReady()
upgrade.sos.writeListener.null=It is illegal to pass null to setWriteListener()
upgrade.sos.writeListener.set=It is illegal to call setWriteListener() more than once for the same upgraded connection
upgradeProcessor.isCloseFail=Failed to close input stream associated with upgraded connection
upgradeProcessor.osCloseFail=Failed to close output stream associated with upgraded connection
upgradeProcessor.requiredClose=Closing upgraded connection due to closeRequired state of streams: Input [{0}], Output [{1}]
upgradeProcessor.stop=Closing upgraded connection as incoming socket status was STOP
upgradeProcessor.unexpectedState=Closing upgraded connection unexpectedly as incoming socket status was [{0}]

View File

@@ -0,0 +1,18 @@
# 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.
upgrade.sis.read.closed=Der InputStream wurde geschlossen.
upgrade.sis.readListener.set=setReadListener() darf nicht mehr als einmal für eine Upgraded-Verbindung aufgerufen werden
upgrade.sos.writeListener.null=setWriteListener() darf nicht mit null aufgerufen werden

View File

@@ -0,0 +1,20 @@
# 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.
upgrade.sis.read.closed=El InputStream ha sido cerrado.
upgrade.sis.readListener.set=Es ilegal llamar a setReadListener() más de una vez para la misma conexión actualizada\n
upgrade.sos.writeListener.null=Es ilegal pasar valor nulo a setWriteListener()\n
upgradeProcessor.unexpectedState=Cerrando la conexión actualizada inesperadamente debido al que el estatus del socket fue [{0}]\n

View File

@@ -0,0 +1,36 @@
# 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.
upgrade.sis.errorCloseFail=Impossible de fermer l'InputStream proprement après une précédente erreur
upgrade.sis.isFinished.ise=Il est illégal d'appeler isFinished() quand le ServletInputStream n'est pas en mode non bloquant, c'est à dire que setReadListener() doit d'abord être appelé
upgrade.sis.isReady.ise=il est illégal d'appeler isReady() quand le ServletInputStream n'est pas en mode non bloquant, c'est à dire que setReadListener() doit d'abord être appelé
upgrade.sis.onErrorFail=Le traitement de onError pour le ReadListener configuré a causé cette erreur qui a été avalée
upgrade.sis.read.closed=Le flux d'entrée (InputStream) a été fermé
upgrade.sis.read.ise=Il est interdit d'appeler une des méthodes read() en mode non bloquant avant de d'abord vérifier qu'il y a des données disponibles en utilisant isReady()
upgrade.sis.readListener.null=Il est illégal de passer un argument null à setReadListener()
upgrade.sis.readListener.set=Il est interdit d'appeler setReadListener() plus d'une fois pour une même connection upgradée
upgrade.sos.canWrite.ise=il est illégal d'appeler canWrite() quand le ServletOutputStream n'est pas en mode non bloquant, c'est à dire que setWriteListener() doit d'abord être appelé
upgrade.sos.errorCloseFail=Impossible de fermer l'OutputStream proprement après une précédente erreur
upgrade.sos.onErrorFail=Le traitement de onError pour le WriteListener configuré a causé cette erreur qui a été avalée
upgrade.sos.write.closed=L'OutputSteam a été fermée
upgrade.sos.write.ise=Il est interdit d'appeler une des méthodes write() en mode non bloquant avant de d'abord vérifier qu'il y a de la place disponible en utilisant isReady()
upgrade.sos.writeListener.null=Il est illégal de passer un argument null à setWriteListener()
upgrade.sos.writeListener.set=Il est interdit d'appeler setWriteListener() plus d'une fois pour une même connection upgradée
upgradeProcessor.isCloseFail=Impossible de fermer l'InputStream associée avec la connection upgradée
upgradeProcessor.osCloseFail=Impossible de fermer l'OutputStream associée avec la connection upgradée
upgradeProcessor.requiredClose=Fermeture de la connection upgradée à cause de l''état du closeRequired des flux: Entrée [{0}] Sortie [{1}]
upgradeProcessor.stop=Fermeture de la connection upgradée car l'état du socket est STOP
upgradeProcessor.unexpectedState=Fermeture inattendue de la connection upgradée alors que le statut du socket en lecture est [{0}]

View File

@@ -0,0 +1,36 @@
# 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.
upgrade.sis.errorCloseFail=直前のエラーの後に完全にInputStreamを閉じることができませんでした。
upgrade.sis.isFinished.ise=ノンブロッキングモードではない ServletInputStream の isFinished() を呼び出すことはできません。(すなわち最初に setReadListener() を呼び出さなければなりません。)
upgrade.sis.isReady.ise=ServletInputStreamが非ブロッキングモードでない場合つまり、最初にsetReadListener()を呼び出さなければならない場合isReady()を呼び出すことはできません。
upgrade.sis.onErrorFail=登録されたReadListenerのonError処理により、さらなるエラーがトリガされました。エラーは飲み込まれました。
upgrade.sis.read.closed=InputStream はすでに切断されています。
upgrade.sis.read.ise=isReady()を呼び出すことによって利用可能なデータがあることを最初にチェックすることなく、非ブロックモードでread()メソッドを呼び出すことは不正です。
upgrade.sis.readListener.null=setReadListener() には null を指定できません。
upgrade.sis.readListener.set=アップグレードしたコネクションに何度も setReadListener() を呼び出すのは不正な操作です。
upgrade.sos.canWrite.ise=ServletOutputStreamが非ブロッキングモードでない場合つまり、setWriteListener()を最初に呼び出さなければならない場合、canWrite()を呼び出すことはできません。
upgrade.sos.errorCloseFail=以前のエラーの後にOutputStreamをきれいに閉じることができませんでした。
upgrade.sos.onErrorFail=登録されたWriteListenerのonError処理により、これがトリガされました。さらなるエラーは飲み込まれました。
upgrade.sos.write.closed=OutputStreamはクローズされました
upgrade.sos.write.ise=ノンブロッキングモードでは初めに isReady() を呼び出して利用可能な領域があることを確かめなければ、あらゆる write() メソッドの呼び出しは不正になります。
upgrade.sos.writeListener.null=setWriteListener() に null を渡すのは不正な操作です。
upgrade.sos.writeListener.set=同じアップグレードされたコネクションに対してsetWriteListener()を複数回呼び出すことはできません。
upgradeProcessor.isCloseFail=アップグレードされたコネクションに関連する入力ストリームを閉じることができませんでした。
upgradeProcessor.osCloseFail=アップグレードされた接続に関連する出力ストリームを閉じることができませんでした
upgradeProcessor.requiredClose=ストリームのcloseRequired状態によるアップグレードされた接続のクローズ入力[{0}]、出力[{1}]
upgradeProcessor.stop=到着したソケットステータスがSTOPであるためにアップグレードされたコネクションを閉じます。
upgradeProcessor.unexpectedState=ソケットの状態は [{0}] でしたがアップグレードしたコネクションは予期せぬ理由で切断しました。

View File

@@ -0,0 +1,36 @@
# 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.
upgrade.sis.errorCloseFail=이전 오류 발생 이후, InputStream을 깨끗하게 닫지 못했습니다.
upgrade.sis.isFinished.ise=ServletInputStream이 non-blocking 모드 안에 있지 않을 때, isFinished()를 호출하는 것은 불허됩니다. (즉, setReadListener()가 먼저 호출되어야만 합니다.)
upgrade.sis.isReady.ise=ServletInputStream이 non-blocking 모드 안에 있지 않을 때, isReady()를 호출하는 것은 불허됩니다. (즉 setReadListener()가 반드시 먼저 호출되어야 합니다.)
upgrade.sis.onErrorFail=등록된 ReadListener를 위한 onError 처리가 더많은 오류를 유발시켰습니다. 이 추가 오류는 별도로 표출되지 않습니다.
upgrade.sis.read.closed=InputStream은 이미 닫혀 있습니다.
upgrade.sis.read.ise=Non-blocking 모드에서는, 먼저 isReady()를 호출하여 가용한 데이터가 있는지 여부를 점검하지 않은 상태에서, 어떤 종류의 read() 메소드라도 호출하는 것은 불허됩니다.
upgrade.sis.readListener.null=setReadListener()에 널을 넘기는 것은 불허됩니다.
upgrade.sis.readListener.set=업그레이드된 동일한 연결을 위해, setReadListener()를 두 번 이상 호출하는 것은 불허됩니다.
upgrade.sos.canWrite.ise=ServletOutputStream이 non-blocking 모드 안에 있지 않을 때, canWrite()를 호출하는 것은 불허됩니다. (즉 setWriteListener()가 반드시 먼저 호출되어야 합니다.)
upgrade.sos.errorCloseFail=이전 오류 발생 이후, OutputStream을 깨끗하게 닫지 못했습니다.
upgrade.sos.onErrorFail=등록된 WriteListener를 위한 onError 처리가 이 오류를 더 유발시켰습니다. 이 추가 오류는 별도로 표출되지 않습니다.
upgrade.sos.write.closed=OutputStream이 이미 닫혀 있습니다.
upgrade.sos.write.ise=Non-blocking 모드에서는, 먼저 isReady()를 호출하여 공간이 남아있는지 점검하지 않고, 어떤 종류의 write() 메소드들을 호출하는 것은 불허됩니다.
upgrade.sos.writeListener.null=setWriteListener() 호출 시, 널을 넘기는 것은 불허됩니다.
upgrade.sos.writeListener.set=동일한 업그레이드된 연결에 대하여, setWriteListener()를 두번 이상 호출하는 것은 불허됩니다.
upgradeProcessor.isCloseFail=업그레이드된 연결과 연관된 입력 스트림을 닫지 못했습니다.
upgradeProcessor.osCloseFail=업그레이드된 연결과 연관된 출력 스트림을 닫지 못했습니다.
upgradeProcessor.requiredClose=스트림들의 closeRequired 상태로 인하여, 업그레이드된 연결을 닫습니다: 입력 [{0}], 출력 [{1}]
upgradeProcessor.stop=Incoming 소켓의 상태가 STOP임에 따라, 업그레이드된 연결을 닫습니다.
upgradeProcessor.unexpectedState=Incoming 소켓의 상태가 [{0}](이)라서, 업그레이드된 연결을 예기치 않게 종료합니다.

View File

@@ -0,0 +1,16 @@
# 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.
upgrade.sis.read.closed=InputStream был закрыт

View File

@@ -0,0 +1,26 @@
# 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.
upgrade.sis.isFinished.ise=当 ServletInputStream 不处于非阻塞模式时调用 isFinished() 是非法的(即必须首先调用 setReadListener()
upgrade.sis.onErrorFail=对注册的readlistener的错误处理触发了这个进一步的错误该错误被吞入
upgrade.sis.read.closed=InputStream 已被关闭
upgrade.sis.read.ise=在非阻塞模式下调用任何read()方法而不首先通过调用isready()检查是否有可用的数据是非法的
upgrade.sis.readListener.set=在同一个upgraded连接上调用多次setReadListener()函数是非法的
upgrade.sos.onErrorFail=对注册的WriteListener 的错误处理触发了这个进一步的错误,该错误被吞入
upgrade.sos.write.closed=输出流已被关闭
upgrade.sos.write.ise=在非阻塞模式下调用任何写()方法都是非法的而无需首先检查是否有可用的空间只需调用isreadi()
upgrade.sos.writeListener.null=对setWriteListener()传递null是非法的
upgradeProcessor.unexpectedState=因传入套接字状态为[{0}]而意外关闭升级连接

View File

@@ -0,0 +1,105 @@
/*
* 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.http11.upgrade;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.servlet.http.WebConnection;
import org.apache.coyote.AbstractProcessorLight;
import org.apache.coyote.Request;
import org.apache.coyote.UpgradeToken;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SocketWrapperBase;
public abstract class UpgradeProcessorBase extends AbstractProcessorLight implements WebConnection {
protected static final int INFINITE_TIMEOUT = -1;
private final UpgradeToken upgradeToken;
public UpgradeProcessorBase(UpgradeToken upgradeToken) {
this.upgradeToken = upgradeToken;
}
// ------------------------------------------- Implemented Processor methods
@Override
public final boolean isUpgrade() {
return true;
}
@Override
public UpgradeToken getUpgradeToken() {
return upgradeToken;
}
@Override
public final void recycle() {
// Currently a NO-OP as upgrade processors are not recycled.
}
// ---------------------------- Processor methods that are NO-OP for upgrade
@Override
public final SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {
return null;
}
@Override
public final SocketState asyncPostProcess() {
return null;
}
@Override
public final boolean isAsync() {
return false;
}
@Override
public final Request getRequest() {
return null;
}
@Override
public ByteBuffer getLeftoverInput() {
return null;
}
@Override
public boolean checkAsyncTimeoutGeneration() {
return false;
}
// ----------------- Processor methods that are NO-OP by default for upgrade
@Override
public void timeoutAsync(long now) {
// NO-OP
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.http11.upgrade;
import java.io.IOException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import org.apache.coyote.UpgradeToken;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
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;
public class UpgradeProcessorExternal extends UpgradeProcessorBase {
private static final Log log = LogFactory.getLog(UpgradeProcessorExternal.class);
private static final StringManager sm = StringManager.getManager(UpgradeProcessorExternal.class);
private final UpgradeServletInputStream upgradeServletInputStream;
private final UpgradeServletOutputStream upgradeServletOutputStream;
public UpgradeProcessorExternal(SocketWrapperBase<?> wrapper,
UpgradeToken upgradeToken) {
super(upgradeToken);
this.upgradeServletInputStream = new UpgradeServletInputStream(this, wrapper);
this.upgradeServletOutputStream = new UpgradeServletOutputStream(this, wrapper);
/*
* Leave timeouts in the hands of the upgraded protocol.
*/
wrapper.setReadTimeout(INFINITE_TIMEOUT);
wrapper.setWriteTimeout(INFINITE_TIMEOUT);
}
@Override
protected Log getLog() {
return log;
}
// --------------------------------------------------- AutoCloseable methods
@Override
public void close() throws Exception {
upgradeServletInputStream.close();
upgradeServletOutputStream.close();
}
// --------------------------------------------------- WebConnection methods
@Override
public ServletInputStream getInputStream() throws IOException {
return upgradeServletInputStream;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return upgradeServletOutputStream;
}
// ------------------------------------------- Implemented Processor methods
@Override
public final SocketState dispatch(SocketEvent status) {
if (status == SocketEvent.OPEN_READ) {
upgradeServletInputStream.onDataAvailable();
} else if (status == SocketEvent.OPEN_WRITE) {
upgradeServletOutputStream.onWritePossible();
} else if (status == SocketEvent.STOP) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("upgradeProcessor.stop"));
}
try {
upgradeServletInputStream.close();
} catch (IOException ioe) {
log.debug(sm.getString("upgradeProcessor.isCloseFail", ioe));
}
try {
upgradeServletOutputStream.close();
} catch (IOException ioe) {
log.debug(sm.getString("upgradeProcessor.osCloseFail", ioe));
}
return SocketState.CLOSED;
} else {
// Unexpected state
if (log.isDebugEnabled()) {
log.debug(sm.getString("upgradeProcessor.unexpectedState"));
}
return SocketState.CLOSED;
}
if (upgradeServletInputStream.isClosed() &&
upgradeServletOutputStream.isClosed()) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("upgradeProcessor.requiredClose",
Boolean.valueOf(upgradeServletInputStream.isClosed()),
Boolean.valueOf(upgradeServletOutputStream.isClosed())));
}
return SocketState.CLOSED;
}
return SocketState.UPGRADED;
}
// ----------------------------------------- Unimplemented Processor methods
@Override
public final void setSslSupport(SSLSupport sslSupport) {
// NO-OP
}
@Override
public void pause() {
// NOOP
}
}

View File

@@ -0,0 +1,101 @@
/*
* 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.http11.upgrade;
import java.io.IOException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import org.apache.coyote.UpgradeToken;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.SocketEvent;
import org.apache.tomcat.util.net.SocketWrapperBase;
public class UpgradeProcessorInternal extends UpgradeProcessorBase {
private static final Log log = LogFactory.getLog(UpgradeProcessorInternal.class);
private final InternalHttpUpgradeHandler internalHttpUpgradeHandler;
public UpgradeProcessorInternal(SocketWrapperBase<?> wrapper,
UpgradeToken upgradeToken) {
super(upgradeToken);
this.internalHttpUpgradeHandler = (InternalHttpUpgradeHandler) upgradeToken.getHttpUpgradeHandler();
/*
* Leave timeouts in the hands of the upgraded protocol.
*/
wrapper.setReadTimeout(INFINITE_TIMEOUT);
wrapper.setWriteTimeout(INFINITE_TIMEOUT);
internalHttpUpgradeHandler.setSocketWrapper(wrapper);
}
@Override
public SocketState dispatch(SocketEvent status) {
return internalHttpUpgradeHandler.upgradeDispatch(status);
}
@Override
public final void setSslSupport(SSLSupport sslSupport) {
internalHttpUpgradeHandler.setSslSupport(sslSupport);
}
@Override
public void pause() {
internalHttpUpgradeHandler.pause();
}
@Override
protected Log getLog() {
return log;
}
@Override
public void timeoutAsync(long now) {
internalHttpUpgradeHandler.timeoutAsync(now);
}
// --------------------------------------------------- AutoCloseable methods
@Override
public void close() throws Exception {
internalHttpUpgradeHandler.destroy();
}
// --------------------------------------------------- WebConnection methods
@Override
public ServletInputStream getInputStream() throws IOException {
return null;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return null;
}
}

View File

@@ -0,0 +1,258 @@
/*
* 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.http11.upgrade;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import org.apache.coyote.ContainerThreadMarker;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.net.DispatchType;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
public class UpgradeServletInputStream extends ServletInputStream {
private static final Log log = LogFactory.getLog(UpgradeServletInputStream.class);
private static final StringManager sm =
StringManager.getManager(UpgradeServletInputStream.class);
private final UpgradeProcessorBase processor;
private final SocketWrapperBase<?> socketWrapper;
private volatile boolean closed = false;
private volatile boolean eof = false;
// Start in blocking-mode
private volatile Boolean ready = Boolean.TRUE;
private volatile ReadListener listener = null;
public UpgradeServletInputStream(UpgradeProcessorBase processor,
SocketWrapperBase<?> socketWrapper) {
this.processor = processor;
this.socketWrapper = socketWrapper;
}
@Override
public final boolean isFinished() {
if (listener == null) {
throw new IllegalStateException(
sm.getString("upgrade.sis.isFinished.ise"));
}
return eof;
}
@Override
public final boolean isReady() {
if (listener == null) {
throw new IllegalStateException(
sm.getString("upgrade.sis.isReady.ise"));
}
if (eof || closed) {
return false;
}
// If we already know the current state, return it.
if (ready != null) {
return ready.booleanValue();
}
try {
ready = Boolean.valueOf(socketWrapper.isReadyForRead());
} catch (IOException e) {
onError(e);
}
return ready.booleanValue();
}
@Override
public final void setReadListener(ReadListener listener) {
if (listener == null) {
throw new IllegalArgumentException(
sm.getString("upgrade.sis.readListener.null"));
}
if (this.listener != null) {
throw new IllegalArgumentException(
sm.getString("upgrade.sis.readListener.set"));
}
if (closed) {
throw new IllegalStateException(sm.getString("upgrade.sis.read.closed"));
}
this.listener = listener;
// Container is responsible for first call to onDataAvailable().
if (ContainerThreadMarker.isContainerThread()) {
processor.addDispatch(DispatchType.NON_BLOCKING_READ);
} else {
socketWrapper.registerReadInterest();
}
// Switching to non-blocking. Don't know if data is available.
ready = null;
}
@Override
public final int read() throws IOException {
preReadChecks();
return readInternal();
}
@Override
public final int readLine(byte[] b, int off, int len) throws IOException {
preReadChecks();
if (len <= 0) {
return 0;
}
int count = 0, c;
while ((c = readInternal()) != -1) {
b[off++] = (byte) c;
count++;
if (c == '\n' || count == len) {
break;
}
}
return count > 0 ? count : -1;
}
@Override
public final int read(byte[] b, int off, int len) throws IOException {
preReadChecks();
try {
int result = socketWrapper.read(listener == null, b, off, len);
if (result == -1) {
eof = true;
}
return result;
} catch (IOException ioe) {
close();
throw ioe;
}
}
@Override
public void close() throws IOException {
eof = true;
closed = true;
}
private void preReadChecks() {
if (listener != null && (ready == null || !ready.booleanValue())) {
throw new IllegalStateException(sm.getString("upgrade.sis.read.ise"));
}
if (closed) {
throw new IllegalStateException(sm.getString("upgrade.sis.read.closed"));
}
// No longer know if data is available
ready = null;
}
private int readInternal() throws IOException {
// Single byte reads for non-blocking need special handling so all
// single byte reads run through this method.
byte[] b = new byte[1];
int result;
try {
result = socketWrapper.read(listener == null, b, 0, 1);
} catch (IOException ioe) {
close();
throw ioe;
}
if (result == 0) {
return -1;
} else if (result == -1) {
eof = true;
return -1;
} else {
return b[0] & 0xFF;
}
}
final void onDataAvailable() {
try {
if (listener == null || !socketWrapper.isReadyForRead()) {
return;
}
} catch (IOException e) {
onError(e);
}
ready = Boolean.TRUE;
ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
try {
if (!eof) {
listener.onDataAvailable();
}
if (eof) {
listener.onAllDataRead();
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
onError(t);
} finally {
processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
}
}
private final void onError(Throwable t) {
if (listener == null) {
return;
}
ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
try {
listener.onError(t);
} catch (Throwable t2) {
ExceptionUtils.handleThrowable(t2);
log.warn(sm.getString("upgrade.sis.onErrorFail"), t2);
} finally {
processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
}
try {
close();
} catch (IOException ioe) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("upgrade.sis.errorCloseFail"), ioe);
}
}
ready = Boolean.FALSE;
}
final boolean isClosed() {
return closed;
}
}

View File

@@ -0,0 +1,282 @@
/*
* 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.http11.upgrade;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import org.apache.coyote.ContainerThreadMarker;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.net.DispatchType;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
public class UpgradeServletOutputStream extends ServletOutputStream {
private static final Log log = LogFactory.getLog(UpgradeServletOutputStream.class);
private static final StringManager sm =
StringManager.getManager(UpgradeServletOutputStream.class);
private final UpgradeProcessorBase processor;
private final SocketWrapperBase<?> socketWrapper;
// Used to ensure that isReady() and onWritePossible() have a consistent
// view of buffer and registered.
private final Object registeredLock = new Object();
// Used to ensure that only one thread writes to the socket at a time and
// that buffer is consistently updated with any unwritten data after the
// write. Note it is not necessary to hold this lock when checking if buffer
// contains data but, depending on how the result is used, some form of
// synchronization may be required (see fireListenerLock for an example).
private final Object writeLock = new Object();
private volatile boolean flushing = false;
private volatile boolean closed = false;
// Start in blocking-mode
private volatile WriteListener listener = null;
// Guarded by registeredLock
private boolean registered = false;
public UpgradeServletOutputStream(UpgradeProcessorBase processor,
SocketWrapperBase<?> socketWrapper) {
this.processor = processor;
this.socketWrapper = socketWrapper;
}
@Override
public final boolean isReady() {
if (listener == null) {
throw new IllegalStateException(
sm.getString("upgrade.sos.canWrite.ise"));
}
if (closed) {
return false;
}
// Make sure isReady() and onWritePossible() have a consistent view of
// fireListener when determining if the listener should fire
synchronized (registeredLock) {
if (flushing) {
// Since flushing is true the socket must already be registered
// for write and multiple registrations will cause problems.
registered = true;
return false;
} else if (registered){
// The socket is already registered for write and multiple
// registrations will cause problems.
return false;
} else {
boolean result = socketWrapper.isReadyForWrite();
registered = !result;
return result;
}
}
}
@Override
public final void setWriteListener(WriteListener listener) {
if (listener == null) {
throw new IllegalArgumentException(
sm.getString("upgrade.sos.writeListener.null"));
}
if (this.listener != null) {
throw new IllegalArgumentException(
sm.getString("upgrade.sos.writeListener.set"));
}
if (closed) {
throw new IllegalStateException(sm.getString("upgrade.sos.write.closed"));
}
this.listener = listener;
// Container is responsible for first call to onWritePossible().
synchronized (registeredLock) {
registered = true;
// Container is responsible for first call to onDataAvailable().
if (ContainerThreadMarker.isContainerThread()) {
processor.addDispatch(DispatchType.NON_BLOCKING_WRITE);
} else {
socketWrapper.registerWriteInterest();
}
}
}
final boolean isClosed() {
return closed;
}
@Override
public void write(int b) throws IOException {
synchronized (writeLock) {
preWriteChecks();
writeInternal(new byte[] { (byte) b }, 0, 1);
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
synchronized (writeLock) {
preWriteChecks();
writeInternal(b, off, len);
}
}
@Override
public void flush() throws IOException {
preWriteChecks();
flushInternal(listener == null, true);
}
private void flushInternal(boolean block, boolean updateFlushing) throws IOException {
try {
synchronized (writeLock) {
if (updateFlushing) {
flushing = socketWrapper.flush(block);
if (flushing) {
socketWrapper.registerWriteInterest();
}
} else {
socketWrapper.flush(block);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
onError(t);
if (t instanceof IOException) {
throw (IOException) t;
} else {
throw new IOException(t);
}
}
}
@Override
public void close() throws IOException {
if (closed) {
return;
}
closed = true;
flushInternal((listener == null), false);
}
private void preWriteChecks() {
if (listener != null && !socketWrapper.canWrite()) {
throw new IllegalStateException(sm.getString("upgrade.sos.write.ise"));
}
if (closed) {
throw new IllegalStateException(sm.getString("upgrade.sos.write.closed"));
}
}
/**
* Must hold writeLock to call this method.
*/
private void writeInternal(byte[] b, int off, int len) throws IOException {
if (listener == null) {
// Simple case - blocking IO
socketWrapper.write(true, b, off, len);
} else {
socketWrapper.write(false, b, off, len);
}
}
final void onWritePossible() {
try {
if (flushing) {
flushInternal(false, true);
if (flushing) {
return;
}
} else {
// This may fill the write buffer in which case the
// isReadyForWrite() call below will re-register the socket for
// write
flushInternal(false, false);
}
} catch (IOException ioe) {
onError(ioe);
return;
}
// Make sure isReady() and onWritePossible() have a consistent view
// of buffer and fireListener when determining if the listener
// should fire
boolean fire = false;
synchronized (registeredLock) {
if (socketWrapper.isReadyForWrite()) {
registered = false;
fire = true;
} else {
registered = true;
}
}
if (fire) {
ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
try {
listener.onWritePossible();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
onError(t);
} finally {
processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
}
}
}
private final void onError(Throwable t) {
if (listener == null) {
return;
}
ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null);
try {
listener.onError(t);
} catch (Throwable t2) {
ExceptionUtils.handleThrowable(t2);
log.warn(sm.getString("upgrade.sos.onErrorFail"), t2);
} finally {
processor.getUpgradeToken().getContextBind().unbind(false, oldCL);
}
try {
close();
} catch (IOException ioe) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("upgrade.sos.errorCloseFail"), ioe);
}
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* 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.http2;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
/**
* Used to managed prioritisation.
*/
abstract class AbstractStream {
private static final Log log = LogFactory.getLog(AbstractStream.class);
private static final StringManager sm = StringManager.getManager(AbstractStream.class);
private final Integer identifier;
private volatile AbstractStream parentStream = null;
private final Set<Stream> childStreams =
Collections.newSetFromMap(new ConcurrentHashMap<Stream,Boolean>());
private long windowSize = ConnectionSettingsBase.DEFAULT_INITIAL_WINDOW_SIZE;
public AbstractStream(Integer identifier) {
this.identifier = identifier;
}
public Integer getIdentifier() {
return identifier;
}
public int getIdAsInt() {
return identifier.intValue();
}
void detachFromParent() {
if (parentStream != null) {
parentStream.getChildStreams().remove(this);
parentStream = null;
}
}
final void addChild(Stream child) {
child.setParentStream(this);
childStreams.add(child);
}
boolean isDescendant(AbstractStream stream) {
// Is the passed in Stream a descendant of this Stream?
// Start at the passed in Stream and work up
AbstractStream parent = stream.getParentStream();
while (parent != null && parent != this) {
parent = parent.getParentStream();
}
return parent != null;
}
AbstractStream getParentStream() {
return parentStream;
}
void setParentStream(AbstractStream parentStream) {
this.parentStream = parentStream;
}
final Set<Stream> getChildStreams() {
return childStreams;
}
protected synchronized void setWindowSize(long windowSize) {
this.windowSize = windowSize;
}
protected synchronized long getWindowSize() {
return windowSize;
}
/**
* Increment window size.
* @param increment The amount by which the window size should be increased
* @throws Http2Exception If the window size is now higher than
* the maximum allowed
*/
protected synchronized void incrementWindowSize(int increment) throws Http2Exception {
// No need for overflow protection here.
// Increment can't be more than Integer.MAX_VALUE and once windowSize
// goes beyond 2^31-1 an error is triggered.
windowSize += increment;
if (log.isDebugEnabled()) {
log.debug(sm.getString("abstractStream.windowSizeInc", getConnectionId(),
getIdentifier(), Integer.toString(increment), Long.toString(windowSize)));
}
if (windowSize > ConnectionSettingsBase.MAX_WINDOW_SIZE) {
String msg = sm.getString("abstractStream.windowSizeTooBig", getConnectionId(), identifier,
Integer.toString(increment), Long.toString(windowSize));
if (identifier.intValue() == 0) {
throw new ConnectionException(msg, Http2Error.FLOW_CONTROL_ERROR);
} else {
throw new StreamException(
msg, Http2Error.FLOW_CONTROL_ERROR, identifier.intValue());
}
}
}
protected synchronized void decrementWindowSize(int decrement) {
// No need for overflow protection here. Decrement can never be larger
// the Integer.MAX_VALUE and once windowSize goes negative no further
// decrements are permitted
windowSize -= decrement;
if (log.isDebugEnabled()) {
log.debug(sm.getString("abstractStream.windowSizeDec", getConnectionId(),
getIdentifier(), Integer.toString(decrement), Long.toString(windowSize)));
}
}
protected abstract String getConnectionId();
protected abstract int getWeight();
@Deprecated // Unused
protected abstract void doNotifyAll();
}

View File

@@ -0,0 +1,94 @@
/*
* 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.http2;
/**
* Utility class for extracting values from byte arrays.
*/
public class ByteUtil {
private ByteUtil() {
// Hide default constructor
}
public static boolean isBit7Set(byte input) {
return (input & 0x80) != 0;
}
public static int get31Bits(byte[] input, int firstByte) {
return ((input[firstByte] & 0x7F) << 24) + ((input[firstByte + 1] & 0xFF) << 16) +
((input[firstByte + 2] & 0xFF) << 8) + (input[firstByte + 3] & 0xFF);
}
public static void set31Bits(byte[] output, int firstByte, int value) {
output[firstByte] = (byte) ((value & 0x7F000000) >> 24);
output[firstByte + 1] = (byte) ((value & 0xFF0000) >> 16);
output[firstByte + 2] = (byte) ((value & 0xFF00) >> 8);
output[firstByte + 3] = (byte) (value & 0xFF);
}
public static int getOneByte(byte[] input, int pos) {
return (input[pos] & 0xFF);
}
public static int getTwoBytes(byte[] input, int firstByte) {
return ((input[firstByte] & 0xFF) << 8) + (input[firstByte + 1] & 0xFF);
}
public static int getThreeBytes(byte[] input, int firstByte) {
return ((input[firstByte] & 0xFF) << 16) + ((input[firstByte + 1] & 0xFF) << 8) +
(input[firstByte + 2] & 0xFF);
}
public static void setOneBytes(byte[] output, int firstByte, int value) {
output[firstByte] = (byte) (value & 0xFF);
}
public static void setTwoBytes(byte[] output, int firstByte, int value) {
output[firstByte] = (byte) ((value & 0xFF00) >> 8);
output[firstByte + 1] = (byte) (value & 0xFF);
}
public static void setThreeBytes(byte[] output, int firstByte, int value) {
output[firstByte] = (byte) ((value & 0xFF0000) >> 16);
output[firstByte + 1] = (byte) ((value & 0xFF00) >> 8);
output[firstByte + 2] = (byte) (value & 0xFF);
}
public static long getFourBytes(byte[] input, int firstByte) {
return ((long)(input[firstByte] & 0xFF) << 24) + ((input[firstByte + 1] & 0xFF) << 16) +
((input[firstByte + 2] & 0xFF) << 8) + (input[firstByte + 3] & 0xFF);
}
public static void setFourBytes(byte[] output, int firstByte, long value) {
output[firstByte] = (byte) ((value & 0xFF000000) >> 24);
output[firstByte + 1] = (byte) ((value & 0xFF0000) >> 16);
output[firstByte + 2] = (byte) ((value & 0xFF00) >> 8);
output[firstByte + 3] = (byte) (value & 0xFF);
}
}

Some files were not shown because too many files have changed in this diff Show More