Home  >  Article  >  Backend Development  >  Handling HTTP requests in Nginx

Handling HTTP requests in Nginx

WBOY
WBOYOriginal
2016-08-08 09:30:17733browse

Overview

During the initial startup process of Nginx, the worker worker process will call the ngx_event_process_init method of the event module to allocate a ngx_connection_t connection for each listening socket ngx_listening_t, and Set up the connection Read event callback method handler is ngx_event_accept, and mounts the read event to the epoll event mechanism to wait for the read event to occur on the listening socket connection. At this point, Nginx can receive and process requests from the client. When a readable event occurs on the listening socket connection, that is, there is a connection request from the client on the connection, the handler callback method ngx_event_accept of the read event will be started, which is called in the ngx_event_accept method The accept() function receives the connection request from the client. After the connection is successfully established, the ngx_event_accept function calls the handler callback method on the listening socket. ls->handler(c) (The callback method is ngx_http_init_connection). Therefore, after the connection is successfully established, the ngx_http_init_connection method starts processing the request data on the connection.

Receive the HTTP request message

Before receiving the HTTP request, the successfully established connection will first be initialized; the function of the ngx_http_init_connection function is to set the callback method for reading and writing events, and the actual writing event The callback method does not perform any operation. The callback method of the read event is to initialize the HTTP request process.

ngx_http_init_connection function execution flow:

  • Set the callback method handler for the write event on the current connection to ngx_http_empty_handler (actually this method does not perform any operation);
  • Set the read event on the current connection The callback method handler is ngx_http_wait_request_handler;
  • checks whether the read event on the current connection is ready (i.e. the ready flag is 1):
    • If the read event ready flag is 1, it means there is a readable TCP stream on the current connection, then the callback method of the read event will be executed. ngx_http_wait_request_handler;
    • If the read event ready flag is 0, indicating that there is no readable TCP stream on the current connection, then the read event will be added to the timer event mechanism (monitoring whether the readable event times out), At the same time, the read event is registered to the epoll event mechanism and waits for the occurrence of a readable event; the

function ngx_http_init_connection is defined in the file src/http/ngx_http_request.c as follows:

void
ngx_http_init_connection(ngx_connection_t *c)
{
    ngx_uint_t              i;
    ngx_event_t            *rev;
    struct sockaddr_in     *sin;
    ngx_http_port_t        *port;
    ngx_http_in_addr_t     *addr;
    ngx_http_log_ctx_t     *ctx;
    ngx_http_connection_t  *hc;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6    *sin6;
    ngx_http_in6_addr_t    *addr6;
#endif

    /* 分配http连接ngx_http_connection_t结构体空间 */
    hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
    if (hc == NULL) {
        ngx_http_close_connection(c);
        return;
    }

    c->data = hc;

    /* find the server configuration for the address:port */

    port = c->listening->servers;

    if (port->naddrs > 1) {

        /*
         * there are several addresses on this port and one of them
         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
         * is required to determine a server address
         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        ...
#endif

        default: /* AF_INET */
            sin = (struct sockaddr_in *) c->local_sockaddr;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (addr[i].addr == sin->sin_addr.s_addr) {
                    break;
                }
            }

            hc->addr_conf = &addr[i].conf;

            break;
        }

    } else {

        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        ...
#endif

        default: /* AF_INET */
            addr = port->addrs;
            hc->addr_conf = &addr[0].conf;
            break;
        }
    }

    /* the default server configuration for the address:port */
    hc->conf_ctx = hc->addr_conf->default_server->ctx;

    ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
    if (ctx == NULL) {
        ngx_http_close_connection(c);
        return;
    }

    ctx->connection = c;
    ctx->request = NULL;
    ctx->current_request = NULL;

    /* 设置当前连接的日志属性 */
    c->log->connection = c->number;
    c->log->handler = ngx_http_log_error;
    c->log->data = ctx;
    c->log->action = "waiting for request";

    c->log_error = NGX_ERROR_INFO;

    /* 设置当前连接读、写事件的handler处理方法 */
    rev = c->read;
    /* 设置当前连接读事件的处理方法handler为ngx_http_wait_request_handler */
    rev->handler = ngx_http_wait_request_handler;
    /*
     * 设置当前连接写事件的处理方法handler为ngx_http_empty_handler,
     * 该方法不执行任何实际操作,只记录日志;
     * 因为处理请求的过程不需要write方法;
     */
    c->write->handler = ngx_http_empty_handler;

#if (NGX_HTTP_SPDY)
   ...
#endif

#if (NGX_HTTP_SSL)
    ...
#endif

    if (hc->addr_conf->proxy_protocol) {
        hc->proxy_protocol = 1;
        c->log->action = "reading PROXY protocol";
    }

    /* 若读事件准备就绪,则判断是否使用同步锁,
     * 根据同步锁情况判断决定是否立即处理该事件;
     */
    if (rev->ready) {
        /* the deferred accept(), rtsig, aio, iocp */

        /*
         * 若使用了同步锁ngx_use_accept_mutex,
         * 则将该读事件添加到待处理事件队列ngx_post_event中,
         * 直到退出锁时,才处理该读事件;
         */
        if (ngx_use_accept_mutex) {
            ngx_post_event(rev, &ngx_posted_events);
            return;
        }

        /* 若没有使用同步锁,则直接处理该读事件;
         * 读事件的处理函数handler为ngx_http_wait_request_handler;
         */
        rev->handler(rev);
        return;
    }

    /*
     * 若当前连接的读事件未准备就绪,
     * 则将其添加到定时器事件机制,并注册到epoll事件机制中;
     */

    /* 将当前连接的读事件添加到定时器机制中 */
    ngx_add_timer(rev, c->listening->post_accept_timeout);
    ngx_reusable_connection(c, 1);

    /* 将当前连接的读事件注册到epoll事件机制中 */
    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
        ngx_http_close_connection(c);
        return;
    }
}

When connected When a readable event occurs for the first time, the ngx_http_wait_request_handler function will be called. The function of this function is to initialize the HTTP request, but it will not initialize the request immediately after the connection is successfully established. Instead, it will initialize the request after the connection is successfully established. The actual initialization work is only carried out when the actual request data from the client is confirmed to be received on the socket buffer. This can reduce unnecessary memory consumption (if the client does not perform actual data communication after the connection is successfully established, and this When Nginx allocates memory due to initialization work).

ngx_http_wait_request_handler Function execution flow:

  • 首先判断当前读事件是否超时(即读事件的 timedout 标志位是否为1):
    • timedout 标志位为1,表示当前读事件已经超时,则调用ngx_http_close_connection 方法关闭当前连接,return 从当前函数返回;
    • timedout 标志位为0,表示当前读事件还未超时,则继续检查当前连接的close标志位;
  • 若当前连接的 close 标志位为1,表示当前连接要关闭,则调用ngx_http_close_connection 方法关闭当前连接,return 从当前函数返回;
  • 若当前连接的 close 标志位为0,表示不需要关闭当前连接,进而调用recv() 函数尝试从当前连接所对应的套接字缓冲区中接收数据,这个步骤是为了确定客户端是否真正的发送请求数据,以免因为客户端不发送实际请求数据,出现初始化请求而导致内存被消耗。根据所读取的数据情况n 来判断是否要真正进行初始化请求工作:
    • n = NGX_AGAIN,表示客户端发起连接请求,但是暂时还没发送实际的数据,则将当前连接上的读事件添加到定时器机制中,同时将读事件注册到epoll 事件机制中,return 从当前函数返回;
    • n = NGX_ERROR,表示当前连接出错,则直接调用ngx_http_close_connection 关闭当前连接,return 从当前函数返回;
    • n = 0,表示客户端已经主动关闭当前连接,所有服务器端调用ngx_http_close_connection 关闭当前连接,return 从当前函数返回;
    • n 大于 0,表示读取到实际的请求数据,因此决定开始初始化当前请求,继续往下执行;
  • 调用 ngx_http_create_request 方法构造ngx_http_request_t 请求结构体,并设置到当前连接的data 成员;
  • 设置当前读事件的回调方法为 ngx_http_process_request_line,并执行该回调方法开始接收并解析请求行;

函数 ngx_http_wait_request_handler 在文件src/http/ngx_http_request.c 中定义如下:

/* 处理连接的可读事件 */
static void
ngx_http_wait_request_handler(ngx_event_t *rev)
{
    u_char                    *p;
    size_t                     size;
    ssize_t                    n;
    ngx_buf_t                 *b;
    ngx_connection_t          *c;
    ngx_http_connection_t     *hc;
    ngx_http_core_srv_conf_t  *cscf;

    /* 获取读事件所对应的连接ngx_connection_t 对象 */
    c = rev->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");

    /* 若当前读事件超时,则记录错误日志,关闭所对应的连接并退出 */
    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        ngx_http_close_connection(c);
        return;
    }

    /* 若当前读事件所对应的连接设置关闭连接标志位,则关闭该链接 */
    if (c->close) {
        ngx_http_close_connection(c);
        return;
    }

    /* 若当前读事件不超时,且其所对应的连接不设置close标志位,则继续指向以下语句 */

    hc = c->data;
    /* 获取当前读事件请求的相关配置项结构 */
    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);

    size = cscf->client_header_buffer_size;

    /* 以下内容是接收缓冲区的操作 */
    b = c->buffer;

    /* 若当前连接的接收缓冲区不存在,则创建该接收缓冲区 */
    if (b == NULL) {
        b = ngx_create_temp_buf(c->pool, size);
        if (b == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        c->buffer = b;

    } else if (b->start == NULL) {
        /* 若当前接收缓冲区存在,但是为空,则为其分配内存 */

        b->start = ngx_palloc(c->pool, size);
        if (b->start == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        /* 初始化接收缓冲区各成员指针 */
        b->pos = b->start;
        b->last = b->start;
        b->end = b->last + size;
    }

    /* 在当前连接上开始接收HTTP请求数据 */
    n = c->recv(c, b->last, size);

    if (n == NGX_AGAIN) {

        if (!rev->timer_set) {
            ngx_add_timer(rev, c->listening->post_accept_timeout);
            ngx_reusable_connection(c, 1);
        }

        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            ngx_http_close_connection(c);
            return;
        }

        /*
         * We are trying to not hold c->buffer's memory for an idle connection.
         */

        if (ngx_pfree(c->pool, b->start) == NGX_OK) {
            b->start = NULL;
        }

        return;
    }

    if (n == NGX_ERROR) {
        ngx_http_close_connection(c);
        return;
    }

    if (n == 0) {
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client closed connection");
        ngx_http_close_connection(c);
        return;
    }

    /* 若接收HTTP请求数据成功,则调整接收缓冲区成员指针 */
    b->last += n;

    if (hc->proxy_protocol) {
        hc->proxy_protocol = 0;

        p = ngx_proxy_protocol_parse(c, b->pos, b->last);

        if (p == NULL) {
            ngx_http_close_connection(c);
            return;
        }

        b->pos = p;

        if (b->pos == b->last) {
            c->log->action = "waiting for request";
            b->pos = b->start;
            b->last = b->start;
            ngx_post_event(rev, &ngx_posted_events);
            return;
        }
    }

    c->log->action = "reading client request line";

    ngx_reusable_connection(c, 0);

    /* 为当前连接创建一个请求结构体ngx_http_request_t */
    c->data = ngx_http_create_request(c);
    if (c->data == NULL) {
        ngx_http_close_connection(c);
        return;
    }

    /* 设置当前读事件的处理方法为ngx_http_process_request_line */
    rev->handler = ngx_http_process_request_line;
    /* 执行该读事件的处理方法ngx_http_process_request_line,接收HTTP请求行 */
    ngx_http_process_request_line(rev);
}

接收 HTTP 请求行

        HTTP 请求的初始化完成之后会调用 ngx_http_process_request_line 方法开始接收并解析 HTTP 请求行。在 HTTP 协议中我们可以知道,请求行的长度并不是固定的,它与URI 长度相关,若当内核套接字缓冲区不能一次性完整的接收 HTTP 请求行时,会多次调用 ngx_http_process_request_line 方法继续接收,即ngx_http_process_request_line 方法重新作为当前连接上读事件的回调方法,必要时将读事件添加到定时器机制,注册到epoll 事件机制,直到接收并解析出完整的 HTTP 请求行。

ngx_http_process_request_line 处理HTTP 请求行函数执行流程:

  • 首先,判断当前请求是否超时,若超时(即读事件的 timedout 标志位为1),则设置当前连接的超时标志位为 1c->timedout = 1),调用 ngx_http_close_request 方法关闭该请求,并 return 从当前函数返回;
  • 若当前请求未超时(读事件的 timedout 标志位为 0),调用 ngx_http_read_request_header 方法开始读取当前请求行,根据该函数的返回值n 进行以下判断:
    • 若返回值 n = NGX_AGAIN,表示当前连接上套接字缓冲区不存在可读TCP 流,则需将当前读事件添加到定时器机制,注册到 epoll 事件机制中,等待可读事件发生。return 从当前函数返回;
    • 若返回值 n = NGX_ERROR,表示当前连接出错,则调用ngx_http_finalize_request 方法结束请求,return 从当前函数返回;
    • 若返回值 n 大于 0,表示读取请求行成功,调用函数 ngx_http_parse_request_line 开始解析由函数ngx_http_read_request_header 读取所返回的请求行,根据函数ngx_http_parse_request_line 函数返回值rc 不同进行判断;
  • 若返回值 rc = NGX_ERROR,表示解析请求行时出错,此时,调用ngx_http_finalize_request 方法终止该请求,并return 从当前函数返回;
  • 若返回值 rc = NGX_AGAIN,表示没有解析到完整的请求行,即仍需接收请求行,首先根据要求调整接收缓冲区header_in 的内存空间,则继续调用函数ngx_http_read_request_header 读取请求数据进入请求行自动处理机制,直到请求行解析完毕;
  • 若返回值 rc = NGX_OK,表示解析到完整的 HTTP 请求行,则设置请求行的成员信息(例如:方法名称、URI 参数、HTTP 版本等信息); 
    •  若 HTTP 协议版本小于 1.0 版本,表示不需要处理 HTTP 请求头部,则直接调用函数ngx_http_process_request 处理该请求,return 从当前函数返回;
    •  若HTTP协议版本不小于 1.0 版本,表示需要处理HTTP请求头部:
      •  调用函数 ngx_list_init 初始化保存 HTTP 请求头部的结构体 ngx_http_request_t 中成员headers_in 链表容器(该链表缓冲区是保存所接收到的 HTTP 请求数据); 
      • 设置当前读事件的回调方法为 ngx_http_process_request_headers 方法,并调用该方法ngx_http_process_request_headers 开始处理HTTP 请求头部。return 从当前函数返回;

函数 ngx_http_process_request_line 在文件src/http/ngx_http_request.c 中定义如下:

/* 处理HTTP请求行 */
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;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                   "http process request line");

    /* 若当前读事件超时,则进行相应地处理,并关闭当前请求 */
    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;
    }

    /* 设置NGX_AGAIN标志,表示请求行还没解析完毕 */
    rc = NGX_AGAIN;

    for ( ;; ) {

        /* 若请求行还没解析完毕,则继续解析 */
        if (rc == NGX_AGAIN) {
            /* 读取当前请求未解析的数据 */
            n = ngx_http_read_request_header(r);

            /* 若没有数据,或读取失败,则直接退出 */
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }

        /* 解析接收缓冲区header_in中的请求行 */
        rc = ngx_http_parse_request_line(r, r->header_in);

        /* 若请求行解析完毕 */
        if (rc == NGX_OK) {

            /* the request line has been parsed successfully */

            /* 设置请求行的成员,请求行是ngx_str_t类型 */
            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;

            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http request line: \"%V\"", &r->request_line);

            /* 设置请求方法名称字符串 */
            r->method_name.len = r->method_end - r->request_start + 1;
            r->method_name.data = r->request_line.data;

            /* 设置HTTP请求协议 */
            if (r->http_protocol.data) {
                r->http_protocol.len = r->request_end - r->http_protocol.data;
            }

            /* 处理请求中的URI */
            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;

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

            /* 设置请求协议版本 */
            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;
                }

                /* 若HTTP版本小于1.0版本,则表示不需要接收HTTP请求头部,则直接处理请求 */
                ngx_http_process_request(r);
                return;
            }


            /* 初始化链表容器,为接收HTTP请求头部做准备 */
            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";

            /* 若请求行解析完毕,则接下来处理请求头部 */

            /* 设置连接读事件的回调方法 */
            rev->handler = ngx_http_process_request_headers;
            /* 开始处理HTTP请求头部 */
            ngx_http_process_request_headers(rev);

            return;
        }

        /* 解析请求行出错 */
        if (rc != NGX_AGAIN) {

            /* there was error while a request line parsing */

            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_AGAIN: a request line parsing is still incomplete */

        /* 请求行仍然未解析完毕,则继续读取请求数据 */

        /* 若当前接收缓冲区内存不够,则分配更大的内存空间 */
        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;
            }
        }
    }
}

        在接收并解析请求行的过程中会调用 ngx_http_read_request_header 读取请求数据,我们看下该函数是如何读取到请求数据的。

ngx_http_read_request_header 读取请求数据函数执行流程:

  • 检测当前请求的接收缓冲区 header_in 是否有数据,若有直接返回该数据n
  • 若接收缓冲区 header_in 没有数据,检查当前读事件是否准备就绪(即判断ready 标志位是否为 0 ):
    • 若当前读事件未准备就绪(即当前读事件 ready 标志位为0),则设置返回值 n= NGX_AGAIN
    • 若当前读事件已经准备就绪(即 ready 标志位为 1),则调用 recv() 方法从当前连接套接字中读取数据并保存到接收缓冲区header_in 中,并设置 nrecv() 方法所读取的数据的返回值;
  • 下面根据 n 的取值执行不同的操作:
    • n = NGX_AGAIN(此时,n 的值可能当前事件未准备就绪而设置的NGX_AGAIN,也可能是 recv() 方法返回的 NGX_AGAIN 值,但是只能是其中一种情况),将当前读事件添加到定时器事件机制中, 将当前读事件注册到epoll 事件机制中,等待事件可读,n 从当前函数返回;
    • n = 0n = ERROR,则调用 ngx_http_finalize_request 结束请求,并返回NGX_ERROR 退出当前函数;

函数 ngx_http_read_request_header 在文件src/http/ngx_http_request.c 中定义如下:

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;

    /* 获取当前请求接收缓冲区的数据,header_in 是ngx_buf_t类型 */
    n = r->header_in->last - r->header_in->pos;

    /* 若接收缓冲区有数据,则直接返回该数据 */
    if (n > 0) {
        return n;
    }

    /* 若当前接收缓冲区没有数据,首先判断当前读事件是否准备就绪 */
    if (rev->ready) {
        /* 若当前读事件已准备就绪,则从其所对应的连接套接字读取数据,并保存到接收缓冲区中 */
        n = c->recv(c, r->header_in->last,
                    r->header_in->end - r->header_in->last);
    } else {
        /* 若接收缓冲区没有数据,且读事件未准备就绪,则设置为NGX_AGAIN */
        n = NGX_AGAIN;
    }

    /* 若接收缓冲区没有数据,且读事件未准备就绪,则设置为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);
        }

        /* 将当前读事件注册到epoll事件机制中 */
        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;
    }

    if (n == 0) {
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client prematurely closed connection");
    }

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

接收 HTTP 请求头部

        前面已经成功接收并解析了 HTTP 请求行,这里根据读事件的回调方法ngx_http_process_request_headers 开始接收并解析HTTP 请求头部,但是并不一定能够一次性接收到完整的 HTTP 请求头部,因此,可以多次调用该函数,直到接收到完整的 HTTP 请求头部。

ngx_http_process_request_headers 处理HTTP 请求头部函数执行流程:

  • 首先,判断当前请求读事件是否超时,若超时(即读事件的 timedout 标志位为1),则设置当前连接超时标志位为 1c->timedout = 1),并调用 ngx_http_close_request 方法关闭该请求,并 return 从当前函数返回;
  • 若当前请求读事件未超时(即读事件的 timedout 标志位为0),检查接收 HTTP 请求头部的 header_in 缓冲区是否有剩余内存空间,若没有剩余的内存空间,则调用ngx_http_alloc_large_header_buffer 方法分配更大的缓冲区。若有剩余的内存,则无需再分配内存空间。
  • 调用 ngx_http_read_request_header 方法开始读取当前请求头部保存到header_in 缓冲区中,根据该函数的返回值 n 进行以下判断:
    • 若返回值 n = NGX_AGAIN,表示当前连接上套接字缓冲区不存在可读TCP 流,则需将当前读事件添加到定时器机制,注册到 epoll 事件机制中,等待可读事件发生。return 从当前函数返回;
    • 若返回值 n = NGX_ERROR,表示当前连接出错,则调用ngx_http_finalize_request 方法结束请求,return 从当前函数返回;
    • 若返回值 n 大于 0,表示读取请求头部成功,调用函数 ngx_http_parse_request_line 开始解析由函数ngx_http_read_request_header 读取所返回的请求头部,根据函数ngx_http_parse_request_line 函数返回值rc不同进行判断;
  • 若返回值 rc = NGX_ERROR,表示解析请求行时出错,此时,调用ngx_http_finalize_request 方法终止该请求,并return 从当前函数返回;
  • 若返回值 rc = NGX_AGAIN,表示没有解析到完整一行的请求头部,仍需继续接收TCP 字符流才能够是完整一行的请求头部,则 continue 继续调用函数ngx_http_read_request_headerngx_http_parse_request_line 方法读取并解析下一行请求头部,直到全部请求头部解析完毕;
  • 若返回值 rc = NGX_OK,表示解析出一行 HTTP 请求头部(注意:一行请求头部只是整个请求头部的一部分),判断当前解析出来的一行请求头部是否合法,若非法,则忽略当前一行请求头部,继续读取并解析下一行请求头部。若合法,则调用ngx_list_push 方法将该行请求头部设置到当前请求 ngx_http_request_t 结构体 header_in 缓冲区成员的headers 链表中,设置请求头部名称的 hash 值,并 continue 继续调用函数 ngx_http_read_request_headerngx_http_parse_request_line 方法读取并解析下一行请求头部,直到全部请求头部解析完毕;
  • 若返回值 rc = NGX_HTTP_PARSE_HEADER_DONE,则表示已经读取并解析出全部请求头部,此时,调用ngx_http_process_request 方法开始处理请求,return 从当前函数返回;

函数 ngx_http_process_request_headers 在文件src/http/ngx_http_request.c 中定义如下:

/* 处理HTTP请求头部 */
static void
ngx_http_process_request_headers(ngx_event_t *rev)
{
    u_char                     *p;
    size_t                      len;
    ssize_t                     n;
    ngx_int_t                   rc, rv;
    ngx_table_elt_t            *h;
    ngx_connection_t           *c;
    ngx_http_header_t          *hh;
    ngx_http_request_t         *r;
    ngx_http_core_srv_conf_t   *cscf;
    ngx_http_core_main_conf_t  *cmcf;

    /* 获取当前读事件所对应的连接 */
    c = rev->data;
    /* 获取当前连接的HTTP请求 */
    r = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                   "http process request header line");

    /* 若当前读事件超时,则关闭该请求,并退出 */
    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;
    }

    /* 获取ngx_http_core_module模块的main级别配置项结构 */
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    /* 表示当前请求头部未解析完毕 */
    rc = NGX_AGAIN;

    for ( ;; ) {

        if (rc == NGX_AGAIN) {
            /* 若当前请求头部未解析完毕,则首先判断接收缓冲区是否有内存空间再次接收请求数据 */

            if (r->header_in->pos == r->header_in->end) {

                /* 若接收缓冲区没有足够内存空间,则分配更大的内存空间 */
                rv = ngx_http_alloc_large_header_buffer(r, 0);

                if (rv == NGX_ERROR) {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                if (rv == NGX_DECLINED) {
                    p = r->header_name_start;

                    r->lingering_close = 1;

                    if (p == NULL) {
                        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                      "client sent too large request");
                        ngx_http_finalize_request(r,
                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
                        return;
                    }

                    len = r->header_in->end - p;

                    if (len > NGX_MAX_ERROR_STR - 300) {
                        len = NGX_MAX_ERROR_STR - 300;
                        p[len++] = '.'; p[len++] = '.'; p[len++] = '.';
                    }

                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                  "client sent too long header line: \"%*s\"",
                                  len, r->header_name_start);

                    ngx_http_finalize_request(r,
                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
                    return;
                }
            }

            /* 读取未解析请求数据 */
            n = ngx_http_read_request_header(r);

            /* 若没有可读的数据,或读取失败,则直接退出 */
            if (n == NGX_AGAIN || n == NGX_ERROR) {
                return;
            }
        }

        /* the host header could change the server configuration context */

        /* 获取ngx_http_core_module模块的srv级别配置项结构 */
        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);

        /* 开始解析HTTP请求头部 */
        rc = ngx_http_parse_header_line(r, r->header_in,
                                        cscf->underscores_in_headers);

        /* 解析出一行请求头部(注意:一行请求头部只是HTTP请求头部的一部分) */
        if (rc == NGX_OK) {

            /* 设置当前请求的长度 */
            r->request_length += r->header_in->pos - r->header_name_start;

            /*
             * 若当前解析出来的一行请求头部是非法的,或Nginx当前版本不支持,
             * 则记录错误日志,并继续解析下一行请求头部;
             */
            if (r->invalid_header && cscf->ignore_invalid_headers) {

                /* there was error while a header line parsing */

                ngx_log_error(NGX_LOG_INFO, c->log, 0,
                              "client sent invalid header line: \"%*s\"",
                              r->header_end - r->header_name_start,
                              r->header_name_start);
                continue;
            }

            /* a header line has been parsed successfully */

            /*
             * 若当前解析出来的一行请求头部是合法的,表示成功解析出该行请求头部,
             * 将该行请求头部保存在当前请求的headers_in的headers链表中;
             * 接着继续解析下一行请求头部;
             */
            h = ngx_list_push(&r->headers_in.headers);
            if (h == NULL) {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

            /* 设置请求头部名称的hash值 */
            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;
            h->key.data = r->header_name_start;
            h->key.data[h->key.len] = '\0';

            h->value.len = r->header_end - r->header_start;
            h->value.data = r->header_start;
            h->value.data[h->value.len] = '\0';

            h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
            if (h->lowcase_key == NULL) {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

            if (h->key.len == r->lowcase_index) {
                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);

            } else {
                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
            }

            hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
                               h->lowcase_key, h->key.len);

            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
                return;
            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http header: \"%V: %V\"",
                           &h->key, &h->value);

            continue;
        }

        /* 若成功解析所有请求头部,则接下来就开始处理该请求 */
        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http header done");

            r->request_length += r->header_in->pos - r->header_name_start;

            /* 设置当前请求的解析状态 */
            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;

            /*
             * 调用该函数主要目的有两个:
             * 1、根据HTTP头部的host字段,调用ngx_http_find_virtual_server查找虚拟主机的配置块;
             * 2、对HTTP请求头部协议版本进行检查,例如http1.1版本,host头部不能为空,否则会返回400 Bad Request错误;
             */
            rc = ngx_http_process_request_header(r);

            if (rc != NGX_OK) {
                return;
            }

            /* 开始处理当前请求 */
            ngx_http_process_request(r);

            return;
        }

        /* 表示当前行的请求头部未解析完毕,则继续读取请求数据进行解析 */
        if (rc == NGX_AGAIN) {

            /* a header line parsing is still not complete */

            continue;
        }

        /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */

        /* 解析请求头部出错,则关闭该请求,并退出 */
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client sent invalid header line: \"%*s\\r...\"",
                      r->header_end - r->header_name_start,
                      r->header_name_start);
        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return;
    }
}

处理 HTTP 请求

        前面的步骤已经接收到完整的 HTTP 请求头部,此时,已经有足够的信息开始处理HTTP 请求。处理 HTTP 请求的过程有 11HTTP 阶段,在不同的阶段由各个 HTTP 模块进行处理。

ngx_http_process_request 处理HTTP 请求函数执行流程:

  • 若当前读事件在定时器机制中,则调用 ngx_del_timer 函数将其从定时器机制中移除,因为在处理HTTP 请求时不存在接收 HTTP 请求头部超时的问题;
  • 由于处理 HTTP 请求不需要再接收 HTTP 请求行或头部,则需重新设置当前连接读、写事件的回调方法,读、写事件的回调方法都设置为 ngx_http_request_handler,即后续处理 HTTP 请求的过程都是通过该方法进行;
  • 设置当前请求 ngx_http_request_t 结构体中的成员read_event_handler 的回调方法为 ngx_http_block_reading,该回调方法实际不做任何操作,即在处理请求时不会对请求的读事件进行任何处理,除非某个HTTP模块重新设置该回调方法;
  • 接下来调用函数 ngx_http_handler 开始处理HTTP 请求;
  • 调用函数 ngx_http_run_posted_requests 处理post 子请求;

函数 ngx_http_process_request 在文件src/http/ngx_http_request.c 中定义如下:

/* 处理HTTP请求 */
void
ngx_http_process_request(ngx_http_request_t *r)
{
    ngx_connection_t  *c;

    /* 获取当前请求所对应的连接 */
    c = r->connection;

#if (NGX_HTTP_SSL)
    ...

#endif

    /*
     * 由于现在不需要再接收HTTP请求头部超时问题,
     * 则需要把当前连接的读事件从定时器机制中删除;
     * timer_set为1表示读事件已添加到定时器机制中,
     * 则将其从定时器机制中删除,0表示不在定时器机制中;
     */
    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

#if (NGX_STAT_STUB)
    ...
#endif

    /* 重新设置当前连接的读、写事件的回调方法 */
    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;

    /*
     * 设置请求读事件的回调方法,
     * 其实ngx_http_block_reading函数实际对读事件不做任何处理;
     * 即在处理请求时,不会对读事件任何操作,除非有HTTP模块重新设置处理方法;
     */
    r->read_event_handler = ngx_http_block_reading;

    /* 开始处理各个HTTP模块的handler方法,该函数定义于ngx_http_core_module.c中*/
    ngx_http_handler(r);

    /* 处理post请求 */
    ngx_http_run_posted_requests(c);
}

ngx_http_handler 函数的执行流程:

  • 检查当前请求 ngx_http_request_tinternal 标志位:
    • internal 标志位为 0,表示当前请求不需要重定向,判断是否使用 keepalive 机制,并设置phase_handler 序号为 0,表示执行 ngx_http_phase_engine_t 结构成员ngx_http_phase_handler_t *handlers数组中的第一个回调方法;
    • internal 标志位为 1,表示需要将当前请求做内部跳转,并将 phase_handler 设置为server_rewriter_index,表示执行 ngx_http_phase_engine_t 结构成员ngx_http_phase_handler_t *handlers 数组在NGX_HTTP_SERVER_REWRITE_PHASE 处理阶段的第一个回调方法;
  • 设置当前请求 ngx_http_request_t 的成员写事件write_event_handlerngx_http_core_run_phases
  • 执行n gx_http_core_run_phases 方法;

函数 ngx_http_handler 在文件 src/http/ngx_http_core_module.c 中定义如下:

void
ngx_http_handler(ngx_http_request_t *r)
{
    ngx_http_core_main_conf_t  *cmcf;

    r->connection->log->action = NULL;

    r->connection->unexpected_eof = 0;

    /* 若当前请求的internal标志位为0,表示不需要重定向 */
    if (!r->internal) {
        /* 下面语句是决定是否使用keepalive机制 */
        switch (r->headers_in.connection_type) {
        case 0:
            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
            break;

        case NGX_HTTP_CONNECTION_CLOSE:
            r->keepalive = 0;
            break;

        case NGX_HTTP_CONNECTION_KEEP_ALIVE:
            r->keepalive = 1;
            break;
        }

        /* 设置延迟关闭标志位 */
        r->lingering_close = (r->headers_in.content_length_n > 0
                              || r->headers_in.chunked);
        /*
         * phase_handler序号设置为0,表示执行ngx_http_phase_engine_t结构体成员
         * ngx_http_phase_handler_t *handlers数组中的第一个回调方法;
         */
        r->phase_handler = 0;

    } else {
    /* 若当前请求的internal标志位为1,表示需要做内部跳转 */
        /* 获取ngx_http_core_module模块的main级别的配置项结构 */
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
        /*
         * 将phase_handler序号设为server_rewriter_index,
         * 该phase_handler序号是作为ngx_http_phase_engine_t结构中成员
         * ngx_http_phase_handler_t *handlers回调方法数组的序号,
         * 即表示回调方法在该数组中所处的位置;
         *
         * server_rewrite_index则是handlers数组中NGX_HTTP_SERVER_REWRITE_PHASE阶段的
         * 第一个ngx_http_phase_handler_t回调的方法;
         */
        r->phase_handler = cmcf->phase_engine.server_rewrite_index;
    }

    r->valid_location = 1;
#if (NGX_HTTP_GZIP)
    r->gzip_tested = 0;
    r->gzip_ok = 0;
    r->gzip_vary = 0;
#endif

    /* 设置当前请求写事件的回调方法 */
    r->write_event_handler = ngx_http_core_run_phases;
    /*
     * 执行该回调方法,将调用各个HTTP模块共同处理当前请求,
     * 各个HTTP模块按照11个HTTP阶段进行处理;
     */
    ngx_http_core_run_phases(r);
}

ngx_http_core_run_phases 函数的执行流程:

  • 判断每个 ngx_http_phase_handler_t 处理阶段是否实现checker 方法:
    • 若实现 checker 方法,则执行 phase_handler 序号在 ngx_http_phase_handler_t *handlers数组中指定的 checker 方法;执行完 checker 方法,若返回 NGX_OK 则退出;若返回非NGX_OK,则继续执行下一个 HTTP 模块在该阶段的 checker 方法;
    • 若没有实现 checker 方法,则直接退出;

函数 ngx_http_core_run_phases 在文件src/http/ngx_http_core_module.c 中定义如下:

void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
    ngx_int_t                   rc;
    ngx_http_phase_handler_t   *ph;
    ngx_http_core_main_conf_t  *cmcf;

    /* 获取ngx_http_core_module模块的main级别的配置项结构体 */
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    /* 获取各个HTTP模块处理请求的回调方法数组 */
    ph = cmcf->phase_engine.handlers;

    /* 若实现了checker方法 */
    while (ph[r->phase_handler].checker) {

        /* 执行phase_handler序号在数组中指定的checker方法 */
        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        /* 成功执行checker方法,则退出,否则继续执行下一个HTTP模块的checker方法 */
        if (rc == NGX_OK) {
            return;
        }
    }
}

处理子请求

        post 子请求是基于 subrequest 机制的,首先看下 post 子请求结构体类型:

/* 子请求的单链表结构 */
typedef struct ngx_http_posted_request_s  ngx_http_posted_request_t;

struct ngx_http_posted_request_s {
    /* 指向当前待处理子请求的ngx_http_request_t结构体 */
    ngx_http_request_t               *request;
    /* 指向下一个子请求 */
    ngx_http_posted_request_t        *next;
};

        在请求结构体 ngx_http_request_t 中有一个与post 子请求相关的成员 posted_requests,该成员把各个 post 子请求按照子请求结构体ngx_http_posted_request_t 的结构连接成单链表的形式,请求结构体ngx_http_request_tmain 成员是子请求的原始请求,parent 成员是子请求的父请求。下面是子请求的处理过程。

ngx_http_run_posted_requests 函数执行流程:

  • 判断当前连接是否已被销毁(即标志位 destroyed 是否为0),若被销毁则直接 return 退出,否则继续执行;
  • 获取原始请求的子请求链表,若子请求链表为空(表示没有 post 请求)则直接return 退出,否则继续执行;
  • 遍历子请求链表,执行每个 post 请求的写事件回调方法write_event_handler

函数 ngx_http_run_posted_requests 在文件src/http/ngx_http_request.c 中定义如下:

void
ngx_http_run_posted_requests(ngx_connection_t *c)
{
    ngx_http_request_t         *r;
    ngx_http_log_ctx_t         *ctx;
    ngx_http_posted_request_t  *pr;

    for ( ;; ) {

        /* 若当前连接已被销毁,则直接退出 */
        if (c->destroyed) {
            return;
        }

        /* 获取当前连接所对应的请求 */
        r = c->data;
        /* 获取原始请求的子请求单链表 */
        pr = r->main->posted_requests;

        /* 若子请求单链表为空,则直接退出 */
        if (pr == NULL) {
            return;
        }

        /* 将原始请求的posted_requests指向单链表的下一个post请求 */
        r->main->posted_requests = pr->next;

        /* 获取子请求链表中的第一个post请求 */
        r = pr->request;

        ctx = c->log->data;
        ctx->current_request = r;

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http posted request: \"%V?%V\"", &r->uri, &r->args);

        /*
         * 调用当前post请求写事件的回调方法write_event_handler;
         * 子请求不被网络事件驱动,因此不需要调用read_event_handler;
         */
        r->write_event_handler(r);
    }
}

处理 HTTP 请求包体

        下面开始要分析 HTTP 框架是如何处理HTTP 请求包体,HTTP 框架有两种处理请求包体的方法:接收请求包体、丢弃请求包体;但是必须要注意的是丢弃请求包体并不意味着就不接受请求包体,只是把接收到的请求包体进行丢弃,不进一步对其进行处理。

        其中有一个很重要的成员就是请求结构体 ngx_http_request_t  中的引用计数count,引用计数是用来决定是否真正结束当前请求,若引用计数为0 时,表示没有其他动作在处理该请求,则可以终止该请求;若引用计数不为0 时,表示当前请求还有其他动作在操作,因此不能结束当前请求,以免发生错误;那怎么样控制这个引用计数呢?例如,当一个请求添加新事件,或是把一些原本从定时器、epoll 事件机制中移除的事件从新加入到其中等等,出现这些情况都是要对引用计数增加1;当要结束请求时,首先会把引用计数减 1,并判断该引用计数是否为 0,再进一步判断是否决定真的结束当前请求。

接收 HTTP 请求包体

        HTTP 请求包体保存在结构体 ngx_http_request_body_t 中,该结构体是存放在保存着请求结构体 ngx_http_request_t 的成员 request_body 中,该结构体定义如下:

/* 存储HTTP请求包体的结构体ngx_http_request_body_t */
typedef struct {
    /* 存放HTTP请求包体的临时文件 */
    ngx_temp_file_t                  *temp_file;
    /*
     * 指向接收HTTP请求包体的缓冲区链表表头,
     * 因为当一个缓冲区ngx_buf_t无法容纳所有包体时,就需要多个缓冲区形成链表;
     */
    ngx_chain_t                      *bufs;
    /* 指向当前保存HTTP请求包体的缓冲区 */
    ngx_buf_t                        *buf;
    /*
     * 根据content-length头部和已接收包体长度,计算还需接收的包体长度;
     * 即当前剩余的请求包体大小;
     */
    off_t                             rest;
    /* 接收HTTP请求包体缓冲区链表空闲缓冲区 */
    ngx_chain_t                      *free;
    /* 接收HTTP请求包体缓冲区链表已使用的缓冲区 */
    ngx_chain_t                      *busy;
    /* 保存chunked的解码状态,供ngx_http_parse_chunked方法使用 */
    ngx_http_chunked_t               *chunked;
    /*
     * HTTP请求包体接收完毕后执行的回调方法;
     * 即ngx_http_read_client_request_body方法传递的第 2 个参数;
     */
    ngx_http_client_body_handler_pt   post_handler;
} ngx_http_request_body_t;

接收 HTTP 请求包体 ngx_http_read_client_request_body 函数执行流程:

  • Original request reference calculationr->main->count increases 1; the management of reference count count is: when the logic starts the process, the reference count increases 1, and when the process ends, The reference count is decremented 1. In the ngx_http_read_client_request_body function, first increase the reference count of the original request by 1. When an abnormal termination is encountered, the reference count will be decremented before the function returns. 1; If it ends normally, the reference count will continue to be maintained by the post_handler callback method;
  • Judge whether the current request body has been completely received (r->request_body is 1) or discarded (r- >discard_bodyfor 1), if one of them is met, there is no need to receive the request package body again, and the post_handler callback method is executed directly, and NGX_OK is returned from the current function;
  • If you need to receive the HTTP request package body, first call the ngx_http_test_expect method to check whether the client sends the Expect:100-continue header to expect to send the request package body, and the server will Reply HTTP/1.1 100 Continue Indicates that the client is allowed to send the request body;
  • Allocate the current request ngx_http_request_t member of the structure request_body, ready to receive the request body;
  • Check the content-length header of the request , if the content-length field in the request header is less than 0, it means that there is no need to continue receiving the request packet body (that is, the complete request packet body has been received), and the post_handler callback method is executed directly, and NGX_OK Return from the current function;
  • If the content-length field in the request header is greater than 0, it means that the request body needs to continue to be received. First, determine whether the header_in member of the current request ngx_http_request_t contains unprocessed data. If there is unprocessed data, it means that the buffer header_in has pre-received the request packet body during the reception of the request header, because during the reception HTTP It is possible to pre-receive the request body during the request header period. Since the request header has been received before the request body is received, if there is unprocessed data in the buffer, it is the request body.
    • If there is unprocessed data in the header_in buffer, which is a pre-received request packet body, first check whether the buffer request packet body length preread is greater than the request packet body length content-length field, if it is greater than it means that the complete HTTP Request package body, if there is no need to continue receiving, execute the post_handler callback method;
    • If there is unprocessed data in the header_in buffer, it is the pre-received request package body, but the buffer request package body The length preread is less than the length of the request packet body The content-length field indicates that the received request body is incomplete, and you need to continue receiving the request body.Call functions ngx_http_request_body_filte Parse and mount the received request body to the request ngx_http_request_t r of request_body->bufs, header_in The remaining space in the buffer is enough to receive the remaining request packet size rest, so there is no need to allocate a new buffer and then set the current request The read_event_handler read event callback method of ngx_http_request_t is ngx_http_read_client_request_body_handler, and the write event write_event_handler callback method is ngx_http_request_empty_handler (i.e. do not perform any operation), and then call the method ngx_http_do_read_client_request_body to actually receive the HTTP request body, which will TCP Read all the character streams in the socket buffer on the connection, and determine whether it needs to be written to a temporary file, and whether to receive all the request packet bodies. At the same time, execute the callback method after receiving the complete packet body. post_handler
    • If there is unprocessed data in the header_in buffer, it is the pre-received request packet body, but the buffer request packet body length preread is smaller than the request packet body length There is no unprocessed data in the content-length field, or the header_in buffer, and the remaining space in header_in is not enough to receive HTTP Request package body, the buffer for receiving the request package body will be reallocated, and then the current request will be set TNGX_HTTP_REQUEST_T The Read_Event_handler Reading method is ngx_http_read_request_BODY_HANDLER. EST_EMPTY_HANDLER (that is, no operation), and then call the method ngx_http_do_read_client_request_body Really received HTTP Request body;
    • function
    ngx_http_read_client_request_body
  • is defined in the file
src/http/ngx_http_request_body.c

as follows: /* Receive HTTP request body */ ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) { size_t preread; ssize_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out, *cl; ngx_http_request_body_t *rb; ngx_http_core_loc_conf_t *clcf; /* * When there is logic to start the process, the reference count will increase by 1. When the process ends, the reference count will decrease by 1; * In the ngx_http_read_client_request_body method, first increase the original request reference count by 1, * When encountering an abnormal termination, the reference count will be decremented by 1 before the function returns; * If it ends normally, the reference count will continue to be maintained by the post_handler method; */ /* Increase the reference count of the original request by 1 */ r->main->count++; #if (NGX_HTTP_SPDY) if (r->spdy_stream && r == r->main) { rc = ngx_http_spdy_read_request_body(r, post_handler); goto done; } #endif /* When the HTTP request body is not processed, the request_body structure is not allocated and will only be allocated when it is processed */ /* * If the current HTTP request is not an original request, or the HTTP request body has been read or discarded; * Then directly execute the callback method post_handler of the HTTP module and return NGX_OK; */ if (r != r->main || r->request_body || r->discard_body) { post_handler(r); return NGX_OK; } /* * ngx_http_test_expect is used to check whether the client sends the Expect:100-continue header, * If the client has sent this header to indicate that it expects to send the request body data, the server will return

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn