设为首页 收藏本站
查看: 389|回复: 0

[经验分享] ngx_http_process_request_line函数解析

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2016-4-29 16:58:48 | 显示全部楼层 |阅读模式
处理请求行,一般的http请求过程中,ngx_http_process_request_line函数被ngx_http_wait_request_handler调用,并且在ngx_http_wait_request_handler中被设置为读事件的回调函数。在连接被accept之后,有数据到达之后会执行ngx_http_create_request函数,之后再有读事件被触发时被调用的回调函数就是ngx_http_process_request_line。主要代码和解析如下。
判断是否超时,如果超时,报错并且结束请求。

1
2
3
4
5
6
if (rev->timedout) {
    ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
    c->timedout = 1;
    ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
    return;
}



剩下的过程全部在一个for(;;){...}块中执行。
ngx_http_read_request_header函数如果返回NGX_AGAIN,说明尚有数据未接受完,读事件还会被触发,而且读事件回调函数依然是ngx_http_process_request_line。如果返回NGX_ERROR,说明出问题了,需要结束请求。正常的话,ngx_http_read_request_header返回了接受到的字符个数,并且说明已经接受完了。
1
2
3
4
5
6
if (rc == NGX_AGAIN) {
    n = ngx_http_read_request_header(r);
    if (n == NGX_AGAIN || n == NGX_ERROR) {
        return;
    }
}



ngx_http_read_request_header函数将接受到的内容存到了r->header_in,接下来ngx_http_parse_request_line函数解析请求行。ngx_http_parse_request_line函数有可能返回的结果有三类,一类是NGX_OK,一类是INVALID相关的,另一类是NGX_AGAIN,下面分类进行分析。
1
rc = ngx_http_parse_request_line(r, r->header_in);



按照代码的顺序,首先是返回了NGX_OK,返回NGX_OK说明请求行已经完整的接受并解析,主要代码如下。
1
if (rc == NGX_OK) {



    根据请求行解析结果初始化r结构体
1
2
3
4
5
6
7
8
    r->request_line.len = r->request_end - r->request_start;
    r->request_line.data = r->request_start;
    r->request_length = r->header_in->pos - r->request_start;
    r->method_name.len = r->method_end - r->request_start + 1;
    r->method_name.data = r->request_line.data;
    if (r->http_protocol.data) {
        r->http_protocol.len = r->request_end - r->http_protocol.data;
    }



    解析uri,函数中主要初始化了r结构体中的uri和args相关的变量,并且判断了请求行中是否存在非法字符。
1
2
3
    if (ngx_http_process_request_uri(r) != NGX_OK) {
        return;
    }



    关于host_start和host_end在ngx_http_parse_request_line函数中有可能被赋值。如果在本机起一个nginx,监听80端口,用curl工具发送这样一条请求:curl -x 127.0.0.1:80 http://192.168.119.51:80/test.html。如果GDB调试的话可以看到请求行是:GET http://192.168.119.51:80/test.html HTTP/1.1xxxxxx。而非常见的GET /test.html HTTP/1.1xxxxxx。个人认为这是为了兼容http 0.9。此时上述两个变量就不是0x0了。在淘宝团队的nginx博客中是这样描述的:请求行的uri里面包含了域名部分。本人也理解的不是很透彻。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    if (r->host_start && r->host_end) {
        host.len = r->host_end - r->host_start;
        host.data = r->host_start;
        rc = ngx_http_validate_host(&host, r->pool, 0);
        if (rc == NGX_DECLINED) {
            ngx_log_error(NGX_LOG_INFO, c->log, 0,
                          "client sent invalid host in request line");
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            return;
        }
        if (rc == NGX_ERROR) {
            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }
        if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
            return;
        }
        r->headers_in.server = host;
    }



    http0.9不需要解析请求头,直接处理请求即可。r->headers_in.server变量是在上一个if块中被赋值的。http0.9的请求不知道怎么模拟。。。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    if (r->http_version < NGX_HTTP_VERSION_10) {
        if (r->headers_in.server.len == 0
            && ngx_http_set_virtual_server(r, &r->headers_in.server)
               == NGX_ERROR)
        {
            return;
        }
        ngx_http_process_request(r);
        return;
    }
    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
                      sizeof(ngx_table_elt_t))
        != NGX_OK)
    {
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }
    c->log->action = "reading client request headers";



    设置读事件回调函数,再有数据到达时调用请求头处理函数。当前可能已经收到了请求头数据,所以马上执行一次。
1
2
3
4
    rev->handler = ngx_http_process_request_headers;
    ngx_http_process_request_headers(rev);
    return;
}



ngx_http_parse_request_line函数返回结果如果不是NGX_OK,也不是NGX_AGAIN,那就一定是INVALID相关的,需要返回错误并且结束请求。
1
2
3
4
5
6
if (rc != NGX_AGAIN) {
    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                  ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);
    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
    return;
}



最后只剩下ngx_http_parse_request_line函数返回NGX_AGAIN的情况了。需要判断是否请求头部过大,过大的话就要开辟更大的buffer,并执行ngx_http_finalize_request函数,这个函数较为复杂,我们只讨论常规的http请求。在这里执行的ngx_http_finalize_request请求,并不会真的结束请求,而且还会再进入到这个函数中。如果没有出现头部过大的情况,就要再次执行for循环语句,继续执行ngx_http_read_request_header函数,读取缓冲区中的数据,继续ngx_http_parse_request_line函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (r->header_in->pos == r->header_in->end) {
    rv = ngx_http_alloc_large_header_buffer(r, 1);
    if (rv == NGX_ERROR) {
        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }
    if (rv == NGX_DECLINED) {
        r->request_line.len = r->header_in->end - r->request_start;
        r->request_line.data = r->request_start;
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client sent too long URI");
        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
        return;
    }
}



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-210428-1-1.html 上篇帖子: nginx1.8.0和php7.0.4编译安装脚本 下篇帖子: Centos6.7编译安装Nginx1.81
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表