in java/org/apache/coyote/http11/Http11InputBuffer.java [316:562]
boolean parseRequestLine(boolean keptAlive, int connectionTimeout, int keepAliveTimeout) throws IOException {
// check state
if (!parsingRequestLine) {
return true;
}
//
// Skipping blank lines
//
if (parsingRequestLinePhase < 2) {
do {
// Read new bytes if needed
if (byteBuffer.position() >= byteBuffer.limit()) {
if (keptAlive) {
// Haven't read any request data yet so use the keep-alive
// timeout.
wrapper.setReadTimeout(keepAliveTimeout);
}
if (!fill(false)) {
return false;
}
// At least one byte of the request has been received.
// Switch to the socket timeout.
wrapper.setReadTimeout(connectionTimeout);
}
if (!keptAlive && byteBuffer.position() == 0 && byteBuffer.limit() >= CLIENT_PREFACE_START.length) {
boolean prefaceMatch = true;
for (int i = 0; i < CLIENT_PREFACE_START.length && prefaceMatch; i++) {
if (CLIENT_PREFACE_START[i] != byteBuffer.get(i)) {
prefaceMatch = false;
}
}
if (prefaceMatch) {
// HTTP/2 preface matched
parsingRequestLinePhase = -1;
return false;
}
}
// Set the start time once we start reading data (even if it is
// just skipping blank lines)
if (parsingRequestLinePhase == 0) {
parsingRequestLinePhase = 1;
request.setStartTimeNanos(System.nanoTime());
}
chr = byteBuffer.get();
} while (chr == Constants.CR || chr == Constants.LF);
byteBuffer.position(byteBuffer.position() - 1);
parsingRequestLineStart = byteBuffer.position();
parsingRequestLinePhase = 2;
}
if (parsingRequestLinePhase == 2) {
//
// Reading the method name
// Method name is a token
//
boolean space = false;
while (!space) {
// Read new bytes if needed
if (byteBuffer.position() >= byteBuffer.limit()) {
if (!fill(false)) {
return false;
}
}
// Spec says method name is a token followed by a single SP but
// also be tolerant of multiple SP and/or HT.
int pos = byteBuffer.position();
chr = byteBuffer.get();
if (chr == Constants.SP || chr == Constants.HT) {
space = true;
request.method().setBytes(byteBuffer.array(), parsingRequestLineStart,
pos - parsingRequestLineStart);
} else if (!HttpParser.isToken(chr)) {
// Avoid unknown protocol triggering an additional error
request.protocol().setString(Constants.HTTP_11);
String invalidMethodValue = parseInvalid(parsingRequestLineStart, byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidmethod", invalidMethodValue));
}
}
parsingRequestLinePhase = 3;
}
if (parsingRequestLinePhase == 3) {
// Spec says single SP but also be tolerant of multiple SP and/or HT
boolean space = true;
while (space) {
// Read new bytes if needed
if (byteBuffer.position() >= byteBuffer.limit()) {
if (!fill(false)) {
return false;
}
}
chr = byteBuffer.get();
if (chr != Constants.SP && chr != Constants.HT) {
space = false;
byteBuffer.position(byteBuffer.position() - 1);
}
}
parsingRequestLineStart = byteBuffer.position();
parsingRequestLinePhase = 4;
}
if (parsingRequestLinePhase == 4) {
// Mark the current buffer position
int end = 0;
//
// Reading the URI
//
boolean space = false;
while (!space) {
// Read new bytes if needed
if (byteBuffer.position() >= byteBuffer.limit()) {
if (!fill(false)) {
return false;
}
}
int pos = byteBuffer.position();
prevChr = chr;
chr = byteBuffer.get();
if (prevChr == Constants.CR && chr != Constants.LF) {
// CR not followed by LF so not an HTTP/0.9 request and
// therefore invalid. Trigger error handling.
// Avoid unknown protocol triggering an additional error
request.protocol().setString(Constants.HTTP_11);
String invalidRequestTarget = parseInvalid(parsingRequestLineStart, byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", invalidRequestTarget));
}
if (chr == Constants.SP || chr == Constants.HT) {
space = true;
end = pos;
} else if (chr == Constants.CR) {
// HTTP/0.9 style request. CR is optional. LF is not.
} else if (chr == Constants.LF) {
// HTTP/0.9 style request
// Stop this processing loop
space = true;
// Set blank protocol (indicates HTTP/0.9)
request.protocol().setString("");
// Skip the protocol processing
parsingRequestLinePhase = 7;
if (prevChr == Constants.CR) {
end = pos - 1;
} else {
end = pos;
}
} else if (chr == Constants.QUESTION && parsingRequestLineQPos == -1) {
parsingRequestLineQPos = pos;
} else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(chr)) {
// Avoid unknown protocol triggering an additional error
request.protocol().setString(Constants.HTTP_11);
// %nn decoding will be checked at the point of decoding
String invalidRequestTarget = parseInvalid(parsingRequestLineStart, byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", invalidRequestTarget));
} else if (httpParser.isNotRequestTargetRelaxed(chr)) {
// Avoid unknown protocol triggering an additional error
request.protocol().setString(Constants.HTTP_11);
// This is a general check that aims to catch problems early
// Detailed checking of each part of the request target will
// happen in Http11Processor#prepareRequest()
String invalidRequestTarget = parseInvalid(parsingRequestLineStart, byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget", invalidRequestTarget));
}
}
if (parsingRequestLineQPos >= 0) {
request.queryString().setBytes(byteBuffer.array(), parsingRequestLineQPos + 1,
end - parsingRequestLineQPos - 1);
request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
parsingRequestLineQPos - parsingRequestLineStart);
} else {
request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
end - parsingRequestLineStart);
}
// HTTP/0.9 processing jumps to stage 7.
// Don't want to overwrite that here.
if (parsingRequestLinePhase == 4) {
parsingRequestLinePhase = 5;
}
}
if (parsingRequestLinePhase == 5) {
// Spec says single SP but also be tolerant of multiple and/or HT
boolean space = true;
while (space) {
// Read new bytes if needed
if (byteBuffer.position() >= byteBuffer.limit()) {
if (!fill(false)) {
return false;
}
}
byte chr = byteBuffer.get();
if (chr != Constants.SP && chr != Constants.HT) {
space = false;
byteBuffer.position(byteBuffer.position() - 1);
}
}
parsingRequestLineStart = byteBuffer.position();
parsingRequestLinePhase = 6;
// Mark the current buffer position
end = 0;
}
if (parsingRequestLinePhase == 6) {
//
// Reading the protocol
// Protocol is always "HTTP/" DIGIT "." DIGIT
//
while (!parsingRequestLineEol) {
// Read new bytes if needed
if (byteBuffer.position() >= byteBuffer.limit()) {
if (!fill(false)) {
return false;
}
}
int pos = byteBuffer.position();
prevChr = chr;
chr = byteBuffer.get();
if (chr == Constants.CR) {
// Possible end of request line. Need LF next else invalid.
} else if (prevChr == Constants.CR && chr == Constants.LF) {
// CRLF is the standard line terminator
end = pos - 1;
parsingRequestLineEol = true;
} else if (chr == Constants.LF) {
// LF is an optional line terminator
end = pos;
parsingRequestLineEol = true;
} else if (prevChr == Constants.CR || !HttpParser.isHttpProtocol(chr)) {
String invalidProtocol = parseInvalid(parsingRequestLineStart, byteBuffer);
throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol", invalidProtocol));
}
}
if (end - parsingRequestLineStart > 0) {
request.protocol().setBytes(byteBuffer.array(), parsingRequestLineStart, end - parsingRequestLineStart);
parsingRequestLinePhase = 7;
}
// If no protocol is found, the ISE below will be triggered.
}
if (parsingRequestLinePhase == 7) {
// Parsing is complete. Return and clean-up.
parsingRequestLine = false;
parsingRequestLinePhase = 0;
parsingRequestLineEol = false;
parsingRequestLineStart = 0;
return true;
}
throw new IllegalStateException(sm.getString("iib.invalidPhase", Integer.valueOf(parsingRequestLinePhase)));
}