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;
    }
}