功能简介

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. Zxing二维码精简(竖屏、拉伸处理、扫描框大小和扫描线移动)

    本帖最后由 levil_ad 于 2013-12-30 13:55 编辑 最近没事做了下二维码扫描,用的是ZXing的开源代码,官方源码地址:http://code.google.com/p/zxin ...

  2. 在win7上安装visual c++ 2008 redistributable 发生错误error 1935

     方案一.原来服务"Windows Modules Installer"被禁用了, 启用该服务后, 问题就解决了.  方案二. 1.点开始——在运行框里输入regedit,按回车键 ...

  3. mac异常删除管理员账户恢复操作

    重新启动电脑,同时按下command+s键进入命令行方式 待系统加载完成后顺序输入以下命令: /sbin/mount -uaw rm var/db/.applesetupdone reboot 待系统 ...

  4. Spring HTTP Service

    基于Spring MVC, 使用Http Service Invoke远程调用方法 (参考: http://blog.csdn.net/hanqunfeng/article/details/43031 ...

  5. 数组全排列 knuth 分解质因数

    template<typename T> void swap(T* a, T* b) { T temp = *a; *a = *b; *b = temp; } //数组的全排列 void ...

  6. Unity开发规范(个人习惯,仅供參考)

    近期整理了一下unity里的文件夹使用和脚本上的一些规范,这个看个人习惯,仅供參考 1.unity中的Project文件夹      总体文件夹大致例如以下:            按资源种类分目录. ...

  7. java数据库连接池简单实现

    package cn.lmj.utils; import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import ...

  8. UITableView 自带编辑删除 自己定义button

    一:UITableView 自带编辑删除 1:实现两个方法就可以 #pragma mark   tableView自带的编辑功能 -(void)tableView:(UITableView *)tab ...

  9. px rem css 转换工具

    http://520ued.com/tools/rem mark 一下 貌似还挺好用

  10. 对A-Star寻路算法的粗略研究

    首先来看看完成后的效果: 其中灰色代表路障,绿色是起点和移动路径,红色代表终点   // = openArray[i+1].F) { minNode = openArray[i+1]; } } sta ...