🔗 特性:更快的 HTTP 解析器
- 目标:将非缓存 Squid3 的性能提升 20+%
- 版本: 3.6
- 状态:已开始
- 预计完成时间: 2016
- 优先级: 1
- 开发者:AmosJeffries 和 FrancescoChemolli
- 特性分支:lp:~squid/squid/parser-ng (旧:lp:~kinkie/squid/http-parser-ng)
🔗 详细信息
避免多次解析相同的 HTTP 标头。实现增量式标头解析。
该特性以及 StringNg 预期带来的主要好处之一是提高 HTTP 解析的清晰度和性能。HTTP 解析器(截至 Squid-3.1)的实现(如下文“基线情况”所述)有点复杂,也需要进行改造。代码显示过去曾尝试过但未完成。
🔗 代码架构
解析由一个 Http::Parser 子类处理,该子类拥有一个 SBuf 缓冲区和一个虚拟的 parse 方法,该方法将缓冲区内容分割成消息段以供后续处理。
目前,MIME 标头块的解析是由 HttpMsg 对象以 char* 字符串的形式处理的,该对象依次使用 {HttpHeader 对象,这些对象位于 Parser 层级结构之外。此对象及其使用的所有逻辑都需要重构,以便在 Http::One::Parser 方法 mimeHeaders 提供的 SBuf 上操作。
当前的 HttpMsg 层级结构对象承载了两个目的;
- 作为通用 HTTP 消息状态存储对象
- 作为 HTTP 和 ICAP 响应消息解析对象
🔗 未来计划
审议中
- 将 ICAP I/O 读取缓冲区转换为 SBuf
进行中
- 添加 HTTP/2 帧解析器
- 添加 ICAP 响应解析器
待办事项
- 使用已解析的 ICAP 响应来解释 ICAP 有效载荷段的解析方式,而不是尝试(不成功地)通过将
HttpMsg解析器应用于它来自动检测。 - 使用
HttpMsg解析器的代码需要重构,以便使用Http::ParserAPI,并从 Squid 中移除重复的解析器。 - 重构
HttpHeader解析逻辑,使用SBuf和::Parser::TokenizerAPI。可能会由新的Parser子类运行。 - 重构
ChunkedDecoder::parse,使用SBuf和::Parser::Tokenizer。
🔗 当前状态
在对 Http::Parser 层级结构进行初步的结构更新之后。
栈是异步的,现在在读取操作之后,解析检查点可以在读取后恢复。
Squid-3.6+ 中的请求解析系统 Http1::RequestParser::parse 如下所示
- 扫描以跳过垃圾前缀
- 任何停止点都是增量检查点(请求行开始或缓冲区为空时)
- 扫描以查找方法
- 方法结束时的增量检查点
- 扫描以查找 URI 和版本
- 在宽松解析器中,扫描以查找 LF,然后向后工作
- 在严格解析器中,扫描 SP 分隔符,并在 URI 后设置额外检查点
- 请求行结束时的增量检查点
- char* 循环扫描标头块的结束位置 (Http1::Parser::findMimeBlock / headersEnd)
- MIME 标头块结束时的增量检查点
- strcmp / scanf / char* 循环解析 URL (urlParse)
- char* 循环扫描每个标头行的结束位置 (
HttpHeader::parse) - strcmp 扫描标头名称的 : 分隔符并生成标头对象
- strListGet 扫描以解析标头内容选项
响应解析系统 Squid-3.6+ 中的 Http1::ResponseParser::parse 如下所示
- 扫描消息版本字段
- 接受“HTTP/1.x”和“ICY”协议版本
- 如果需要,生成一个假的 HTTP/0.9 回复并终止解析。
- 版本标签结束时的增量检查点
- 扫描消息状态码字段
- 状态码结束时的增量检查点
- 扫描第一行的结束位置
- 行结束时的增量检查点
- char* 循环扫描标头块的结束位置 (Http1::Parser::findMimeBlock / headersEnd)
- MIME 标头块结束时的增量检查点
- char* 循环扫描标头块的结束位置 (HttpMsg::httpMsgIsolateStart)
- strcmp 扫描标头名称的 : 分隔符并生成标头对象 (
HttpHeader::parse) - strListGet 扫描以解析标头内容选项
parser-ng-icap-pt2 分支中 ICAP 响应解析系统 Adaptation::Icap::ResponseParser::parse 如下所示
类继承自
Http1::ResponseParser解析器,但用 ICAP 特定的扫描替换了阶段 1 的版本扫描。
- 扫描消息版本字段
- 仅接受“ICAP/1.0”协议版本
- 版本标签结束时的增量检查点
- 扫描消息状态码字段
- 状态码结束时的增量检查点
- 扫描第一行的结束位置
- 行结束时的增量检查点
- char* 循环扫描标头块的结束位置 (Http1::Parser::findMimeBlock / headersEnd)
- MIME 标头块结束时的增量检查点
- char* 循环扫描标头块的结束位置 (HttpMsg::httpMsgIsolateStart)
- strcmp 扫描标头名称的 : 分隔符并生成标头对象 (
HttpHeader::parse) - strListGet 扫描以解析标头内容选项
注意:ICAP 响应消息和有效载荷段的解析仍然使用下面记录的 HTTP 响应的旧
HttpMsgAPI,当有效载荷段是请求时,它使用HttpMsg::parse请求行代码路径。
🔗 基线情况
- 保存以供比较。
对 Squid-3 中的请求解析系统的初步分析显示解析器栈如下
整个栈是异步的,在读取操作(消息未完整接收)后,会完全重置到步骤 1。
-
扫描以跳过垃圾前缀
-
解析请求行以查找 LF,以及无效的 CR 和 NIL (
HttpParser::parseRequestLine)- 丢弃先前的解析信息!!
-
再次,解析请求行以查找 SP 位置 (
HttpParser::parseRequestLine)- 丢弃先前的解析信息!!
-
解析请求行内的每个令牌以检查方法/URL/版本语法 (
HttpParser::parseRequestLine)- 丢弃先前的解析信息!!
-
char* 循环扫描标头块的结束位置 (headersEnd)
-
sscanf 重新扫描并进行请求行和(HttpRequest
::sanityCheck)的完整性检查- 不完整,重复步骤 2 和 3,部分重复步骤 5。
-
strcmp 解析出请求方法、URL、版本 (
HttpRequest::parseFirstLine)- 重复步骤 3 和 4
-
strcmp / scanf / char* 循环解析 URL (urlParse)
-
char* 循环扫描每个标头行的结束位置 (headersEnd)
-
strcmp 扫描标头名称的 : 分隔符并生成标头对象
-
strListGet 扫描以解析标头内容选项
解析序列在标头行解析(步骤 6)处连接,在完整性检查(步骤 3)处有一些交叉。响应解析如下
-
processReplyHeader 调用
HttpMsg::parse- 丢弃所有先前的解析信息!!
-
char* 循环扫描标头块的结束位置 (headersEnd)
-
sscanf 重新扫描并进行第一行的完整性检查 (
HttpReply::sanityCheck)- 失败时跳至下面的阶段 ii
-
strcspn 扫描以查找标头行的结束位置
-
char* 循环扫描标头块的结束位置 (HttpMSg::httpMsgIsolateStart)
-
strcmp 解析出响应版本、状态消息 (
HttpReply::parseFirstLine) -
strcspn 扫描以查找标头行的结束位置
-
char* 循环扫描标头块的结束位置(哇,6 次!)(
HttpMSg::httpMsgIsolateStart) -
strcmp 扫描标头名称的 : 分隔符并生成标头对象
-
strListGet 扫描以解析标头内容选项
ii. 检查特殊情况,缺少“HTTP”和“ICY”协议版本
-
生成一个假的 HTTP/0.9 回复
-
将其打包到缓冲区
-
解析假的回复!!
-
丢弃所有先前的解析信息!!
-
重复阶段 i 的所有内容
-
iii. char* 循环扫描标头块的结束位置 (headersEnd)
- 因为我们在阶段 i 中扫描的次数似乎不够
待办事项:记录 ICAP 响应解析序列。尽管有明显努力使其简单化,但由于需要运行上述整个响应和请求解析链来自动检测哪个将成功,因此比 HTTP 响应解析更糟糕。
分类: WantedFeature
导航:站点搜索,站点页面,类别,🔼 向上