Nginx源码分析:变量系统的设计与请求生命周期中的日志记录

Nginx的变量系统是其配置灵活性的基石之一,允许用户在配置文件中引用动态值(如 <math xmlns="http://www.w3.org/1998/Math/MathML"> r e m o t e a d d r 、 remote_addr、 </math>remoteaddr、request_uri等),并在请求处理过程中进行赋值或读取。本文基于Nginx 1.22.1的源码,深入分析变量的注册、存储、获取机制,并结合请求的最终化过程,探讨变量在日志记录阶段的应用。我们将重点关注提供的代码片段,揭示Nginx内部如何管理变量,以及如何在日志处理中高效地获取变量值。

1. 变量的注册与存储

在Nginx中,变量分为两类:普通变量和前缀变量(prefix variables)。前者通过哈希表快速查找,后者适用于名称以特定前缀开头的变量(如$upstream_http_系列变量)。注册变量的核心函数是ngx_http_add_variable,它根据传入的flags决定是否调用前缀变量专用函数。

1.1 普通变量的添加

c

arduino 复制代码
ngx_http_variable_t *
ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
    // ... 省略错误检查 ...
    if (flags & NGX_HTTP_VAR_PREFIX) {
        return ngx_http_add_prefix_variable(cf, name, flags);
    }
    // 否则作为普通变量处理
}

普通变量被存储在ngx_http_core_main_conf_t结构体的variables_keys哈希键数组中。在添加之前,会遍历variables_keys->keys检查是否已存在同名变量。如果变量已存在且不可变(未设置NGX_HTTP_VAR_CHANGEABLE),则报错退出;若允许变更,则根据新变量的NGX_HTTP_VAR_WEAK标志调整原变量的弱属性。这种设计确保了变量定义的一致性,同时允许模块覆盖某些内置变量的行为(如弱变量允许在子请求中修改)。

新变量分配内存后,名称会通过ngx_strlow转换为小写存储(Nginx变量名不区分大小写)。然后调用ngx_hash_add_key将变量加入variables_keys哈希表,准备后续构建快速查找的哈希。

1.2 前缀变量的特殊处理

前缀变量用于处理那些无法枚举的变量名,例如从上游响应头部动态生成的变量。它们不参与哈希查找,而是存储在cmcf->prefix_variables动态数组中。ngx_http_add_prefix_variable同样检查重复,但只针对数组中的元素。如果已存在同名变量且不可变,则报错;否则更新弱标志。新增时,名称同样转换为小写存入内存。

前缀变量在请求处理中通过遍历数组进行匹配,虽然性能不如哈希直接,但其灵活性使得Nginx能够支持任意自定义变量名(如$upstream_http_系列)。

1.3 核心变量的初始化

在配置解析的早期阶段,ngx_http_core_preconfiguration函数被调用,它通过ngx_http_variables_add_core_vars将核心模块定义的内置变量(ngx_http_core_variables数组)注册到系统中。这些变量包括$uri$request_uri$args等,它们被逐个添加到variables_keysprefix_variables中,并设置了对应的get_handlerset_handler

c

ini 复制代码
for (cv = ngx_http_core_variables; cv->name.len; cv++) {
    v = ngx_http_add_variable(cf, &cv->name, cv->flags);
    if (v == NULL) {
        return NGX_ERROR;
    }
    *v = *cv;
}

注意,这里将ngx_http_core_variables中预定义的函数指针赋值给了新创建的变量,使得这些变量在后续被获取时能够执行特定的逻辑。

2. 请求处理与变量获取

变量最终在请求处理过程中被读取,最常见的场景之一是访问日志记录。当一个请求结束时,Nginx会执行ngx_http_finalize_request,其中可能触发日志记录。

2.1 请求的最终化

ngx_http_finalize_request是一个庞大的函数,负责根据返回码(rc)决定请求的后续动作。关键分支包括:

  • rc == NGX_DECLINED时,重置内容处理器并继续执行阶段;
  • 当发生错误或特殊响应时,可能调用ngx_http_terminate_request
  • 正常结束时,若为主请求且无缓冲数据,则调用ngx_http_finalize_connection关闭连接。

在主请求正常结束前,会检查是否启用了post_action,否则标记请求完成。其中,子请求的处理涉及ngx_http_log_request的调用:

c

scss 复制代码
if (r != r->main) {
    // ... 子请求处理 ...
    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;
    }
    // ...
}

无论是主请求还是子请求,最终都会通过ngx_http_log_request调用日志模块的handler,而日志模块的handler正是ngx_http_log_handler

2.2 日志处理中的变量展开

ngx_http_log_handler负责将日志格式中的变量展开为实际字符串,并写入文件或syslog。其核心流程如下:

  1. 获取日志模块的配置lcf,若日志关闭则直接返回。

  2. 遍历所有日志条目(可能多个日志文件或syslog),对每条日志:

    • 若定义了filter,则计算过滤条件,跳过不满足的请求。
    • 计算日志行的总长度:遍历格式操作符数组ops,累加固定长度和变量长度(通过getlen回调)。
    • 若使用缓冲(buffer),尝试将日志写入缓冲区,否则分配临时内存。
    • 通过run回调将变量值写入缓冲区。

其中,变量长度的计算由ngx_http_log_variable_getlen完成,它通过索引获取变量值,并返回转义后的长度。这个索引(data参数)是在配置解析阶段,根据变量名在variables数组中的位置确定的。

c

ini 复制代码
value = ngx_http_get_indexed_variable(r, data);
if (value == NULL || value->not_found) {
    return 1;
}
len = ngx_http_log_escape(NULL, value->data, value->len);
value->escape = len ? 1 : 0;
return value->len + len * 3;

2.3 获取索引变量的内部实现

ngx_http_get_indexed_variable是变量获取的核心函数,它通过索引快速访问请求的变量值缓存(r->variables数组),避免重复计算。其逻辑:

  • 检查索引是否超出cmcf->variables范围。
  • 如果请求变量缓存有效或未找到,直接返回缓存值。
  • 否则调用变量的get_handler计算值,并存入缓存。同时使用ngx_http_variable_depth计数器防止递归循环(如变量A依赖变量B,变量B又依赖A)。

c

ini 复制代码
if (ngx_http_variable_depth == 0) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                  "cycle while evaluating variable "%V"",
                  &v[index].name);
    return NULL;
}
ngx_http_variable_depth--;
if (v[index].get_handler(r, &r->variables[index], v[index].data) == NGX_OK) {
    ngx_http_variable_depth++;
    // 设置nocacheable标志等
}

这种设计既保证了性能(缓存),又通过深度限制保护了递归解析。

2.4 动态未知头部的变量处理

对于$http_sent_http_开头的变量,Nginx提供了通用处理函数ngx_http_variable_unknown_header。它遍历请求头或响应头链表,将变量名中的下划线转换为连字符(或反之),进行不区分大小写的匹配。例如,变量$http_user_agent会匹配请求头中的User-Agent

c

ini 复制代码
for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
    ch = header[i].key.data[n];
    if (ch >= 'A' && ch <= 'Z') {
        ch |= 0x20;
    } else if (ch == '-') {
        ch = '_';
    }
    if (var->data[n + prefix] != ch) {
        break;
    }
}

这种动态匹配机制使得Nginx能够处理任意客户端发送的头部,无需预先定义所有变量。

3. 变量系统的设计巧思

从上述代码中,我们可以提炼出Nginx变量系统的几个关键设计:

  • 两阶段注册:普通变量通过哈希表快速访问,前缀变量通过数组遍历,兼顾了效率与灵活性。
  • 不可变性与弱变量NGX_HTTP_VAR_CHANGEABLE标志控制变量是否允许被多次定义,防止模块间意外覆盖;NGX_HTTP_VAR_WEAK标志允许子请求修改变量值,但不影响主请求,这为复杂请求处理提供了便利。
  • 缓存与深度保护 :每个请求维护一份变量值缓存,避免重复计算;同时通过ngx_http_variable_depth防止循环依赖,体现了对异常情况的防御。
  • 统一的接口 :变量通过get_handlerset_handler函数指针提供操作,使得自定义变量可以轻松集成,如核心变量、第三方模块变量等。

4. 结语

通过分析Nginx变量系统的注册、存储与获取流程,我们看到了一个高性能、可扩展的设计典范。变量不仅在配置解析阶段被定义,更在请求处理的全生命周期中扮演着信息载体的角色。而日志记录作为变量最常见的消费场景,展示了Nginx如何高效地将变量展开为字符串。理解这些底层机制,有助于我们编写更高效的Nginx模块,也能更深刻地理解Nginx的内部运作原理。\r\n

源码
ini 复制代码
static ngx_http_variable_t *
ngx_http_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
    ngx_uint_t                  i;
    ngx_http_variable_t        *v;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    v = cmcf->prefix_variables.elts;
    for (i = 0; i < cmcf->prefix_variables.nelts; i++) {
        if (name->len != v[i].name.len
            || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
        {
            continue;
        }

        v = &v[i];

        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "the duplicate \"%V\" variable", name);
            return NULL;
        }

        if (!(flags & NGX_HTTP_VAR_WEAK)) {
            v->flags &= ~NGX_HTTP_VAR_WEAK;
        }

        return v;
    }

    v = ngx_array_push(&cmcf->prefix_variables);
    if (v == NULL) {
        return NULL;
    }

    v->name.len = name->len;
    v->name.data = ngx_pnalloc(cf->pool, name->len);
    if (v->name.data == NULL) {
        return NULL;
    }

    ngx_strlow(v->name.data, name->data, name->len);

    v->set_handler = NULL;
    v->get_handler = NULL;
    v->data = 0;
    v->flags = flags;
    v->index = 0;

    return v;
}

ngx_http_variable_t *
ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
{
    ngx_int_t                   rc;
    ngx_uint_t                  i;
    ngx_hash_key_t             *key;
    ngx_http_variable_t        *v;
    ngx_http_core_main_conf_t  *cmcf;

    if (name->len == 0) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "invalid variable name \"$\"");
        return NULL;
    }

    if (flags & NGX_HTTP_VAR_PREFIX) {
        return ngx_http_add_prefix_variable(cf, name, flags);
    }

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    key = cmcf->variables_keys->keys.elts;
    for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
        if (name->len != key[i].key.len
            || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
        {
            continue;
        }

        v = key[i].value;

        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "the duplicate \"%V\" variable", name);
            return NULL;
        }

        if (!(flags & NGX_HTTP_VAR_WEAK)) {
            v->flags &= ~NGX_HTTP_VAR_WEAK;
        }

        return v;
    }

    v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));
    if (v == NULL) {
        return NULL;
    }

    v->name.len = name->len;
    v->name.data = ngx_pnalloc(cf->pool, name->len);
    if (v->name.data == NULL) {
        return NULL;
    }

    ngx_strlow(v->name.data, name->data, name->len);

    v->set_handler = NULL;
    v->get_handler = NULL;
    v->data = 0;
    v->flags = flags;
    v->index = 0;

    rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);

    if (rc == NGX_ERROR) {
        return NULL;
    }

    if (rc == NGX_BUSY) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "conflicting variable name \"%V\"", name);
        return NULL;
    }

    return v;
}

ngx_int_t
ngx_http_variables_add_core_vars(ngx_conf_t *cf)
{
    ngx_http_variable_t        *cv, *v;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
                                       sizeof(ngx_hash_keys_arrays_t));
    if (cmcf->variables_keys == NULL) {
        return NGX_ERROR;
    }

    cmcf->variables_keys->pool = cf->pool;
    cmcf->variables_keys->temp_pool = cf->pool;

    if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8,
                       sizeof(ngx_http_variable_t))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    for (cv = ngx_http_core_variables; cv->name.len; cv++) {
        v = ngx_http_add_variable(cf, &cv->name, cv->flags);
        if (v == NULL) {
            return NGX_ERROR;
        }

        *v = *cv;
    }

    return NGX_OK;
}

static ngx_int_t
ngx_http_core_preconfiguration(ngx_conf_t *cf)
{
    return ngx_http_variables_add_core_vars(cf);
}

void
ngx_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);
    }

    ngx_http_finalize_connection(r);
}


static void
ngx_http_log_request(ngx_http_request_t *r)
{
    ngx_uint_t                  i, n;
    ngx_http_handler_pt        *log_handler;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
    n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts; //在ngx_http_log_init函数把ngx_http_log_handler函数添加到handlers

    for (i = 0; i < n; i++) {
        log_handler[i](r);
    }
}


static ngx_int_t
ngx_http_log_handler(ngx_http_request_t *r)
{
    u_char                   *line, *p;
    size_t                    len, size;
    ssize_t                   n;
    ngx_str_t                 val;
    ngx_uint_t                i, l;
    ngx_http_log_t           *log;
    ngx_http_log_op_t        *op;
    ngx_http_log_buf_t       *buffer;
    ngx_http_log_loc_conf_t  *lcf;

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

    lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);

    if (lcf->off) {
        return NGX_OK;
    }

    log = lcf->logs->elts;
    for (l = 0; l < lcf->logs->nelts; l++) {

        if (log[l].filter) {
            if (ngx_http_complex_value(r, log[l].filter, &val) != NGX_OK) {
                return NGX_ERROR;
            }

            if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
                continue;
            }
        }

        if (ngx_time() == log[l].disk_full_time) {

            /*
             * on FreeBSD writing to a full filesystem with enabled softupdates
             * may block process for much longer time than writing to non-full
             * filesystem, so we skip writing to a log for one second
             */

            continue;
        }

        ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);

        len = 0;
        op = log[l].format->ops->elts;
        for (i = 0; i < log[l].format->ops->nelts; i++) {
            if (op[i].len == 0) {
                len += op[i].getlen(r, op[i].data);

            } else {
                len += op[i].len;
            }
        }

        if (log[l].syslog_peer) {

            /* length of syslog's PRI and HEADER message parts */
            len += sizeof("<255>Jan 01 00:00:00 ") - 1
                   + ngx_cycle->hostname.len + 1
                   + log[l].syslog_peer->tag.len + 2;

            goto alloc_line;
        }

        len += NGX_LINEFEED_SIZE;

        buffer = log[l].file ? log[l].file->data : NULL;

        if (buffer) {

            if (len > (size_t) (buffer->last - buffer->pos)) {

                ngx_http_log_write(r, &log[l], buffer->start,
                                   buffer->pos - buffer->start);

                buffer->pos = buffer->start;
            }

            if (len <= (size_t) (buffer->last - buffer->pos)) {

                p = buffer->pos;

                if (buffer->event && p == buffer->start) {
                    ngx_add_timer(buffer->event, buffer->flush);
                }

                for (i = 0; i < log[l].format->ops->nelts; i++) {
                    p = op[i].run(r, p, &op[i]);
                }

                ngx_linefeed(p);

                buffer->pos = p;

                continue;
            }

            if (buffer->event && buffer->event->timer_set) {
                ngx_del_timer(buffer->event);
            }
        }

    alloc_line:

        line = ngx_pnalloc(r->pool, len);
        if (line == NULL) {
            return NGX_ERROR;
        }

        p = line;

        if (log[l].syslog_peer) {
            p = ngx_syslog_add_header(log[l].syslog_peer, line);
        }

        for (i = 0; i < log[l].format->ops->nelts; i++) {
            p = op[i].run(r, p, &op[i]);
        }

        if (log[l].syslog_peer) {

            size = p - line;

            n = ngx_syslog_send(log[l].syslog_peer, line, size);

            if (n < 0) {
                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                              "send() to syslog failed");

            } else if ((size_t) n != size) {
                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
                              "send() to syslog has written only %z of %uz",
                              n, size);
            }

            continue;
        }

        ngx_linefeed(p);

        ngx_http_log_write(r, &log[l], line, p - line);
    }

    return NGX_OK;
}

static size_t
ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)
{
    uintptr_t                   len;
    ngx_http_variable_value_t  *value;

    value = ngx_http_get_indexed_variable(r, data);

    if (value == NULL || value->not_found) {
        return 1;
    }

    len = ngx_http_log_escape(NULL, value->data, value->len);

    value->escape = len ? 1 : 0;

    return value->len + len * 3;
}


ngx_http_variable_value_t *
ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)
{
    ngx_http_variable_t        *v;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    if (cmcf->variables.nelts <= index) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                      "unknown variable index: %ui", index);
        return NULL;
    }

    if (r->variables[index].not_found || r->variables[index].valid) {
        return &r->variables[index];
    }

    v = cmcf->variables.elts;

    if (ngx_http_variable_depth == 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "cycle while evaluating variable \"%V\"",
                      &v[index].name);
        return NULL;
    }

    ngx_http_variable_depth--;

    if (v[index].get_handler(r, &r->variables[index], v[index].data)
        == NGX_OK)
    {
        ngx_http_variable_depth++;

        if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {
            r->variables[index].no_cacheable = 1;
        }

        return &r->variables[index];
    }

    ngx_http_variable_depth++;

    r->variables[index].valid = 0;
    r->variables[index].not_found = 1;

    return NULL;
}

static ngx_int_t
ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
                                            &r->headers_out.headers.part,
                                            sizeof("sent_http_") - 1);
}


ngx_int_t
ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,
    ngx_list_part_t *part, size_t prefix)
{
    u_char            ch;
    ngx_uint_t        i, n;
    ngx_table_elt_t  *header;

    header = part->elts;

    for (i = 0; /* void */ ; i++) {

        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }

            part = part->next;
            header = part->elts;
            i = 0;
        }

        if (header[i].hash == 0) {
            continue;
        }

        for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
            ch = header[i].key.data[n];

            if (ch >= 'A' && ch <= 'Z') {
                ch |= 0x20;

            } else if (ch == '-') {
                ch = '_';
            }

            if (var->data[n + prefix] != ch) {
                break;
            }
        }

        if (n + prefix == var->len && n == header[i].key.len) {
            v->len = header[i].value.len;
            v->valid = 1;
            v->no_cacheable = 0;
            v->not_found = 0;
            v->data = header[i].value.data;

            return NGX_OK;
        }
    }

    v->not_found = 1;

    return NGX_OK;
}

//日志初始化模块
static ngx_int_t
ngx_http_log_init(ngx_conf_t *cf)
{
    ngx_str_t                  *value;
    ngx_array_t                 a;
    ngx_http_handler_pt        *h;
    ngx_http_log_fmt_t         *fmt;
    ngx_http_log_main_conf_t   *lmcf;
    ngx_http_core_main_conf_t  *cmcf;

    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);

    if (lmcf->combined_used) {
        if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {
            return NGX_ERROR;
        }

        value = ngx_array_push(&a);
        if (value == NULL) {
            return NGX_ERROR;
        }

        *value = ngx_http_combined_fmt;
        fmt = lmcf->formats.elts;

        if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)
            != NGX_CONF_OK)
        {
            return NGX_ERROR;
        }
    }

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); //日志阶段执行的处理函数列表 存储了所有注册到日志阶段的处理函数
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_log_handler;

    return NGX_OK;
}
相关推荐
十二7403 天前
前端缓存踩坑实录:从版本号管理到自动化构建
前端·javascript·nginx
可观测性用观测云4 天前
云原生网关 Ingress-Nginx 链路追踪实战:OpenTelemetry 采集与观测云集成方案
nginx·kubernetes
闲云一鹤6 天前
nginx 快速入门教程 - 写给前端的你
前端·nginx·前端工程化
何中应9 天前
Nginx转发请求错误
前端·后端·nginx
芝士雪豹只抽瑞克五10 天前
Nginx 高性能Web服务器笔记
服务器·nginx
失重外太空啦10 天前
nginx
运维·nginx
天蓝不会忘记0210 天前
lvs,haproxy,keepalived,nginx,tomcat介绍和实验
nginx·tomcat·lvs
feng68_10 天前
Nginx高性能Web服务器
linux·运维·服务器·nginx