功能简介

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. 转:如何mac下使用wireshark

    Mac OS Mountain Lion默认是没有安装X11的,而wireshark运行需要x11,因此如果直接安装wireshark而没有安装x11,wireshark不会正常运行. 去苹果主页下载 ...

  2. 制作一个可以滑动操作的 Table View Cell

    本文转载至 https://github.com/nixzhu/dev-blog Apple 通过 iOS 7 的邮件(Mail)应用介绍了一种新的用户界面方案——向左滑动以显示一个有着多个操作的菜单 ...

  3. 【转】Ubuntu下出现Mysql error(2002)的解决方法

    过了一阵子后,为了写分布式作业,重新使用Mysql时,发现虽然启动成功了,但是连接的时候去出现如下错误ERROR 2002 (HY000): Can't connect to local MySQL ...

  4. Construct Binary Tree from Inorder and Postorder Traversal ——通过中序、后序遍历得到二叉树

    题意:根据二叉树的中序遍历和后序遍历恢复二叉树. 解题思路:看到树首先想到要用递归来解题.以这道题为例:如果一颗二叉树为{1,2,3,4,5,6,7},则中序遍历为{4,2,5,1,6,3,7},后序 ...

  5. C# 将cookie写入WebBrowser

    string cookie = ""; foreach (string c in cookie.Split(';')) { string[] item = c.Split('=') ...

  6. An unexpected error occured when contacting the server .

    I logged into to the arcsight command center ,however I found an unexpected error occurred when cont ...

  7. 转:DDR原理详解

    首先,我们先了解一下内存的大体结构工作流程,这样会比较容量理解这些参数在其中所起到的作用.这部分的讲述运用DDR3的简化时序图. DDR3的内部是一个存储阵列,将数据“填”进去,你可以它想象成一张表格 ...

  8. php跳转

    header("Location: http://bbs. lampbrother.net"); header("refresh:0;url=./login.php&qu ...

  9. Hive调优实战

    Hive是将符合SQL语法的字符串解析生成可以在Hadoop上执行的MapReduce的工具. 使用Hive尽量按照分布式计算的一些特点来设计sql,和传统关系型数据库有区别,所以需要去掉原有关系型数 ...

  10. Java Web工作原理(转载)

    知识要点: 1.HTTP协议 2.web服务器的缺陷及其解决方案 3.对Servlet的认识 4.Servlet的主要任务 5.web容器对Servlet的支持包括的内容 HTTP协议---(Hype ...