Nginx 中處理 HTTP 請求

WBOY
發布: 2016-08-08 09:30:17
原創
767 人瀏覽過

概述

        在Nginx 的初始化啟動過程中,worker 工作進程會呼叫事件模組的ngx_event_process_init_proclist 方法為每個監聽套件 連接,並設定該連接上讀取事件的回調方法 handlerngx_event_accept,同時將讀取事件掛載到epoll 事件機制中等待監聽套接字連接上的可讀事件發生,到此,Nginx 就可以接收來自客戶端的請求。當監聽套接字連線上的可讀事件發生時,也就是該連線上有來自客戶端發出的連線要求,則會啟動讀取事件的handler 回呼方法ngx_event_accept,在ngx_event_accept 方法中呼叫 accept() 函式接收來自客戶端的連線請求,成功建立連線之後,ngx_event_accept 函式呼叫監聽套接字上的handler 回呼方法 ls->handler(c)(此回呼方法就是ngx_http_init_connection)。因此,成功建立連線之後由ngx_http_init_connection 方法開始處理該連線上的請求資料。 接收HTTP 請求訊息

        在接收HTTP 請求之前,首先會初始化已成功建立的連接;

ngx_http_init_connection

函數的功能是讀回回呼方法並沒有進行任何操作,讀取事件的回呼方法是對HTTP 請求進程初始化工作。 ngx_http_init_connection

函數的執行流程:

設定當前連接上寫事件的回呼方法handler

    ngx_http.的回呼方法
  • handlerngx_http_wait_request_handler
  • 檢查目前連接上讀取事件是否準備就緒(即
  • ready 標誌位元為1): 若讀事件 ready
  • 標誌位元為1,表示目前連線上有可讀的
  • TCP 流,則執行讀取事件的回呼方法 ngx_http_wait_request_handler
    • 若讀事件ready 標誌位元為0,表示目前連接上沒有可讀取的TCP 串流,則將讀取事件加入定時事件機制中(監控是否可讀事件機制(監控事件是否為超時)同時將讀事件註冊到epoll
    • 事件機制中,等待可讀事件的發生;
    • 函數ngx_http_init_connection 在文件
    • src/http/ngx_http_request.c
    中定義如下:
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;
    }
}

登入後複製
        當連接上第一次可讀事件時,會呼叫

ngx_http_wait_request_handler 函數,該函數的功能是初始化HTTP 請求,但是它並不會在成功建立連線之後就立刻初始化請求,而是在目前連線所對應的在套接字緩衝區上確定接收到來自客戶端的實際請求資料時才真正進行初始化工作,這樣做可以減少不必要的記憶體消耗(若當成功建立連接之後,客戶端並不進行實際資料通信,而此時Nginx

卻因為初始化工作分配記憶體)。

ngx_http_wait_request_handler 函數的執行流程:

  • 首先判断当前读事件是否超时(即读事件的 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 函数执行流程:

  • 原始請求引用計算r->main->count 增加1;引用計數count 的管理是:當邏輯開啟流程時,引用計數就會增加1,結束此流程時,引用計數就減 1。在ngx_http_read_client_request_body 函數中,首先將原始請求的參考計數增加1,當遇到異常終止時,引用計數會在該函數返回之前減 1;若正常結束時,引用計數由post_handler回呼方法繼續維護;
  • 判斷目前請求包體是否已完整接收(r->request_body1)或丟棄(r-> discard_body1),若滿足其中一個則不需要再次接收請求包體,直接執行post_handler 回調方法,並 NGX_OK 從目前函數回傳;
  • 若需要接收HTTP 請求包體,首先呼叫ngx_http_test_expect 方法,檢查客戶端是否發送Expect:100-continue回覆HTTP/1.1 100 Continue 表示允許客戶端發送請求包體;
  • 分配當前請求
  • ngx_http_request_t 結構體request_body 成員,準備接收請求包體; ,若請求頭部的 content-length
  • 欄位小於
  • 0,則表示不需要繼續接收請求包體(即已經接收到完整的請求包體),直接執行post_handler 回調方法,並NGX_OKpost_handler 回調方法,並NGX_OK從目前函數傳回;若要求頭部的
  • content-length
  • 欄位大於0,則表示需要繼續接收請求包體。首先判斷目前請求ngx_http_request_theader_in 成員是否存在未處理數據,若存在未被處理的數據,表示該緩衝區header_in 在接收請求頭部期間已經預先接收了請求包體,因為在接收HTTP 請求頭部期間有可能預先接收請求包體,由於在接收請求包體之前,請求頭部已經被接收完畢,所以若該緩衝區存在未被處理的數據,那就是請求包體。
      header_in
    • 緩衝區存在未被處理的數據,即是預先接收的請求包體,首先檢查緩衝區請求包體長度preread 是否大於請求包體長度的 content-length 字段,若大於則表示已接收完整的 HTTP 請求包體,不需要繼續接收,則執行post_handler 回調方法;
    • header_in
    • 緩衝區存在未被處理的資料,即是預先接收的請求包體,但是緩衝區請求包體長度preread 小於請求包體長度的 content-length 字段,表示已收到的請求包體不完整,則需要繼續接收請求包體。呼叫函數 ngx_http_request_body_filte 解析並把已接收的請求包體掛載到請求 ngx_http_request_t r 的 request_body->bufsheader_in 緩衝區剩餘的空間足以接收剩餘的請求包體大小rest,則不需要分配新的緩衝區,進而設置當前請求 ngx_http_request_t  的 read_event_handler 讀取事件回呼方法為ngx_http_read_client_request_body_handler,寫事件write_event_handler 回調方法為 ngx_http_request_empty_handler (即不執行任何操作),然後呼叫方法ngx_http_do_read_client_request_body 真正接收HTTP 包體,該方法將請求包體,該方法將請求 TCP 連接上的套接字緩衝區中的字元流全部讀取出來,並判斷是否需要寫入到臨時文件,以及是否接收全部的請求包體,同時在接收到完整包體後執行回調方法 post_handler
    • header_in 緩衝區存在未被處理的數據,即是預先接收的請求包體,但是緩衝區請求包體長度preread 小於請求包體長度的 content-length 字段,或 header_in 緩衝區不存在未被處理的數據,且header_in 剩餘的空間不足夠接收 HTTP 請求包體,則會重新分配接收請求包體的緩衝區,再進而設定目前請求 ngx_http_request_t read_event_handler 讀事件回調方法為ngx_http_read_client_request_body_handler,寫事件write_event_handler 回調方法為 ngx_http_request_empty_handler (即不執行任何操作),然後調用方法 ngx_http_do_read_client_request_body 真正接收 HTTP 請求包體;

函數 ngx_http_read_client_request_body 在檔案src/http/ngx_http_request_body.c 在檔案

src/http/ngx_http_request_body.c
 在檔案🎜src/http/ngx_http_request_body.c🎜 程式包中🎜*
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;

    /*
     * 當有邏輯開啟流程時,引用計數會增加1,此流程結束時,引用計數將減1;
     * 在ngx_http_read_client_request_body方法中,首先將原始請求參考計數增加1,
     * 當遇到異常終止時,則在該函數返回前會將引用計數減1,;
     * 若正常結束時,引用計數由post_handler方法繼續維護;
     */
    /* 原始請求的引用計數count加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

    /* HTTP請求包體未被處理時,request_body結構是不被分配的,只有處理時才會分配 */
    /*
     * 若目前HTTP請求不是原始請求,或HTTP請求包體已被讀取或被丟棄;
     * 則直接執行HTTP模組的回呼方法post_handler,並傳回NGX_OK;
     */
    if (r != r->main || r->request_body || r->discard_body) {
        post_handler(r);
        return NGX_OK;
    }

    /*
     * ngx_http_test_expect 用於檢查客戶端是否發送Expect:100-continue頭部,
     * 若用戶端已發送該頭部表示期望發送請求包體數據,則伺服器回
登入後複製

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板