1、主要结构说明
1.1、导出参数
模块对外提供的指定相关参数入口,主要kamailio脚本加载模块时指定初始化参数
对应结构如下:
typedef struct param_export {
char* name; /**< null terminated param. name */
modparam_t type; /**< param. type */
void* param_pointer; /**< pointer to the param. memory location */
} param_export_t;
参数说明:
name:导出到脚本中对应的参数名称
type:参数类型
param_pointer:程序内存储对应参数的指针
1.2、导出方法
模块对外提供的函数调用入口,主要是kamailio脚本处理对应业务时调用对应模块函数实现具体业务逻辑。
对应结构如下:
typedef struct cmd_export {
char* name; /**< null terminated command name */
cmd_function function; /**< pointer to the corresponding function */
int param_no; /**< number of parameters used by the function */
fixup_function fixup; /**< pointer to the function called to "fix" the
parameters */
free_fixup_function free_fixup; /**< function called to free the "fixed"
parameters */
unsigned int flags; /**< Function flags */
} cmd_export_t;
参数说明:
name:导出到脚本中对应的方法名
function:模块内对应的处理函数,一般函数返回值等于0表示成功,小于0表示失败
param_no:方法对应的参数个数
fixup:解析参数的方法
free_fixup:释放参数资源的方法
flag:描述导出方法可以在脚本内的哪段路由中被执行
flag可选值:
#define REQUEST_ROUTE (1 << 0)
#define FAILURE_ROUTE (1 << 1)
#define TM_ONREPLY_ROUTE (1 << 2)
#define BRANCH_ROUTE (1 << 3)
#define ONSEND_ROUTE (1 << 4)
#define ERROR_ROUTE (1 << 5)
#define LOCAL_ROUTE (1 << 6)
#define CORE_ONREPLY_ROUTE (1 << 7)
#define BRANCH_FAILURE_ROUTE (1 << 8)
#define ONREPLY_ROUTE (TM_ONREPLY_ROUTE|CORE_ONREPLY_ROUTE)
#define ONEVENT_ROUTE (1 << 9)
#define EVENT_ROUTE (REQUEST_ROUTE|ONEVENT_ROUTE)
#define ANY_ROUTE (0xFFFFFFFF)
1.3、导出模块
对应脚本内调用的模块信息
对应结构如下:
typedef struct module_exports {
/**< null terminated module name */
char* name;
/**< flags for dlopen */
unsigned int dlflags;
/**< null terminated array of the exported commands (config functions)*/
cmd_export_t* cmds;
/**< null terminated array of the exported module parameters */
param_export_t* params;
/**< null terminated array of exported rpc methods */
rpc_export_t* rpc_methods;
/*!< null terminated array of the exported module items (pseudo-variables) */
pv_export_t* pv_items;
/**< function used for responses, returns yes or no; can be null */
response_function response_f;
/**< Initialization function */
init_function init_mod_f;
/**< function called by all processes after the fork */
child_init_function init_child_f;
/**< function called when the module is "destroyed" (on server shut down) */
destroy_function destroy_mod_f;
} module_exports_t;
主要参数说明:
name:模块名
cmds:导出的方法
params:导出的参数
init_mod_f:初始化函数
init_child_f:模块在每个子进程中的初始化函数
destroy_mod_f:模块销毁时的处理函数
2、http服务模块编写
2.1、编写模块基本结构
static str test_mod_uri = str_init("/self/test_mod");
static int w_test_dispatch(sip_msg_t* msg);
static int w_test_check_uri(sip_msg_t* msg);
static int mod_init(void);
static void mod_destroy(void);
//需要用http_api接口对收到的请求做响应操作
xhttp_api_t xhttp_api;
int int_param = 0;
str str_param = STR_NULL;
void* testInit = NULL;
/* module commands */
static cmd_export_t cmds[] = {
{"test_dispatch", (cmd_function)w_test_dispatch, 0, 0, 0, REQUEST_ROUTE|EVENT_ROUTE},
{"test_check_uri",(cmd_function)w_test_check_uri,0, 0, 0, REQUEST_ROUTE|EVENT_ROUTE},
{ 0, 0, 0, 0, 0, 0}
};
static param_export_t params[]={
{"test_int_param", INT_PARAM, &int_param},
{"test_str_param", PARAM_STR, &str_param},
{0, 0, 0}
};
struct module_exports exports = {
"xhttp_self",
DEFAULT_DLFLAGS, /* dlopen flags */
cmds,
params,
0, /* exported RPC methods */
0, /* exported pseudo-variables */
0, /* response function */
mod_init, /* module initialization function */
0, /* per child init function */
mod_destroy /* destroy function */
};
static int mod_init(void)
{
LM_DBG("init\n");
/* bind the XHTTP API */
if (xhttp_load_api(&xhttp_api) < 0) {
LM_ERR("cannot bind to XHTTP API\n");
return -1;
}
testInit = malloc(10000);
return 0;
}
static void mod_destroy(void)
{
LM_DBG("cleaning up\n");
free(testInit);
}
static int ki_test_dispatch(sip_msg_t* msg)
{
int ret = 0;
if(msg==NULL) {
LM_ERR("No message\n");
return -1;
}
if(!IS_HTTP(msg)) {
LM_DBG("Got non HTTP msg\n");
return NONSIP_MSG_PASS;
}
ret = self_handle(msg);
if (ret < 0) {
return -1;
}
return 0;
}
static int w_test_dispatch(sip_msg_t* msg)
{
return ki_test_dispatch(msg);
}
static int w_test_check_uri(sip_msg_t* msg)
{
if(msg==NULL) {
LM_ERR("No message\n");
return -1;
}
str *uri = &msg->first_line.u.request.uri;
LM_DBG("URI: %.*s\n", uri->len, uri->s);
if (0 == strncmp(uri->s, test_mod_uri.s, test_mod_uri.len))
{
LM_DBG("URI matches: %.*s\n", uri->len, uri->s);
/* Return True */
return 1;
}
/* Return False */
LM_DBG("URI does not match: %.*s (%.*s)\n", uri->len, uri->s, test_mod_uri.len, test_mod_uri.s);
return -1;
}
/**
*
*/
/* clang-format off */
static sr_kemi_t sr_kemi_xhttp_self_exports[] = {
{ str_init("xhttp_self"), str_init("dispatch"),
SR_KEMIP_INT, ki_test_dispatch,
{ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE,
SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
},
{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
};
/* clang-format on */
/**
*
*/
int mod_register(char *path, int *dlflags, void *p1, void *p2)
{
sr_kemi_modules_add(sr_kemi_xhttp_self_exports);
return 0;
}
2.2、http请求处理入口
//处理入口
int self_handle(sip_msg_t* msg)
{
str uri = {0, 0};
str method = {0, 0};
str body = {0, 0};
//解析方法
method.s = msg->first_line.u.request.method.s;
method.len = msg->first_line.u.request.method.len;
//解析url
uri.s = msg->first_line.u.request.uri.s;
uri.len = msg->first_line.u.request.uri.len;
LM_BUG("method:%.*s\n", method.len, method.s);
LM_BUG("uri:%.*s\n", uri.len, uri.s);
//解析body
body.s = get_body( msg );
if(body.s == NULL)
{
LM_DBG("no message body\n");
}
body.len = msg->buf + msg->len - body.s;
LM_DBG("body len=%d body:%.*s\n", body.len, body.len, body.s);
//这里需要开始处理具体业务,省略
char resp_body[1024];
sprintf(resp_body, "int_param:%d\n str_param:%.*s\n Method:%.*s\n uri:%.*s\n body:%.*s\n",
int_param,
str_param.len, str_param.s,
method.len, method.s,
uri.len, uri.s,
body.len, body.s);
str str_reason = str_init("OK");
str str_type = str_init("text/html");
str str_body;
str_body.s = resp_body;
str_body.len = strlen(resp_body);
//回复请求
xhttp_api.reply(msg, 200, &str_reason, &str_type, &str_body);
return 0;
}
2.3、编写Makefile
include ../../Makefile.defs
auto_gen=
NAME=xhttp_self.so
LIBS=
include ../../Makefile.modules
通过以上实现,一个自定义的http服务模块基本实现完成
3、配置文件加载模块和路由
loadmodule "xhttp_self" # 加载模块
modparam("xhttp_self", "test_int_param", 123456) #设置参数
modparam("xhttp_self", "test_str_param", "abcdefg") #设置参数
event_route[xhttp:request] {
set_reply_close();
set_reply_no_connect();
if (test_check_uri()){ #检测uri是否是需要处理的
test_dispatch(); #处理请求
exit;
}
xhttp_reply("200", "OK", "text/html",
"<html><body>Wrong URL $hu</body></html>");
exit;
}
4、验证接口
启动kamailio,通过curl调用接口测试
4.1、测试post请求
[root@vss-local-dev-001 home]# curl 127.0.0.1:18089/self/test_mod?abc=def -d "this is test body" -X POST
int_param:123456
str_param:abcdefg
Method:POST
uri:/self/test_mod?abc=def
body:this is test body
[root@vss-local-dev-001 home]#
程序打印:
59(30975) DEBUG: <core> [core/parser/msg_parser.c:555]: parse_headers(): Via found, flags=2
59(30975) DEBUG: <core> [core/parser/msg_parser.c:557]: parse_headers(): this is the first via
59(30975) DEBUG: xhttp_self [xhttp_self.c:165]: w_test_check_uri(): URI: /self/test_mod?abc=def
59(30975) DEBUG: xhttp_self [xhttp_self.c:169]: w_test_check_uri(): URI matches: /self/test_mod?abc=def
59(30975) BUG: xhttp_self [xhttp_self.c:96]: self_handle(): method:POST
59(30975) BUG: xhttp_self [xhttp_self.c:97]: self_handle(): uri:/self/test_mod?abc=def
59(30975) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [User-Agent] type 28
59(30975) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Host] type 0
59(30975) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Accept] type 23
59(30975) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Content-Length] type 12
59(30975) DEBUG: <core> [core/parser/msg_parser.c:187]: get_hdr_field(): content_length=17
59(30975) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Content-Type] type 11
59(30975) DEBUG: <core> [core/parser/msg_parser.c:91]: get_hdr_field(): found end of header
59(30975) DEBUG: xhttp_self [xhttp_self.c:106]: self_handle(): body len=17 body:this is test body
59(30975) DEBUG: xhttp [xhttp_mod.c:416]: xhttp_send_reply(): response with content-type: text/html
59(30975) DEBUG: xhttp [xhttp_mod.c:424]: xhttp_send_reply(): response with body: int_param:123456
str_param:abcdefg
Method:POST
uri:/self/test_mod?abc=def
body:this is test body
59(30975) DEBUG: xhttp [xhttp_mod.c:426]: xhttp_send_reply(): sending out response: 200 OK
4.2、测试get请求
[root@vss-local-dev-001 home]# curl 127.0.0.1:18089/self/test_mod?abc=def -d "this is test body" -X GET
int_param:123456
str_param:abcdefg
Method:GET
uri:/self/test_mod?abc=def
body:this is test body
[root@vss-local-dev-001 home]#
程序打印:
60(30976) DEBUG: <core> [core/parser/msg_parser.c:555]: parse_headers(): Via found, flags=2
60(30976) DEBUG: <core> [core/parser/msg_parser.c:557]: parse_headers(): this is the first via
60(30976) DEBUG: xhttp_self [xhttp_self.c:165]: w_test_check_uri(): URI: /self/test_mod?abc=def
60(30976) DEBUG: xhttp_self [xhttp_self.c:169]: w_test_check_uri(): URI matches: /self/test_mod?abc=def
60(30976) BUG: xhttp_self [xhttp_self.c:96]: self_handle(): method:GET
60(30976) BUG: xhttp_self [xhttp_self.c:97]: self_handle(): uri:/self/test_mod?abc=def
60(30976) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [User-Agent] type 28
60(30976) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Host] type 0
60(30976) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Accept] type 23
60(30976) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Content-Length] type 12
60(30976) DEBUG: <core> [core/parser/msg_parser.c:187]: get_hdr_field(): content_length=17
60(30976) DEBUG: <core> [core/parser/parse_hname2.c:301]: parse_sip_header_name(): parsed header name [Content-Type] type 11
60(30976) DEBUG: <core> [core/parser/msg_parser.c:91]: get_hdr_field(): found end of header
60(30976) DEBUG: xhttp_self [xhttp_self.c:106]: self_handle(): body len=17 body:this is test body
60(30976) DEBUG: xhttp [xhttp_mod.c:416]: xhttp_send_reply(): response with content-type: text/html
60(30976) DEBUG: xhttp [xhttp_mod.c:424]: xhttp_send_reply(): response with body: int_param:123456
str_param:abcdefg
Method:GET
uri:/self/test_mod?abc=def
body:this is test body
60(30976) DEBUG: xhttp [xhttp_mod.c:426]: xhttp_send_reply(): sending out response: 200 OK
通过上面两次打印结果也可以看出通过脚本modparam设置的变量的值
4.3、异常url测试
我们自定义模块的对应url路径为"/self/test_mod"开头,所以弄个不匹配的测试
例如 "/self/no_test_mod"
[root@vss-local-dev-001 home]# curl 127.0.0.1:18089/self/no_test_mod?abc=def -d "this is test body" -X GET
<html><body>Wrong URL /self/no_test_mod?abc=def</body></html>
[root@vss-local-dev-001 home]#
程序打印:
53(30969) DEBUG: <core> [core/parser/msg_parser.c:555]: parse_headers(): Via found, flags=2
53(30969) DEBUG: <core> [core/parser/msg_parser.c:557]: parse_headers(): this is the first via
53(30969) DEBUG: xhttp_self [xhttp_self.c:165]: w_test_check_uri(): URI: /self/no_test_mod?abc=def
53(30969) DEBUG: xhttp_self [xhttp_self.c:175]: w_test_check_uri(): URI does not match: /self/no_test_mod?abc=def (/self/test_mod)
53(30969) DEBUG: xhttp [xhttp_mod.c:416]: xhttp_send_reply(): response with content-type: text/html
53(30969) DEBUG: xhttp [xhttp_mod.c:424]: xhttp_send_reply(): response with body: <html><body>Wrong URL /self/no_test_mod?abc=def</body></html>
53(30969) DEBUG: xhttp [xhttp_mod.c:426]: xhttp_send_reply(): sending out response: 200 OK