nginx脚本原理if指令实现详解
之前的文章我们探讨了nginx的变量,接着就是脚本原理,也就是复杂变量,理解了前面的实现原理,接下来了解if,break,return,set就要简单多。
指令有不少,没必要全部探讨,了解了其中之一即可,实现基本原理都一样,实现方式大同小异。理解了指令实现原理,我们就可以开发属于自己的配置指令。
我们以if指令为例,配置如下
if($remote=127.0.0.1){ #注:是= 不是==
return 200 'you request is from local';
}
以此来分析nginx是如何编译(翻译)该指令,并如何执行的。
(题外话,我的源码取自angie,nginx版本为1.25.4)
其脚本基本原理不变:
将指令翻译成一个个执行单元,然后依次执行每个单元
其指令存放在ngx_http_rewrite_loc_conf_t的code数组中,后续是否有指令需要执行也是判断此数组是否为空
if指令的实现源码在 ngx_http_rewrite_module.c中,(此模块在http rewrite阶段实现,为什么在此阶段实现可以自行google或bing,但是确实有必要去了解下)
编译:
我们先看if的配置解析函数,一开始就是重新建立了一个loc_conf,至于为什么就是上面黑字提到的。
其中的ngx_http_rewrite_if_condition则是处理和编译if($remote=127.0.0.1)这个条件字符串
大致流程是
找出变量调用ngx_http_rewrite_variable生成其code_t
找到=号后的值,调用ngx_http_rewrite_value生成其code_t
最后生成=号的code_t和if的code_t
1.首先找出表达式中变量 remote和 值 127.0.0.1,并顺带判断表达式的合法性
2.调用ngx_http_rewrite_variable为变量remote生成值计算的code_t,code_t取自上面说的code数组,其执行函数为ngx_http_script_var_code。跟之前的复杂变量不同的是,这里不需要计算变量长度。
3.提取=号后面的常量值或变量或复杂变量,我们看处理函数ngx_http_rewrite_value的源码
static char *
ngx_http_rewrite_value(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,ngx_str_t *value)
{ngx_int_t n;ngx_http_script_compile_t sc;ngx_http_script_value_code_t *val;ngx_http_script_complex_value_code_t *complex;n = ngx_http_script_variables_count(value);//获取变量数量if (n == 0) {//按常量处理,常量值使用val = ngx_http_script_start_code(cf->pool, &lcf->codes,sizeof(ngx_http_script_value_code_t));if (val == NULL) {return NGX_CONF_ERROR;}n = ngx_atoi(value->data, value->len);if (n == NGX_ERROR) {n = 0;}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;//保存常量值首地址return NGX_CONF_OK;}//下面走复杂变量的编译逻辑,前面文章有详述,这不再解析了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;ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));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;}return NGX_CONF_OK;
}
函数也比较简单,=号后面的条件是常量还是变量(或复杂变量),如果是常量直接生成ngx_http_script_value_code_t,存放常量的值和长度,执行函数为ngx_http_script_value_code
然后就是为运算符=,生成了一个code_t ,其执行函数为ngx_http_script_equal_code
最后为if生成一个ngx_http_script_if_code_t,其执行函数是ngx_http_script_if_code
到这里的,我们配置示例中的if指令就算编译完成了。
执行:
从上面的编译不知道大家是否能看出或体会一点点"味道",熟悉函数调用的可能会体会到似曾相识的感觉。有一种压栈的感觉,先把参数和其值压栈,再压运算符=,最后再压入if指令。
接下来我们看执行了,我们看ngx_http_rewrite_handler函数
首先是看有没有需要执行的指令,即codes数组是否为空。
如果有,则生成ngx_http_script_engine_t来执行之前编辑好的指令集。
e->sp = ngx_pcalloc(r->pool,
rlcf->stack_size * sizeof(ngx_http_variable_value_t));
与前面复杂变量不同的是,这里会为engine_t中的sp分配“栈”空间,栈大小为 rlcf->stack_size(这个大小是固定的,虽然在merge有合并,但是未提供配置,固定是10),生成可以存储10个变量值的空间(类似cpu的sp寄存器)。看到这应该有点相似感觉了吧。
engine_t的ip类似cpu的指令寄存器,sp类似堆栈寄存器,指令执行的结果存放在sp中。前面的复杂变量只用到了ip,因此未做解析。
下面看执行,也是一样的如下
while (*(uintptr_t *) e->ip) {
code = *(ngx_http_script_code_pt *) e->ip;//取当前指令code_t
code(e); //执行指令函数
}
然后我们逐个来看编译生成的code_t的执行函数
1.执行remote变量的code_t,执行函数为ngx_http_script_var_code,计算(获取)出remote的值
void
ngx_http_script_var_code(ngx_http_script_engine_t *e)
{ngx_http_variable_value_t *value;ngx_http_script_var_code_t *code;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,"http script var");code = (ngx_http_script_var_code_t *) e->ip;//取当前code_te->ip += sizeof(ngx_http_script_var_code_t);//ip偏移到下个code_tvalue = ngx_http_get_flushed_variable(e->request, code->index);//计算变量值if (value && !value->not_found) {ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,"http script var: \"%v\"", value);*e->sp = *value; //值结果存放到sp中,e->sp++; //sp偏移到下个位置return;}*e->sp = ngx_http_variable_null_value;e->sp++;
}
2.执行等号后的常量值的code_t,执行函数为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;//获取当前code_te->ip += sizeof(ngx_http_script_value_code_t);//ip偏移到下个code_te->sp->len = code->text_len;//由于此code_t是常量,其值直接存入sp中e->sp->data = (u_char *) code->text_data;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,"http script value: \"%v\"", e->sp);e->sp++;//sp偏移到下个位置
}
3.执行等号code_t,执行函数ngx_http_script_equal_code
void ngx_http_script_equal_code(ngx_http_script_engine_t *e)
{ngx_http_variable_value_t *val, *res;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,"http script equal");e->sp--; //sp回退val = e->sp; //取到值res = e->sp - 1;//取变量e->ip += sizeof(uintptr_t);//判断变量和值是否相等if (val->len == res->len&& ngx_strncmp(val->data, res->data, res->len) == 0){*res = ngx_http_variable_true_value;//相等则设置为true值,将remote的值设置为truereturn;}ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,"http script equal: no");*res = ngx_http_variable_null_value;//不等则置为空值
}
4.最后执行if指令的code_t,执行函数ngx_http_script_if_code
void ngx_http_script_if_code(ngx_http_script_engine_t *e)
{ngx_http_script_if_code_t *code;code = (ngx_http_script_if_code_t *) e->ip;//取if_codengx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,"http script if");e->sp--;//这里为什么还要回退呢,前面的等号运算符的执行回退了一次,执行了值的sp,再回退一次,指 //向了remote的spif (e->sp->len && (e->sp->len != 1 || e->sp->data[0] != '0')) {if (code->loc_conf) {e->request->loc_conf = code->loc_conf;ngx_http_update_location_config(e->request);//这里需要更新location}//第一个值有效,则 当前判断成功,指向下个指令,即if()后,{}里面的指令,在这里就是指向 //return的code_te->ip += sizeof(ngx_http_script_if_code_t);return;}ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,"http script if: false");e->ip += code->next;//
}
整个if的执行就到此结束了,接下来要执行的就是我们if条件成立后,{}内部的指令了。
总结如下:
编译:
1.生成运算符等号前变量的code_t,(运算符前面的必须是变量,源码就是这样实现的),
2.生成运算符后的值code_t,值可以是常量,变量,复杂变量。
3.生成运算符的code_t
4.生成if的code_t
执行:
逐个执行code的函数,最终结果的处理逻辑是由if_code_t执行函数来完成的。
但是欲彻底理解,就如我前面提到的必须,了解这些指令为什么要在rewrite阶段,而不其他阶段,nginx的框架是如此设计的,具体的原因也不是几句话能说清楚的,文章篇幅有限,本文直将if指令的实现,其他的自行google和bing
在此感谢大家的关注和点赞,若有描述不妥或不正确不准确的希望评论区指正,感谢~
相关文章:
nginx脚本原理if指令实现详解
之前的文章我们探讨了nginx的变量,接着就是脚本原理,也就是复杂变量,理解了前面的实现原理,接下来了解if,break,return,set就要简单多。 指令有不少,没必要全部探讨,了解了其中之一…...
数据提取与治理:企业数字化转型的双引擎
在当今数字化浪潮中,企业正面临着前所未有的挑战和机遇。为了在这场变革中立于不败之地,数字化转型成为了企业不可或缺的战略选择。而在数字化转型的众多关键要素中,数据提取与治理技术无疑扮演着至关重要的角色,它们如同双引擎一…...
Java8 新特性 记录【持续更新】
目录 一、Stream 相关 1、findFirst 方法 二、Optional 1、如何构造Optional 2、ifPresent 方法 一、Stream 相关 1、findFirst 方法 Stream的findFirst方法在此流中查找第一个元素作为Optional。 如果流中没有元素,findFirst返回空的Optional。 如果流没…...
Protobuf详解及入门指南
Protobuf详解及入门指南 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!在分布式系统和跨平台通信中,高效、轻量的序列化协议尤为重要。Google的Pro…...
[Java基本语法] 逻辑控制与方法
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏:🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 🧀线程与…...
新手教学系列-基础知识(SSH使用)
基础知识(SSH使用) 什么是ssh Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境[1]。SSH通过在网络中创建安全隧道来实现SSH客户端与服务器之间的连接[2]。虽然任何网络服务都可以通过SSH实现安全传输,SS…...
如何通过细节处理,让展馆展示效果倍增?
如今,企业展览馆已经成为企业树立形象、产品推广的重要平台,他们利用当下流行的多媒体技术,精心设计出一个展览馆,不仅能够吸引观众的眼球,还能够让企业品牌形象更加突出,不过想要制作出一个优质的企业展览…...
汽车IVI中控开发入门及进阶(二十九):i.MX6
前言: i.MX 6双/6Quad处理器集成多媒体应用处理器,是不断增长的多媒体产品系列的一部分,提供高性能处理,并针对最低功耗进行了优化。 i.MX 6Dual/6Quad处理器采用先进的quad-ArmCortex-A9内核,运行速度高达800 MHz,包括2D和3D图形处理器、1080p视频处理和集成电源管理。…...
2024-Pop!_OS新版本,新桌面环境的消息
原文:A Blog to Satisfy Your Monthly COSMIC Fix(es) - System76 Blog Pop!_OS开发团队正在为他们的发行版开发一个定制桌面。这个新的桌面环境被称为COSMIC,是用Rust语言编写的,超快的COSMIC应用商店几乎已经实现!alpha版本只剩下一些次要…...
三分钟了解链动3+1模式
在电商领域的营销策略中,链动31模式以其独特的魅力和优势,吸引了众多商家的目光。下面,我们将对这一模式进行深度剖析,并探讨其相较于链动21模式的优势所在。 一、身份设置与奖励机制 链动31模式在身份设置上分为三种࿱…...
加密excel(Python)
文章目录 一、EXCEL加密 一、EXCEL加密 import randomfrom win32com.client import Dispatchdef random_password(length20):默认返回20位随机密码key ""characters "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"for i in range(l…...
解决Unity-2020 安卓异形屏黑边
背景 Unity 2020.3.17 版本开发的游戏,打apk包,发现两个问题 如图下午所示,实体白色导航栏,阻挡了整个安卓UI界面,难看还影响美观。 安卓系统 12-13 版本手机,异形屏。一侧安全区黑边遮挡,占空间…...
python-给你比个五彩斑斓的❤️
import numpy as np import matplotlib.pyplot as plt from matplotlib.collections import LineCollectiont np.linspace(0, 2 * np.pi, 1000) x 16 * np.sin(t)**3 y 13 * np.cos(t) - 5 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(4 * t)# 创建一个颜色序列 colors …...
【Go】使用Go语言实现AES CBC No Padding加密和解密
冷雨悄悄停吧 天真的心因为你 那管多风雨天仍和你一起 告诉你我其实多么的想你 其实我我真的爱着你 🎵 蒋明周《真的爱着你》 引言 高级加密标准(AES)是一种广泛使用的加密算法。它可以工作在多种模式下,最…...
安装VS Code 提示This User Installer is not meant to be run as an Administrator问题
目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 在vs code官网(https://code.visualstudio.com/)下载安装包,显示如下提示信息: This User Installer is not meant to be run as an Administrator.…...
keepalived服务详解与实验 基于centos8
目录 keepalivedHA简介常用的高可用软件keepalived简介 keepalived常用模块keepalived功能简介keepalived常用文件keepalived配置文件详解keepalived实验1-上手环境准备安装服务主配置文件修改启动服务效果查看 keepalived脑裂1. 脑裂现象简介2. 脑裂的原因3. 脑裂的预防和解决…...
vue技巧(十)全局配置使用(打包后可修改配置文件)
1、背景 vue打包目前主流用的有webpack和vite两种,默认用的webpack。(二者的区别大家可以各自上网查,我没用过vite,所以不过多介绍)vue通过webpack打包后,源码会被压缩,但一些关键配置可…...
计算机网络 —— 运输层(运输层概述)
计算机网络 —— 运输层(运输层概述) 运输层运输层端口号复用分用复用(Multiplexing)分用(Demultiplexing) 常用端口号页面响应流程 我们今天进入到运输层的学习: 运输层 我们之前学习的物理层…...
BKP备份寄存器RTC实时时钟
BKP备份寄存器&RTC实时时钟 VDDA和VSSA是内部模拟部分的电路 VDD和VSS_1、2、3是内部数字电路的供电。系统以VDD开头的电源都是主电源。在正常使用STM32时,全部需要接到3.3v电源上。 VBAT备用电池供电引脚,如使用STM32内部的BKP和RTC,引…...
基于协同过滤算法的电影推荐
基于协同过滤算法的电影推荐 电影推荐系统使用了基于**协同过滤(Collaborative Filtering)的算法来生成推荐。具体来说,使用了基于用户的协同过滤(User-Based Collaborative Filtering)**算法,步骤如下&am…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
2.2.2 ASPICE的需求分析
ASPICE的需求分析是汽车软件开发过程中至关重要的一环,它涉及到对需求进行详细分析、验证和确认,以确保软件产品能够满足客户和用户的需求。在ASPICE中,需求分析的关键步骤包括: 需求细化:将从需求收集阶段获得的高层需…...
