31.Nginx HTTP之请求头解析函数ngx_http_parse_header_line

2023-05-16

HTTP >= 1.0 的版本中,请求行后紧跟的就是请求头了,Nginx使用ngx_http_parse_header_line来对请求头进行解析。


/* 解析HTTP请求头
 * param r: 待处理的HTTP请求r
 *       b: 存放请求头的缓冲区
 * return : 解析完请求头的一行时返回NGX_OK;
 *          解析完整个请求头时返回NGX_HTTP_PARSE_HEADER_DONE;
 *          解析出错时返回NGX_HTTP_PARSE_INVALID_HEADER;
 *          其他返回NGX_AGAIN, 表示需要读取新的请求头内容
 */
ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b)
{
    /* HTTP请求头格式, 头部字段名不区分大小写, 值中可以包含空格: 
     *  [头部字段名]:(空格..空格)[值](空格..空格)[回车符][换行符] 
     *          ...... 
     *  [头部字段名]:(空格..空格)[值](空格..空格)[回车符][换行符] 
     *  [回车符][换行符] 
     */
     
    u_char  c, ch, *p;
    enum {
        sw_start = 0,           // 初始状态
        sw_name,                // 解析请求头字段名
        sw_space_before_value,  // 解析请求头字段值前的空格
        sw_value,               // 解析请求头字段值
        sw_space_after_value,   // 解析请求头字段值后紧跟空格后的空格
        sw_almost_done,         // 解析标记请求头的行尾的换行符
        sw_header_almost_done,  // 解析标记请求头结束的换行符
        sw_ignore_line,         // 忽略请求头前的无用行
        sw_done,                // 解析完请求头的一行
        sw_header_done          // 解析完整个请求头
    } state;                    // 枚举类型变量: 请求头解析状态

    // 获取HTTP请求r的当前状态state
    state = r->state;
    // 获取缓冲区b中未解析的有效内容的起始位置p
    p = b->pos;

    while (p < b->last && state < sw_done) {
        // p小于b->last, 表示缓冲区内仍要未解析的有效内容;
        // state小于sw_done, 表示对于请求头的某一行仍未解析完毕
        
        // 获取当前待解析的字符ch
        ch = *p++;

        switch (state) {

        case sw_start:
            // 当前状态为初始状态
            
            switch (ch) {
            case CR:
                // 如果当前字符为\r, 表明请求头为空, 且遇到了标记请求头结束的回车符
                
                // 置r->header_end为p-1, 记录请求头在缓冲区中的结束位置
                r->header_end = p - 1;
                // 置state为sw_header_almost_done, 表示解析标记请求头结束的换行符
                state = sw_header_almost_done;
                break;
            case LF:
                // 如果当前字符为\n, 表明请求头为空, 且遇到了结束的换行符
                
                // 置r->header_end为p-1, 记录请求头在缓冲区中的结束位置
                r->header_end = p - 1;
                // 置state为sw_header_done, 表示解析完整个请求头
                state = sw_header_done;
                break;
            default:
                // 置state为sw_name, 表示解析请求头字段名
                state = sw_name;
                // 置r->header_name_start为p-1, 标记当前字段名的起始位置
                r->header_name_start = p - 1;

                // 尝试将当前字符转换为对应的小写字母
                c = (u_char) (ch | 0x20);
                if (c >= 'a' && c <= 'z') {
                    // 如果c为小写字母, 说明是有效的, 直接跳过
                    break;
                }

                if (ch == '-' || ch == '_' || ch == '~' || ch == '.') {
                    // 如果当前字符为-、_、~、., 也是有效的, 直接跳过
                    break;
                }

                if (ch >= '0' && ch <= '9') {
                    // 如果当前字符为数字, 也是有效的, 直接跳过
                    break;
                }
                // 当前字符为其他字符, 说明是无效的, 返回NGX_HTTP_PARSE_INVALID_HEADER
                return NGX_HTTP_PARSE_INVALID_HEADER;

            }
            break;

        case sw_name:
            // 当前状态为解析请求头字段名
            
            // 尝试将当前字符转换为对应的小写字母
            c = (u_char) (ch | 0x20);
            if (c >= 'a' && c <= 'z') {
                // 如果c为小写字母, 说明是有效的, 直接跳过
                break;
            }

            if (ch == ':') {
                // 如果当前字符为:, 说明遇到头部字段名后紧跟的:
                
                // 置r->header_name_end为p-1, 记录当前头部字段名的结束位置
                r->header_name_end = p - 1;
                // 置state为sw_space_before_value, 表示解析请求头字段值前的空格
                state = sw_space_before_value;
                break;
            }

            if (ch == '-' || ch == '_' || ch == '~' || ch == '.') {
                // 如果当前字符为-、_、~、., 也是有效的, 直接跳过
                break;
            }

            if (ch >= '0' && ch <= '9') {
                // 如果当前字符为数字, 也是有效的, 直接跳过
                break;
            }

            /* IIS can send duplicate "HTTP/1.1 ..." lines */
            // 看此注释, 是说IIS在请求头前可能会发送重复的"HTTP/1.1 ..."行, 对于这种行我们需要忽略
            if (ch == '/'
                && r->proxy
                && p - r->header_start == 5
                && ngx_strncmp(r->header_start, "HTTP", 4) == 0)
            {
                // 置state为sw_ignore_line, 表示忽略请求头前的无用行
                state = sw_ignore_line;
                break;
            }
            // 当前字符为其他字符, 说明是无效的, 返回NGX_HTTP_PARSE_INVALID_HEADER
            return NGX_HTTP_PARSE_INVALID_HEADER;

        case sw_space_before_value:
            // 当前状态为解析请求头字段值前的空格
            
            switch (ch) {
            case ' ':
                // 如果当前字符正是空格, 那么直接跳过
                break;
            case CR:
                // 如果当前字符为\r, 说明请求头为空, 而该字符为标记请求头结束的回车符
                
                // 置r->header_start和r->header_end为p-1, 记录当前行的字段值的起始位置和结束位置
                r->header_start = r->header_end = p - 1;
                // 置state为sw_almost_done, 表示解析标记请求头结束的换行符
                state = sw_almost_done;
                break;
            case LF:
                // 如果当前字符为\n, 说明请求头为空, 而该字符为标记请求头结束的换行符
                
                // 置r->header_start和r->header_end为p-1, 记录当前行的字段值的起始位置和结束位置
                r->header_start = r->header_end = p - 1;
                // 置state为sw_done, 表示解析完整个请求头
                state = sw_done;
                break;
            default:
                // 当前字符是其他字符, 说明是请求头字段值的一部分
                
                // 置r->header_start为p-1, 记录当前行的字段值的起始位置
                r->header_start = p - 1;
                // 置state为sw_value, 表示解析请求头字段值
                state = sw_value;
                break;
            }
            break;

        case sw_value:
            // 当前状态为解析请求头字段值
            
            switch (ch) {
            case ' ':
                // 如果当前字符为空格, 说明遇到字段值后紧跟的一个空格了
                
                // 置r->header_end为p-1, 记录请求头的当前行的结束位置
                r->header_end = p - 1;
                // 置state为sw_space_after_value, 表示解析请求头字段值后紧跟空格后的空格
                state = sw_space_after_value;
                break;
            case CR:
                // 如果当前字符为\r, 说明遇到标记请求头的行尾的回车符
                
                // 置r->header_end为p-1, 记录当前行的字段值的结束位置
                r->header_end = p - 1;
                // 置state为sw_almost_done, 表示解析标记请求头的行尾的换行符
                state = sw_almost_done;
                break;
            case LF:
                // 如果当前字符为\n, 说明遇到标记请求头的行尾的换行符
                
                // 置r->header_end为p-1, 记录当前行的字段值的结束位置
                r->header_end = p - 1;
                // 置state为sw_done, 表示解析完请求头的一行
                state = sw_done;
                break;
            }
            break;

        case sw_space_after_value:
            // 当前状态为解析请求头字段值后紧跟空格后的空格
            
            switch (ch) {
            case ' ':
                // 如果当前字符正是空格, 那么直接跳过
                break;
            case CR:
                // 如果当前字符是\r, 表示遇到标记请求头的行尾的回车符
                
                // 置state为sw_almost_done, 表示解析标记请求头的行尾的换行符
                state = sw_almost_done;
                break;
            case LF:
                // 如果当前字符是\n, 表示遇到标记请求头的行尾的换行符
                
                // 置state为sw_done, 表示解析完请求头的一行
                state = sw_done;
                break;
            default:
                // 当前字符是其他字符, 说明也是请求头字段值的一部分, 因为值中可以包含空格
                
                // 置state为sw_value
                state = sw_value;
                break;
            }
            break;

        case sw_ignore_line:
            // 当前状态为忽略请求头前的无用行
            switch (ch) {
            case LF:
                // 如果当前字符为\n, 说明遇到无用行的结尾
                
                // 置state为sw_start, 即初始状态
                state = sw_start;
                break;
            default:
                // 如果当前字符是其他字符, 则直接忽略
                break;
            }
            break;

        case sw_almost_done:
            // 当前状态为解析标记请求头的行尾的换行符
            switch (ch) {
            case LF:
                // 如果当前字符正是\n, 说明该行解析完毕
                
                // 置state为sw_done
                state = sw_done;
                break;
            default:
                // 当前字符是其他字符, 说明是非法字符
                // 返回NGX_HTTP_PARSE_INVALID_HEADER
                return NGX_HTTP_PARSE_INVALID_HEADER;
            }
            break;

        case sw_header_almost_done:
            // 当前状态为解析标记请求头结束的换行符
            switch (ch) {
            case LF:
                // 如果当前字符正是\n, 说明整个请求头解析完毕
                
                // 置state为sw_header_done
                state = sw_header_done;
                break;
            default:
                // 当前字符是其他字符, 说明是非法字符
                // 返回NGX_HTTP_PARSE_INVALID_HEADER
                return NGX_HTTP_PARSE_INVALID_HEADER;
            }
            break;

        case sw_done:
        case sw_header_done:
            break;
        }
    }

    // p指向待解析的下一个字符, 置b->pos为p
    b->pos = p;

    if (state == sw_done) {
        // 如果当前状态为sw_done, 说明解析完请求头的一行
        
        // 重置r->state为sw_start初始状态以用于解析下一行
        r->state = sw_start;
        // 返回NGX_OK
        return NGX_OK;

    } else if (state == sw_header_done) {
        // 如果当前状态为sw_header_done, 说明解析完整个请求头
        
        // 重置r->state为sw_start初始状态
        r->state = sw_start;
        // 返回NGX_HTTP_PARSE_HEADER_DONE
        return NGX_HTTP_PARSE_HEADER_DONE;

    } else {
        // 如果当前状态是其他状态, 说明只解析了某行的一部分, 然而缓冲区中已没有多余的有效内容
        
        // 记录当前状态
        r->state = state;
        // 返回NGX_AGAIN
        return NGX_AGAIN;
    }
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

31.Nginx HTTP之请求头解析函数ngx_http_parse_header_line 的相关文章

  • 如何发送http basic auth post?

    我的任务是使用基本身份验证创建 http 帖子 我正在 asp net MVC 应用程序中使用 C 进行开发 我也得到过这个例子 POST v2 token endpoint HTTP 1 1 Authorization Basic Y2x
  • 将照片上传到 Google Photos API 不返回上传令牌

    我正在使用 2018 版 Google Photos API 来上传图像和媒体 如下所述 上传字节 https developers google com photos library guides upload media uploadi
  • 浏览器和服务器在实践中是否使用 HTTP 内容协商?

    我正在学习关于HTTP内容协商 https developer mozilla org en US docs Web HTTP Content negotiation眼下 我已经了解客户端和服务器能够协商所请求内容的表示的基本方式 但我不知
  • 如何在 nginx.conf 中引用操作系统环境变量

    在 nginx conf 中 设置变量后set name value 我可以像这样参考它 name 但是当我导出操作系统环境变量时 经过env name from env like https nginx org en docs ngx c
  • nginx 服务器中不允许方法 405 错误

    我们的反应应用程序在我们的本地机器上正常工作 但我们将其延迟到更高的环境中 它不起作用 它发送405 Method not allowed error 页面正在加载 每当我们要求时submit form这个问题来了 下面是我的nginx c
  • 网页编码,设置矛盾[重复]

    这个问题在这里已经有答案了 如果一个网页有 但http标头有 Content Type text html charset UTF 8 那么假设什么编码呢 在 HTML5 中 优先级定义为 用户浏览器设置 字节顺序标记 HTTP 标头 or
  • Nginx 对 cms 后端的重写规则

    我需要在 nginx 服务器中制定 url 重写规则 服务器块 就像我之前的 apache 服务器一样 这是 htaccess 中的代码 我需要将其实现 转换 到我现有的代码中 RewriteRule A Za z0 9 A Za z0 9
  • 为什么 Python 3 http.client 比 python-requests 快这么多?

    我今天测试了不同的 Python HTTP 库 我意识到http client https docs python org 3 library http client html库的执行速度似乎比requests http docs pyth
  • 多个资源的 REST 接口使用

    我目前正在通过 http 添加 REST API 到在线服务 我遇到了一个非常简单的问题 我找不到令我满意的答案 我主要有 2 个资源 用户 和 报告 正如您所猜测的那样 报告与用户相关联 与一个且仅一个 我的数据库中的外键 不管怎样 我有
  • 维梅奥上传。无法获取响应中的complete_uri字段

    我在上传到 vimeo 时花了很多功夫 我已经提出了门票请求 我已经上传文件了 我已经检查了文件是否已上传 我需要使用我应该从票证中获得的complete uri 响应来运行DELETE 方法 但是 我没有从票证响应中收到任何complet
  • 如何使用 Jade 和 Node.js 迭代 JSON 数组

    所以我有这个 JSON 数组apiData被传递到视图作为data Backend router get function req res var data JSON stringify apiData res render gallery
  • 为从 nginx 反向代理转发的请求添加唯一 id

    我们运行 nginx 作为反向代理 将请求转发到运行 Compojure 的 Clojure 应用程序 Compojure 是一个封装 Jetty 的库 为我们的应用程序提供服务 Web 请求的能力 目前 我们捕获 nginx 和 Cloj
  • 如何设置http请求的源IP?

    在发送 http 请求之前 我需要设置源 IP 地址 用于 IP 欺骗等 用于建立http连接的类是HTTPURLConnection 我在 stackoverflow 上找到了下面的链接 这非常有用 注册和使用自定义 java net U
  • IIS7 和 HTTP 状态代码处理

    我因试图对 IIS7 集成模式 中的错误呈现进行完整的编程控制而感到非常头疼 我想要做的是给出一个错误 找不到页面 内部服务器错误 未经过身份验证等 将整个请求传输到自定义 ASPX 或 HTML 我更喜欢后者 并使用正确的 HTTP 状态
  • 为什么要使用 Node.js 安装服务器(Nginx、Apache...)? [复制]

    这个问题在这里已经有答案了 可能的重复 为什么 Node js Express Web 框架下需要 apache https stackoverflow com questions 9287747 why do we need apache
  • 如何修改 HttpUrlConnection 的标头

    我试图稍微改进一下 Java Html 文档 但我遇到了问题HttpUrlConntion 有一件事是 如果用户代理是 Java VM 某些服务器会阻止请求 另一个问题是HttpUrlConnection不设置Referrer or Loc
  • Nginx vs Apache 用于高流量站点

    Would nginx作为高流量网站的网络服务器是否是更合适的选择 我们将建立的网站是一个电子商务网站 如果这有什么不同的话 无论哪种方式 从技术角度来看 我真的对实际的 原因 感兴趣 即 为什么会nginx从技术角度来看 对于此类网站来说
  • 如何在 http POST 请求中发送 HTML 代码?

    我正在从 Google Web Toolkit 应用程序向 php 脚本发送 HTTP POST 请求 内容类型现在是 application x www form urlencoded 所以我可以将变量发送到 php 并使用 POST 在
  • Angular:将数据从工厂 ajax 调用传递回我的控制器

    我一直在使用 Angular 并且已经从使用本地数据 似乎工作正常 转向尝试通过工厂中的 ajax 调用来填充我的视图 这是代码 div h2 Get data using a Factory h2 div div div
  • REST DELETE 真的是幂等的吗?

    DELETE 应该是幂等的 如果我删除http example com account 123 http example com account 123它将删除该帐户 如果我再次这样做 我会收到 404 错误吗 因为该帐户已不存在 如果我尝

随机推荐