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;
}
相关推荐
liucan20121 天前
nginx服务器实现上传文件功能_使用nginx-upload-module模块
服务器·前端·nginx
摇滚侠1 天前
Windows 版 Nginx 关闭
运维·windows·nginx
Meepo_haha1 天前
Nginx 反向代理配置
运维·nginx
星辰徐哥1 天前
C语言Web开发:CGI、FastCGI、Nginx深度解析
c语言·前端·nginx
sunwenjian8861 天前
httpslocalhostindex 配置的nginx,一刷新就报404了
运维·nginx
bearpping1 天前
nginx 代理 redis
运维·redis·nginx
ywf12152 天前
Nginx 缓存清理
运维·nginx·缓存
dustcell.2 天前
企业级高可用电商平台实战项目设计
运维·redis·nginx·docker·web·lvs·haproxy
chehaoman2 天前
Failed to restart nginx.service Unit nginx.service not found
运维·nginx
今晚务必早点睡2 天前
Nginx 从入门到精通:一篇讲透原理、功能、配置与实战场景
运维·nginx·负载均衡