28.Nginx HTTP之读取处理请求行函数ngx_http_process_request_line

来源:互联网 发布:cf一键调烟雾头软件 编辑:程序博客网 时间:2024/05/24 03:34

前面我们看到,Nginx会在适当的时机(启用HTTPS时那就是在SSL握手之后)为HTTP连接的读事件注册处理函数ngx_http_process_request_line,该函数用于读取并处理HTTP请求行。

HTTP请求行格式为: [请求方法][空格][URL][空格][协议版本][回车符][换行符]

一般来说,HTTP请求行不会很长,但是依旧有可能在单次系统调用中无法读取完整的请求行,所以可能需要多次调用ngx_http_process_request_line才能读取和处理完请求行;也正因如此,Nginx在请求行的解析过程中对状态进行了记录。

我觉得,能否在读取到/r/n时才对请求行进行解析呢?这种做法好处是比较简单,但是会有性能问题,因为在读取到非法的请求行内容时,就不需要在读取之后的内容呢!而Nginx是以追求高性能著称的,显然一边读取一遍解析会更优。


/* http/ngx_http_request.c *//* 读取HTTP请求头(包含请求行) * param r: 待读取的HTTP请求 * return : 读取的字节长度 */static ssize_t ngx_http_read_request_header(ngx_http_request_t *r){    ssize_t                    n;    ngx_event_t               *rev;    ngx_http_core_srv_conf_t  *cscf;    // 从r可以获取对应的连接, 从连接可以获取对应的读事件rev    rev = r->connection->read;    // HTTP请求r的header_in缓冲区用于缓存读取的内容    // 缓冲区的last-pos, 即为当前缓存的未处理的内容长度    n = r->header_in->last - r->header_in->pos;    if (n > 0) {        // 如果n大于0, 说明我们可以直接从缓冲区获取内容, 而不是通过系统调用来获取        return n;    }    if (!rev->ready) {        // 如果rev->ready为0, 说明当前不可读, 返回NGX_AGAIN        return NGX_AGAIN;    }    // 调用连接的recv指针所指函数来读取内容;    // 我们在ngx_event_accept中看到过Nginx会为建立的连接注册recv函数ngx_recv;    // 可想而知ngx_recv肯定是通过某个系统调用来从已连接套接字读取内容    n = r->connection->recv(r->connection, r->header_in->last,                            r->header_in->end - r->header_in->last);    if (n == NGX_AGAIN) {        // 如果n为NGX_AGAIN, 说明未读取到任何内容                if (!r->header_timeout_set) {            // 如果r->header_timeout_set为0, 即表示读取header的定时器未设置                        // 获取ngx_http_core_module模块的server层次配置上下文            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);            // 添加读事件rev到定时器红黑树, 超时时间为client_header_timeout            ngx_add_timer(rev, cscf->client_header_timeout);            // 置r->header_timeout_set为1            r->header_timeout_set = 1;        }        // 将读事件rev添加到epoll句柄, 因为前面已经添加过, 所以这里不会重复添加        if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);            ngx_http_close_connection(r->connection);            return NGX_ERROR;        }        return NGX_AGAIN;    }    if (n == 0) {        // 如果n为0, 说明客户端断开了连接                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,                      "client closed prematurely connection");    }    if (n == 0 || n == NGX_ERROR) {        // 如果n为0或者NGX_ERROR, 说明出错, 那么关闭当前HTTP请求和连接        ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST);        ngx_http_close_connection(r->connection);        return NGX_ERROR;    }    // r->header_in缓冲区的last指针后移n个字节    r->header_in->last += n;    // 返回读取的字节长度    return n;}/* 读取和处理HTTP请求行 * param rev: 待处理的读事件 */static void ngx_http_process_request_line(ngx_event_t *rev){    u_char              *p;    ssize_t              n;    ngx_int_t            rc, rv;    ngx_connection_t    *c;    ngx_http_request_t  *r;    ngx_http_log_ctx_t  *ctx;    c = rev->data;    r = c->data;    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,                   "http process request line");    if (rev->timedout) {        // 如果rev->timedout为1, 说明客户端超时                // 记录错误并发送错误信息到客户端        ngx_http_client_error(r, 0, NGX_HTTP_REQUEST_TIME_OUT);        return;    }    rc = NGX_AGAIN;    for ( ;; ) {        if (rc == NGX_AGAIN) {            // 读取HTTP请求行            n = ngx_http_read_request_header(r);            if (n == NGX_AGAIN || n == NGX_ERROR) {                // 如果n为NGX_AGAIN(当前不可读)或者NGX_ERROR(出错), 那么返回;                // 所以可能需要多次调用ngx_http_process_request_line才能读取完整的请求行                return;            }        }        // 对读取的请求行进行解析, 只有当解析完请求行时才会返回NGX_OK;        // 当没有解析完请求行但是也没有出错时, 返回NGX_AGAIN; 否则返回其他        rc = ngx_http_parse_request_line(r, r->header_in);        if (rc == NGX_OK) {            // 如果rc为NGX_OK, 表明已经解析完请求行            // 在解析请求行中URL的路径时, 只是进行了一些基本的字符检查工作,            // 所以下面称之为unparsed_uri            r->unparsed_uri.len = r->uri_end - r->uri_start;            r->unparsed_uri.data = ngx_palloc(r->pool, r->unparsed_uri.len + 1);            if (r->unparsed_uri.data == NULL) {                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                ngx_http_close_connection(c);                return;            }            ngx_cpystrn(r->unparsed_uri.data, r->uri_start,                        r->unparsed_uri.len + 1);            // 路径长度不包括参数长度            if (r->args_start) {                r->uri.len = r->args_start - 1 - r->uri_start;            } else {                r->uri.len = r->uri_end - r->uri_start;            }            if (!(r->uri.data = ngx_palloc(r->pool, r->uri.len + 1))) {                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                ngx_http_close_connection(c);                return;            }            if (r->complex_uri) {                // 如果r->complex_uri为1, 说明是复杂的路径                                // 调用ngx_http_parse_complex_uri来对路径进行解析                rc = ngx_http_parse_complex_uri(r);                if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {                    ngx_http_close_request(r, rc);                    ngx_http_close_connection(c);                    return;                }                if (rc != NGX_OK) {                    r->request_line.len = r->request_end - r->request_start;                    r->request_line.data = r->request_start;                    ngx_http_client_error(r, rc, NGX_HTTP_BAD_REQUEST);                    return;                }            } else {                // 注意r->uri与r->unparsed_uri的区别, 前者不包含参数部分; 后者则包含                ngx_cpystrn(r->uri.data, r->uri_start, r->uri.len + 1);            }            r->request_line.len = r->request_end - r->request_start;            r->request_line.data = r->request_start;            r->request_line.data[r->request_line.len] = '\0';            if (r->method == 0) {                r->method_name.len = r->method_end - r->request_start + 1;                r->method_name.data = r->request_line.data;            }            if (r->uri_ext) {                // 拷贝URL中的扩展名到r->exten                                if (r->args_start) {                    r->exten.len = r->args_start - 1 - r->uri_ext;                } else {                    r->exten.len = r->uri_end - r->uri_ext;                }                if (!(r->exten.data = ngx_palloc(r->pool, r->exten.len + 1))) {                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                    ngx_http_close_connection(c);                    return;                }                ngx_cpystrn(r->exten.data, r->uri_ext, r->exten.len + 1);            }            if (r->args_start && r->uri_end > r->args_start) {                // 拷贝URL参数到r->args                r->args.len = r->uri_end - r->args_start;                if (!(r->args.data = ngx_palloc(r->pool, r->args.len + 1))) {                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                    ngx_http_close_connection(c);                    return;                }                ngx_cpystrn(r->args.data, r->args_start, r->args.len + 1);            }            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,                           "http request line: \"%s\"", r->request_line.data);            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,                           "http uri: \"%s\"", r->uri.data);            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,                           "http args: \"%s\"",                           r->args.data ? r->args.data : (u_char *) "");            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,                           "http exten: \"%s\"",                           r->exten.data ? r->exten.data : (u_char *) "");            if (r->http_version < NGX_HTTP_VERSION_10) {                // 如果HTTP版本低于1.0                                // 置读事件rev的事件处理函数为ngx_http_block_read                rev->event_handler = ngx_http_block_read;                ngx_http_handler(r);                return;            }            // 初始化r->headers_in.headers单向链表, 顾名思义, 这个链表是用来存放请求头的            if (ngx_list_init(&r->headers_in.headers, r->pool, 20,                                         sizeof(ngx_table_elt_t)) == NGX_ERROR)            {                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                ngx_http_close_connection(c);                return;            }            // 初始化r->headers_in.cookies数组            if (ngx_array_init(&r->headers_in.cookies, r->pool, 5,                                       sizeof(ngx_table_elt_t *)) == NGX_ERROR)            {                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                ngx_http_close_connection(c);                return;            }            ctx = c->log->data;            ctx->action = "reading client request headers";            ctx->url = r->unparsed_uri.data;            // 读取处理完请求行, 那么接下来就是读取处理请求头了            // 为读事件rev注册事件处理函数ngx_http_process_request_headers            rev->event_handler = ngx_http_process_request_headers;            // 可能请求头已经可以读取了, 先调用一次ngx_http_process_request_headers进行处理            ngx_http_process_request_headers(rev);            return;        } else if (rc != NGX_AGAIN) {            // 如果rc不为NGX_OK或NGX_AGAIN, 表明请求行有非法内容            for (p = r->request_start; p < r->header_in->last; p++) {                if (*p == CR || *p == LF) {                    break;                }            }            r->request_line.len = p - r->request_start;            r->request_line.data = r->request_start;            if (rc == NGX_HTTP_PARSE_INVALID_METHOD) {                r->http_version = NGX_HTTP_VERSION_10;            }            ngx_http_client_error(r, rc,                                  (rc == NGX_HTTP_PARSE_INVALID_METHOD) ?                                         NGX_HTTP_NOT_IMPLEMENTED:                                         NGX_HTTP_BAD_REQUEST);            return;        }        // rc为NGX_AGAIN时, 表明仍要继续读取请求行                if (r->header_in->pos == r->header_in->end) {            // 如果header_in缓冲区pos等于end, 表明缓冲区已被耗尽, 实际上里面没有存放任何有效内容                        // 需要对header_in缓冲区进行重置, 或者创建更大的缓冲区            rv = ngx_http_alloc_large_header_buffer(r, 1);            if (rv == NGX_ERROR) {                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);                ngx_http_close_connection(c);                return;            }            if (rv == NGX_DECLINED) {                ngx_http_client_error(r, NGX_HTTP_PARSE_TOO_LONG_URI,                                      NGX_HTTP_REQUEST_URI_TOO_LARGE);                return;            }        }    }}



原创粉丝点击