在nginx中变量引擎机制随处可见,今天来分析一下这个机制。拿一个比较容易理解的指令set进行分析。
简单模式:set $name "HELLONGX";
对于$name的解析,就是把name作为变量加入到全局的变量表中,这个比较容易理解,今天暂不做分析。
主要分析这个“HELLONGX”是如何应用的。
在配置解析阶段调用如下函数:
if (ngx_http_rewrite_value(cf, lcf, &value[2]) != NGX_CONF_OK) {
return NGX_CONF_ERROR;
}
在上述函数中,会n = ngx_http_script_variables_count(value);调用该函数计算变量值中是否存在变量.如果不存在val = ngx_http_script_start_code(cf->pool, &lcf->codes,sizeof(ngx_http_script_value_code_t)); 获取对应结构体。
变量的赋值如下:
val->code = ngx_http_script_value_code;
val->value = (uintptr_t) n;
val->text_len = (uintptr_t) value->len;
val->text_data = (uintptr_t) value->data;
上述设置完成之后,仅仅是对value获取的方法,最后还需要加一个函数,保证值可以被获取
vcode = ngx_http_script_start_code(cf->pool, &lcf->codes,sizeof(ngx_http_script_var_code_t));
if (vcode == NULL) {
return NGX_CONF_ERROR;
}
vcode->code = ngx_http_script_set_var_code;
vcode->index = (uintptr_t) index; //index为全部变量数组中的下标
变量如何使用:
在ngx_http_rewrite_handler(ngx_http_request_t *r)rewrite的回调函数中,检查rlcf->codes是否赋值过,如果赋值 执行脚本引擎。
e->sp = ngx_pcalloc(r->pool, rlcf->stack_size * sizeof(ngx_http_variable_value_t));
if (e->sp == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
//e->ip 为char*类型,直接指向elts的起始地址
e->ip = rlcf->codes->elts;
e->request = r;
e->quote = 1;
e->log = rlcf->log;
e->status = NGX_DECLINED;
while (*(uintptr_t *) e->ip) {
code = *(ngx_http_script_code_pt *) e->ip; //在配置解析时 ngx_http_script_value_code_t 结构体
//的第一个变量就是回调函数
code(e);
}
===================
上面的code其实调用的回调函数为ngx_http_script_value_code
void
ngx_http_script_value_code(ngx_http_script_engine_t *e) //获取变量值
{
ngx_http_script_value_code_t *code;
code = (ngx_http_script_value_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_value_code_t);//偏移到下一个结构体的开始位置
//将保存的变量放入到sp中
e->sp->len = code->text_len;
e->sp->data = (u_char *) code->text_data;
//sp偏移加一
e->sp++;
}
void
ngx_http_script_set_var_code(ngx_http_script_engine_t *e) //保存变量值
{
ngx_http_request_t *r;
ngx_http_script_var_code_t *code;
code = (ngx_http_script_var_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_code_t);
r = e->request;
e->sp--;
r->variables[code->index].len = e->sp->len;
r->variables[code->index].valid = 1;
r->variables[code->index].no_cacheable = 0;
r->variables[code->index].not_found = 0;
r->variables[code->index].data = e->sp->data; //把上一次执行的回调函数获取的值,在这里赋值
}
复杂模式:set $name "$value1-$value2-hellongx";
对于$name的解析,同上,本次暂不做分析。
主要分析这个“$value1-$value2-hellongx”是如何应用的。
首先通过n = ngx_http_script_variables_count(value);确定值域中变量的个数,如果大于0,则为复合模式。
complex = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(ngx_http_script_complex_value_code_t));
if (complex == NULL) {
return NGX_CONF_ERROR;
}
complex->code = ngx_http_script_complex_value_code; //同简单模式一样,该回调会获取具体值
complex->lengths = NULL;
//下面函数块是将 值域中进行拆分处理
sc.cf = cf;
sc.source = value;
sc.lengths = &complex->lengths;
sc.values = &lcf->codes;
sc.variables = n;
sc.complete_lengths = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
//值域拆分函数
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;
//主要对sc->lengths 和 sc->values进行内存空间的分配 会根据变量的个数
if (ngx_http_script_init_arrays(sc) != NGX_OK) {
return NGX_ERROR;
}
for (i = 0; i < sc->source->len; /* void */ ) {
//循环分析“$value1-$value2-hellongx”值
//本示例中 先分析$value, 通过ngx_http_script_add_var_code(sc, &name)添加回调函数
{
code = ngx_http_script_add_code(*sc->lengths,sizeof(ngx_http_script_var_code_t), NULL);
if (code == NULL) {
return NGX_ERROR;
}
//计算变量值的长度
code->code = (ngx_http_script_code_pt) (void *)ngx_http_script_copy_var_len_code;
code->index = (uintptr_t) index;
code = ngx_http_script_add_code(*sc->values,sizeof(ngx_http_script_var_code_t), &sc->main);
if (code == NULL) {
return NGX_ERROR;
}
//计算变量值
code->code = ngx_http_script_copy_var_code;
code->index = (uintptr_t) index;
}
}
//最后加上空指针表示结束
return ngx_http_script_done(sc);
接下来看一下变量如何生效
在ngx_http_rewrite_handler(ngx_http_request_t *r)rewrite的回调函数中,检查rlcf->codes是否赋值过,如果赋值 执行脚本引擎。
e->sp = ngx_pcalloc(r->pool, rlcf->stack_size * sizeof(ngx_http_variable_value_t));
if (e->sp == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
//e->ip 为char*类型,直接指向elts的起始地址
e->ip = rlcf->codes->elts;
e->request = r;
e->quote = 1;
e->log = rlcf->log;
e->status = NGX_DECLINED;
while (*(uintptr_t *) e->ip) {
code = *(ngx_http_script_code_pt *) e->ip; //在配置解析时 ngx_http_script_value_code_t 结构体
//的第一个变量就是回调函数
code(e);
}
===================
第一个code的回调是
void
ngx_http_script_complex_value_code(ngx_http_script_engine_t *e)
{
size_t len;
ngx_http_script_engine_t le;
ngx_http_script_len_code_pt lcode;
ngx_http_script_complex_value_code_t *code;
code = (ngx_http_script_complex_value_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_complex_value_code_t);
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script complex value");
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
le.ip = code->lengths->elts;
le.line = e->line;
le.request = e->request;
le.quote = e->quote;
/*这里的主要工作是遍历lengths,计算所有变量的长度,然后申请内存*/
for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
lcode = *(ngx_http_script_len_code_pt *) le.ip;
}
e->buf.len = len;
e->buf.data = ngx_pnalloc(e->request->pool, len);
if (e->buf.data == NULL) {
e->ip = ngx_http_script_exit;
e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
return;
}
e->pos = e->buf.data;
e->sp->len = e->buf.len;
e->sp->data = e->buf.data;
e->sp++;
}
接下来遍历执行code 也就是rlcf->codes下的函数
变量值:
void
ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
{
u_char *p;
ngx_http_variable_value_t *value;
ngx_http_script_var_code_t *code;
code = (ngx_http_script_var_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_code_t);
if (!e->skip) {
if (e->flushed) {
value = ngx_http_get_indexed_variable(e->request, code->index);
} else {
value = ngx_http_get_flushed_variable(e->request, code->index);
}
if (value && !value->not_found) {
p = e->pos;
e->pos = ngx_copy(p, value->data, value->len);
}
}
}
固定值:
void
ngx_http_script_copy_code(ngx_http_script_engine_t *e)
{
u_char *p;
ngx_http_script_copy_code_t *code;
code = (ngx_http_script_copy_code_t *) e->ip;
p = e->pos;
if (!e->skip) {
e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t),
code->len);
}
e->ip += sizeof(ngx_http_script_copy_code_t)
+ ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
"http script copy: \"%*s\"", e->pos - p, p);
}
最后一步:将具体值保存在全局变量数组中
void
ngx_http_script_set_var_code(ngx_http_script_engine_t *e) //保存变量值
{
ngx_http_request_t *r;
ngx_http_script_var_code_t *code;
code = (ngx_http_script_var_code_t *) e->ip;
e->ip += sizeof(ngx_http_script_var_code_t);
r = e->request;
e->sp--;
r->variables[code->index].len = e->sp->len;
r->variables[code->index].valid = 1;
r->variables[code->index].no_cacheable = 0;
r->variables[code->index].not_found = 0;
r->variables[code->index].data = e->sp->data; //把上一次执行的回调函数获取的值,在这里赋值
}
========================
rewrite return if 等指令 大同小异