nginx开发_ngx_http_script源码解析
功能简介
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源码解析的更多相关文章
- nginx开发_ngx_palloc源码解析
功能简介 ngx_pool_t是nginx开发中最经常使用到的内存容器.对动态内存的封装,由框架进行创建与释放,模块开发过程中仅需要进行内存申请,不需要关注何时释放.常见的pool对象有: 1. ng ...
- httprunner开发实践&源码解析
上次作业讲解 排错 控制台查看报错信息 打开代理工具,调试脚本 注释掉其他接口,先跑一个接口 pip uninstall httprunner 修复断言100为int型问题 修复两次登陆问题 报告 p ...
- iOS开发SDWebImage源码解析之SDWebImageManager的注解
最近看了两篇博客,写得很不错,关于SDWebImage源码解析之SDWebImageManager的注解: 1.http://www.jianshu.com/p/6ae6f99b6c4c 2.http ...
- iOS开发——GPUImage源码解析
一.基本概念 GPUImage:一个开源的.基于openGL的图片或视频的处理框架,其本身内置了多达120多种常见的滤镜效果,并且支持照相机和摄像机的实时滤镜,并且能够自定义图像滤镜.同时也很方便在原 ...
- nginx开发笔记_ngx_hash源码解析
ngx_hash源码解析 ngx_hash是nginx中的hash表结构,具有以下特点: 静态结构,hash表创建后无法动态添加/删除KV. 采用连续存储方式解决碰撞问题.即出现碰撞的KV存放在连续地 ...
- 【源码解析】凭什么?spring boot 一个 jar 就能开发 web 项目
问题 为什么开发web项目,spring-boot-starter-web 一个jar就搞定了?这个jar做了什么? 通过 spring-boot 工程可以看到所有开箱即用的的引导模块 spring- ...
- 从源码解析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/ ...
- 【Android应用开发】EasyDialog 源码解析
示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...
- odoo开发笔记 -- odoo源码解析
odoo 源码解析:http://blog.csdn.net/weixin_35737303
随机推荐
- hibernater-validator jar包冲突的问题
在引用hibernater-validator jar包时一直抛出异常,在引用带有该包的项目,或者同时在一个项目中使用该包和validator包都会抛出以下异常 最后发现是在Eclipse环境下,不能 ...
- Win7/Win2008下IIS配置Asp网站启用父路径的设置方法(已解决)
Win7/Win2008下IIS配置Asp网站启用父路径的设置方法(已解决) 在Win7/Win2008下IIS配置Asp网站启用父路径的设置方法与win2003下不同,看看下图就知道了.
- asp .net 为图片添加图片水印 .
首先写好一个写入图片水印的类,先创建一个ImageWriter类库 (该类中有包含枚举类型和方法) using System; using System.Collections.Generic; ...
- crtmp Server 开启rtsp服务功能
Crtmp Server 包含了rtsp 服务功能,如果需要一个简单轻量的rtsp服务,Crtmp Server会是不错的选择. 默认情况下,rtsp功能是关闭的,需要在配置文件中打开.window环 ...
- x264 编码数配置
记录项目中用到一组x264快速编码参数配置,具体如下: param->i_frame_reference = 1; param->i_scenecut_threshold = 0; par ...
- oracle insert/update
insert into table (,,) values (,,) where update table set (,)=(select , from ) where
- hdu1878欧拉回路(DFS+欧拉回路)
欧拉回路 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- 有趣的Ruby-学习笔记3
Ruby方法 方法名要以小写字母开头.假设用大写字母开头会被作为常量 (这点非常奇怪) 定义一个无參的方法 def method_name expr.. end 定义一个有參的方法 def metho ...
- OpenKM安装(CentOS6)
OpenKM全称是Open Knowledge Management,是一个DMS(文档管理系统).本文介绍如何在CentOS下安装它.本文的安装程序和资料全部来自OpenKM官网:http://ww ...
- 九度OJ 1085:求root(N, k) (迭代)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1407 解决:523 题目描述: N<k时,root(N,k) = N,否则,root(N,k) = root(N',k).N'为N的 ...