一、准备解析Http11Processor#prepareRequest
private void prepareRequest() throws IOException {
contentDelimitation = false;
if (endpoint.isSSLEnabled()) {
request.scheme().setString("https");
}
MimeHeaders headers = request.getMimeHeaders();
// Check connection header
// 判断是否支持keepalive
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
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 != null &&
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
// 根据encoding选择对应的inputfilter
// GO
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) {
// contentDelimitation为true,及代表出现transfer-encoding header,则忽略content-length
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 {
// 没有出现chunked,则是普通的body,指定读取request body的filter为identityFilter解析
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);
}
}
- 判断是否支持keepalive
- 判断是否支持expect,当post的数据大于1024字节的时候,不会直接就发起POST请求,而是分为2步
- 发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
- 接收到Server返回的100-continue应答以后, 才把数据POST给Server
- 检查user-agent
- 检查host
- 检查URI
- 检查transfer-encoding
- 如果是"chunked",添加ChunkedInputFilter,并将contentDelimitation置为true
- 根据其它名称添加相应的InputFilter
- 判断contentDelimitation是否为true:若为true,设置content-length为-1;若不为true,添加IdentityInputFilter
- 如果没有content-length,则添加VoidInputFilter
二、解析请求体
1、org.apache.catalina.connector.Request#getParameter
@Override
public String getParameter(String name) {
// parametersParsed默认是false,解析完后设置为true,防止重复读
if (!parametersParsed) {
// GO
parseParameters();
}
return coyoteRequest.getParameters().getParameter(name);
}
- 请求体是在调用getParameter的时候开始解析的
- 解析前先判断是否解析,避免重复解析
- 解析后获取参数值
2、org.apache.catalina.connector.Request#parseParameters
protected void parseParameters() {
parametersParsed = true;
Parameters parameters = coyoteRequest.getParameters();
boolean success = false;
try {
// Set this every time in case limit has been changed via JMX
parameters.setLimit(getConnector().getMaxParameterCount());
// getCharacterEncoding() may have been overridden to search for
// hidden form field containing request encoding
Charset charset = getCharset();
boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
parameters.setCharset(charset);
if (useBodyEncodingForURI) {
parameters.setQueryStringCharset(charset);
}
// Note: If !useBodyEncodingForURI, the query string encoding is
// that set towards the start of CoyoyeAdapter.service()
// 解析url后面的参数
parameters.handleQueryParameters();
// usingInputStream为true,或者usingReader为true则直接返回
if (usingInputStream || usingReader) {
success = true;
return;
}
String contentType = getContentType();
if (contentType == null) {
contentType = "";
}
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
} else {
contentType = contentType.trim();
}
if ("multipart/form-data".equals(contentType)) {
parseParts(false);
success = true;
return;
}
if( !getConnector().isParseBodyMethod(getMethod()) ) {
success = true;
return;
}
if (!("application/x-www-form-urlencoded".equals(contentType))) {
success = true;
return;
}
// 获取content-length,如果chunked,则返回-1
int len = getContentLength();
// len大于0则是普通的body
if (len > 0) {
int maxPostSize = connector.getMaxPostSize();
// 检查body的大小是否超过了maxPostSize的大小,maxPostSize默认是2m
if ((maxPostSize >= 0) && (len > maxPostSize)) {
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.postTooLarge"));
}
checkSwallowInput();
parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
return;
}
// body字节部分
byte[] formData = null;
// 如果body的大小小于CACHED_POST_LEN,默认8192及8k,tomcat是用缓存的字节数组,不是重新创建一个,新建一个意味着向os申请一块
// 连续的内存,如果不重用,则会出现频繁的os申请
if (len < CACHED_POST_LEN) {
if (postData == null) {
postData = new byte[CACHED_POST_LEN];
}
formData = postData;
} else {
formData = new byte[len];
}
try {
// 这里是从inputbuffer里读数据.
// GO
if (readPostBody(formData, len) != len) {
parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE);
return;
}
} catch (IOException e) {
// Client disconnect
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.parseParameters"), e);
}
parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
return;
}
parameters.processParameters(formData, 0, len);
} else if ("chunked".equalsIgnoreCase(
coyoteRequest.getHeader("transfer-encoding"))) {
byte[] formData = null;
try {
formData = readChunkedPostBody();
} catch (IllegalStateException ise) {
// chunkedPostTooLarge error
parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.parseParameters"),
ise);
}
return;
} catch (IOException e) {
// Client disconnect
parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.parseParameters"), e);
}
return;
}
if (formData != null) {
parameters.processParameters(formData, 0, formData.length);
}
}
success = true;
} finally {
if (!success) {
parameters.setParseFailedReason(FailReason.UNKNOWN);
}
}
}
- parameters.handleQueryParameters()解析url后面的参数
- 判断是否执行过getInputStream(),如果通过stream流的形式读了,则在getParameter是读不到body里的参数的。只能获取url后面的参数
if (usingInputStream || usingReader) {
success = true;
return;
}
- 检查header content-type,如果不是multipart/form-data 或者
application/x-www-form-urlencoded 的,则不解析body
String contentType = getContentType();
if (contentType == null) {
contentType = "";
}
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
} else {
contentType = contentType.trim();
}
if ("multipart/form-data".equals(contentType)) {
parseParts(false);
success = true;
return;
}
if( !getConnector().isParseBodyMethod(getMethod()) ) {
success = true;
return;
}
if (!("application/x-www-form-urlencoded".equals(contentType))) {
success = true;
return;
}
- 开始解析请求体
- 如果getContentLength()大于0,先检查请求体大小是否超过最大值,超过了返回;没超过,开始解析readPostBody(formData, len)
if (len > 0) {
int maxPostSize = connector.getMaxPostSize();
// 检查body的大小是否超过了maxPostSize的大小,maxPostSize默认是2m
if ((maxPostSize >= 0) && (len > maxPostSize)) {
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.postTooLarge"));
}
checkSwallowInput();
parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
return;
}
// body字节部分
byte[] formData = null;
// 如果body的大小小于CACHED_POST_LEN,默认8192及8k,tomcat是用缓存的字节数组,不是重新创建一个,新建一个意味着向os申请一块
// 连续的内存,如果不重用,则会出现频繁的os申请
if (len < CACHED_POST_LEN) {
if (postData == null) {
postData = new byte[CACHED_POST_LEN];
}
formData = postData;
} else {
formData = new byte[len];
}
try {
// 这里是从inputbuffer里读数据.
// GO
if (readPostBody(formData, len) != len) {
parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE);
return;
}
} catch (IOException e) {
// Client disconnect
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.parseParameters"), e);
}
parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
return;
}
parameters.processParameters(formData, 0, len);
}
- chunked模式解析,readChunkedPostBody()
else if ("chunked".equalsIgnoreCase(
coyoteRequest.getHeader("transfer-encoding"))) {
byte[] formData = null;
try {
formData = readChunkedPostBody();
} catch (IllegalStateException ise) {
// chunkedPostTooLarge error
parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.parseParameters"),
ise);
}
return;
} catch (IOException e) {
// Client disconnect
parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
Context context = getContext();
if (context != null && context.getLogger().isDebugEnabled()) {
context.getLogger().debug(
sm.getString("coyoteRequest.parseParameters"), e);
}
return;
}
if (formData != null) {
parameters.processParameters(formData, 0, formData.length);
}
}
success = true;
}
三、IdentityFilter请求体
1、org.apache.catalina.connector.Request#readPostBody
protected int readPostBody(byte[] body, int len)
throws IOException {
int offset = 0;
do {
// 这里最终是Http11InputBuffer根据activeFilter去读body
// GO
int inputLen = getStream().read(body, offset, len - offset);
if (inputLen <= 0) {
return offset;
}
offset += inputLen;
} while ((len - offset) > 0);
return len;
}
- while循环读取,将读取的数据放入body
- 读取的其实位置为offset,读取的长度为len-offset,从而控制读取的数据是请求体的数据
2、 CoyoteInputStream#read
public int read(final byte[] b, final int off, final int len) throws IOException {
checkNonBlockingRead();
if (SecurityUtil.isPackageProtectionEnabled()) {
try {
Integer result = AccessController
.doPrivileged(new PrivilegedExceptionAction<Integer>() {
@Override
public Integer run() throws IOException {
Integer integer = Integer.valueOf(ib.read(b, off, len));
return integer;
}
});
return result.intValue();
} catch (PrivilegedActionException pae) {
Exception e = pae.getException();
if (e instanceof IOException) {
throw (IOException) e;
} else {
throw new RuntimeException(e.getMessage(), e);
}
}
} else {
return ib.read(b, off, len);
}
}
3、 InputBuffer#read
public int read(byte[] b, int off, int len) throws IOException {
throwIfClosed();
if (checkByteBufferEof()) {
return -1;
}
int n = Math.min(len, bb.remaining());
bb.get(b, off, n);
return n;
}
4、 InputBuffer#checkByteBufferEof
private boolean checkByteBufferEof() throws IOException {
if (bb.remaining() == 0) {
int n = realReadBytes();
if (n < 0) {
return true;
}
}
return false;
}
5、 InputBuffer#realReadBytes
public int realReadBytes() throws IOException {
if (closed) {
return -1;
}
if (coyoteRequest == null) {
return -1;
}
if (state == INITIAL_STATE) {
state = BYTE_STATE;
}
try {
return coyoteRequest.doRead(this);
} catch (IOException ioe) {
coyoteRequest.setErrorException(ioe);
// An IOException on a read is almost always due to
// the remote client aborting the request.
throw new ClientAbortException(ioe);
}
}
6、 org.apache.coyote.Request#doRead
public int doRead(ApplicationBufferHandler handler) throws IOException {
if (getBytesRead() == 0 && !response.isCommitted()) {
action(ActionCode.ACK, ContinueResponseTiming.ON_REQUEST_BODY_READ);
}
int n = inputBuffer.doRead(handler);
if (n > 0) {
bytesRead+=n;
}
return n;
}
7、 Http11InputBuffer#doRead
public int doRead(ApplicationBufferHandler handler) throws IOException {
if (lastActiveFilter == -1) {
return inputStreamInputBuffer.doRead(handler);
} else {
return activeFilters[lastActiveFilter].doRead(handler);
}
}
8、 IdentityInputFilter#doRead
public int doRead(ApplicationBufferHandler handler) throws IOException {
int result = -1;
// contentLength,remaining是在filter设置request的时候确定的
if (contentLength >= 0) {
if (remaining > 0) {
// 这里又是buffer,这个buffer是在设置activeFilter时确定的,一个filter的话,该buffer就是SocketInputBuffer,handler是
// ApplicationBufferHandler
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;
}
四、chunked模式解析请求体
1、org.apache.catalina.connector.Request#readChunkedPostBody
protected byte[] readChunkedPostBody() throws IOException {
ByteChunk body = new ByteChunk();
byte[] buffer = new byte[CACHED_POST_LEN];
int len = 0;
while (len > -1) {
len = getStream().read(buffer, 0, CACHED_POST_LEN);
if (connector.getMaxPostSize() >= 0 &&
(body.getLength() + len) > connector.getMaxPostSize()) {
// Too much data
checkSwallowInput();
throw new IllegalStateException(
sm.getString("coyoteRequest.chunkedPostTooLarge"));
}
if (len > 0) {
body.append(buffer, 0, len);
}
}
if (body.getLength() == 0) {
return null;
}
if (body.getLength() < body.getBuffer().length) {
int length = body.getLength();
byte[] result = new byte[length];
System.arraycopy(body.getBuffer(), 0, result, 0, length);
return result;
}
return body.getBuffer();
}
- 进入到CoyoteInputStream#read
- 最终是进入到ChunkedInputFilter#doRead
2、ChunkedInputFilter#doRead
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;
}