Tomcat之Http11Processor源码分析

Http11Processor继承关系


image.png

java.nio.ByteBuffer


image.png

ByteBuffer有三个属性值:position、limit、capacity
初始化时,position=0,limit=capacity=最大容量10,下标是10的位置不可以写入数据

如果position<limit,是可写模式。如果这中间(position到limit)有数据,将会被覆盖。
调用flip()方法,由写模式变为可读模式,此时limit=position=2,position=0。
position到limit是在写入模式写入的数据,此时可以读取出来。读取一个数据position位置加1

解析调用入口

在SocketProcessor任务类中调用doRun会调用到ConnectionHandler#process
1、doRun
org.apache.tomcat.util.net.NioEndpoint.SocketProcessor#doRun


process.png

2、process
org.apache.coyote.AbstractProtocol.ConnectionHandler#process


process.png

3、process
org.apache.coyote.AbstractProcessorLight#process
process.png

解析处理方法service

org.apache.coyote.http11.Http11Processor#service


service.png

0、setSocketWrapper
org.apache.coyote.http11.Http11Processor#setSocketWrapper
Http11InputBuffer inputBuffer和outputBuffer的初始化,此时activeFilters 是null


image.png

设置keepAlive、openSocket、readComplete的值。
keepAlive表示客户端与服务端连接是长连接Keep-alive,就是说客户端socket连接到服务端之后,告诉服务端这个socket你别给我关了,要一直保持连接。

1、解析请求行

image.png
1.1、parsingRequestLinePhase < 2

跳过空白行,并读取socket数据到缓冲区
①、parseRequestLine
org.apache.coyote.http11.Http11InputBuffer#parseRequestLine


parseRequestLine.png

此时byteBuffer的position和limit都是0,将会通过fill方法从操作系统的输入缓冲区读取数据
②、fill
是个非阻塞方法,如果从输入缓冲区没有读取到数据会立即返回。并不会像Tomcat7使用BIO读取,如果没数据一直会等待。
org.apache.coyote.http11.Http11InputBuffer#fill


fill.png

byteBuffer设置标记为mark=position=0
设置lim位置是最大位置cap大小。java.nio.HeapByteBuffer[pos=0 lim=16384 cap=16384]
③、read
从socketWrapper通道中读取数据到byteBuffer,以非阻塞方式

org.apache.tomcat.util.net.NioEndpoint.NioSocketWrapper#read(boolean, java.nio.ByteBuffer)


read.png

populateReadBuffer从to缓存区读取,如果没读取到说明此时ByteBuffer缓冲区中没有数据继续往下走
read.png

④、populateReadBuffer
从读缓冲区readBuffer数据读取到字节数组to
org.apache.tomcat.util.net.SocketWrapperBase#populateReadBuffer(java.nio.ByteBuffer)
image.png

⑤、setReadBufferConfiguredForWrite
如果readBuffer缓冲区处于正确状态,就可以反转flip读取
org.apache.tomcat.util.net.SocketBufferHandler#setReadBufferConfiguredForWrite
readBuffer切换到读java.nio.HeapByteBuffer[pos=0 lim=0 cap=8192]的lim=0
image.png

⑥、read
与③相同方法内
image.png

⑦、fillReadBuffer
org.apache.tomcat.util.net.NioEndpoint.NioSocketWrapper#fillReadBuffer(boolean, java.nio.ByteBuffer)


fillReadBuffer.png

if方法block=false && nRead == 0 如果读取nRead 为0,由于是非阻塞所以会立即返回
image.png

⑧、parseRequestLine
返回到①方法,socket数据读取到缓冲区。遇到“\r”或\n结束parsingRequestLinePhase<2循环
java.nio.HeapByteBuffer[pos=0 lim=510 cap=16384] 设置lim=510
image.png
1.2、parsingRequestLinePhase = 2

读取方法名,并在byteBuffer中标记


image.png

image.png

java.nio.HeapByteBuffer[pos=4 lim=510 cap=16384]

1.3、parsingRequestLinePhase = 3

空格和\t处理
parsingRequestLinePhase=3跳过方法后的Constants.SP(' ' )和Constants.HT('\t')


image.png
1.4、parsingRequestLinePhase = 4

解析uri,并标记位置


requestURI.png
1.5、parsingRequestLinePhase = 5

空格和\t处理
parsingRequestLinePhase=5跳过方法后的Constants.SP(' ' )和Constants.HT('\t')


image.png
1.6、parsingRequestLinePhase = 6

parsingRequestLinePhase == 6解析协议


protocol.png

如果缓冲区没数据,任意一步也会调用fill方法读取

MessageBytes

MessageBytes是请求数据存储类型
1、请求数据存储


image.png

2、Request属性
org.apache.coyote.Request类中的请求属性值都是MessageBytes类型


Request.png

3、MessageBytes属性
MessageBytes有两个byteC、charC属性,数据块
private final ByteChunk byteC=new ByteChunk();

private final CharChunk charC=new CharChunk();
4、当设置请求数据时,便是调用setBytes
org.apache.tomcat.util.buf.MessageBytes#setBytes


setBytes.png

org.apache.tomcat.util.buf.ByteChunk#setBytes
setBytes.png
ByteBuff数据解析请求行数据块标记

以localhost:8080/,methodMB、uriMB和protoMB(不是解析请求行设置的)的标记为例。这里只是说明数据块标记效果


ByteBuff.png

2、解析请求头

image.png

解析过程与解析请求行类似,就是读取缓存的数据进行解析。如果缓冲区数据不足,则会继续读取操作系统输出缓冲区数据
1、parseHeaders
org.apache.coyote.http11.Http11InputBuffer#parseHeaders


image.png

2、setBytes


headerValue.png

keep-alive
image.png

3、请求头数据request headers
image.png

java.nio.HeapByteBuffer[pos=581 lim=581 cap=16384]

4、缓冲区数据块headers
请求头数据也会在缓冲区HeapByteBuffer类中标记,在Request类的MimeHeaders headers属性

3、prepareRequest

在处理请求头之前,设置一些过滤器


image.png

org.apache.coyote.http11.Http11Processor#prepareRequest
1、设置协议protocolMB


prepareRequest.png

2、检查keepAlive
image.png

3、检查expect和user-agent


image.png

image.png

4、检查host
image.png

5、检查uri是否以http开头
image.png

6、检查uri字符
image.png

image.png

7、创建InputFilter数组


image.png

InputFilter[4]
{BufferedInputFilter、VoidInputFilter、ChunkedInputFilter、IdentityInputFilter}
8、解析transfer-encoding header
image.png

9、获取contentLength
image.png

如果contentLength大于0,inputBuffer是IdentityInputFilter
inputFilters[Constants.IDENTITY_FILTER]=inputFilters[0]
image.png

image.png

10、检验hostname和port
image.png

如果contentLength大于0则contentDelimitation=true。inputBuffer是inputFilters[0]
如果不成立,则inputFilters[Constants.VOID_FILTER]=2是VoidInputFilter类型
11、检查maxKeepAliveRequests
如果maxKeepAliveRequests=1或keepAliveLeft小于等于0,会把是否保持连接设置为keepAlive=false,则当前socket处理将会被关闭
image.png
处理请求getAdapter().service
image.png

4、解析请求体

InputFilter实现类用来处理请求数据的请求体,相对应的OutputFilter用于处理Servlet服务响应给客户端的数据。
BufferedInputFilter
VoidInputFilter(contentLength=0)
ChunkedInputFilter(被标记为Chunked)
IdentityInputFilter(contentLength>0)

5、结束请求

image.png

1、endRequest
org.apache.coyote.http11.Http11Processor#endRequest


image.png

2、endRequest
org.apache.coyote.http11.Http11InputBuffer#endRequest
调用activeFilters的end方法,用来处理请求体。得到返回的结果extraBytes,并设置正确的position位置


endRequest.png

3、end
org.apache.coyote.http11.filters.VoidInputFilter#end
①、VoidInputFilter说明没有请求体需要处理
image.png

②、IdentityInputFilter请求体的处理方法
org.apache.coyote.http11.filters.IdentityInputFilter#end


image.png

IdentityInputFilter根据还有剩余的数据需要处理,如果是负值说明请求读取了下一个的请求数据,所以需要修复。如果remaining大于0说明,还有这些数据没有读取到,则会继续读取。
缓冲区读取请求是按照请求行请求头请求体以此读取,然后是下一个请求行请求头请求体
image.png

如果此时请求体数据是123,此时只读取到了123后,又读取了下一个的get方法,remaining小于0需要往回退移动指针到正确位置。
如果此时请求体数据是123456789,此时只读取到了123,remaining大于0需要读取剩余的,需要往后移动指针到正确位置。
③、ChunkedInputFilter请求体的处理方法
org.apache.coyote.http11.filters.ChunkedInputFilter#end
image.png

4、action
返回到1的处理
org.apache.coyote.AbstractProcessor#action
image.png

5、prepareResponse

用来响应数据,并验证响应头和构建输出过滤器outputFilters
org.apache.coyote.http11.Http11Processor#prepareResponse
6、nextRequest
处理下个请求


nextRequest.png

①、Request类重置
org.apache.coyote.Request request属性重置
image.png

②、Http11InputBuffer 类重置
byteBuffer指针重置、activeFilters及其他属性值重置
image.png

总结:

Http11Processor类是客户端请求时用来处理Http协议和向客户端响应数据时构建Http协议的核心代码。
请求处理过程主要包括:
解析请求行、解析请求头、解析请求体、Servlet服务调用、重置基本类属性用来处理下次请求等过程。
其中InputFilter和OutputFilter涉及请求体的读写数据及其他处理。
Servlet服务调用是通过getAdapter().service方法进行调用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275