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; } } }}
阅读全文
0 0
- 28.Nginx HTTP之读取处理请求行函数ngx_http_process_request_line
- 30.Nginx HTTP之读取处理请求头函数ngx_http_process_request_headers
- nginx处理http请求
- 29.Nginx HTTP之请求行解析函数ngx_http_parse_request_line
- Nginx的HTTP请求处理
- Nginx 中处理 HTTP 请求
- nginx http处理请求入口
- nginx处理http请求流程
- 31.Nginx HTTP之请求头解析函数ngx_http_parse_header_line
- Nginx多阶段处理HTTP请求
- Nginx中http请求的处理过程
- nginx 如何处理一个HTTP请求
- Nginx处理HTTP请求的路由过程
- Servlet之HTTP请求处理
- 《深入理解Nginx》笔记之 HTTP请求的11个处理阶段
- nginx处理post请求(http响应包体收发之下游网速优先策略)
- nginx处理post请求(http响应包体收发之上游网速优先策略)
- nginx处理post请求之数据转发
- 一些距离及相似度计算公式
- Python日期格式化符
- web移动开发问题录
- 使用IntelliJ IDEA 配置Maven(入门)
- matlab中用unique函数去除矩阵重复的行,且不改变矩阵顺序
- 28.Nginx HTTP之读取处理请求行函数ngx_http_process_request_line
- 程序设计基础—入门知识点(一)
- MATLAB2012b启动,总是打开激活页面无法启动
- matplotlib调整子图间距,调整整体空白
- Spring AutoConfigurationMetadataLoader是如何工作的 ?
- Linux 安装字体库-宋体
- NYOJ F. 邮票分你一半
- Web 单点登录SSO
- 简单的单层神经网络的实现,附代码