国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

nginx http模塊

這篇具有很好參考價值的文章主要介紹了nginx http模塊。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1.模塊依賴

nginx http模塊

2. 模塊的初始化

nginx http模塊

2.1 location的定義

location的定義包含以下幾種

location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }

=:表示精確匹配,只有請求的url路徑與后面的字符串完全相等時,才會命中,不支持location嵌套

~:表示使用正則定義的,區(qū)分大小寫

~*:表示是使用正則定義的,不區(qū)分大小寫

^~:表示該符號后面的字符是最佳匹配,采用該規(guī)則,不再進行后續(xù)的查找

@name:用于定義一個內(nèi)部 Location 塊,該塊不能被外部 Client 所訪問,只能被 NGINX 內(nèi)部配置指令所訪問,比如 try_files 或者error_page。其修飾的location不能嵌套到其它location,也不能再嵌套其它location,即只能是server這一層的

2.2 分配ngx_http_conf_ctx_t

2.2.1 ngx_http_block

其是在解析配置文件中的http分配

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

2.2.2 ngx_http_core_location

其是在解析配置文件中的http塊內(nèi)location時分配

其中main_conf,srv_conf是延用上一層級的,loc_conf會再一次分配內(nèi)存(每一層級location會再次分配

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
pctx = cf->ctx;
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_conf;
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

同時也會遍歷模塊調(diào)用create_loc_conf創(chuàng)建location的配置

for (i = 0; cf->cycle->modules[i]; i++) {
        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[i]->ctx;

        if (module->create_loc_conf) {
            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =
                                                   module->create_loc_conf(cf);
            if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

設(shè)置http_core_module配置的loc_conf來源

clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
clcf->loc_conf = ctx->loc_conf;

2.3 ngx_http_add_location

構(gòu)造ngx_http_location_queue_t,將當(dāng)前ngx_http_core_loc_conf_t添加到上一層級ngx_http_core_loc_conf_t中的location隊列中。如果是精確匹配,正則,有名或者是無名,構(gòu)造的ngx_http_location_queue_t的exact來存放ngx_http_core_loc_conf_t配置,否則使用ngx_http_location_queue_t的inclusive來存放ngx_http_core_loc_conf_t配置

if (clcf->exact_match
#if (NGX_PCRE)
        || clcf->regex
#endif
        || clcf->named || clcf->noname)
    {
        lq->exact = clcf;
        lq->inclusive = NULL;

    } else {
        lq->exact = NULL;
        lq->inclusive = clcf;
    }

將構(gòu)造的隊列添加到上一層級的隊列中

ngx_queue_insert_tail(*locations, &lq->queue);

2.4 主配置中的server

在ngx_http_block中會分配main_conf

ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);

在處理server時(ngx_http_core_server),當(dāng)前層的ctx中的main_conf會延用上一層的main_conf

http_ctx = cf->ctx;
ctx->main_conf = http_ctx->main_conf;

將server放入cmcf->server中

cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
cscf->ctx = ctx;


cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

cscfp = ngx_array_push(&cmcf->servers);
if (cscfp == NULL) {
    return NGX_CONF_ERROR;
}

*cscfp = cscf;

2.5 初始化location(ngx_http_init_locations)

處理ngx_http_core_srv_conf_t中的有名location(named_locations)以及ngx_http_core_loc_conf_t中的正則location(regex_locations),在作割裂之前,會先對ngx_http_core_loc_conf_t中的locations排序,使用的排序規(guī)則為ngx_http_cmp_locations,即按照exact(sorted) -> inclusive(sorted) -> regex -> named -> noname的原則進行排序,經(jīng)過處理后,原先的location隊列就只剩下經(jīng)過排序后的exact以及inclusive類型的location了。這兩類location對應(yīng)配置文件中的定義,就是不含修飾符的location,帶有=和^~前綴的location。

2.6 將queue轉(zhuǎn)為list(ngx_http_create_locations_list)

將locations queue變成locations list

nginx http模塊

2.7 創(chuàng)建location的二叉查找樹(ngx_http_create_locations_tree)

創(chuàng)建精確匹配location的二叉查找樹,使用ngx_queue_middle(其時間度為O(n))得到location_list中的中間位置,如果location_list的元素個數(shù)為奇數(shù),則是中間的一個,否則是后半部分的第一個。

ngx_queue_t *
ngx_queue_middle(ngx_queue_t *queue)
{
    ngx_queue_t  *middle, *next;

    middle = ngx_queue_head(queue);

    if (middle == ngx_queue_last(queue)) {
        return middle;
    }

    next = ngx_queue_head(queue);

    for ( ;; ) {
        middle = ngx_queue_next(middle);

        next = ngx_queue_next(next);

        if (next == ngx_queue_last(queue)) {
            return middle;
        }

        next = ngx_queue_next(next);

        if (next == ngx_queue_last(queue)) {
            return middle;
        }
    }
}

使用遞歸來構(gòu)建二叉查找樹



/*
 * to keep cache locality for left leaf nodes, allocate nodes in following
 * order: node, left subtree, right subtree, inclusive subtree
 */

static ngx_http_location_tree_node_t *
ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,
    size_t prefix)
{
    size_t                          len;
    ngx_queue_t                    *q, tail;
    ngx_http_location_queue_t      *lq;
    ngx_http_location_tree_node_t  *node;

    q = ngx_queue_middle(locations);

    lq = (ngx_http_location_queue_t *) q;
    len = lq->name->len - prefix;

    node = ngx_palloc(cf->pool,
                      offsetof(ngx_http_location_tree_node_t, name) + len);
    if (node == NULL) {
        return NULL;
    }

    node->left = NULL;
    node->right = NULL;
    node->tree = NULL;
    node->exact = lq->exact;
    node->inclusive = lq->inclusive;

    node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect)
                           || (lq->inclusive && lq->inclusive->auto_redirect));

    node->len = (u_short) len;
    ngx_memcpy(node->name, &lq->name->data[prefix], len);

    ngx_queue_split(locations, q, &tail);

    if (ngx_queue_empty(locations)) {
        /*
         * ngx_queue_split() insures that if left part is empty,
         * then right one is empty too
         */
        goto inclusive;
    }

    node->left = ngx_http_create_locations_tree(cf, locations, prefix);
    if (node->left == NULL) {
        return NULL;
    }

    ngx_queue_remove(q);

    if (ngx_queue_empty(&tail)) {
        goto inclusive;
    }

    node->right = ngx_http_create_locations_tree(cf, &tail, prefix);
    if (node->right == NULL) {
        return NULL;
    }

inclusive:

    if (ngx_queue_empty(&lq->list)) {
        return node;
    }

    node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len);
    if (node->tree == NULL) {
        return NULL;
    }

    return node;
}

2.8 http的處理階段

包含11個階段

枚舉

名稱

checker方法

NGX_HTTP_POST_READ_PHASE

在接收到完整的HTTP頭部后處理的HTTP階段

ngx_http_core_generic_phase

NGX_HTTP_SERVER_REWRITE_PHASE

在將請求的URI與location表達(dá)式匹配前, 修改請求的URI(所謂的重定向) 是一個獨立的HTTP階段

ngx_http_core_rewrite_phase

NGX_HTTP_FIND_CONFIG_PHASE

根據(jù)請求的URI尋找匹配的location表達(dá)式, 這個階段只能由ngx_http_core_module模塊實現(xiàn), 不建議其他HTTP模塊重新定義這一階段的行為

ngx_http_core_find_config_phase

NGX_HTTP_REWRITE_PHASE

在NGX_HTTP_FIND_CONFIG_PHASE階段尋找到匹配的location之后再修改請求的URI

ngx_http_core_rewrite_phase

NGX_HTTP_POST_REWRITE_PHASE

這一階段是用于在rewrite重寫URL后, 防止錯誤的

nginx.conf配置導(dǎo)致死循環(huán)(遞歸地修改URI) , 因此, 這一階段僅由ngx_http_core_module模塊處理。 目前, 控制死循環(huán)的方式很簡單, 首先檢查

rewrite的次數(shù), 如果一個請求超過10次重定向

,就認(rèn)為進入了rewrite死循環(huán), 這時在

NGX_HTTP_POST_REWRITE_PHASE階段就會向用戶返回500, 表示服務(wù)器內(nèi)部錯誤

ngx_http_core_post_rewrite_phase

NGX_HTTP_PREACCESS_PHASE

表示在處理NGX_HTTP_ACCESS_PHASE階段決定請求的訪問權(quán)限前HTTP模塊可以介入的處理階段

ngx_http_core_generic_phase

NGX_HTTP_ACCESS_PHASE

這個階段用于讓HTTP模塊判斷是否允許這個請求訪問

Nginx服務(wù)器

ngx_http_core_access_phase

NGX_HTTP_POST_ACCESS_PHASE

在NGX_HTTP_ACCESS_PHASE階段中, 當(dāng)

HTTP模塊的handler處理函數(shù)返回不允許訪問的錯誤碼時(實際就是NGX_HTTP_FORBIDDEN或者

NGX_HTTP_UNAUTHORIZED) , 這里將負(fù)責(zé)向用戶發(fā)送拒絕服務(wù)的錯誤響應(yīng)。 因此, 這個階段實際上用于給NGX_HTTP_ACCESS_PHASE階段收尾

ngx_http_core_post_access_phase

NGX_HTTP_PRECONTENT_PHASE

http請求內(nèi)容前置處理

ngx_http_core_generic_phase

NGX_HTTP_CONTENT_PHASE

用于處理HTTP請求內(nèi)容的階段, 這是大部分

HTTP模塊最愿意介入的階段

ngx_http_core_content_phase

NGX_HTTP_LOG_PHASE

處理完請求后記錄日志的階段

ngx_http_core_generic_phase

2.9 階段處理器的初始化

ngx_http_init_phases初始化以下階段的handlers

  • NGX_HTTP_POST_READ_PHASE

  • NGX_HTTP_SERVER_REWRITE_PHASE

  • NGX_HTTP_REWRITE_PHASE

  • NGX_HTTP_PREACCESS_PHASE

  • NGX_HTTP_ACCESS_PHASE

  • NGX_HTTP_PRECONTENT_PHASE

  • NGX_HTTP_CONTENT_PHASE

  • NGX_HTTP_LOG_PHASE

static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
                       cf->pool, 2, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers,
                       cf->pool, 2, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
                       cf->pool, 4, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
                       cf->pool, 1, sizeof(ngx_http_handler_pt))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    return NGX_OK;
}

2.10 配置后置處理(postconfiguration)

遍歷調(diào)用http模塊的postconfiguration,用來注冊階段的handler

for (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[m]->ctx;

        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

2.11 階段引擎handler的初始化

將各個不同階段的handler匯聚成一個處理鏈表



static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
    ngx_int_t                   j;
    ngx_uint_t                  i, n;
    ngx_uint_t                  find_config_index, use_rewrite, use_access;
    ngx_http_handler_pt        *h;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_handler_pt   checker;

    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
    find_config_index = 0;
    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;

    n = 1                  /* find config phase */
        + use_rewrite      /* post rewrite phase */
        + use_access;      /* post access phase */

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += cmcf->phases[i].handlers.nelts;
    }

    ph = ngx_pcalloc(cf->pool,
                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
    if (ph == NULL) {
        return NGX_ERROR;
    }

    cmcf->phase_engine.handlers = ph;
    n = 0;

    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        h = cmcf->phases[i].handlers.elts;

        switch (i) {

        case NGX_HTTP_SERVER_REWRITE_PHASE:
            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.server_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_FIND_CONFIG_PHASE:
            find_config_index = n;

            ph->checker = ngx_http_core_find_config_phase;
            n++;
            ph++;

            continue;

        case NGX_HTTP_REWRITE_PHASE:
            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
                cmcf->phase_engine.location_rewrite_index = n;
            }
            checker = ngx_http_core_rewrite_phase;

            break;

        case NGX_HTTP_POST_REWRITE_PHASE:
            if (use_rewrite) {
                ph->checker = ngx_http_core_post_rewrite_phase;
                ph->next = find_config_index;
                n++;
                ph++;
            }

            continue;

        case NGX_HTTP_ACCESS_PHASE:
            checker = ngx_http_core_access_phase;
            n++;
            break;

        case NGX_HTTP_POST_ACCESS_PHASE:
            if (use_access) {
                ph->checker = ngx_http_core_post_access_phase;
                ph->next = n;
                ph++;
            }

            continue;

        case NGX_HTTP_CONTENT_PHASE:
            checker = ngx_http_core_content_phase;
            break;

        default:
            checker = ngx_http_core_generic_phase;
        }

        n += cmcf->phases[i].handlers.nelts;

        for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {
            ph->checker = checker;
            ph->handler = h[j];
            ph->next = n;
            ph++;
        }
    }

    return NGX_OK;
}

2.12 初始監(jiān)聽端口, 服務(wù)以及監(jiān)聽回調(diào)

ngx_http_optimize_servers中的ngx_http_add_listening會設(shè)置端口的回調(diào)

ls->handler = ngx_http_init_connection;

3. 運行時的處理

連接上的讀寫回調(diào)

狀態(tài)

讀handler

寫handler

連接建立后

ngx_http_wait_request_handler

ngx_http_empty_handler

讀取請求行

ngx_http_process_request_line

ngx_http_empty_handler

讀取請求頭

ngx_http_process_request_headers

ngx_http_empty_handler

處理請求

ngx_http_request_handler

ngx_http_request_handler

http請求的讀寫回調(diào)

狀態(tài)

讀handler

寫handler

業(yè)務(wù)處理開始

ngx_http_block_reading

ngx_http_core_run_phases

3.1 accept事件處理

在處理accept連接事件時,會調(diào)用ngx_listening_t的回調(diào)handler函數(shù)ngx_http_init_connection

對于新分配的連接,如果讀事件的ready為1,即iocp或者延時的accept事件,在有使用accept鎖情況 下,將事件放入posted_events隊列中,否則直接調(diào)用事件的回調(diào)handler

if (rev->ready) {
    /* the deferred accept(), iocp */

    if (ngx_use_accept_mutex) {
        ngx_post_event(rev, &ngx_posted_events);
        return;
    }

    rev->handler(rev);
    return;
}

如果讀事件的ready不為1,則將事件加入定時器的紅黑樹中。定時器超時后,就會調(diào)用它的 handler ngx_http_wait_request_handler 函數(shù)。

ngx_add_timer(rev, cscf->client_header_timeout);

將連接設(shè)置為可重用,因為該連接上還沒有請求到來,所以當(dāng)連接池中的連接不夠用時,就可以重用這個連接。將當(dāng)前connection添加可重用的連接隊列中,同時可重用連接數(shù)加1

ngx_reusable_connection(c, 1);



void
ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
{
    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                   "reusable connection: %ui", reusable);

    if (c->reusable) {
        ngx_queue_remove(&c->queue);
        ngx_cycle->reusable_connections_n--;

#if (NGX_STAT_STUB)
        (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);
#endif
    }

    c->reusable = reusable;

    if (reusable) {
        /* need cast as ngx_cycle is volatile */

        ngx_queue_insert_head(
            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
        ngx_cycle->reusable_connections_n++;

#if (NGX_STAT_STUB)
        (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);
#endif
    }
}

ngx_handle_read_event將分配連接的事件添加到事件驅(qū)動模塊中

ngx_int_t
ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
{
    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {

        /* kqueue, epoll */

        if (!rev->active && !rev->ready) {
            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)
                == NGX_ERROR)
            {
                return NGX_ERROR;
            }
        }

        return NGX_OK;

    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {

        /* select, poll, /dev/poll */

        if (!rev->active && !rev->ready) {
            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
                == NGX_ERROR)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }

        if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {
            if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)
                == NGX_ERROR)
            {
                return NGX_ERROR;
            }

            return NGX_OK;
        }

    } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {

        /* event ports */

        if (!rev->active && !rev->ready) {
            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return NGX_ERROR;
            }

            return NGX_OK;
        }

        if (rev->oneshot && rev->ready) {
            if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                return NGX_ERROR;
            }

            return NGX_OK;
        }
    }

    /* iocp */

    return NGX_OK;
}

3.2 首次可讀事件處理

是通過ngx_http_wait_request_handler來處理

首先從網(wǎng)絡(luò)上讀取數(shù)據(jù)到連接中的buffer

ngx_connection_t          *c;
ngx_buf_t                 *b;
c = rev->data;
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;
}
n = c->recv(c, b->last, size);
b->last += n;

在可重用連接中刪除當(dāng)前連接

ngx_reusable_connection(c, 0);

創(chuàng)建http_request,在創(chuàng)建請求中,會將上面讀取的緩沖區(qū)放在ngx_http_request_t中的header_in用于處理請求頭

c->data = ngx_http_create_request(c);

處理請求頭,同時將當(dāng)前連接讀事件的回調(diào)函數(shù)設(shè)置為ngx_http_process_request_line,用于處理單次接收的數(shù)據(jù)不完整

rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);

3.3 請求行的處理

是通過ngx_http_process_request_line來處理的

先解析請求行

rc = ngx_http_parse_request_line(r, r->header_in);

在解析請求行中,狀態(tài)變換為

nginx http模塊

在解析請求行中,會有狀態(tài)sw_http_09表示http的0.9版本

HTTP 0.9 請求行格式: [請求方法][空格..空格][URL](空格..空格)(回車符)[換行符]

HTTP >= 1.0 請求行格式: [請求方法][空格..空格][URL][空格..空格][協(xié)議版本][回車符][換行符]

在狀態(tài)機處理中,會將處理狀態(tài)放在結(jié)構(gòu)體ngx_http_request_s 的state中

返回值有以下三種情況

  • NGX_OK:表示成功解析到完整的http請求行

  • NGX_AGAIN:表示接收到字符流不能構(gòu)成完整的請求行

  • NGX_HTTP_PARSE_INVALID_09_METHOD和NGX_HTTP_PARSE_INVALID_REQUEST:表示接收到非法的請求行

當(dāng)返回NGX_AGAIN時,并且沒有可用的內(nèi)存繼續(xù)接收字符流時,會調(diào)用

rv = ngx_http_alloc_large_header_buffer(r, 1);

分配的大小由large_client_header_buffers來決定

解析完成后,會設(shè)置ngx_http_request_t中的request_line, method_name, http_protocol, uri, unparsed_uri, exten, args,schema等

對于http版本號小于1.0的,如果請求頭時沒有server,會調(diào)用ngx_http_set_virtual_server,內(nèi)部ngx_http_find_virtual_server來找到虛擬主機,后面調(diào)用ngx_http_process_request來處理請求。

對于http版本號大于等于1.0的,會初始化請求頭列表

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

調(diào)整連接讀事件的回調(diào)

rev->handler = ngx_http_process_request_headers;

調(diào)用ngx_http_process_request_headers開始處理請求頭

ngx_http_process_request_headers(rev);

3.4 請求頭的處理

首先檢查當(dāng)前的讀事件是否已經(jīng)超時,檢查事件的timeout標(biāo)志位,如果為1,表示已經(jīng)超時,調(diào)用ngx_http_close_request關(guān)閉連接。

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

檢查接收http請求頭部的header_in緩沖區(qū)是否用盡,當(dāng)pos成員指向了end成員時,表示已經(jīng)用完,需要分配更大的緩沖區(qū)

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

調(diào)用ngx_http_parse_header_line來解析請求頭,當(dāng)中也使用了狀態(tài)機,將解析得到的請求頭放到ngx_http_headers_in_t中的headers列表中。

當(dāng)所有請求頭處理完后,會調(diào)用ngx_http_process_request_header對請求頭信息作一些驗證

http版本大于1.0時,請求頭中的host不能為空

if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
               "client sent HTTP/1.1 request without \"Host\" header");
    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
    return NGX_ERROR;
}

請求頭中的content_length必須是合法的數(shù)字

    if (r->headers_in.content_length) {
        r->headers_in.content_length_n =
                            ngx_atoof(r->headers_in.content_length->value.data,
                                      r->headers_in.content_length->value.len);

        if (r->headers_in.content_length_n == NGX_ERROR) {
            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                          "client sent invalid \"Content-Length\" header");
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            return NGX_ERROR;
        }
    }

當(dāng)使用傳輸編碼時,要求http版本為大于等于1.1, 編碼值為chunked時,不能傳content_length

    if (r->headers_in.transfer_encoding) {
        if (r->http_version < NGX_HTTP_VERSION_11) {
            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                          "client sent HTTP/1.0 request with "
                          "\"Transfer-Encoding\" header");
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            return NGX_ERROR;
        }

        if (r->headers_in.transfer_encoding->value.len == 7
            && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
                               (u_char *) "chunked", 7) == 0)
        {
            if (r->headers_in.content_length) {
                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                              "client sent \"Content-Length\" and "
                              "\"Transfer-Encoding\" headers "
                              "at the same time");
                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
                return NGX_ERROR;
            }

            r->headers_in.chunked = 1;

        } else {
            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                          "client sent unknown \"Transfer-Encoding\": \"%V\"",
                          &r->headers_in.transfer_encoding->value);
            ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);
            return NGX_ERROR;
        }
    }

方法名不能為CONNECT和TRACE

    if (r->method == NGX_HTTP_CONNECT) {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                      "client sent CONNECT method");
        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
        return NGX_ERROR;
    }

    if (r->method == NGX_HTTP_TRACE) {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                      "client sent TRACE method");
        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
        return NGX_ERROR;
    }

最后調(diào)用ngx_http_process_request來處理請求

3.5 請求處理

由于開始準(zhǔn)備調(diào)用各Http模塊處理請求,不再存在接收http請求頭部超時的問題,需要從定時器中將當(dāng)前連接的讀事件移除。檢查讀事件對應(yīng)的timer_set標(biāo)識位,為1表示讀事件已經(jīng)添加到定時器中,需要刪除

if (c->read->timer_set) {
    ngx_del_timer(c->read);
}

設(shè)置連接的讀寫事件,以及http請求的讀事件

    c->read->handler = ngx_http_request_handler;
    c->write->handler = ngx_http_request_handler;
    r->read_event_handler = ngx_http_block_reading;

如果internal標(biāo)志位為1,則表示請求當(dāng)前需要做內(nèi)部跳轉(zhuǎn),將結(jié)構(gòu)體中的phase_handler序號置為server_rewrite_index,即從NGX_HTTP_SERVER_REWRITE_PHASE階段開始,否則將phase_handler序號置為0

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

設(shè)置http請求的寫回調(diào)為ngx_http_core_run_phases,并且執(zhí)行ngx_http_core_run_phases

    r->write_event_handler = ngx_http_core_run_phases;
    ngx_http_core_run_phases(r);

ngx_http_core_run_phases就是執(zhí)行http處理階段中設(shè)置的phanse_handler

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;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    ph = cmcf->phase_engine.handlers;

    while (ph[r->phase_handler].checker) {

        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {
            return;
        }
    }
}

在階段的cheker方法內(nèi)會調(diào)用handler

checker方法返回 NGX_OK時,會將控制權(quán)交給nginx的事件模塊,當(dāng)返回非NGX_OK時,向下執(zhí)行phase_engine中的各處理方法

3.6 子請求和post請求

ngx_http_request_t結(jié)構(gòu)中與子請求有關(guān)的成員

nginx http模塊

post_subrequest:用于子請求的后置處理,即子請求處理結(jié)束時的上下文,包含處理回調(diào),其定義為

typedef struct {
    ngx_http_post_subrequest_pt       handler;//子請求的完成回調(diào)
    void                             *data;
} ngx_http_post_subrequest_t;

posted_requests:用來表示主請求包含的子請求鏈表

postponed:用于表示有嵌套層級的子請求,其結(jié)構(gòu)定義為

struct ngx_http_postponed_request_s {
    ngx_http_request_t               *request;//第一個子請求
    ngx_chain_t                      *out;
    ngx_http_postponed_request_t     *next;//用來表示后繼子請求
};

subrequest是通過將一個請求拆分成多個子請求來完成整個請求過程

post請求是為了實現(xiàn)subrequest

子請求的設(shè)計是通過ngx_http_request_t結(jié)構(gòu)體中的三個成員posted_requests, main和parent來完成。posted_requests的數(shù)據(jù)類型為ngx_http_posted_request_t,其結(jié)構(gòu)定義為

struct ngx_http_posted_request_s {
    ngx_http_request_t               *request;//子請求
    ngx_http_posted_request_t        *next;
};

在創(chuàng)建subrequest時,會設(shè)置一些參數(shù),將原始請求即main指向請求的引用計數(shù)+1

//ngx_http_subrequest
ngx_http_request_t            *sr;
sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));
if (sr == NULL) {
    return NGX_ERROR;
}

sr->signature = NGX_HTTP_MODULE;
c = r->connection;
sr->connection = c;
sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
if (sr->ctx == NULL) {
    return NGX_ERROR;
}

if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,
                  sizeof(ngx_table_elt_t))
    != NGX_OK)
{
    return NGX_ERROR;
}

if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4,
                  sizeof(ngx_table_elt_t))
    != NGX_OK)
{
    return NGX_ERROR;
}

cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
sr->main_conf = cscf->ctx->main_conf;
sr->srv_conf = cscf->ctx->srv_conf;
sr->loc_conf = cscf->ctx->loc_conf;

sr->pool = r->pool;

sr->headers_in = r->headers_in;

ngx_http_clear_content_length(sr);
ngx_http_clear_accept_ranges(sr);
ngx_http_clear_last_modified(sr);
sr->request_body = r->request_body;

#if (NGX_HTTP_V2)
    sr->stream = r->stream;
#endif

sr->method = NGX_HTTP_GET;
sr->http_version = r->http_version;

sr->request_line = r->request_line;
sr->uri = *uri;

if (args) {
    sr->args = *args;

sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;
sr->background = (flags & NGX_HTTP_SUBREQUEST_BACKGROUND) != 0;

sr->unparsed_uri = r->unparsed_uri;
sr->method_name = ngx_http_core_get_method;
sr->http_protocol = r->http_protocol;
sr->schema = r->schema;

ngx_http_set_exten(sr);

sr->main = r->main;
sr->parent = r;
sr->post_subrequest = ps;
sr->read_event_handler = ngx_http_request_empty_handler;
sr->write_event_handler = ngx_http_handler;

sr->variables = r->variables;

sr->log_handler = r->log_handler;

if (sr->subrequest_in_memory) {
    sr->filter_need_in_memory = 1;
}
sr->internal = 1;

sr->discard_body = r->discard_body;
sr->expect_tested = 1;
sr->main_filter_need_in_memory = r->main_filter_need_in_memory;

sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
sr->subrequests = r->subrequests - 1;

tp = ngx_timeofday();
sr->start_sec = tp->sec;
sr->start_msec = tp->msec;

r->main->count++;

*psr = sr;

對于需要延時的子請求,是放在請求結(jié)構(gòu)體ngx_http_request_s中的postponed中

if (!sr->background) {
    if (c->data == r && r->postponed == NULL) {
        c->data = sr;
    }

    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
    if (pr == NULL) {
        return NGX_ERROR;
    }

    pr->request = sr;
    pr->out = NULL;
    pr->next = NULL;

    if (r->postponed) {
        for (p = r->postponed; p->next; p = p->next) { /* void */ }
        p->next = pr;

    } else {
        r->postponed = pr;
    }
}

創(chuàng)建posted_request,放在main中的posted_requests鏈表尾

ngx_int_t
ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)
{
    ngx_http_posted_request_t  **p;

    if (pr == NULL) {
        pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
        if (pr == NULL) {
            return NGX_ERROR;
        }
    }

    pr->request = r;
    pr->next = NULL;

    for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }

    *p = pr;

    return NGX_OK;
}

什么時候執(zhí)行subrequest的回調(diào)呢?

是在ngx_http_run_posted_requests中,會遍歷主請求中的posted_requests鏈表,執(zhí)行對應(yīng)的write_event_handler回調(diào)

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

    for ( ;; ) {

        if (c->destroyed) {
            return;
        }

        r = c->data;
        pr = r->main->posted_requests;

        if (pr == NULL) {
            return;
        }

        r->main->posted_requests = pr->next;

        r = pr->request;

        ngx_http_set_log_request(c->log, r);

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

        r->write_event_handler(r);
    }
}

子請求的類型有

  • NGX_HTTP_SUBREQUEST_IN_MEMORY:不支持嵌套,也是upstream的處理方式

  • NGX_HTTP_SUBREQUEST_WAITED:如果子請求提前完成,會將子請求的done標(biāo)識設(shè)置為1

  • NGX_HTTP_SUBREQUEST_BACKGROUND:創(chuàng)建的「后臺子請求」不參與響應(yīng)生產(chǎn)過程,所以并不 需要加入「子請求關(guān)系樹」。

3.7 消息體處理

主要使用數(shù)據(jù)結(jié)構(gòu)有

nginx http模塊

ngx_http_request_body_t:請求消息體結(jié)構(gòu)

ngx_chain_t:用于存放消息體的內(nèi)容,是一個由ngx_buf_t組合的鏈表

其結(jié)構(gòu)定義為

typedef struct {
    ngx_temp_file_t                  *temp_file;
    ngx_chain_t                      *bufs;
    ngx_buf_t                        *buf;
    off_t                             rest;//消息體剩余未處理的長度
    off_t                             received;
    ngx_chain_t                      *free;//空閑緩沖區(qū)鏈表
    ngx_chain_t                      *busy;//繁忙緩沖區(qū)鏈表
    ngx_http_chunked_t               *chunked;
    ngx_http_client_body_handler_pt   post_handler;
    unsigned                          filter_need_buffering:1;
    unsigned                          last_sent:1;
    unsigned                          last_saved:1;//表示是否需要寫入文件以及是否已經(jīng)寫入文件
} ngx_http_request_body_t;

struct ngx_chain_s {
    ngx_buf_t    *buf;
    ngx_chain_t  *next;
};

消息體的處理入口函數(shù)為ngx_http_read_client_request_body

請求消息體的初始化,設(shè)置rest為-1,以及讀取完成后的后置處理handler

rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
if (rb == NULL) {
    rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
    goto done;
}
rb->rest = -1;
rb->post_handler = post_handler;

如果之前的http請求中讀取的數(shù)據(jù)還沒有處理完,及讀取的內(nèi)容中包含部分消息體的內(nèi)容,則進入下面邏輯

preread = r->header_in->last - r->header_in->pos;
if (preread) {

    /* there is the pre-read part of the request body */

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http client request body preread %uz", preread);

    out.buf = r->header_in;
    out.next = NULL;

    rc = ngx_http_request_body_filter(r, &out);

    if (rc != NGX_OK) {
        goto done;
    }

    r->request_length += preread - (r->header_in->last - r->header_in->pos);

    if (!r->headers_in.chunked
        && rb->rest > 0
        && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
    {
        /* the whole request body may be placed in r->header_in */

        b = ngx_calloc_buf(r->pool);
        if (b == NULL) {
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
            goto done;
        }

        b->temporary = 1;
        b->start = r->header_in->pos;
        b->pos = r->header_in->pos;
        b->last = r->header_in->last;
        b->end = r->header_in->end;

        rb->buf = b;

        r->read_event_handler = ngx_http_read_client_request_body_handler;
        r->write_event_handler = ngx_http_request_empty_handler;

        rc = ngx_http_do_read_client_request_body(r);
        goto done;
    }

}

ngx_http_request_body_filter中會區(qū)分http流類型,分為兩種

  • chunked類型(ngx_http_request_body_chunked_filter)

  • 請求頭中有content-length(ngx_http_request_body_length_filter)

static ngx_int_t
ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    if (r->headers_in.chunked) {
        return ngx_http_request_body_chunked_filter(r, in);

    } else {
        return ngx_http_request_body_length_filter(r, in);
    }
}

ngx_http_request_body_length_filter中,會在rest為-1時,重新設(shè)置rest

rb->rest = r->headers_in.content_length_n;

然后根據(jù)讀取的請求體內(nèi)容,構(gòu)建ngx_chain_t緩沖區(qū)鏈表out,其中in是根據(jù)r->head_in構(gòu)造的ngx_chain_t,實際只有一個。通過二級指針實現(xiàn)了鏈表的插入

ngx_chain_t               *out,**ll;
out = NULL;
ll = &out;
for (cl = in; cl; cl = cl->next) {

    if (rb->rest == 0) {
        break;
    }

    tl = ngx_chain_get_free_buf(r->pool, &rb->free);
    if (tl == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    b = tl->buf;

    ngx_memzero(b, sizeof(ngx_buf_t));

    b->temporary = 1;
    b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
    b->start = cl->buf->pos;
    b->pos = cl->buf->pos;
    b->last = cl->buf->last;
    b->end = cl->buf->end;
    b->flush = r->request_body_no_buffering;

    size = cl->buf->last - cl->buf->pos;

    if ((off_t) size < rb->rest) {
        cl->buf->pos = cl->buf->last;
        rb->rest -= size;

    } else {
        cl->buf->pos += (size_t) rb->rest;
        rb->rest = 0;
        b->last = cl->buf->pos;
        b->last_buf = 1;
    }

    *ll = tl;
    ll = &tl->next;
}

調(diào)用ngx_http_top_request_body_filter來將消息體內(nèi)容放到臨時文件中,ngx_http_top_request_body_filter是在ngx_http_core_module模塊的postconfiguration方法中來初始化的,對應(yīng)的是ngx_http_request_body_save_filter

static ngx_int_t
ngx_http_core_postconfiguration(ngx_conf_t *cf)
{
    ngx_http_top_request_body_filter = ngx_http_request_body_save_filter;

    return NGX_OK;
}

將上面生成的out鏈表放入到消息請求體中的bufs中

ngx_chain_t               *cl, *tl, **ll;
ll = &rb->bufs;

//指向最后一個
for (cl = rb->bufs; cl; cl = cl->next) {
    ll = &cl->next;
}
for (cl = in; cl; cl = cl->next) {

    if (cl->buf->last_buf) {

        if (rb->last_saved) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                          "duplicate last buf in save filter");
            *ll = NULL;
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        rb->last_saved = 1;
    }

    tl = ngx_alloc_chain_link(r->pool);
    if (tl == NULL) {
        *ll = NULL;
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    tl->buf = cl->buf;
    *ll = tl;
    ll = &tl->next;
}

*ll = NULL;

通過ngx_http_write_request_body將緩沖區(qū)鏈表中的數(shù)據(jù)寫入到臨時文件中,當(dāng)寫完文件后,會創(chuàng)建一個ngx_chain_t結(jié)構(gòu)的數(shù)據(jù),將其中buf填充,設(shè)置in_file標(biāo)記,file名以及文件最后個位置的偏移

,最后賦值給消息請求體中的bufs

if (rb->temp_file || r->request_body_in_file_only) {

        if (rb->bufs && rb->bufs->buf->in_file) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                          "body already in file");
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        if (ngx_http_write_request_body(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        if (rb->temp_file->file.offset != 0) {

            cl = ngx_chain_get_free_buf(r->pool, &rb->free);
            if (cl == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

            b = cl->buf;

            ngx_memzero(b, sizeof(ngx_buf_t));

            b->in_file = 1;
            b->file_last = rb->temp_file->file.offset;
            b->file = &rb->temp_file->file;

            rb->bufs = cl;
        }
    }

另外一種處理消息體的方式是丟棄即ngx_http_discard_request_body

3.8 發(fā)送響應(yīng)

發(fā)送消息體主要 涉及到消息頭和消息體的處理,nginx抽象出了兩個函數(shù)接口ngx_http_output_header_filter_pt和ngx_http_output_body_filter_pt,其定義為

typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
typedef ngx_int_t (*ngx_http_output_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t *chain);

對于消息頭和消息體的處理,是通過兩個鏈表來處理

nginx http模塊

其中ngx_http_top_header_filter指向處理消息頭鏈表的頭,ngx_http_top_body_filter指向處理消息體鏈表的頭。在每個http子模塊中會定義兩個靜態(tài)變量

static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

通過每個模塊的postconfiguration來將當(dāng)前模塊的請求頭和請求體處理分別添加到兩個處理鏈表中文章來源地址http://www.zghlxwxcb.cn/news/detail-404786.html

到了這里,關(guān)于nginx http模塊的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 結(jié)構(gòu)體聲明、定義和初始化的幾種方式

    五種結(jié)構(gòu)體聲明方式: 直接聲明結(jié)構(gòu)體類型 聲明結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體變量 不指定結(jié)構(gòu)體名而直接定義結(jié)構(gòu)體變量 使用結(jié)構(gòu)體標(biāo)記和類型別名 直接聲明結(jié)構(gòu)體別名 在C語言中,標(biāo)記(tag)是在定義struct, union或enum之后使用的標(biāo)識符。 之所以稱其為結(jié)構(gòu)體的“

    2023年04月11日
    瀏覽(23)
  • TabView 初始化與自定義 TabBar 屬性相關(guān)

    SWift TabView 與 UIKit 中的 UITabBarController 如出一轍.在 TabView 組件中配置對應(yīng)的圖片和標(biāo)題; 其中,Tag 用來設(shè)置不同 TabView 可動態(tài)設(shè)置當(dāng)前可見 Tab;另也有一些常用的屬性與 UIKit 中的類似,具體可以按需參考 api 中屬性進行單獨修改定制; 在 iOS 15.0 之后還可設(shè)置角標(biāo)記 .badge 對 TabBa

    2024年02月10日
    瀏覽(19)
  • cv庫學(xué)習(xí),一 Mat類矩陣的定義初始化

    cv庫學(xué)習(xí),一 Mat類矩陣的定義初始化

    1,由多維數(shù)組定義初始化Mat類矩陣; ?????double m[2][2]={{1.0,2.0},{3.0,4.0}};? ??? ?Mat M(2,2,CV_64F,m); 2,構(gòu)造函數(shù)定義法 ? ? ?Mat M(2,2,CV_32FC3,Scalar(100,200,300)); ? ? ? Mat M(2,2,CV_32FC2,Scalar(100,200)); ? ? ? Mat M(2,2,CV_8UC1,Scalar(100)); 3,? ?M.create(Size(10, 20), CV_32FC3); 在原有的M矩陣上修改大

    2023年04月24日
    瀏覽(19)
  • Android 自定義view 中增加屬性,初始化時讀取

    因為自定義View 有正向和反向兩個狀態(tài),所以需要在初始化時區(qū)分加載哪個layout 在Android中,要在自定義View中增加屬性,你需要完成以下步驟: 在res/values/attrs.xml文件中定義屬性。 在自定義View的構(gòu)造函數(shù)中獲取這些屬性。 在布局文件中使用這些屬性。 attrs.xml: 自定義VIEW 中

    2024年04月25日
    瀏覽(25)
  • 物聯(lián)網(wǎng)Lora模塊從入門到精通(四)對某些端口的初始化

    物聯(lián)網(wǎng)Lora模塊從入門到精通(四)對某些端口的初始化

    ? ? ? ? 由于程序設(shè)計開發(fā)具有的不確定性,我們常常需要初始化某些特定的引腳,并讀取引腳電平狀態(tài)或向引腳輸出高低電平。 ? ? ? ? 快速找到端口的初始化語句: ? ? ? ? 首先,找到board.c文件,在下圖的位置,我們可以看到關(guān)于LED燈的端口的初始化語句。 ? ? ? ?

    2024年02月08日
    瀏覽(13)
  • Vue 先初始化父組件再初始化子組件的方法(自定義父子組件mounted執(zhí)行順序)

    寫在前面: 本篇內(nèi)容內(nèi)容主要講述了,在使用 Konva 進行開發(fā)過程中遇到的一些問題。(既然是組件加載順序,主要牽扯到的就是,父子組件的關(guān)系,父子組件的生命周期) 眾所周知, Vue 中父子組件生命周期的執(zhí)行順序為: 然而,在某些情況下我們有其他需求,例如我們不

    2024年02月12日
    瀏覽(26)
  • C語言——結(jié)構(gòu)體類型(一)【結(jié)構(gòu)體定義,創(chuàng)建,初始化和引用】

    C語言——結(jié)構(gòu)體類型(一)【結(jié)構(gòu)體定義,創(chuàng)建,初始化和引用】

    ??前言: 在實際編程過程中,我們可能會希望把一些關(guān)聯(lián)的數(shù)據(jù)存放在一起,這樣方便我們使用。但是這些數(shù)據(jù)的類型有時候并不一致,例如一個學(xué)生的信息:有名字(字符串),有年齡(整數(shù)),性別(字符)······這時候,我們就可以使用 自定義類型——結(jié)構(gòu)體類型

    2024年02月03日
    瀏覽(35)
  • Rancher部署k8s集群測試安裝nginx(節(jié)點重新初始化方法,親測)

    Rancher部署k8s集群測試安裝nginx(節(jié)點重新初始化方法,親測)

    一、安裝前準(zhǔn)備工作 計算機 機器名 IP地址 部署內(nèi)容 rancher 172.16.5.221 rancher k8smaster 172.16.5.222 Control Plane, Etcd k8sworker01 172.16.5.223 worker k8sworker02 172.16.5.224 worker k8sworker03 172.16.5.225 worker 需在每個節(jié)點都進行操作,可以使用xshell工具分屏進行批量操作。 升級linux內(nèi)核 時間同步 Hos

    2024年01月20日
    瀏覽(27)
  • 面試之快速學(xué)習(xí)c++11- 列表初始化和 lambda匿名函數(shù)的定義

    學(xué)習(xí)地址: http://c.biancheng.net/view/3730.html 我們知道,在 C++98/03 中的對象初始化方法有很多種,請看下面的代碼: 2 .為了統(tǒng)一初始化方式,并且讓初始化行為具有確定的效果,C++11 中提出了列表初始化(List-initialization)的概念。 3 . 在上面我們已經(jīng)看到了,對于普通數(shù)組和

    2024年02月13日
    瀏覽(22)
  • 1.3寸OLED模塊初始化,驅(qū)動芯片為SH1106,i2c通訊

    1.3寸OLED模塊初始化,驅(qū)動芯片為SH1106,i2c通訊

    之前使用過0.96寸的OLED,驅(qū)動芯片SSD1306,看了下兩個芯片數(shù)據(jù)手冊,差異不是很大,買了一個1.3寸的,花了一個下午點亮了,在過程中遇到了些問題,網(wǎng)上的資料不多,于是做個總結(jié)。 主要遇到的問題有, 1.顯示不正常,出現(xiàn)花屏等現(xiàn)象。 從我遇到的情況來說,出現(xiàn)花屏可

    2024年02月12日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包