nginx源码分析之script (一)

本文主要探讨nginx源码中的变量 ngx_http_variable_t,它在ngx_http_core_main_conf_t结构中的应用,以及如何利用这些变量进行重定向操作。通过代码分析,揭示了nginx如何处理字符串、数组和字符等数据类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

nginx的变量的处理流程可以分为 变量的创建与变量对应值的获取流程。
nginx的变量存储也可以分为两部分 ngx_http_core_main_conf_t用于存放变量创建时的相关成员,ngx_http_request_t保存的变量对应的值。


1.nginx变量 相关的结构体:

a.用于代表变量的ngx_http_variable_t,该变量被适用于ngx_http_core_main_conf_t

[code=C/C++]

struct ngx_http_variable_s {
     ngx_str_t name; /* must be first to build the hash */
     ngx_http_set_variable_pt set_handler; //用于设置该变量的值,用得比较少。
     ngx_http_get_variable_pt get_handler; //用于获取该变量对应的值,用得比较多,两个handler中设置一个即可。
     //typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
     uintptr_t data; //传入get_handler或者set_handler的第三个参数data.
     ngx_uint_t flags;
     ngx_uint_t index; //当我们建立索引变量时, 会在
};
[/code]

b.代表某个变量对应value的ngx_http_variable_value_t,
该成员一般用于ngx_http_request_s的ngx_http_variable_value_t *variables成员.
ngx_http_request_s的variables成员是用于缓冲所有indexed变量的值,初始化该数组的操作在ngx_http_init_request;
//r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t));

typedef struct {
unsigned len:28;//变量值的长度

unsigned valid:1; //用于标识变量是否有效。
unsigned no_cacheable:1;
unsigned not_found:1;
unsigned escape:1;

u_char *data; //变量对应的值。
} ngx_variable_value_t;



2. 变量的创建流程,以nginx map模块的变量创建过程为例,

首先对于ngx_http_core_variable变量进行简单的演示。

因为nginx的core_variable 是core模块分配的static ngx_http_variables_t数组
即ngx_http_core_variables[];从而大量的创建、注册操作就节省了。
static ngx_http_variable_t ngx_http_core_variables[] = {
{ ngx_string("http_host"), NULL, ngx_http_variable_header,//可知core变量在定义时已经设置了get_handler.
offsetof(ngx_http_request_t, headers_in.host), 0, 0 },
.....
};

其次开始介绍ngx_http_map_block中对于变量的解析.
map配置格式:
map $uri $new {
default http://www.domain.com/home/; //默认值

/aa http://aa.domain.com/; //当$uri匹配/aa时 $new对应的值为http://aa.domain.com/。
/bb http://bb.domain.com/;
/john http://my.domain.com/users/john/;
}

相应的解析函数为ngx_http_map_block()
static char *
ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_map_conf_t *mcf = conf;

char *rv;
ngx_str_t *value, name;
ngx_conf_t save;
ngx_pool_t *pool;
ngx_hash_init_t hash;
ngx_http_map_ctx_t *map;
ngx_http_variable_t *var;
ngx_http_map_conf_ctx_t ctx;
ngx_http_compile_complex_value_t ccv;

if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
mcf->hash_max_size = 2048;
}

if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
mcf->hash_bucket_size = ngx_cacheline_size;

} else {
mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
ngx_cacheline_size);
}

map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
if (map == NULL) {
return NGX_CONF_ERROR;
}

value = cf->args->elts;

ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
//首先是对$uri进行compile相应的value.最终保存在map->value中.

//####################此处首先编译 获取变量值的ngx_http_complex_value_t结构体,最终保存在map->value中
//map $uri$args $new{ }
//此处即解析$uri$args

ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &map->value;//Here,the value is "ngx_http_complex_value_t value;"

if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
//Here,ngx_http_compile_complex_value函数会编译一个code数组在map->value中
//由ngx_http_comile_complex_value函数编译的code,一般由ngx_http_complex_value()函数来获取相应的复杂变量对应的值.
//后面可以看到get_handler对应的ngx_http_map_variable()函数会调用ngx_http_complex_value()获取复杂变量对应的值.
return NGX_CONF_ERROR;
}


//###################此处是编译map指令的
//map $uri$args $new{ }
//此处即解析$new

name = value[2];
name.len--; //Here,skip the '$' character.
name.data++;

//调用添加变量的通用接口ngx_http_add_variable(),该函数将创建ngx_http_variable_t 来代表该变量
//然后将该该ngx_http_variable_t添加至cmcf->variables_keys.keys成员,此时并没有直接添加至cmcf->variables_key.hash_key中
//在ngx_http_block()解析快结束的位置调用ngx_http_variables_init_var()函数对keys的建立静态哈希表即variables_key.hash_key
var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
if (var == NULL) {
return NGX_CONF_ERROR;
}
var->get_handler = ngx_http_map_variable; //设置variable的get_handler
var->data = (uintptr_t) map; //该variable调用get_handler时需要传入的第三个参数。
..........
}


3.变量的获取流程
此处仍然以map模块为例,假如我们在rewrite指令中用到了相应的$foo变量
map $uri $redirect_host{
default www.cc_test.com;
^/redirect/(.*)\.(png)|(gif) {$host}.pic;
^/redirect/(.*)\.(js)|(JS) {$host}.js;
}
rewrite ^/(.*)/redirect/(.*)\.(.*) http://$redirect_host/$2$agrs;

功能主要是根据请求类型的跳转
那么由上可知map模块已经将$redirect_host已经被添加至变量表中,在解析rewrite指令的value部分时,会调用
ngx_http_script_compile()函数来compile rewrite命令的value部分,该函数的核心流程如下:


ngx_int_t
ngx_http_script_compile(ngx_http_script_compile_t *sc)
{
u_char ch;
ngx_str_t name;
ngx_uint_t i, bracket;
//############## 当ngx_http_script_compile_t的value,length,flush的成员为空时,初始化这些成员,
//############## 一般这些成员都是预先分配好空间的
if (ngx_http_script_init_arrays(sc) != NGX_OK) {
return NGX_ERROR;
}
//#############此处sc->source即我们的value string,比如rewrite指令中的"http://$redirect_host/$2$agrs;"
for (i = 0; i < sc->source->len; /* void */ ) {

name.len = 0;
if (sc->source->data[i] == '$') {//表明该串是一个变量名.
if (++i == sc->source->len) {
goto invalid_variable;
}
#if (NGX_PCRE)
{
ngx_uint_t n;
//Means we use $1 $2...$9 to divide the request_uri string to server part
if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {//表明该串为一个capture variable,比如$2
n = sc->source->data[i] - '0';

if (sc->captures_mask & (1 << n)) {
sc->dup_capture = 1;
}
//Mark this variable is state which part
sc->captures_mask |= 1 << n;

if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {//此处添加相应的capture_code handler,用于获取$2对应的value
return NGX_ERROR;
}

i++;

continue;
}
}
#endif
//Here "${variable}.abc.com"
if (sc->source->data[i] == '{') {//当变量名与字符串混合连接时,使用'{}'将变量名与其它字符串分开.
bracket = 1;
if (++i == sc->source->len) {//Must be invalid;
goto invalid_variable;
}
//Here,point to the variable name first char;
name.data = &sc->source->data[i];//指向变量名的起始位置.
} else {
bracket = 0;
name.data = &sc->source->data[i];
}
//Here,we need to traverse the variable string.
//until hit the '}'
for ( /* void */ ; i < sc->source->len; i++, name.len++) {
ch = sc->source->data[i];
if (ch == '}' && bracket) {//If get a right bracket.表示变量结束,
i++;
bracket = 0;
break;
}

if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_') //Means for the blank_space and the other character will terminate the
//variable string.
{
continue;
}//直到遇到非 数字与字母的字符才结束.
break;
if (bracket) {
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
"the closing bracket in \"%V\" "
"variable is missing", &name);
return NGX_ERROR;
}
//Here,we get a variable name!!
if (name.len == 0) {
goto invalid_variable;
}

sc->variables++;//Successfully get a variable
//###############对于普通的变量,添加var_code handler去获取相应的value、value length、index.
if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {
return NGX_ERROR;
}

continue;
}

//##############对于args 参数部分的解析.
if (sc->source->data[i] == '?' && sc->compile_args) {
sc->args = 1;
sc->compile_args = 0;

if (ngx_http_script_add_args_code(sc) != NGX_OK) {
return NGX_ERROR;
}

i++;

continue;
}
//###################此处是普通的常量字符串.
//Here,point to the comm string start position.
name.data = &sc->source->data[i];

while (i < sc->source->len) {

if (sc->source->data[i] == '$') {
break;
}

if (sc->source->data[i] == '?') {

sc->args = 1;

if (sc->compile_args) {
break;
}
}
i++;
name.len++;
}
//Means Get a comm string;
sc->size += name.len;
//################# 将普通的常量字符串保存至code数组中.具体过程查看ngx_http_script_add_copy_code的流程.
if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len)) != NGX_OK)
{
return NGX_ERROR;
}
}
.......
}

ngx_http_script_compile函数的主要过程就是将一个value string进行切割,
如果是变量,则在codes 数组中设置相应的code_handler,该code_handler调用相应的get_handler获取变量的内容,
如果是普通的值,则直接将常量串保存在code数组中,并注册相应的copy_handler,进行相应的拷贝。

ngx_http_script_compile函数的最核心的过程是构造一个code_handler数组,即ngx_http_script_compile_t->values成员。
该成员为一个数组,但是里面的内容是以一条一条的记录的形式存放的。
每条记录都是以handler开头,接其他数据成员,

比如ngx_http_script_add_copy_code()添加的记录是ngx_http_script_copy_code_t;


ngx_http_script_code_pt code; //即拷贝的handler
uintptr_t len; //后续的数据长度
comm_string //真正的数据,

此处注册的handler为ngx_http_script_copy_code(),它的工作即将comm_string拷贝至ngx_http_script_engine_t->pos处。








//if (v[index].get_handler(r, &r->variables[index], v[index].data) == NGX_OK)
//次数get_handler传入的三个参数分别是当前请求(ngx_http_request_t)、
//第二个是当前请求的variables value缓冲数组对应的ngx_http_variable_value_t变量&ngx_http_request_t->variables[index]
//第三个参数是我们在创建该variable变量时,保存在ngx_http_core_main_conf_t->variables[]数组中的v[index].data参数。;
//





get_handler的调用函数主要有两个地方ngx_http_get_variable(),ngx_http_get_indexed_variable()



ngx_http_complex_value_t //很重要的一个结构体,



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值