in java/org/apache/coyote/http11/Http11Processor.java [586:810]
private void prepareRequest() throws IOException {
contentDelimitation = false;
if (protocol.isSSLEnabled()) {
request.scheme().setString("https");
}
MimeHeaders headers = request.getMimeHeaders();
// Check connection header
MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);
if (connectionValueMB != null && !connectionValueMB.isNull()) {
Set<String> tokens = new HashSet<>();
TokenList.parseTokenList(headers.values(Constants.CONNECTION), tokens);
if (tokens.contains(Constants.CLOSE)) {
keepAlive = false;
} else if (tokens.contains(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN)) {
keepAlive = true;
}
}
if (http11) {
MessageBytes expectMB = headers.getValue("expect");
if (expectMB != null && !expectMB.isNull()) {
if (expectMB.toString().trim().equalsIgnoreCase("100-continue")) {
inputBuffer.setSwallowInput(false);
request.setExpectation(true);
} else {
response.setStatus(HttpServletResponse.SC_EXPECTATION_FAILED);
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
}
}
// Check user-agent header
Pattern restrictedUserAgents = protocol.getRestrictedUserAgentsPattern();
if (restrictedUserAgents != null && (http11 || keepAlive)) {
MessageBytes userAgentValueMB = headers.getValue("user-agent");
// Check in the restricted list, and adjust the http11
// and keepAlive flags accordingly
if(userAgentValueMB != null && !userAgentValueMB.isNull()) {
String userAgentValue = userAgentValueMB.toString();
if (restrictedUserAgents.matcher(userAgentValue).matches()) {
http11 = false;
keepAlive = false;
}
}
}
// Check host header
MessageBytes hostValueMB = null;
try {
hostValueMB = headers.getUniqueValue("host");
} catch (IllegalArgumentException iae) {
// Multiple Host headers are not permitted
badRequest("http11processor.request.multipleHosts");
}
if (http11 && hostValueMB == null) {
badRequest("http11processor.request.noHostHeader");
}
// 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;
}
}
// Input filter setup
InputFilter[] inputFilters = inputBuffer.getFilters();
// Parse transfer-encoding header
if (http11) {
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) {
// "identity" codings are ignored
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);
} else {
inputBuffer.addActiveFilter(inputFilters[Constants.IDENTITY_FILTER]);
contentDelimitation = true;
}
}
// Validate host name and extract port if present
parseHost(hostValueMB);
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;
}
if (!getErrorState().isIoAllowed()) {
getAdapter().log(request, response, 0);
}
}