目录
前言
1、什么是伪变量
2、添加伪变量的两种方法
2.1 添加伪变量$appId场景实例
2.2 使用核心伪变量实现$appId
2.3 使用模块伪变量实现$appId
2.4 使用脚本使用$appId伪变量
小结
前言
伪变量在编写脚本路由逻辑的过程中起到举足轻重的作用,路由逻辑可以通过伪变量获知SIP某个字段的值或者其它变量值。当我们希望在脚本中获知某个值,并使用这个值进行路由逻辑判断、存储等时,如果OpenSIPS没有提供这样的一个值,那这时可能就需要进行开发,添加一个自定义的伪变量来获得该值。
1、什么是伪变量
在使用OpensSIPS的脚本进行请求路由逻辑处理的过程中,我们经常会使用到一些已经预设了值的变量,如
$proto:请求的传输协议类型,如UDP、TCP、WS、WSS
$ci:请求的callid
$fU:From URI中的用户名
$rs:请求响应状态码
而这些变量不是理所当然就存在了的,这些就是OpenSIPS实现了的伪变量(Pseudo-Variable)。在脚本中可以直接调用获得这些伪变量,获取其值:
if ( proto == WS || proto == WSS) setflag(SRC_WS); xlog("$C(xg) callid:$ci from userid:$fU $C(xx)\n");
# $si - 引用消息的IP源地址, $sp - 引用消息的源端口 if ( lb_is_destination("$si","$sp","1") ) { lb_count_call("$si","$sp","1", "pstn"); } onreply_route { if( $rs=="180" || $rs=="183") { xlog("$C(xg) $tU ringing $C(xx)\n"); } } |
2、添加伪变量的两种方法
有两种方法可以实现伪变量,第一种是添加为核心伪变量(core Pseudo-Variable),这种变量使用时是不需要包含其它模块的,所有路由中都可以调用。第二种是隶属于模块的伪变量,在模块中定义、实现,仅当该模块被加载(loadmodule)时才可用。
下面用实例来分别讲解两种定义伪变量的实现。
2.1 添加伪变量$appId场景实例
在我的实际使用场景中,需要获得注册到OpenSIPS上的SIP终端对应的appid(应用ID),并在注册的时候将appid和用户ID等注册信息保存到redis中,像这样:
... cache_raw_query("redis:group1", "HMSET user:$au appId $appId ip $Ri port $Rp"); ... |
但是保存注册信息到redis上的操作是在脚本中实现的,appid这个变量是不存在的,这时就可以考虑添加一个自定义的伪变量。
2.2 使用核心伪变量(core Pseudo-Variable)实现$appId
OpenSIPS所有的核心伪变量都定义在pvar.c文件中的_pv_names_table结构数组中 :
OpenSIPS伪变量是由pv_export_t数据结构来描述的。
下面是pv_export_t结构的定义:
下面是核心伪变量的实现步骤:
第一步:在pvar.h文件中的_pv_type枚举对象中添加一个类型,这里命名为“PVT_APPID”,用来设置pv_export_t伪变量结构中的type类型。
第二步:在_pv_names_table结构数组对象中添加一个新的自定义的伪变量结构。伪变量名为”appId”,变量类型为”PVT_APPID”,获取变量值函数getf的值为”pv_get_appid”,其他设置变量函数为空,因为在脚本中不需要修改这个变量值,所以setf值是空的。
第三步:实现pv_get_appid函数。
static str str_appid = {_pv_appid, 32};
static int pv_get_appid(struct sip_msg *msg, pv_param_t *param,
pv_value_t *res)
{
if (str_appid.s[0] == '\0')
return pv_get_null(msg, param, res);
return pv_get_strval(msg, param, res, &str_appid);
}
(手机查看代码显示不全可左滑)
其中_pv_appid定义在auth_db模块的auth_db.h中,因为这里的实现是appid在注册的时候从数据库中读取userName和password字段用于鉴权的同时也读取出来的。读取出appid后存储到_pv_appid中(数据库读取这里省略,可以参考auth_db模块读取userName和password的代码实现)。
2.3使用模块伪变量实现
模块伪变量的实现是在创建模块的代码结构对象中,加入伪变量的结构对象实现的。上面讲到appid是在注册的时候从数据库中读取出来的,所以这里就在auth_db模块中添加appId伪变量的声明和实现。
第一步,切换到OpenSIPS源码根目录,进入modules/auth_db目录,编辑authdb_mod.c文件,加入声明模块伪变量的pv_export_t结构对象(OpenSIPS模块有很多类型的export结构,下一篇模块开发将详细介绍)。
static pv_export_t pvars[] = {
{str_init("appId"), 1000, pv_get_appid, NULL, NULL, NULL, NULL, 0},
{{0, 0}, 0, 0, 0, 0, 0, 0, 0}
};
伪变量名为“appId”,获取变量值函数getf的值为”pv_get_appid”,其他设置变量函数为空。这和核心伪变量的设置是一样的,包括pv_get_appid函数的实现,区别在于这里不需要区分变量类型,只要这个值不超过核心变量类型的枚举(_pv_type)定义的值就行,这里类型定义的值是1000。从定义pvars是一个数组对象可以看出,可以同时定义多个模块伪变量,这里只定义了一个。
第二步,将伪变量结构数组pvars加入到模块定义结构mod_exports中。
/*
* Module interface
*/
struct module_exports mod_exports = {
"auth_db",
MOD_TYPE_DEFAULT,/* class of this module */
MODULE_VERSION,
DEFAULT_DLFLAGS, /* dlopen flags */
&deps, /* OpenSIPS module dependencies */
cmds, /* Exported functions */
0, /* Exported async functions */
params, /* Exported parameters */
0, /* exported statistics */
0, /* exported MI functions */
pvars, /* exported pseudo-variables */
0, /* extra processes */
mod_init, /* module initialization function */
0, /* response function */
destroy, /* destroy function */
child_init /* child initialization function */
};
这样就定义模块伪变量就定义完成了。pv_get_appid函数和核心伪变量实现一样,_pv_appid对象也是在注册的时候从数据库中读取userName和password字段用于鉴权的同时也读取出来赋值的。
2.4 使用脚本使用$appId伪变量
现在可以在脚本中调用这个伪变量,在注册请求路由逻辑中加入保存注册信息到Redis的路由逻辑,注册时候保存,注销的时候删除掉保存的信息:
if (is_method("REGISTER")){
...
$var(expire_value) = -1;
if(is_present_hf("Expires")){
$var(expire_value) = $(hdr(Expires){s.int});
}else if($ct.fields(expires)){
$var(expire_value) = $(ct.fields(expires){s.int});
}
# > 0 注册,== 0 注销
if ($var(expire_value) > 0){
cache_raw_query("redis:group1",
"HMSET user:$au appId $appId ip $Ri port $Rp");
cache_raw_query("redis:group1",
"EXPIRE user:$au $var(expire_value)");
}else if($var(expire_value) == 0){
cache_raw_query("redis:group1",
"HDEL user:$au appId $appId ip $Ri port $Rp");
}
...
}
需要注意的是,这里例子里只在注册的时候才会设置appId这个伪变量,所以不应该在注册请求路由之外使用这个伪变量,否则很可能将得不到预期的值。
小结
伪变量实现很容易,但是需要小心处理伪变量的赋值以及在脚本中的使用时机,不然得到的值可能是非预期的。在遇到可能需要实现伪变量的需求时,也要权衡是否有必要去实现,有没有更好的方法去实现这个需求,从而减少不必要的修改操作,避免出现风险。
(全文完)
提示:该公众号系列是循序渐进的,如果对该篇涉及到的内容有不清楚的,建议先阅读前几篇内容。
更多可以参考官网文档
http://www.opensips.org/Documentation/Development-Manual