yos*_*aan 3 c varnish varnish-vcl
我开始拿起清漆并在我们的配置中遇到了C代码中的VRT函数的引用(以及网上的示例),我找不到文档(据我所知,我的C知识是不存在的) .这是我能找到的最好的,但它只是原型:http://fossies.org/dox/varnish-4.0.2/vrt__obj_8h.html#a7b48e87e48beb191015eedf37489a290
所以这是我们使用的一个例子(这似乎是来自网络的copypasta,因为我发现它很多次):
C{
#include <ctype.h>
static void strtolower(char *c) {
for (; *c; c++) {
if (isupper(*c)) {
*c = tolower(*c);
}
}
}
}C
sub vcl_recv {
...stuff....
if (req.url ~ "<condition>" && (<another if condition>)) {
C{
strtolower((char *)VRT_r_req_url(sp));
}C
}
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:
是否有文档说明所有这些都做了什么?例如,我也见过几次:
sub detectmobile {
C{
VRT_SetHdr(sp, HDR_BEREQ, "\020X-Varnish-TeraWurfl:", "no1", vrt_magic_string_end);
}C
}
Run Code Online (Sandbox Code Playgroud)
那么这里的HDR_BEREQ和vrt_magic_string_end是什么?
这将是一个很长的答案,因为对你的问题有一点说法.首先,关于你的VCL中的C代码的一些尼特:
strtolower可能是不必要的; 标准vmod具有std.tolower功能.如果你正在运行Varnish 3,你应该使用它.(也就是说,这种情况的存在似乎意味着你可能会使用Varnish 2,所以谁知道呢?)VRT_SetHdr似乎没必要.我没有看到它和之间有任何区别set bereq.http.X-Varnish-TeraWurfl = "no1";我的一些答案可能不太准确,因为不清楚你正在使用的是什么版本的清漆,但我猜
现在,来回答你的问题:
- 什么是sp?它从何而来?它没有在任何地方定义,也没有任何关于它的信息
sp在Varnish中是惯用的意思是会话指针.它是类型struct sess,包含有关正在进行的请求的一些上下文.根据您使用的Varnish版本,这可能有更多或更少的上下文,因此很难真正定义范围.在Varnish 2中,会话包含从工作空间到请求状态(以及介于两者之间)的所有内容.Varnish 4大大分裂了这一点.
我猜你正在使用Varnish 2或Varnish 3.在Varnish 4中,你会绕过一些叫做的东西ctx.
无论如何,从配置的角度来看,你真正需要知道的唯一事情sp是它始终是任何VRT函数的第一个参数.
- VRT_r_req_url做什么?为什么VRT_前缀和r是什么(我也看到VRT_l_函数).它从这个结构获取数据是什么?
VRT代表V CL R un T ime.它是一组在Varnish二进制文件中实现的函数.函数签名和一些不透明结构通过头文件暴露给VCL.VCL编译器使用此头文件以及它从VCL生成的C代码的输出来创建可加载到Varnish中的共享对象.此外,还有一个TCL脚本(它是Varnish 4中的Python),它将不同的VCL内置函数和变量与VRT函数相关联.
的[R和升立场右和左和这与其中一个变量在一个表情评估做.因为VCL不允许任何类型的"复杂"表达式(如加法或减法;除非你将max_restarts设置为无界限值,否则它确实无法接近图灵完成),实际上只有两个地方可以访问变量:在右边 - 手边或左手边.例如:
set req.url = req.url + "/"
Run Code Online (Sandbox Code Playgroud)
将编译为
VRT_l_req_url(sp, VRT_r_req_url(sp), "/", vrt_magic_string_end);
Run Code Online (Sandbox Code Playgroud)
在左侧访问req.url会导致编译器调用VRT_l_req_url,而右侧的访问会导致它使用VRT_r_req_url.
考虑它的一种更简单的方法可能是l表示"设置",r表示"获取"(或"读取",如果您愿意).但它确实意味着左右.
要将其与您的代码段绑定:
strtolower((char *)VRT_r_req_url(sp));
Run Code Online (Sandbox Code Playgroud)
VRT_r_req_url返回const char *表示值的值req.url.正在强制转换此指针char *以删除const限定符.(此转换是您的配置中的错误.)转换指针被发送到strtolower,然后降低字符串的范围.
由于一些原因,这是错误的.VRT_r_req_url给了你一个const char *回,所以你真的不应该修改它.我认为这不会破坏任何东西,但这违反了您给出的API合同.此外,您的写入方式req.url是通过VRT_l_req_url接口 - 而不是直接在您的strtolower实现中.因此,正确的方法是使用std.tolower vmod,或者在会话工作区中创建URL的副本,以修改该副本,然后使用VRT_l_req_url将其存储回来.
另外,strtolower实现不需要if (isupper(*c))检查.此检查仅用于混淆处理器的分支预测器.tolower(3)基本上每个实现都使用无分支查找表,并且不会转换没有小写等效的字符(如数字).
- 所有这些VRT函数是否相似,以获得与C块之外的req.url相同的变量?
是.所有VRT函数都实现函数调用或变量查找.但我认为你的意思是"在C区内".
- 是否有文档说明所有这些都做了什么?例如,我也见过几次:
sub detectmobile {
C{
VRT_SetHdr(sp, HDR_BEREQ, "\020X-Varnish-TeraWurfl:", "no1", vrt_magic_string_end);
}C
}
Run Code Online (Sandbox Code Playgroud)
那么这里的HDR_BEREQ和vrt_magic_string_end是什么?
有一些文档,但相当一部分需要源头潜水.如果您可以说出您正在使用的Varnish版本,我可以为您指出一些可能有助于了解正在发生的事情的文件.
HDR_BEREQ告诉VRT_SetHdr我使用包含将发送到后端的请求的特定工作空间.
vrt_magic_string_end是一个哨兵.基本上所有可以采用字符串参数的函数也可以将一堆字符串连接在一起.Varnish通过对这些函数使用varargs解决了这个问题,并将多个char *参数传递给函数.通常,如果您的函数具有可变数量的参数,这些参数都是指针,那么您只需使用NULL指针来表示不再有可用的参数.但是,将NULL值传递给许多这些函数是完全有效的.vrt_magic_string_end是一个常量指针值,不能与任何其他指针混淆,因此是一种安全的方法,用于确定何时不再向该函数传递参数.
考虑一下这样的log电话:
log req.url + " " + req.http.Wookies + "ha!"
Run Code Online (Sandbox Code Playgroud)
此调用将转换为:
VRT_log(sp, VRT_r_req_url(sp), " ", VRT_GetHdr(sp, HDR_REQ, "\10Wookies:"), "ha!", vrt_magic_string_end);
Run Code Online (Sandbox Code Playgroud)
如果我们没有使用vrt_magic_string_end,而是依赖NULL,我们永远无法弄清楚"哈!" 还需要打印.
无论如何,这里有很多回应.我希望它有用; 如果你有更多,请随时提问.
- 那么C块之外的所有操作实际上只是调用C函数,因此VCL中的所有函数和变量都与VRT函数匹配?
是的,有效.从技术角度来看,VCL并没有真正的变量(或者可以说是功能).从严格意义上讲,它并不是一种真正的编程语言.它只是一种调整Varnish HTTP状态机的语言.
- 在VRT_SetHdr中为什么要指定工作空间但在VRT_r_req_url中却没有?正如我在运行VRT_r_bereq_url以获取后端URL或者我是否需要使用工作空间调用它来获取它,例如VRT_r_req_url(sp,BEREQ)(或者这不是有效的操作,因为你从不查找后端网址)?
- 我如何知道何时需要传递工作空间以及它们都是什么(例如,HDR_BEREQ显然是后端请求标头,但其他工作空间是什么)?
这些答案是相关的,所以我会在一个地方回答它们.
这是因为解析req.url的地方嵌入在函数名中,这是由于VCL编译器的工作方式有些奇怪.在HTTP中,URL实际上不是标题的一部分,但是Varnish就像它一样对待它.类似地,像是beresp.ttl或req.hash_always_miss不是标题.当我们看到的位不是标题时,我们需要特别实现它们.
事实上,找到req.url实施的地方很难,因为一些相当不幸的宏观使用没有任何评论.您对cache_vrt_var.c:64-95感兴趣.
无论如何,标题是动态的,并且在您收到请求之前,您不知道它们将在何处(如果它们存在).当通过任何的各种状态的接口(访问的标题req.http.*,bereq.http.*,beresp.http.*,和resp.http.*),你需要解决他们的具体状态.为了减少代码重复,通过这些方法读取或设置的任何标题分别通过VRT_GetHdr或VRT_SetHdr.因为这些功能对所有VCL状态共享,传递一个提示给他们,告诉他们不管你是在谈论req,bereq,beresp,或resp头.所以你可以想像或许,你有HDR_REQ,HDR_BEREQ,HDR_BERESP,和HDR_RESP.
- 为了学习(忽略有一个vmod)你会介意更新你的帖子以显示实现strtolower函数的最佳方法,避免通过狡猾的强制转换修改const并将不正确的类型传递给tolower功能?
老实说,你不能真正安全地做到这一点,因为VCL编译器给出了一个opaque类型struct sess.如果没有制作VMOD,您可以做的最好的事情是:
#include <ctype.h>
static void
strtolower(char *c)
{
while (*c != '\0) {
*c++ = tolower(*c);
}
}
Run Code Online (Sandbox Code Playgroud)
如果您使用C99支持进行编译,则可以执行以下操作:
C{
#include <ctype.h>
static void
strtolower(const char *c, char *obuf)
{
while (*c != '\0') {
*obuf++ = tolower(*c++);
}
*obuf = '\0';
}
}C
...
if (req.url ~ "[A-Z]") {
C{
const char *url = VRT_r_req_url(sp);
size_t urllen = strlen(url) + 1;
char obuf[urllen];
strtolower(url, obuf, urllen);
VRT_l_req_url(sp, obuf, vrt_magic_str_end);
}C
}
Run Code Online (Sandbox Code Playgroud)
老实说,这个实现也不是很好.当你得到一个很长的URL,并且你不想在VCL中使用malloc时,你可能会冒这个风险.实际的strtolower实现不进行任何边界检查; 它只需要你有一个足够大的缓冲区来容纳字符串.这些都是可以解决的问题,但我真的不想花费大量时间,因为这是错误的做法.这就是VMOD创建的确切原因.
您可以看到标准的strtoupper/strtolower实现有很大的不同:它从工作区预留空间,复制到工作区缓冲区,然后释放它不使用的空间.
(PS我摆脱了未定义的行为注释,因为我意识到我引用的tolower(3)手册页意味着输入必须可以在unsigned char中表示.这是因为tolower(3)采用整数参数;值为传球可能会超出范围.所以这是糟糕的信息,我已经收回了.)