/* * 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;
/** * Tracks how many internal filters are in the filter library so they are skipped when looking for pluggable * filters.
*/ privateint pluggableFilterIndex = Integer.MAX_VALUE;
/** * Content delimiter for the request (if false, the connection will be closed at the end of the request).
*/ privateboolean contentDelimitation = true;
/** * Instance of the new protocol to use after the HTTP connection has been upgraded.
*/ private UpgradeToken upgradeToken = null;
httpParser = new HttpParser(protocol.getRelaxedPathChars(), protocol.getRelaxedQueryChars());
inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpRequestHeaderSize(),
protocol.getRejectIllegalHeader(), httpParser);
request.setInputBuffer(inputBuffer);
outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpResponseHeaderSize());
response.setOutputBuffer(outputBuffer);
// Create and add the identity filters.
inputBuffer.addFilter(new IdentityInputFilter(protocol.getMaxSwallowSize()));
outputBuffer.addFilter(new IdentityOutputFilter());
// Create and add the chunked filters.
inputBuffer.addFilter( new ChunkedInputFilter(protocol.getMaxTrailerSize(), protocol.getAllowedTrailerHeadersInternal(),
protocol.getMaxExtensionSize(), protocol.getMaxSwallowSize()));
outputBuffer.addFilter(new ChunkedOutputFilter());
// Create and add the void filters.
inputBuffer.addFilter(new VoidInputFilter());
outputBuffer.addFilter(new VoidOutputFilter());
// Create and add buffered input filter
inputBuffer.addFilter(new BufferedInputFilter(protocol.getMaxSwallowSize()));
// Create and add the gzip filters. // inputBuffer.addFilter(new GzipInputFilter());
outputBuffer.addFilter(new GzipOutputFilter());
/** * Determine if we must drop the connection because of the HTTP status code. Use the same list of codes as * Apache/httpd.
*/ privatestaticboolean statusDropsConnection(int status) { return status == 400 /* SC_BAD_REQUEST */ || status == 408 /* SC_REQUEST_TIMEOUT */ ||
status == 411 /* SC_LENGTH_REQUIRED */ || status == 413 /* SC_REQUEST_ENTITY_TOO_LARGE */ ||
status == 414 /* SC_REQUEST_URI_TOO_LONG */ || status == 500 /* SC_INTERNAL_SERVER_ERROR */ ||
status == 503 /* SC_SERVICE_UNAVAILABLE */ || status == 501 /* SC_NOT_IMPLEMENTED */;
}
/** * Add an input filter to the current request. If the encoding is not supported, a 501 response will be returned to * the client.
*/ privatevoid addInputFilter(InputFilter[] inputFilters, String encodingName) { if (contentDelimitation) { // Chunked has already been specified and it must be the final // encoding. // 400 - Bad request
response.setStatus(400);
setErrorState(ErrorState.CLOSE_CLEAN, null); if (log.isDebugEnabled()) {
log.debug(sm.getString("http11processor.request.prepare") + " Transfer encoding lists chunked before [" + encodingName + "]");
} return;
}
// Parsing trims and converts to lower case. if (encodingName.equals("chunked")) {
inputBuffer.addActiveFilter(inputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
} else { for (int i = pluggableFilterIndex; i < inputFilters.length; i++) { if (inputFilters[i].getEncodingName().toString().equals(encodingName)) {
inputBuffer.addActiveFilter(inputFilters[i]); return;
}
} // Unsupported transfer encoding // 501 - Unimplemented
response.setStatus(501);
setErrorState(ErrorState.CLOSE_CLEAN, null); if (log.isDebugEnabled()) {
log.debug(sm.getString("http11processor.request.prepare") + " Unsupported transfer encoding [" +
encodingName + "]");
}
}
}
@Override public SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O
setSocketWrapper(socketWrapper);
// Parsing the request header try { if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(),
protocol.getKeepAliveTimeout())) { if (inputBuffer.getParsingRequestLinePhase() == -1) { return SocketState.UPGRADING;
} elseif (handleIncompleteRequestLineRead()) { break;
}
}
// Process the Protocol component of the request line // Need to know if this is an HTTP 0.9 request before trying to // parse headers.
prepareRequestProtocol();
if (protocol.isPaused()) { // 503 - Service unavailable
response.setStatus(503);
setErrorState(ErrorState.CLOSE_CLEAN, null);
} else {
keptAlive = true; // Set this every time in case limit has been changed via JMX
request.getMimeHeaders().setLimit(protocol.getMaxHeaderCount()); // Don't parse headers for HTTP/0.9 if (!http09 && !inputBuffer.parseHeaders()) { // We've read part of the request, don't recycle it // instead associate it with the socket
openSocket = true;
readComplete = false; break;
} if (!protocol.getDisableUploadTimeout()) {
socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());
}
}
} catch (IOException e) { if (log.isDebugEnabled()) {
log.debug(sm.getString("http11processor.header.parse"), e);
}
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); break;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
UserDataHelper.Mode logMode = userDataHelper.getNextMode(); if (logMode != null) {
String message = sm.getString("http11processor.header.parse"); switch (logMode) { case INFO_THEN_DEBUG:
message += sm.getString("http11processor.fallToDebug"); //$FALL-THROUGH$ case INFO:
log.info(message, t); break; case DEBUG:
log.debug(message, t);
}
} // 400 - Bad Request
response.setStatus(400);
setErrorState(ErrorState.CLOSE_CLEAN, t);
}
// Has an upgrade been requested? if (isConnectionToken(request.getMimeHeaders(), "upgrade")) { // Check the protocol
String requestedProtocol = request.getHeader("Upgrade");
// Process the request in the adapter if (getErrorState().isIoAllowed()) { try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
getAdapter().service(request, response); // Handle when the response was committed before a serious // error occurred. Throwing a ServletException should both // set the status to 500 and set the errorException. // If we fail here, then the response is likely already // committed, so we can't try and set headers. if (keepAlive && !getErrorState().isError() && !isAsync() &&
statusDropsConnection(response.getStatus())) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
} catch (InterruptedIOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
} catch (HeadersTooLargeException e) {
log.error(sm.getString("http11processor.request.process"), e); // The response should not have been committed but check it // anyway to be safe if (response.isCommitted()) {
setErrorState(ErrorState.CLOSE_NOW, e);
} else {
response.reset();
response.setStatus(500);
setErrorState(ErrorState.CLOSE_CLEAN, e);
response.setHeader("Connection", "close"); // TODO: Remove
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("http11processor.request.process"), t); // 500 - Internal Server Error
response.setStatus(500);
setErrorState(ErrorState.CLOSE_CLEAN, t);
getAdapter().log(request, response, 0);
}
}
// Finish the handling of the request
rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT); if (!isAsync()) { // If this is an async request then the request ends when it has // been completed. The AsyncContext is responsible for calling // endRequest() in that case.
endRequest();
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
// If there was an error, make sure the request is counted as // and error, and update the statistics counter if (getErrorState().isError()) {
response.setStatus(500);
}
if (!isAsync() || getErrorState().isError()) {
request.updateCounters(); if (getErrorState().isIoAllowed()) {
inputBuffer.nextRequest();
outputBuffer.nextRequest();
}
}
if (!protocol.getDisableUploadTimeout()) { int connectionTimeout = protocol.getConnectionTimeout(); if (connectionTimeout > 0) {
socketWrapper.setReadTimeout(connectionTimeout);
} else {
socketWrapper.setReadTimeout(0);
}
}
private Request cloneRequest(Request source) throws IOException {
Request dest = new Request();
// Transfer the minimal information required for the copy of the Request // that is passed to the HTTP upgrade process
dest.decodedURI().duplicate(source.decodedURI());
dest.method().duplicate(source.method());
dest.getMimeHeaders().duplicate(source.getMimeHeaders());
dest.requestURI().duplicate(source.requestURI());
dest.queryString().duplicate(source.queryString());
// Preparation for reading the request body
MimeHeaders headers = source.getMimeHeaders();
prepareExpectation(headers);
prepareInputFilters(headers);
ack(ContinueResponseTiming.ALWAYS);
// Need to read and buffer the request body, if any. RFC 7230 requires // that the request is fully read before the upgrade takes place.
ByteChunk body = new ByteChunk(); int maxSavePostSize = protocol.getMaxSavePostSize(); if (maxSavePostSize != 0) {
body.setLimit(maxSavePostSize);
ApplicationBufferHandler buffer = new UpgradeApplicationBufferHandler();
while (source.getInputBuffer().doRead(buffer) >= 0) {
body.append(buffer.getByteBuffer());
}
}
// Make the buffered request body available to the upgraded protocol.
SavedRequestInputFilter srif = new SavedRequestInputFilter(body);
dest.setInputBuffer(srif);
return dest;
}
privateboolean handleIncompleteRequestLineRead() { // Haven't finished reading the request so keep the socket // open
openSocket = true; // Check to see if we have read any of the request line yet if (inputBuffer.getParsingRequestLinePhase() > 1) { // Started to read request line. if (protocol.isPaused()) { // Partially processed the request so need to respond
response.setStatus(503);
setErrorState(ErrorState.CLOSE_CLEAN, null); returnfalse;
} else { // Need to keep processor associated with socket
readComplete = false;
}
} returntrue;
}
privatevoid checkExpectationAndResponseStatus() { if (request.hasExpectation() && !isRequestBodyFullyRead() &&
(response.getStatus() < 200 || response.getStatus() > 299)) { // Client sent Expect: 100-continue but received a // non-2xx final response. Disable keep-alive (if enabled) // to ensure that the connection is closed. Some clients may // still send the body, some may send the next request. // No way to differentiate, so close the connection to // force the client to send the next request.
inputBuffer.setSwallowInput(false);
keepAlive = false;
}
}
privatevoid checkMaxSwallowSize() { // Parse content-length header long contentLength = -1; try {
contentLength = request.getContentLengthLong();
} catch (Exception e) { // Ignore, an error here is already processed in prepareRequest // but is done again since the content length is still -1
} if (contentLength > 0 && protocol.getMaxSwallowSize() > -1 &&
(contentLength - request.getBytesRead() > protocol.getMaxSwallowSize())) { // There is more data to swallow than Tomcat will accept so the // connection is going to be closed. Disable keep-alive which will // trigger adding the "Connection: close" header if not already // present.
keepAlive = false;
}
}
/** * After reading the request headers, we have to setup the request filters.
*/
@SuppressWarnings("deprecation") privatevoid prepareRequest() throws IOException {
if (protocol.isSSLEnabled()) {
request.scheme().setString("https");
}
// Check for an absolute-URI less the query string which has already // been removed during the parsing of the request line
ByteChunk uriBC = request.requestURI().getByteChunk(); byte[] uriB = uriBC.getBytes(); if (uriBC.startsWithIgnoreCase("http", 0)) { int pos = 4; // Check for https if (uriBC.startsWithIgnoreCase("s", pos)) {
pos++;
} // Next 3 characters must be "://" if (uriBC.startsWith("://", pos)) {
pos += 3; int uriBCStart = uriBC.getStart();
// '/' does not appear in the authority so use the first // instance to split the authority and the path segments int slashPos = uriBC.indexOf('/', pos); // '@' in the authority delimits the userinfo int atPos = uriBC.indexOf('@', pos); if (slashPos > -1 && atPos > slashPos) { // First '@' is in the path segments so no userinfo
atPos = -1;
}
if (slashPos == -1) {
slashPos = uriBC.getLength(); // Set URI as "/". Use 6 as it will always be a '/'. // 01234567 // http://> // https://>
request.requestURI().setBytes(uriB, uriBCStart + 6, 1);
} else {
request.requestURI().setBytes(uriB, uriBCStart + slashPos, uriBC.getLength() - slashPos);
}
// Skip any user info if (atPos != -1) { // Validate the userinfo for (; pos < atPos; pos++) { byte c = uriB[uriBCStart + pos]; if (!HttpParser.isUserInfo(c)) { // Strictly there needs to be a check for valid %nn // encoding here but skip it since it will never be // decoded because the userinfo is ignored
badRequest("http11processor.request.invalidUserInfo"); break;
}
} // Skip the '@'
pos = atPos + 1;
}
if (http11) { // Missing host header is illegal but handled above if (hostValueMB != null) { // Any host in the request line must be consistent with // the Host header if (!hostValueMB.getByteChunk().equals(uriB, uriBCStart + pos, slashPos - pos)) { if (protocol.getAllowHostHeaderMismatch()) { // The requirements of RFC 2616 are being // applied. If the host header and the request // line do not agree, the request line takes // precedence
hostValueMB = headers.setValue("host");
hostValueMB.setBytes(uriB, uriBCStart + pos, slashPos - pos);
} else { // The requirements of RFC 7230 are being // applied. If the host header and the request // line do not agree, trigger a 400 response.
badRequest("http11processor.request.inconsistentHosts");
}
}
}
} else { // Not HTTP/1.1 - no Host header so generate one since // Tomcat internals assume it is set try {
hostValueMB = headers.setValue("host");
hostValueMB.setBytes(uriB, uriBCStart + pos, slashPos - pos);
} catch (IllegalStateException e) { // Edge case // If the request has too many headers it won't be // possible to create the host header. Ignore this as // processing won't reach the point where the Tomcat // internals expect there to be a host header.
}
}
} else {
badRequest("http11processor.request.invalidScheme");
}
}
// Validate the characters in the URI. %nn decoding will be checked at // the point of decoding. for (int i = uriBC.getStart(); i < uriBC.getEnd(); i++) { if (!httpParser.isAbsolutePathRelaxed(uriB[i])) {
badRequest("http11processor.request.invalidUri"); break;
}
}
// Parse transfer-encoding header // HTTP specs say an HTTP 1.1 server should accept any recognised // HTTP 1.x header from a 1.x client unless the specs says otherwise. if (!http09) {
MessageBytes transferEncodingValueMB = headers.getValue("transfer-encoding"); if (transferEncodingValueMB != null) {
List<String> encodingNames = new ArrayList<>(); if (TokenList.parseTokenList(headers.values("transfer-encoding"), encodingNames)) { for (String encodingName : encodingNames) {
addInputFilter(inputFilters, encodingName);
}
} else { // Invalid transfer encoding
badRequest("http11processor.request.invalidTransferEncoding");
}
}
}
// Parse content-length header long contentLength = -1; try {
contentLength = request.getContentLengthLong();
} catch (NumberFormatException e) {
badRequest("http11processor.request.nonNumericContentLength");
} catch (IllegalArgumentException e) {
badRequest("http11processor.request.multipleContentLength");
} if (contentLength >= 0) { if (contentDelimitation) { // contentDelimitation being true at this point indicates that // chunked encoding is being used but chunked encoding should // not be used with a content length. RFC 2616, section 4.4, // bullet 3 states Content-Length must be ignored in this case - // so remove it.
headers.removeHeader("content-length");
request.setContentLength(-1);
keepAlive = false;
} else {
inputBuffer.addActiveFilter(inputFilters[Constants.IDENTITY_FILTER]);
contentDelimitation = true;
}
}
if (!contentDelimitation) { // If there's no content length // (broken HTTP/1.0 or HTTP/1.1), assume // the client is not broken and didn't send a body
inputBuffer.addActiveFilter(inputFilters[Constants.VOID_FILTER]);
contentDelimitation = true;
}
}
/** * When committing the response, we have to validate the set of headers, as well as setup the response filters.
*/
@Override protectedfinalvoid prepareResponse() throws IOException {
int statusCode = response.getStatus(); if (statusCode < 200 || statusCode == 204 || statusCode == 205 || statusCode == 304) { // No entity body
outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
entityBody = false;
contentDelimitation = true; if (statusCode == 205) { // RFC 7231 requires the server to explicitly signal an empty // response in this case
response.setContentLength(0);
} else {
response.setContentLength(-1);
}
}
if (request.method().equals("HEAD")) { // No entity body
outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
contentDelimitation = true;
}
// Sendfile support if (protocol.getUseSendfile()) {
prepareSendfile(outputFilters);
}
// Check for compression boolean useCompression = false; if (entityBody && sendfileData == null) {
useCompression = protocol.useCompression(request, response);
}
MimeHeaders headers = response.getMimeHeaders(); // A SC_NO_CONTENT response may include entity headers if (entityBody || statusCode == HttpServletResponse.SC_NO_CONTENT) {
String contentType = response.getContentType(); if (contentType != null) {
headers.setValue("Content-Type").setString(contentType);
}
String contentLanguage = response.getContentLanguage(); if (contentLanguage != null) {
headers.setValue("Content-Language").setString(contentLanguage);
}
}
long contentLength = response.getContentLengthLong(); boolean connectionClosePresent = isConnectionToken(headers, Constants.CLOSE); if (http11 && response.getTrailerFields() != null) { // If trailer fields are set, always use chunking
outputBuffer.addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
} elseif (contentLength != -1) {
headers.setValue("Content-Length").setLong(contentLength);
outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
contentDelimitation = true;
} else { // If the response code supports an entity body and we're on // HTTP 1.1 then we chunk unless we have a Connection: close header if (http11 && entityBody && !connectionClosePresent) {
outputBuffer.addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
headers.addValue(Constants.TRANSFERENCODING).setString(Constants.CHUNKED);
} else {
outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]);
}
}
if (useCompression) {
outputBuffer.addActiveFilter(outputFilters[Constants.GZIP_FILTER]);
}
// Add date header unless application has already set one (e.g. in a // Caching Filter) if (headers.getValue("Date") == null) {
headers.addValue("Date").setString(FastHttpDateFormat.getCurrentDate());
}
// FIXME: Add transfer encoding header
if ((entityBody) && (!contentDelimitation) || connectionClosePresent) { // Disable keep-alive if: // - there is a response body but way for the client to determine // the content length information; or // - there is a "connection: close" header present // This will cause the "connection: close" header to be added if it // is not already present.
keepAlive = false;
}
// This may disable keep-alive so check before working out the Connection header
checkExpectationAndResponseStatus();
// This may disable keep-alive if there is more body to swallow // than the configuration allows
checkMaxSwallowSize();
// If we know that the request is bad this early, add the // Connection: close header. if (keepAlive && statusDropsConnection(statusCode)) {
keepAlive = false;
} if (!keepAlive) { // Avoid adding the close header twice if (!connectionClosePresent) {
headers.addValue(Constants.CONNECTION).setString(Constants.CLOSE);
}
} elseif (!getErrorState().isError()) { if (!http11) {
headers.addValue(Constants.CONNECTION).setString(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
}
if (protocol.getUseKeepAliveResponseHeader()) { boolean connectionKeepAlivePresent = isConnectionToken(request.getMimeHeaders(),
Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
if (connectionKeepAlivePresent) { int keepAliveTimeout = protocol.getKeepAliveTimeout();
if (keepAliveTimeout > 0) {
String value = "timeout=" + keepAliveTimeout / 1000L;
headers.setValue(Constants.KEEP_ALIVE_HEADER_NAME).setString(value);
if (http11) { // Append if there is already a Connection header, // else create the header
MessageBytes connectionHeaderValue = headers.getValue(Constants.CONNECTION); if (connectionHeaderValue == null) {
headers.addValue(Constants.CONNECTION)
.setString(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
} else {
connectionHeaderValue.setString(connectionHeaderValue.getString() + ", " +
Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
}
}
}
}
}
}
// Add server header
String server = protocol.getServer(); if (server == null) { if (protocol.getServerRemoveAppProvidedValues()) {
headers.removeHeader("server");
}
} else { // server always overrides anything the app might set
headers.setValue("Server").setString(server);
}
// Build the response header try {
outputBuffer.sendStatus();
int size = headers.size(); for (int i = 0; i < size; i++) { try {
outputBuffer.sendHeader(headers.getName(i), headers.getValue(i));
} catch (IllegalArgumentException iae) { // Log the problematic header
log.warn(sm.getString("http11processor.response.invalidHeader", headers.getName(i),
headers.getValue(i)), iae); // Remove the problematic header
headers.removeHeader(i);
size--; // Header buffer is corrupted. Reset it and start again.
outputBuffer.resetHeaderBuffer();
i = 0;
outputBuffer.sendStatus();
}
}
outputBuffer.endHeaders();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t); // If something goes wrong, reset the header buffer so the error // response can be written instead.
outputBuffer.resetHeaderBuffer(); throw t;
}
Set<String> tokens = new HashSet<>();
TokenList.parseTokenList(headers.values(Constants.CONNECTION), tokens); return tokens.contains(token);
}
privatevoid prepareSendfile(OutputFilter[] outputFilters) {
String fileName = (String) request.getAttribute(org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR); if (fileName == null) {
sendfileData = null;
} else { // No entity body sent here
outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
contentDelimitation = true; long pos = ((Long) request.getAttribute(org.apache.coyote.Constants.SENDFILE_FILE_START_ATTR)).longValue(); long end = ((Long) request.getAttribute(org.apache.coyote.Constants.SENDFILE_FILE_END_ATTR)).longValue();
sendfileData = socketWrapper.createSendfileData(fileName, pos, end - pos);
}
}
/* * Note: populateHost() is not over-ridden. request.serverName() will be set to return the default host name by the * Mapper.
*/
/** * {@inheritDoc} * <p> * This implementation provides the server port from the local port.
*/
@Override protectedvoid populatePort() { // Ensure the local port field is populated before using it.
request.action(ActionCode.REQ_LOCALPORT_ATTRIBUTE, request);
request.setServerPort(request.getLocalPort());
}
@Override protectedboolean flushBufferedWrite() throws IOException { if (outputBuffer.hasDataToWrite()) { if (outputBuffer.flushBuffer(false)) { // The buffer wasn't fully flushed so re-register the // socket for write. Note this does not go via the // Response since the write registration state at // that level should remain unchanged. Once the buffer // has been emptied then the code below will call // Adaptor.asyncDispatch() which will enable the // Response to respond to this event.
outputBuffer.registerWriteInterest(); returntrue;
}
} returnfalse;
}
/* * No more input will be passed to the application. Remaining input will be swallowed or the connection dropped * depending on the error and expectation status.
*/ privatevoid endRequest() { if (getErrorState().isError()) { // If we know we are closing the connection, don't drain // input. This way uploading a 100GB file doesn't tie up the // thread if the servlet has rejected it.
inputBuffer.setSwallowInput(false);
} else { // Need to check this again here in case the response was // committed before the error that requires the connection // to be closed occurred.
checkExpectationAndResponseStatus();
}
// Finish the handling of the request if (getErrorState().isIoAllowed()) { try {
inputBuffer.endRequest();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t); // 500 - Internal Server Error // Can't add a 500 to the access log since that has already been // written in the Adapter.service method.
response.setStatus(500);
setErrorState(ErrorState.CLOSE_NOW, t);
log.error(sm.getString("http11processor.request.finish"), t);
}
} if (getErrorState().isIoAllowed()) { try {
action(ActionCode.COMMIT, null);
outputBuffer.end();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setErrorState(ErrorState.CLOSE_NOW, t);
log.error(sm.getString("http11processor.response.finish"), t);
}
}
}
@Override protectedfinalvoid ack(ContinueResponseTiming continueResponseTiming) { // Only try and send the ACK for ALWAYS or if the timing of the request // to send the ACK matches the current configuration. if (continueResponseTiming == ContinueResponseTiming.ALWAYS ||
continueResponseTiming == protocol.getContinueResponseTimingInternal()) { // Acknowledge request // Send a 100 status back if it makes sense (response not committed // yet, and client specified an expectation for 100-continue) if (!response.isCommitted() && request.hasExpectation()) { try {
outputBuffer.sendAck();
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
}
}
}
}
@Override protectedfinalvoid sslReHandShake() throws IOException { if (sslSupport != null) { // Consume and buffer the request body, so that it does not // interfere with the client's handshake messages
InputFilter[] inputFilters = inputBuffer.getFilters();
((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]).setLimit(protocol.getMaxSavePostSize());
inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]);
/* * Outside the try/catch because we want I/O errors during renegotiation to be thrown for the caller to * handle since they will be fatal to the connection.
*/
socketWrapper.doClientAuth(sslSupport); try { /* * Errors processing the cert chain do not affect the client connection so they can be logged and * swallowed here.
*/
Object sslO = sslSupport.getPeerCertificateChain(); if (sslO != null) {
request.setAttribute(SSLSupport.CERTIFICATE_KEY, sslO);
}
} catch (IOException ioe) {
log.warn(sm.getString("http11processor.socket.ssl"), ioe);
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.