功能简介

nginx中有很多配置项支持以变量的形式存在,在运行时根据实时值进行处理。例如如下配置:

location / {
sub_filter '<a href="http://127.0.0.1:8080/' '<a href="https://$host/';
}

使用到了host变量,每个请求的host变量内容不同,运行的结果也不同。

解析带有变量的配置项,并在运行时求值,就是用通过ngx_http_script.c文件实现的。

使用方法

使用方式相对简单,需要了解2个结构体和2个函数。

typedef struct {
ngx_conf_t *cf;
ngx_str_t *value;
ngx_http_complex_value_t *complex_value;
//...
} ngx_http_compile_complex_value_t; //代码中常用ccv作为变量名 typedef struct {
//...
} ngx_http_complex_value_t; //编译解析带变量的配置,常在配置阶段调用
//ccv->cf和cc->value(带变量配置的字符串)为入参
//ccv->complex_value 为出参,常保存在xxx_conf_t中。
ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv); //将带变量的配置项求值,常在运行阶段调用
//r,val为入参,val通过ngx_http_compile_complex_value()获得
//value为出参,即求值后的具体值。
ngx_int_t ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val, ngx_str_t *value);

使用示例

static char *
ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
//...
ccv.cf = cf;
ccv.value = &value[1];
ccv.complex_value = &pair->match;//编译解析后的变量记录在match中
if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
return NGX_CONF_ERROR;
}
//...
} static ngx_int_t
ngx_http_sub_header_filter(ngx_http_request_t *r)
{
//...
m = &matches[j].match; //m为具体的取值
if (ngx_http_complex_value(r, &pairs[i].match, m) != NGX_OK) {
return NGX_ERROR;
}
//...
}

数据结构

整体上功能比较简单,但在ngx_http_script中设计得比较复杂,个人觉得存在过设计和部分设计冗余的问题。我主要记录核心的东西。

typedef struct {
ngx_conf_t *cf;
ngx_str_t *value;
ngx_http_complex_value_t *complex_value;
} ngx_http_compile_complex_value_t; typedef struct {
ngx_str_t value; //编译前的值,与ccv->value一致
ngx_uint_t *flushes;
void *lengths;
void *values;
//这3个指针是核心,分别指向了计算变量和长度的函数指针。flushes记录了变量的索引。
} ngx_http_complex_value_t; typedef struct {
u_char *ip;
u_char *pos;
//....
} ngx_http_script_engine_t;
//用于求值执行的结构体
//ip是一些列求值函数,常来自cv->values
//pos是存放求值结果的指针 typedef struct {
ngx_conf_t *cf;
ngx_str_t *source; ////编译前的值,与ccv->value一致
ngx_array_t **flushes;
ngx_array_t **lengths;
ngx_array_t **values;
//...
} ngx_http_script_compile_t;
//用于编译解释时的结构体,几个核心指针与cv中的一致。

这个文件中涉及的数据结构还有很多其他的字段,都只在比较少的场景用使用到。抓住以上几个核心字段,代码理解起来就不困难了。

内部算法

算法没有高深的东西,就是把字符串变成一堆函数指针,再求值时依次执行函数。

解析过程中可以关注对变量的解析方式,核心代码如下:

主要处理了$var${var}的场景。关于?做参数的使用方式,实际运用中比较少。为了看方便,我注释掉部分分支的处理

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; //...
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 (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
//...
} if (sc->source->data[i] == '{') {
bracket = 1;
if (++i == sc->source->len) {
goto invalid_variable;
}
name.data = &sc->source->data[i];
} else {
bracket = 0;
name.data = &sc->source->data[i];
} for ( /* void */ ; i < sc->source->len; i++, name.len++) {
ch = sc->source->data[i];
if (ch == '}' && bracket) {
i++;
bracket = 0;
break;
} if ((ch >= 'A' && ch <= 'Z')
|| (ch >= 'a' && ch <= 'z')
|| (ch >= '0' && ch <= '9')
|| ch == '_')
{
continue;
}
break;
} if (bracket) {
//...
return NGX_ERROR;
}
if (name.len == 0) {
goto invalid_variable;
} sc->variables++; if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {
return NGX_ERROR;
}
continue;
}
//...
}
return ngx_http_script_done(sc); invalid_variable:
ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");
return NGX_ERROR;
}

求值部分带代码就更简单了,合适就是执行一圈cv->lengts和cv->values

ngx_int_t
ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,
ngx_str_t *value)
{
size_t len;
ngx_http_script_code_pt code;
ngx_http_script_len_code_pt lcode;
ngx_http_script_engine_t e; //...
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = val->lengths;
e.request = r;
e.flushed = 1;
len = 0;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_script_len_code_pt *) e.ip;
len += lcode(&e);
} value->len = len;
value->data = ngx_pnalloc(r->pool, len);
if (value->data == NULL) {
return NGX_ERROR;
}
e.ip = val->values;
e.pos = value->data;
e.buf = *value;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
*value = e.buf; return NGX_OK;
}

具体执行的函数一般以xxx_var_code和xxx_var_len_code命名,我贴一段变量的代码

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);
//...
}
}
} size_t
ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
{
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->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) {
return value->len;
}
return 0;
}

nginx开发_ngx_http_script源码解析的更多相关文章

  1. nginx开发_ngx_palloc源码解析

    功能简介 ngx_pool_t是nginx开发中最经常使用到的内存容器.对动态内存的封装,由框架进行创建与释放,模块开发过程中仅需要进行内存申请,不需要关注何时释放.常见的pool对象有: 1. ng ...

  2. httprunner开发实践&源码解析

    上次作业讲解 排错 控制台查看报错信息 打开代理工具,调试脚本 注释掉其他接口,先跑一个接口 pip uninstall httprunner 修复断言100为int型问题 修复两次登陆问题 报告 p ...

  3. iOS开发SDWebImage源码解析之SDWebImageManager的注解

    最近看了两篇博客,写得很不错,关于SDWebImage源码解析之SDWebImageManager的注解: 1.http://www.jianshu.com/p/6ae6f99b6c4c 2.http ...

  4. iOS开发——GPUImage源码解析

    一.基本概念 GPUImage:一个开源的.基于openGL的图片或视频的处理框架,其本身内置了多达120多种常见的滤镜效果,并且支持照相机和摄像机的实时滤镜,并且能够自定义图像滤镜.同时也很方便在原 ...

  5. nginx开发笔记_ngx_hash源码解析

    ngx_hash源码解析 ngx_hash是nginx中的hash表结构,具有以下特点: 静态结构,hash表创建后无法动态添加/删除KV. 采用连续存储方式解决碰撞问题.即出现碰撞的KV存放在连续地 ...

  6. 【源码解析】凭什么?spring boot 一个 jar 就能开发 web 项目

    问题 为什么开发web项目,spring-boot-starter-web 一个jar就搞定了?这个jar做了什么? 通过 spring-boot 工程可以看到所有开箱即用的的引导模块 spring- ...

  7. 从源码解析Nginx对 Native aio支持_运维_youbingchen的博客-CSDN博客 https://blog.csdn.net/youbingchen/article/details/51767587

    从源码解析Nginx对 Native aio支持_运维_youbingchen的博客-CSDN博客 https://blog.csdn.net/youbingchen/article/details/ ...

  8. 【Android应用开发】EasyDialog 源码解析

    示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...

  9. odoo开发笔记 -- odoo源码解析

    odoo 源码解析:http://blog.csdn.net/weixin_35737303

随机推荐

  1. 深度排序与alpha混合 【转】

      翻译:李现民 最后修改:2012-07-03 原文:Depth sorting alpha blended objects 先说个题外话,本来我想回答在 Creators Club论坛上的一个常见 ...

  2. qt实现又一次登录

    1.需求 须要实现程序操作过程中的又一次启动,即常常说的又一次登录功能 2.解决 在main函数中检測exec的返回值决定是关闭还是重新启动.使用注冊函数atexit(relogin)来实现这个功能 ...

  3. 转: 写给想成为前端工程师的同学们 (from 360前端团队)

    转自:     http://www.75team.com/post/to-be-a-good-frontend-engineer.html 前端工程师是做什么的? 前端工程师是互联网时代软件产品研发 ...

  4. C++ 继承与接口 知识点 小结(一)

    [摘要] 要求理解覆盖.重载.隐藏的概念与相互之间的差别.熟记类继承中对象.函数的訪问控制:掌握虚函数.虚函数表.虚函数指针的联系:理解区分虚函数和虚继承在虚方法.虚指针在空间分配上的重点与难点:熟练 ...

  5. find and xargs

    调整搜索深度 -mandepth 搜索当前目录,而不进入子目录: find . -maxdepth 0 -name "debug*" Linux中find常见用法示例 ·find  ...

  6. IOS开发之Iphone和Ipad应用程序图标和启动动画

    本文转载至 http://blog.csdn.net/yesjava/article/details/8782060 当我们用xcode开发iphone和ipad应用程序的时候,我们可以用一下表中所显 ...

  7. bc - An arbitrary precision calculator language

    bc(1) General Commands Manual bc(1) NAME bc - An arbitrary precision calculator language SYNTAX bc [ ...

  8. ubuntu下配置rails环境遇到的错误

    1.Could not find gem 'sqlite3 (>= 0)' in any of the gem sources listed in your Gemfile 解决:sudo ge ...

  9. java 图形化界面 布局管理器

    package Layout; import java.awt.*; import javax.swing.*; public class MyBorderLayout extends JFrame{ ...

  10. apache配置文件详解及虚拟主机的搭建

    1.404跳转: <IfModule dir_module>    DirectoryIndex index.php index.html /error.php</IfModule& ...