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