<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Nginx(三):http模塊的處理流程解析之正向代理

          共 52112字,需瀏覽 105分鐘

           ·

          2021-01-18 22:00

          走過(guò)路過(guò)不要錯(cuò)過(guò)

          點(diǎn)擊藍(lán)字關(guān)注我們


          無(wú)疑,在nginx的核心服務(wù)中,http服務(wù)占據(jù)了相當(dāng)大的份量。那么,要想多了解nginx多一點(diǎn),則必須要了解其http模塊的工作機(jī)制。

          而在上一篇文章中,我們已完全了解了nginx的worker工作機(jī)制,以及它是如何接入http服務(wù)的,但很明顯那很粗,我們需要更深入點(diǎn)理解http模塊的工作原理。

          而本身nginx對(duì)模塊的支持又是復(fù)雜的,至少我們認(rèn)為有兩個(gè)大方向,正向代理和反向代理。正向代理實(shí)際上就是一個(gè)http服務(wù)器,明顯簡(jiǎn)單些,所以,我們本篇就來(lái)說(shuō)說(shuō)nginx的正向代理實(shí)現(xiàn)吧。

          0. 整體時(shí)序圖

          如果你對(duì)nginx的http模塊工作原理有過(guò)深入理解,相信只需要這一張時(shí)序圖就夠。為了節(jié)省大家寶貴時(shí),可以先一覽宏圖。

          1.異步io事件的交接

          我們知道,nginx的核心是事件io機(jī)制的使用,當(dāng)外部網(wǎng)絡(luò)io就緒時(shí),內(nèi)核會(huì)回應(yīng)nginx, 而nginx則會(huì)通過(guò)accept(), receive(), fd_set 等方法,將事件接入進(jìn)來(lái),從而轉(zhuǎn)交到http服務(wù)模塊。其中select模塊我們上一篇中已經(jīng)講過(guò),此時(shí)再簡(jiǎn)單回顧下:(需要的話)

          // event/modules/ngx_select_module.c// io 事件監(jiān)聽(tīng)static ngx_int_tngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,    ngx_uint_t flags) {    int                ready, nready;    ngx_err_t          err;    ngx_uint_t         i, found;    ngx_event_t       *ev;    ngx_queue_t       *queue;    struct timeval     tv, *tp;    ngx_connection_t  *c;    // 獲取 max_fd, 系統(tǒng)傳值需要    if (max_fd == -1) {        for (i = 0; i < nevents; i++) {            c = event_index[i]->data;            if (max_fd < c->fd) {                max_fd = c->fd;            }        }
          ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "change max_fd: %i", max_fd); }
          #if (NGX_DEBUG) if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) { for (i = 0; i < nevents; i++) { ev = event_index[i]; c = ev->data; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select event: fd:%d wr:%d", c->fd, ev->write); }
          ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "max_fd: %i", max_fd); }#endif
          if (timer == NGX_TIMER_INFINITE) { tp = NULL;
          } else { tv.tv_sec = (long) (timer / 1000); tv.tv_usec = (long) ((timer % 1000) * 1000); tp = &tv; }
          ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select timer: %M", timer);
          work_read_fd_set = master_read_fd_set; work_write_fd_set = master_write_fd_set; // 在此處交由內(nèi)核進(jìn)行處理網(wǎng)絡(luò)事件,epoll 機(jī)制,至少有一個(gè)事件到來(lái)時(shí)返回 // tp 代表是否要超時(shí)退出 ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);
          err = (ready == -1) ? ngx_errno : 0;
          if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) { // 事件結(jié)束后,先嘗試更新gmtTime 時(shí)間信息 ngx_time_update(); }
          ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select ready %d", ready);
          if (err) { ngx_uint_t level;
          if (err == NGX_EINTR) {
          if (ngx_event_timer_alarm) { ngx_event_timer_alarm = 0; return NGX_OK; }
          level = NGX_LOG_INFO;
          } else { level = NGX_LOG_ALERT; }
          ngx_log_error(level, cycle->log, err, "select() failed");
          if (err == NGX_EBADF) { ngx_select_repair_fd_sets(cycle); }
          return NGX_ERROR; }
          if (ready == 0) { if (timer != NGX_TIMER_INFINITE) { return NGX_OK; }
          ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select() returned no events without timeout"); return NGX_ERROR; }
          nready = 0; // 遍歷所有事件 for (i = 0; i < nevents; i++) { ev = event_index[i]; c = ev->data; found = 0; // 寫(xiě)事件處理 if (ev->write) { if (FD_ISSET(c->fd, &work_write_fd_set)) { found = 1; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select write %d", c->fd); }
          } // 讀或accept事件 else { if (FD_ISSET(c->fd, &work_read_fd_set)) { found = 1; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "select read %d", c->fd); } } // 讀寫(xiě)就緒事件 found 都為1 if (found) { ev->ready = 1; ev->available = -1; // 如果是 accept 事件則取 ngx_posted_accept_events 隊(duì)列 // 否則取 ngx_posted_events 隊(duì)列 queue = ev->accept ? &ngx_posted_accept_events : &ngx_posted_events; // 將事件插入到相應(yīng)隊(duì)列尾部 ngx_post_event(ev, queue); // 有效就緒事件+1 nready++; } } // 如果兩個(gè)值不相等,則需要修正下 if (ready != nready) { ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select ready != events: %d:%d", ready, nready);
          ngx_select_repair_fd_sets(cycle); }
          return NGX_OK;}


          反正大概意思就是會(huì)調(diào)用內(nèi)核級(jí)別的select/poll或epoll等io機(jī)制,等待io事件的發(fā)生,然后返回到用戶態(tài)。當(dāng)然,為了保證系統(tǒng)例外情況,都會(huì)進(jìn)行超時(shí)設(shè)置,避免系統(tǒng)事件檢測(cè)的偶發(fā)異常,可以在超時(shí)機(jī)制幫助下正常工作。

          此處接收到的事件可能寫(xiě)入兩個(gè)隊(duì)列: 即是否是 accept 隊(duì)列之分。最開(kāi)始建立連接時(shí),自然是放入accept隊(duì)列的,后續(xù)則一般放ngx_posted_events隊(duì)列中。這兩個(gè)隊(duì)列,后續(xù)將被分開(kāi)處理。accept 隊(duì)列會(huì)將socket接入,并注冊(cè)read監(jiān)聽(tīng)。而 posted_events 則是需要進(jìn)行正式處理的隊(duì)列,將會(huì)讀取數(shù)據(jù),寫(xiě)入客戶端等更多工作。

          其中,ngx_http_init_connection 的初始化過(guò)程,如需要請(qǐng)點(diǎn)擊查看.

          // http/ngx_http_request.c// 初始化socket連接, 接入 http模塊voidngx_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    // 分配數(shù)據(jù)內(nèi)存    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; } // 根據(jù)網(wǎng)絡(luò)類型處理 switch (c->local_sockaddr->sa_family) {
          #if (NGX_HAVE_INET6) case AF_INET6: sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
          addr6 = port->addrs;
          /* the last address is "*" */
          for (i = 0; i < port->naddrs - 1; i++) { if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) { break; } }
          hc->addr_conf = &addr6[i].conf;
          break;#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) case AF_INET6: addr6 = port->addrs; hc->addr_conf = &addr6[0].conf; break;#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; // 每個(gè)http server 都有自己的日志記錄控制 c->log->handler = ngx_http_log_error; c->log->data = ctx; c->log->action = "waiting for request";
          c->log_error = NGX_ERROR_INFO;
          rev = c->read; // 設(shè)置接收數(shù)據(jù)處理器為 ngx_http_wait_request_handler rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler;
          #if (NGX_HTTP_V2) if (hc->addr_conf->http2) { rev->handler = ngx_http_v2_init; }#endif
          #if (NGX_HTTP_SSL) { ngx_http_ssl_srv_conf_t *sscf;
          sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
          if (sscf->enable || hc->addr_conf->ssl) { hc->ssl = 1; c->log->action = "SSL handshaking"; rev->handler = ngx_http_ssl_handshake; } }#endif
          if (hc->addr_conf->proxy_protocol) { hc->proxy_protocol = 1; c->log->action = "reading PROXY protocol"; }
          if (rev->ready) { /* the deferred accept(), iocp */
          if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; }
          rev->handler(rev); return; } // 將rev 放入到 ngx_event_timer_rbtree 隊(duì)列中, 紅黑樹(shù)實(shí)現(xiàn) ngx_add_timer(rev, c->listening->post_accept_timeout); // 重用 connection ngx_reusable_connection(c, 1); // 處理 讀就緒事件,注冊(cè) read 監(jiān)聽(tīng) if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_http_close_connection(c); return; }}

          經(jīng)過(guò) ngx_http_init_connection 之后,就注冊(cè)了read事件, 該事件基本已就緒, 所以將會(huì)在下一次進(jìn)行select操作時(shí)返回該事件, 即下一次worker巡檢時(shí)觸發(fā) read. 而此時(shí)的handler則被設(shè)置為 ngx_http_wait_request_handler.

          // http/ngx_http_request.c// 處理socket讀事件static voidngx_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;
          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; }
          hc = c->data; cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); // 默認(rèn)1024 緩沖大小 size = cscf->client_header_buffer_size;
          b = c->buffer; // 首次接入時(shí),創(chuàng)建初始空間 if (b == NULL) { // 創(chuàng)建緩沖區(qū)接收http傳過(guò)來(lái)的數(shù)據(jù) 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; } // 接收數(shù)據(jù) 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. */ // 如果還要等待更多數(shù)據(jù),釋放占有空間 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; }
          b->last += n; // 如果配置了 proxy_pass (且匹配了模式), 則走代理邏輯 if (hc->proxy_protocol) { hc->proxy_protocol = 0;
          p = ngx_proxy_protocol_read(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"; // 設(shè)置不可重用連接 ngx_reusable_connection(c, 0); // 創(chuàng)建 http 連接請(qǐng)求, 分配內(nèi)存空, 設(shè)置下一個(gè) handler 等等 c->data = ngx_http_create_request(c); if (c->data == NULL) { ngx_http_close_connection(c); return; } // 設(shè)置讀取數(shù)據(jù)的處理器為 ngx_http_process_request_line, 以便下次使用 rev->handler = ngx_http_process_request_line; ngx_http_process_request_line(rev);}

          ngx_http_wait_request_handler 非常重要的一個(gè)任務(wù)就是接收客戶端的傳送數(shù)據(jù),即調(diào)用 recv 方法處理數(shù)據(jù), 使用緩沖區(qū)的方式進(jìn)行讀取, 默認(rèn)緩沖區(qū)大小為 1024, 即實(shí)際是處理不了太多數(shù)據(jù)的. 最多讀取1024字節(jié), 然后正常情況下就進(jìn)入到 ngx_http_process_request_line() 邏輯了. 也就是說(shuō), 剩下的數(shù)據(jù)會(huì)在接下再被讀取, 而非一次性被讀取完成. 其中, ngx_http_create_request 主要是為 body體分配內(nèi)存空間.

          // http/ngx_http_request.cngx_http_request_t *ngx_http_create_request(ngx_connection_t *c){    ngx_http_request_t        *r;    ngx_http_log_ctx_t        *ctx;    ngx_http_core_loc_conf_t  *clcf;    // 重要: 分配請(qǐng)求的上下文信息    r = ngx_http_alloc_request(c);    if (r == NULL) {        return NULL;    }
          c->requests++;
          clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
          ngx_set_connection_log(c, clcf->error_log);
          ctx = c->log->data; ctx->request = r; ctx->current_request = r;
          #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, 1); r->stat_reading = 1; (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);#endif
          return r;}
          // http/ngx_http_request.cstatic ngx_http_request_t *ngx_http_alloc_request(ngx_connection_t *c){    ngx_pool_t                 *pool;    ngx_time_t                 *tp;    ngx_http_request_t         *r;    ngx_http_connection_t      *hc;    ngx_http_core_srv_conf_t   *cscf;    ngx_http_core_main_conf_t  *cmcf;
          hc = c->data;
          cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
          pool = ngx_create_pool(cscf->request_pool_size, c->log); if (pool == NULL) { return NULL; }
          r = ngx_pcalloc(pool, sizeof(ngx_http_request_t)); if (r == NULL) { ngx_destroy_pool(pool); return NULL; }
          r->pool = pool;
          r->http_connection = hc; r->signature = NGX_HTTP_MODULE; r->connection = c; // 設(shè)置配置信息備用 r->main_conf = hc->conf_ctx->main_conf; r->srv_conf = hc->conf_ctx->srv_conf; r->loc_conf = hc->conf_ctx->loc_conf; // 可能使用讀取方式為 blocking, 如果是異步讀取, 則無(wú)需blocking r->read_event_handler = ngx_http_block_reading; // 將剛剛讀取出的數(shù)據(jù)引用給到header指針, 以便重新讀取 r->header_in = hc->busy ? hc->busy->buf : c->buffer;
          if (ngx_list_init(&r->headers_out.headers, r->pool, 20, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_destroy_pool(r->pool); return NULL; }
          if (ngx_list_init(&r->headers_out.trailers, r->pool, 4, sizeof(ngx_table_elt_t)) != NGX_OK) { ngx_destroy_pool(r->pool); return NULL; }
          r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module); if (r->ctx == NULL) { ngx_destroy_pool(r->pool); return NULL; }
          cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
          r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t)); if (r->variables == NULL) { ngx_destroy_pool(r->pool); return NULL; }
          #if (NGX_HTTP_SSL) if (c->ssl) { r->main_filter_need_in_memory = 1; }#endif
          r->main = r; r->count = 1; // 分配系統(tǒng)默認(rèn)值 tp = ngx_timeofday(); r->start_sec = tp->sec; r->start_msec = tp->msec;
          r->method = NGX_HTTP_UNKNOWN; r->http_version = NGX_HTTP_VERSION_10;
          r->headers_in.content_length_n = -1; r->headers_in.keep_alive_n = -1; r->headers_out.content_length_n = -1; r->headers_out.last_modified_time = -1;
          r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;
          r->http_state = NGX_HTTP_READING_REQUEST_STATE;
          r->log_handler = ngx_http_log_error_handler;
          return r;}

          而接下來(lái)的handler被設(shè)置為了 ngx_http_process_request_line , 則會(huì)進(jìn)一步讀取數(shù)據(jù), 處理事件, 也是處理的核心任務(wù).

          2. 核心數(shù)據(jù)讀取解析

          前面我們看到, nginx 通過(guò)調(diào)用系統(tǒng)級(jí)recv() 接收部分客戶端數(shù)據(jù)過(guò)來(lái), 但那里僅有一個(gè)緩沖區(qū)的大小, 有可能取到的數(shù)據(jù)是不完整的. 那么, 自然需要進(jìn)一步處理, 即: ngx_http_process_request_line .? 它會(huì)在第一次接到數(shù)據(jù)時(shí)就進(jìn)行調(diào)用, 但如果存在多數(shù)據(jù)段, 則會(huì)反復(fù)進(jìn)行該 handler 的調(diào)用(這是一個(gè)核心的異步io的處理實(shí)現(xiàn), 類似斷點(diǎn)續(xù)傳):

          // http/ngx_http_request.c// 讀取body數(shù)據(jù),并響應(yīng)客戶端static voidngx_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; }
          rc = NGX_AGAIN;
          for ( ;; ) {
          if (rc == NGX_AGAIN) { // 讀取header, 因?yàn)榍懊嬉呀?jīng)讀取了部分buff, 可能就直接返回 n = ngx_http_read_request_header(r);
          if (n == NGX_AGAIN || n == NGX_ERROR) { break; } } // 讀取body 數(shù)據(jù), 按照http協(xié)議解析,非常長(zhǎng) // 如解析出 GET HTTP1.1... rc = ngx_http_parse_request_line(r, r->header_in);
          if (rc == NGX_OK) {
          /* the request line has been parsed successfully */ // 解析成功部分信息, 進(jìn)行記錄處理 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;
          if (r->http_protocol.data) { r->http_protocol.len = r->request_end - r->http_protocol.data; } // 處理 uri, 解析路徑, 放入 r->uri.data if (ngx_http_process_request_uri(r) != NGX_OK) { break; }
          if (r->schema_end) { r->schema.len = r->schema_end - r->schema_start; r->schema.data = r->schema_start; }
          if (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); break; }
          if (rc == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; }
          if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) { break; }
          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) { break; }
          ngx_http_process_request(r); break; }

          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); break; }
          c->log->action = "reading client request headers"; // 如果運(yùn)行到此處, 則意味著header數(shù)據(jù)未讀取完成, 需要下一次io事件繼續(xù) // handler 變更為 ngx_http_process_request_headers rev->handler = ngx_http_process_request_headers; ngx_http_process_request_headers(rev);
          break; }
          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]);
          if (rc == NGX_HTTP_PARSE_INVALID_VERSION) { ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED);
          } else { ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); }
          break; }
          /* 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); break; }
          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); break; } } } // 處理請(qǐng)求, 響應(yīng)客戶端 ngx_http_run_posted_requests(c);}

          // http/ngx_http_request.c// 進(jìn)一步處理 header 信息static voidngx_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; 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; }
          cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
          rc = NGX_AGAIN;
          for ( ;; ) { // 依次解析每一行 header 信息 if (rc == NGX_AGAIN) { // 如果解析到最后還沒(méi)完, 說(shuō)明后續(xù)還需要讀取數(shù)據(jù) 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); break; }
          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); break; }
          len = r->header_in->end - p;
          if (len > NGX_MAX_ERROR_STR - 300) { len = NGX_MAX_ERROR_STR - 300; }
          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); break; } } // 將會(huì)嘗試讀取更多的 header 數(shù)據(jù) n = ngx_http_read_request_header(r);
          if (n == NGX_AGAIN || n == NGX_ERROR) { break; } }
          /* the host header could change the server configuration context */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); // 重新解析 header 信息 rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers);
          if (rc == NGX_OK) {
          r->request_length += r->header_in->pos - r->header_name_start;
          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 */
          h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); break; }
          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); break; }
          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); // ngx_http_process_host, 處理 host 驗(yàn)證 // ngx_http_process_connection, 處理 keepalive if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { break; }
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header: \"%V: %V\"", &h->key, &h->value);
          continue; } // header 解析完成后, 處理body 數(shù)據(jù)了 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;
          rc = ngx_http_process_request_header(r);
          if (rc != NGX_OK) { break; }
          ngx_http_process_request(r);
          break; }
          if (rc == NGX_AGAIN) {
          /* a header line parsing is still not complete */
          continue; }
          /* rc == NGX_HTTP_PARSE_INVALID_HEADER */
          ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line");
          ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); break; }
          ngx_http_run_posted_requests(c);}


          以上是整個(gè)http的主要流程之一, 主要分為讀取 header 處理header, 讀取body處理body, 最后 ngx_http_run_posted_requests, 做后置處理. 其中重要的實(shí)現(xiàn)方式是, 依次讀取每個(gè)字節(jié), 進(jìn)行http協(xié)議解析, 按行劃分 header, 以及讀取部分buffer就處理部分header等優(yōu)化.

          接下來(lái), 我們看看其對(duì) body 部分的處理:

          // http/ngx_http_request.c// 處理 body 部分處理, 并處理響應(yīng)對(duì)應(yīng)請(qǐng)求voidngx_http_process_request(ngx_http_request_t *r){    ngx_connection_t  *c;
          c = r->connection;
          #if (NGX_HTTP_SSL) // https 處理...#endif // 刪除 timer if (c->read->timer_set) { ngx_del_timer(c->read); }
          #if (NGX_STAT_STUB) (void) ngx_atomic_fetch_add(ngx_stat_reading, -1); r->stat_reading = 0; (void) ngx_atomic_fetch_add(ngx_stat_writing, 1); r->stat_writing = 1;#endif // 設(shè)置讀寫(xiě) handler 為 ngx_http_request_handler, 以便在必要的時(shí)候使用 c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler; r->read_event_handler = ngx_http_block_reading; // 由 ngx_http_handler 處理細(xì)節(jié), 下節(jié)再看 ngx_http_handler(r);}
          // http/ngx_http_request.c// 讀寫(xiě)處理器static voidngx_http_request_handler(ngx_event_t *ev){ ngx_connection_t *c; ngx_http_request_t *r;
          c = ev->data; r = c->data;
          ngx_http_set_log_request(c->log, r);
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http run request: \"%V?%V\"", &r->uri, &r->args);
          if (c->close) { r->main->count++; ngx_http_terminate_request(r, 0); ngx_http_run_posted_requests(c); return; }
          if (ev->delayed && ev->timedout) { ev->delayed = 0; ev->timedout = 0; }
          if (ev->write) { r->write_event_handler(r);
          } else { r->read_event_handler(r); }
          ngx_http_run_posted_requests(c);}

          大體就是如何使用 recv() 讀取數(shù)據(jù)的過(guò)程, 看著流程多, 但實(shí)際上其時(shí)間復(fù)雜度基本為 O(1), 所以效率蠻高的.

          3. http 請(qǐng)求的處理

          經(jīng)過(guò)數(shù)據(jù)準(zhǔn)備, 數(shù)據(jù)解析后, 就可以進(jìn)行邏輯處理了. ngx 中支持許多的功能操作, 如配置內(nèi)容跳轉(zhuǎn), 反向代理, 負(fù)載均衡 等等. 這些都統(tǒng)一歸為一類操作.

          voidngx_http_handler(ngx_http_request_t *r){    ngx_http_core_main_conf_t  *cmcf;
          r->connection->log->action = NULL; // 設(shè)置 keepalive 標(biāo)識(shí) if (!r->internal) { 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); r->phase_handler = 0;
          } else { cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); 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 // 設(shè)置寫(xiě)處理器為 ngx_http_core_run_phases, 即開(kāi)始響應(yīng)客戶端流程 r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r);}
          // http/ngx_http_core_module.c// 響應(yīng)客戶端操作, 多階段式操作voidngx_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;
          cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
          ph = cmcf->phase_engine.handlers; // 依次調(diào)用各 checker, 直到有響應(yīng) OK 的checker為止 while (ph[r->phase_handler].checker) { // 每次調(diào)用 checker 之后, 內(nèi)部都會(huì)將 r->phase_handler++, 即迭代下一個(gè) // 此處的 checker 非常之多, 是在各模塊啟動(dòng)時(shí), 自動(dòng)向 ngx_http_core_module.main_conf 中進(jìn)行注冊(cè)的 /** * 定義如下: typedef enum { NGX_HTTP_POST_READ_PHASE = 0,
          NGX_HTTP_SERVER_REWRITE_PHASE,
          NGX_HTTP_FIND_CONFIG_PHASE, NGX_HTTP_REWRITE_PHASE, NGX_HTTP_POST_REWRITE_PHASE,
          NGX_HTTP_PREACCESS_PHASE,
          NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE,
          NGX_HTTP_PRECONTENT_PHASE,
          NGX_HTTP_CONTENT_PHASE,
          NGX_HTTP_LOG_PHASE } ngx_http_phases;
          // 注冊(cè)方式 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); *h = ngx_http_access_handler; */ // 將請(qǐng)求信息和 handler 本身傳入調(diào)用(不是面向, 只能這么做了) rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); // 只要有一個(gè)處理成功, 則后續(xù)不再調(diào)用 if (rc == NGX_OK) { return; } }}

          // http/modules/ngx_http_rewrite_module.c// 路徑重寫(xiě)模塊ngx_int_tngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){ ngx_int_t rc;
          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "rewrite phase: %ui", r->phase_handler);
          rc = ph->handler(r);
          if (rc == NGX_DECLINED) { r->phase_handler++; return NGX_AGAIN; }
          if (rc == NGX_DONE) { return NGX_OK; }
          /* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_... */
          ngx_http_finalize_request(r, rc);
          return NGX_OK;}static ngx_int_tngx_http_rewrite_handler(ngx_http_request_t *r){ ngx_int_t index; ngx_http_script_code_pt code; ngx_http_script_engine_t *e; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; ngx_http_rewrite_loc_conf_t *rlcf;
          cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); index = cmcf->phase_engine.location_rewrite_index;
          if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) { /* skipping location rewrite phase for server null location */ return NGX_DECLINED; }
          rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
          if (rlcf->codes == NULL) { return NGX_DECLINED; }
          e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t)); if (e == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          e->sp = ngx_pcalloc(r->pool, rlcf->stack_size * sizeof(ngx_http_variable_value_t)); if (e->sp == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          e->ip = rlcf->codes->elts; e->request = r; e->quote = 1; e->log = rlcf->log; e->status = NGX_DECLINED;
          while (*(uintptr_t *) e->ip) { code = *(ngx_http_script_code_pt *) e->ip; code(e); }
          return e->status;}
          // http/modules/ngx_http_core_module.cngx_int_tngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){ u_char *p; size_t len; ngx_int_t rc; ngx_http_core_loc_conf_t *clcf;
          r->content_handler = NULL; r->uri_changed = 0;
          rc = ngx_http_core_find_location(r);
          if (rc == NGX_ERROR) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; }
          clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
          if (!r->internal && clcf->internal) { ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); return NGX_OK; }
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "using configuration \"%s%V\"", (clcf->noname ? "*" : (clcf->exact_match ? "=" : "")), &clcf->name);
          ngx_http_update_location_config(r);
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http cl:%O max:%O", r->headers_in.content_length_n, clcf->client_max_body_size);
          if (r->headers_in.content_length_n != -1 && !r->discard_body && clcf->client_max_body_size && clcf->client_max_body_size < r->headers_in.content_length_n) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "client intended to send too large body: %O bytes", r->headers_in.content_length_n);
          r->expect_tested = 1; (void) ngx_http_discard_request_body(r); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE); return NGX_OK; }
          if (rc == NGX_DONE) { ngx_http_clear_location(r);
          r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; }
          r->headers_out.location->hash = 1; ngx_str_set(&r->headers_out.location->key, "Location");
          if (r->args.len == 0) { r->headers_out.location->value = clcf->name;
          } else { len = clcf->name.len + 1 + r->args.len; p = ngx_pnalloc(r->pool, len);
          if (p == NULL) { ngx_http_clear_location(r); ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return NGX_OK; }
          r->headers_out.location->value.len = len; r->headers_out.location->value.data = p;
          p = ngx_cpymem(p, clcf->name.data, clcf->name.len); *p++ = '?'; ngx_memcpy(p, r->args.data, r->args.len); }
          ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY); return NGX_OK; }
          r->phase_handler++; return NGX_AGAIN;}
          // http/ngx_http_core_module.cngx_int_tngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){ ngx_int_t rc;
          /* * generic phase checker, * used by the post read and pre-access phases */
          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "generic phase: %ui", r->phase_handler);
          rc = ph->handler(r);
          if (rc == NGX_OK) { r->phase_handler = ph->next; return NGX_AGAIN; }
          if (rc == NGX_DECLINED) { r->phase_handler++; return NGX_AGAIN; }
          if (rc == NGX_AGAIN || rc == NGX_DONE) { return NGX_OK; }
          /* rc == NGX_ERROR || rc == NGX_HTTP_... */
          ngx_http_finalize_request(r, rc);
          return NGX_OK;}
          static ngx_int_tngx_http_index_handler(ngx_http_request_t *r){ u_char *p, *name; size_t len, root, reserve, allocated; ngx_int_t rc; ngx_str_t path, uri; ngx_uint_t i, dir_tested; ngx_http_index_t *index; ngx_open_file_info_t of; ngx_http_script_code_pt code; ngx_http_script_engine_t e; ngx_http_core_loc_conf_t *clcf; ngx_http_index_loc_conf_t *ilcf; ngx_http_script_len_code_pt lcode;
          if (r->uri.data[r->uri.len - 1] != '/') { return NGX_DECLINED; }
          if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { return NGX_DECLINED; }
          ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
          allocated = 0; root = 0; dir_tested = 0; name = NULL; /* suppress MSVC warning */ path.data = NULL;
          index = ilcf->indices->elts; for (i = 0; i < ilcf->indices->nelts; i++) {
          if (index[i].lengths == NULL) {
          if (index[i].name.data[0] == '/') { return ngx_http_internal_redirect(r, &index[i].name, &r->args); }
          reserve = ilcf->max_index_len; len = index[i].name.len;
          } else { ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
          e.ip = index[i].lengths->elts; e.request = r; e.flushed = 1;
          /* 1 is for terminating '\0' as in static names */ len = 1;
          while (*(uintptr_t *) e.ip) { lcode = *(ngx_http_script_len_code_pt *) e.ip; len += lcode(&e); }
          /* 16 bytes are preallocation */
          reserve = len + 16; }
          if (reserve > allocated) {
          name = ngx_http_map_uri_to_path(r, &path, &root, reserve); if (name == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          allocated = path.data + path.len - name; }
          if (index[i].values == NULL) {
          /* index[i].name.len includes the terminating '\0' */
          ngx_memcpy(name, index[i].name.data, index[i].name.len);
          path.len = (name + index[i].name.len - 1) - path.data;
          } else { e.ip = index[i].values->elts; e.pos = name;
          while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); }
          if (*name == '/') { uri.len = len - 1; uri.data = name; return ngx_http_internal_redirect(r, &uri, &r->args); }
          path.len = e.pos - path.data;
          *e.pos = '\0'; }
          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "open index \"%V\"", &path);
          ngx_memzero(&of, sizeof(ngx_open_file_info_t));
          of.read_ahead = clcf->read_ahead; of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.test_only = 1; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events;
          if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { if (of.err == 0) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err, "%s \"%s\" failed", of.failed, path.data);
          #if (NGX_HAVE_OPENAT) if (of.err == NGX_EMLINK || of.err == NGX_ELOOP) { return NGX_HTTP_FORBIDDEN; }#endif
          if (of.err == NGX_ENOTDIR || of.err == NGX_ENAMETOOLONG || of.err == NGX_EACCES) { return ngx_http_index_error(r, clcf, path.data, of.err); }
          if (!dir_tested) { rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
          if (rc != NGX_OK) { return rc; }
          dir_tested = 1; }
          if (of.err == NGX_ENOENT) { continue; }
          ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, "%s \"%s\" failed", of.failed, path.data);
          return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          uri.len = r->uri.len + len - 1;
          if (!clcf->alias) { uri.data = path.data + root;
          } else { uri.data = ngx_pnalloc(r->pool, uri.len); if (uri.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          p = ngx_copy(uri.data, r->uri.data, r->uri.len); ngx_memcpy(p, name, len - 1); }
          return ngx_http_internal_redirect(r, &uri, &r->args); }
          return NGX_DECLINED;}

          static ngx_int_tngx_http_static_handler(ngx_http_request_t *r){ u_char *last, *location; size_t root, len; ngx_str_t path; ngx_int_t rc; ngx_uint_t level; ngx_log_t *log; ngx_buf_t *b; ngx_chain_t out; ngx_open_file_info_t of; ngx_http_core_loc_conf_t *clcf;
          if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { return NGX_HTTP_NOT_ALLOWED; }
          if (r->uri.data[r->uri.len - 1] == '/') { return NGX_DECLINED; }
          log = r->connection->log;
          /* * ngx_http_map_uri_to_path() allocates memory for terminating '\0' * so we do not need to reserve memory for '/' for possible redirect */
          last = ngx_http_map_uri_to_path(r, &path, &root, 0); if (last == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          path.len = last - path.data;
          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http filename: \"%s\"", path.data);
          clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
          ngx_memzero(&of, sizeof(ngx_open_file_info_t));
          of.read_ahead = clcf->read_ahead; of.directio = clcf->directio; of.valid = clcf->open_file_cache_valid; of.min_uses = clcf->open_file_cache_min_uses; of.errors = clcf->open_file_cache_errors; of.events = clcf->open_file_cache_events;
          if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) != NGX_OK) { switch (of.err) {
          case 0: return NGX_HTTP_INTERNAL_SERVER_ERROR;
          case NGX_ENOENT: case NGX_ENOTDIR: case NGX_ENAMETOOLONG:
          level = NGX_LOG_ERR; rc = NGX_HTTP_NOT_FOUND; break;
          case NGX_EACCES:#if (NGX_HAVE_OPENAT) case NGX_EMLINK: case NGX_ELOOP:#endif
          level = NGX_LOG_ERR; rc = NGX_HTTP_FORBIDDEN; break;
          default:
          level = NGX_LOG_CRIT; rc = NGX_HTTP_INTERNAL_SERVER_ERROR; break; }
          if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { ngx_log_error(level, log, of.err, "%s \"%s\" failed", of.failed, path.data); }
          return rc; }
          r->root_tested = !r->error_page;
          ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
          if (of.is_dir) {
          ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
          ngx_http_clear_location(r);
          r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          len = r->uri.len + 1;
          if (!clcf->alias && r->args.len == 0) { location = path.data + root;
          *last = '/';
          } else { if (r->args.len) { len += r->args.len + 1; }
          location = ngx_pnalloc(r->pool, len); if (location == NULL) { ngx_http_clear_location(r); return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          last = ngx_copy(location, r->uri.data, r->uri.len);
          *last = '/';
          if (r->args.len) { *++last = '?'; ngx_memcpy(++last, r->args.data, r->args.len); } }
          r->headers_out.location->hash = 1; ngx_str_set(&r->headers_out.location->key, "Location"); r->headers_out.location->value.len = len; r->headers_out.location->value.data = location;
          return NGX_HTTP_MOVED_PERMANENTLY; }
          #if !(NGX_WIN32) /* the not regular files are probably Unix specific */
          if (!of.is_file) { ngx_log_error(NGX_LOG_CRIT, log, 0, "\"%s\" is not a regular file", path.data);
          return NGX_HTTP_NOT_FOUND; }
          #endif
          if (r->method == NGX_HTTP_POST) { return NGX_HTTP_NOT_ALLOWED; }
          rc = ngx_http_discard_request_body(r);
          if (rc != NGX_OK) { return rc; }
          log->action = "sending response to client";
          r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = of.size; r->headers_out.last_modified_time = of.mtime;
          if (ngx_http_set_etag(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          if (r != r->main && of.size == 0) { return ngx_http_send_header(r); }
          r->allow_ranges = 1;
          /* we need to allocate all before the header would be sent */
          b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); if (b->file == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; }
          rc = ngx_http_send_header(r);
          if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; }
          b->file_pos = 0; b->file_last = of.size;
          b->in_file = b->file_last ? 1: 0; b->last_buf = (r == r->main) ? 1: 0; b->last_in_chain = 1;
          b->file->fd = of.fd; b->file->name = path; b->file->log = log; b->file->directio = of.is_directio;
          out.buf = b; out.next = NULL;
          return ngx_http_output_filter(r, &out);}
          // http/ngx_http_request.cvoidngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc){ ngx_connection_t *c; ngx_http_request_t *pr; ngx_http_core_loc_conf_t *clcf;
          c = r->connection;
          ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, "http finalize request: %i, \"%V?%V\" a:%d, c:%d", rc, &r->uri, &r->args, r == c->data, r->main->count);
          if (rc == NGX_DONE) { ngx_http_finalize_connection(r); return; }
          if (rc == NGX_OK && r->filter_finalize) { c->error = 1; }
          if (rc == NGX_DECLINED) { r->content_handler = NULL; r->write_event_handler = ngx_http_core_run_phases; ngx_http_core_run_phases(r); return; }
          if (r != r->main && r->post_subrequest) { rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc); }
          if (rc == NGX_ERROR || rc == NGX_HTTP_REQUEST_TIME_OUT || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST || c->error) { if (ngx_http_post_action(r) == NGX_OK) { return; }
          ngx_http_terminate_request(r, rc); return; }
          if (rc >= NGX_HTTP_SPECIAL_RESPONSE || rc == NGX_HTTP_CREATED || rc == NGX_HTTP_NO_CONTENT) { if (rc == NGX_HTTP_CLOSE) { c->timedout = 1; ngx_http_terminate_request(r, rc); return; }
          if (r == r->main) { if (c->read->timer_set) { ngx_del_timer(c->read); }
          if (c->write->timer_set) { ngx_del_timer(c->write); } }
          c->read->handler = ngx_http_request_handler; c->write->handler = ngx_http_request_handler;
          ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc)); return; }
          if (r != r->main) {
          if (r->buffered || r->postponed) {
          if (ngx_http_set_write_handler(r) != NGX_OK) { ngx_http_terminate_request(r, 0); }
          return; }
          pr = r->parent;
          if (r == c->data || r->background) {
          if (!r->logged) {
          clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
          if (clcf->log_subrequest) { ngx_http_log_request(r); }
          r->logged = 1;
          } else { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "subrequest: \"%V?%V\" logged again", &r->uri, &r->args); }
          r->done = 1;
          if (r->background) { ngx_http_finalize_connection(r); return; }
          r->main->count--;
          if (pr->postponed && pr->postponed->request == r) { pr->postponed = pr->postponed->next; }
          c->data = pr;
          } else {
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http finalize non-active request: \"%V?%V\"", &r->uri, &r->args);
          r->write_event_handler = ngx_http_request_finalizer;
          if (r->waited) { r->done = 1; } }
          if (ngx_http_post_request(pr, NULL) != NGX_OK) { r->main->count++; ngx_http_terminate_request(r, 0); return; }
          ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wake parent request: \"%V?%V\"", &pr->uri, &pr->args);
          return; }
          if (r->buffered || c->buffered || r->postponed) {
          if (ngx_http_set_write_handler(r) != NGX_OK) { ngx_http_terminate_request(r, 0); }
          return; }
          if (r != c->data) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http finalize non-active request: \"%V?%V\"", &r->uri, &r->args); return; }
          r->done = 1;
          r->read_event_handler = ngx_http_block_reading; r->write_event_handler = ngx_http_request_empty_handler;
          if (!r->post_action) { r->request_complete = 1; }
          if (ngx_http_post_action(r) == NGX_OK) { return; }
          if (c->read->timer_set) { ngx_del_timer(c->read); }
          if (c->write->timer_set) { c->write->delayed = 0; ngx_del_timer(c->write); }
          if (c->read->eof) { ngx_http_close_request(r, 0); return; }
          ngx_http_finalize_connection(r);}


          可以看出,nginx處理http流程框架非常簡(jiǎn)單,即一直遍歷執(zhí)行所有checker,但本身checker數(shù)量又比較多,所以給我們的處理帶來(lái)了復(fù)雜度。此處相當(dāng)于是責(zé)任鏈模式的應(yīng)用,而且這鏈?zhǔn)强梢员蝗我庾?cè)的,所以也為我們擴(kuò)展性打開(kāi)了方便之門。從部分上按一定序執(zhí)行checker,只要有一個(gè)處理完成,即帶調(diào)用后續(xù)checker。

          這些checker大體上被分為了幾類,body數(shù)據(jù)讀取類,config配置文件讀取類,rewrite路徑重寫(xiě)類,access權(quán)限驗(yàn)證類,content內(nèi)容處理類,log日志記錄類。從總體上是有序的,但對(duì)于某類處理,則是任意的。

          本文講解了nginx作為正向代理(http服務(wù)器)的處理過(guò)程,當(dāng)然我們可以簡(jiǎn)單認(rèn)為是一個(gè)文件路徑查找的過(guò)程。無(wú)非就是解析請(qǐng)求頭請(qǐng)求體信息,然后查找所有可能的地方,驗(yàn)證可能的權(quán)限,然后就輸出內(nèi)容到客戶端了。其實(shí)和其他的http服務(wù)器沒(méi)啥差別,但nginx的優(yōu)勢(shì)在于性能,在于配置的簡(jiǎn)便性。性能上基于非阻塞io,配置上則已形成自有的一套簡(jiǎn)潔語(yǔ)法。




          往期精彩推薦



          騰訊、阿里、滴滴后臺(tái)面試題匯總總結(jié) — (含答案)

          面試:史上最全多線程面試題 !

          最新阿里內(nèi)推Java后端面試題

          JVM難學(xué)?那是因?yàn)槟銢](méi)認(rèn)真看完這篇文章


          END


          關(guān)注作者微信公眾號(hào) —《JAVA爛豬皮》


          了解更多java后端架構(gòu)知識(shí)以及最新面試寶典


          你點(diǎn)的每個(gè)好看,我都認(rèn)真當(dāng)成了


          看完本文記得給作者點(diǎn)贊+在看哦~~~大家的支持,是作者源源不斷出文的動(dòng)力

          作者:等你歸去來(lái)

          出處:https://www.cnblogs.com/yougewe/p/13742238.html

          瀏覽 50
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄片网站在线看 | luamlumwuma | 最近日韩中文字幕中文翻译歌词 | 成人污污网站在线观看 | 欧美激情国产91在线 |