龙空技术网

nginx请求行读取流程

晚晚Linux服务器 572

前言:

此时各位老铁们对“nginx读文件”大约比较注意,大家都需要分析一些“nginx读文件”的相关资讯。那么小编也在网摘上汇集了一些有关“nginx读文件””的相关资讯,希望你们能喜欢,看官们一起来学习一下吧!

在读取数据完成之后,nginx会将读取事件的回调方法设置为ngx_http_process_request_line(),这个方法主要有如下几个作用:

读取客户端请求的数据,如果客户端数据读取不全,则继续监听客户端读事件以读取完整数据;解析读取到的客户端数据,将各个参数存储到表征当前请求的ngx_http_request_t结构体中;将读事件的回调方法设置为ngx_http_process_request_headers(),以继续处理客户端发送来的header数据。

这里需要说明的一点是,所谓的请求行指的是http请求报文中类似于GET /index HTTP/1.1的部分,根据http协议,这一部分下面的数据才是各个header数据,而这里解析请求行数据的过程是不包含如何解析header数据的(这部分我们将在下一篇文章中进行讲解)。

1. 请求行处理主流程

请求行处理的主流程主要是在ngx_http_process_request_line()方法中,如下是该方法的源码:

static void ngx_http_process_request_line(ngx_event_t *rev) {  ssize_t n;  ngx_int_t rc, rv;  ngx_str_t host;  ngx_connection_t *c;  ngx_http_request_t *r;  c = rev->data;  r = c->data;  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;  }  rc = NGX_AGAIN;  for (;;) {    if (rc == NGX_AGAIN) {      // 这里的ngx_http_read_request_header()方法的主要作用是返回在连接上读取到的数据      n = ngx_http_read_request_header(r);      // n为NGX_AGAIN时,说明已经将当前事件添加到事件队列中了,因而直接返回,而NGX_ERROR则说明      // 当前读取失败了,也直接返回      if (n == NGX_AGAIN || n == NGX_ERROR) {        return;      }    }    // 这里主要是解析请求行的数据,比如"GET /index HTTP/1.1"    rc = ngx_http_parse_request_line(r, r->header_in);    // NGX_OK表示请求行的数据是完整的,并且已经解析完成    if (rc == NGX_OK) {      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;      }      // 标记与参数相关的属性      if (ngx_http_process_request_uri(r) != NGX_OK) {        return;      }      if (r->host_start && r->host_end) {        host.len = r->host_end - r->host_start;        host.data = r->host_start;        // 校验host,并且将host转换为小写形式        rc = ngx_http_validate_host(&host, r->pool, 0);        if (rc == NGX_DECLINED) {          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;        }        // 设置用于处理当前请求的server和location块        if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {          return;        }        r->headers_in.server = host;      }      // NGX_HTTP_VERSION_10表示当前请求是http 1.0,这里就是判断当前请求是否为0.9版本的请求,      // 如果是0.9版本的请求,那么是没有请求头的      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;        }        // 对于0.9版本的请求,直接处理请求        ngx_http_process_request(r);        return;      }      // 初始化headers链表      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";      // 将事件的处理方法设置为ngx_http_process_request_headers()方法      rev->handler = ngx_http_process_request_headers;      ngx_http_process_request_headers(rev);      return;    }    if (rc != NGX_AGAIN) {      ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);      return;    }    /* NGX_AGAIN: a request line parsing is still incomplete */    // 走到这里,说明返回值是NGX_AGAIN,也即读取请求行还未读取完全,如果缓冲区还有可用数据,    // 则不做任何处理,否则继续下一次循环    if (r->header_in->pos == r->header_in->end) {      // 为当前request申请大块内存      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_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);        return;      }    }  }}

对于这里的主流程,我们可以看到,其首先是检查读事件是否过期,如果过期了,则直接返回。如果没有过期,则进入一个无限for循环,如下是这个循环的工作:

调用ngx_http_read_request_header()方法读取数据,在这个方法中,nginx会检查当前连接句柄上是否存在可读的数据,如果不存在,继续将当前事件添加到事件框架中。由于当前事件的回调方法是上面的主流程方法,也即ngx_http_process_request_line(),因而当再次触发读事件时,还是会走到这里的ngx_http_read_request_header()方法读取数据,这也就是为什么在ngx_http_read_request_header()方法返回NGX_AGAIN时,主流程可以直接返回而不做任何处理的原因;调用ngx_http_parse_request_line()方法解析请求行的数据,这个方法比较长,但是逻辑非常简单,主要工作就是一个字符一个字符的比对请求行中的各个数据,然后将其设置到ngx_http_request_t中,我们这里不对齐进行深入讲解;根据前面两个方法的返回值处理当前请求,主要分为NGX_OK、NGX_AGAIN、NGX_DECLINED和NGX_ERROR。当返回值是NGX_DECLINED时,表示请求行太长,此时会给客户端返回414状态码;当返回值是NGX_ERROR时,会直接关闭当前连接,并且返回500状态码;而NGX_AGAIN则会继续等待触发下一次的事件循环;在响应码为NGX_OK时,会设置ngx_http_request_t中的相关字段,并且会检查请求行数据的合法性。需要注意的是,在这个分支最后检查完时会分情况,如果当前http请求版本为0.9版本,由于0.9版本没有请求头,因而直接调用ngx_http_process_request()进入http模块的11个阶段进行处理。如果当前版本是1.0以上,由于其是需要传请求头的,因而会调用ngx_http_process_request_headers()方法读取并处理请求头数据。2. 请求行数据读取

我们这里主要看一下nginx是如何读取请求行数据的,如下是ngx_http_read_request_header()方法的源码:

static ssize_t ngx_http_read_request_header(ngx_http_request_t *r) {  ssize_t n;  ngx_event_t *rev;  ngx_connection_t *c;  ngx_http_core_srv_conf_t *cscf;  c = r->connection;  rev = c->read;  // 计算当前还有多少数据未处理  n = r->header_in->last - r->header_in->pos;  // 如果n大于0,说明还有读取到的数据未处理,则直接返回n  if (n > 0) {    return n;  }  // 走到这里,说明当前读取到的数据都已经处理完了,因而这里会进行判断,如果当前事件的ready参数为1,  // 则表示当前连接的句柄上存储还未读取的数据,因而调用c->recv()方法读取数据,否则继续将当前事件  // 添加到事件队列中,并且继续监听当前连接句柄的读事件  // 这里的c->recv()方法实际指向的是ngx_unix_recv()方法,主要作用就是读取指定连接上的数据  if (rev->ready) {    // 在连接文件描述符上读取数据    n = c->recv(c, r->header_in->last, r->header_in->end - r->header_in->last);  } else {    n = NGX_AGAIN;  }  // 如果n为NGX_AGAIN,则将当前事件添加到事件监听器中,并且继续监听当前epoll句柄的读事件  if (n == NGX_AGAIN) {    if (!rev->timer_set) {      cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);      ngx_add_timer(rev, cscf->client_header_timeout);    }    if (ngx_handle_read_event(rev, 0) != NGX_OK) {      ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);      return NGX_ERROR;    }    return NGX_AGAIN;  }  // 如果n为0,说明客户端关闭了连接  if (n == 0) {    ngx_log_error(NGX_LOG_INFO, c->log, 0, "client prematurely closed connection");  }  // 如果客户端关闭了连接或者读取异常,则回收当前的request结构体  if (n == 0 || n == NGX_ERROR) {    c->error = 1;    c->log->action = "reading client request headers";    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);    return NGX_ERROR;  }  // 更新当前读取到的数据指针  r->header_in->last += n;  return n;}

这里读取请求行的数据流程整体也比较简单,其主要流程如下:

检查当前读取缓冲区中是否存在还未处理的数据,如果存在,则直接返回,以让后面的ngx_http_parse_request_line()方法解析这些数据;检查当前的事件是否处于可执行状态,是则调用c->recv()方法从连接的句柄上读取数据,返回值n表示读取到的数据大小;如果当前事件处于不可执行状态,则将当前事件添加到事件框架中,并且在epoll句柄上为当前连接注册读事件;在第二步中,如果读取数据的返回值是NGX_ERROR或者0(0表示客户端断开了连接),则给客户端返回400响应码。

可以看到,这里对于请求行数据的处理过程,简单的说就是如果存在数据就读取,如果不存在则注册当前的读取事件。

标签: #nginx读文件