背景
gzcompress
模块用于压缩和解压缩消息体,不仅可以用在SIP
消息上,还能用在HTTP
消息上。
通过Content-Encoding
头来决定是否要压缩和解压缩的。 当udp
数据超过MTU时,
使用该功能比较有效果。压缩的大小为原来的50%~67%左右, 依赖zlib
库。
官方文档地址: gzcompress
本次测试的版本为
kamailio 5.8.5
重要参数解析
1
2
3
4
5
6
|
# 表示要进行压缩和解压缩的头名称,默认: Content-Encoding
modparam("gzcompress", "header_name", "Encoded")
# 表示要进行压缩和解压缩的头的值,默认: deflate
modparam("gzcompress", "header_value", "gzip")
# 是否和sanity模块绑定,默认: 0
modparam("gzcompress", "sanity_checks", 1)
|
实战
官方测试示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#!KAMAILIO
debug=3
memdbg=5
memlog=5
children=2
log_stderror=yes
listen=udp:172.16.4.111:5461
listen=tcp:172.16.4.111:5461
tcp_accept_no_cl=yes
http_reply_parse=yes
mpath="modules/"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "xlog.so"
loadmodule "corex.so"
loadmodule "textops.so"
loadmodule "xhttp.so"
loadmodule "gzcompress.so"
modparam("gzcompress", "header_value", "deflate")
request_route {
xlog("received sip request from $si:$sp\r\n");
if(src_port==5060) {
remove_hf("Content-Encoding");
$du = "sip:127.0.0.1:9";
} else {
append_hf("Content-Encoding: deflate\r\n");
$du = "sip:127.0.0.1:5060";
}
forward();
exit;
}
event_route[xhttp:request] {
xlog("received http request from $si:$sp,$rb,$hdr(Content-Encoding)\n");
append_to_reply("Content-Encoding: deflate\r\n");
xhttp_reply("200", "OK", "application/json",
"{\"status\":\"ok\",\"data\":\"hello\"}");
}
|
在event_route[xhttp:request]
中, 获取请求,然后在返回的请求头中添加Content-Encoding
字段,
xhttp_reply会自动把body
压缩。
测试HTTP请求
使用golang编写http客户端,发送的body使用zlib
压缩,示例为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
package main
import (
"bytes"
"compress/zlib"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
data := map[string]interface{}{
"name": "test",
"city": "Taipei",
}
// 1. 转成 JSON
jsonBytes, err := json.Marshal(data)
if err != nil {
panic(err)
}
// 2. zlib 压缩
var buf bytes.Buffer
zw := zlib.NewWriter(&buf)
_, err = zw.Write(jsonBytes)
if err != nil {
panic(err)
}
zw.Close() // 必须关闭,否则数据不完整
// 3. 创建 HTTP 请求
req, err := http.NewRequest("POST", "http://172.16.4.111:5461/", &buf)
if err != nil {
panic(err)
}
// 设置头部
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Content-Encoding", "deflate")
// 4. 发送
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
// 5. 打印响应
body, _ := io.ReadAll(resp.Body)
fmt.Println("Status:", resp.Status)
fmt.Println("Response:", string(body))
}
|
使用go run main.go
运行客户端, 可以看到kamailio
的日志为:
1
|
39(1152) INFO: <script>: received http request from 172.16.1.136:55992,{"city":"Taipei","name":"test"},deflate
|
kamailio
会自动把zlib
压缩的body解压, 所以可以直接使用$rb
来获取请求体。
如果发送的body
没有压缩,则kamailio
会报错,但是还是能获取到body
的内容:
1
2
3
4
|
41(1154) ERROR: gzcompress [gzcompress_mod.c:276]: gzc_msg_received(): error decompressing body (-3)
41(1154) INFO: <script>: received http request from 172.16.80.13:21776,
{"city":"Taipei","name":"test"}
,deflate
|
测试SIP请求
基础玩法
测试方法:
kamailio
做代理, 软电话1005
注册到kamailio(172.16.4.111)
上, 软电话1008
注册到freeswitch(172.16.4.114)
上,
然后1005
拨打1008
, kamailio
的配置方式为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
route[LOCATION] {
#!ifdef WITH_SPEEDDIAL
# search for short dialing - 2-digit extension
if($rU=~"^[0-9][0-9]$") {
if(sd_lookup("speed_dial")) {
route(SIPOUT);
}
}
#!endif
#!ifdef WITH_ALIASDB
# search in DB-based aliases
if(alias_db_lookup("dbaliases")) {
route(SIPOUT);
}
#!endif
$avp(oexten) = $rU;
#if (!lookup("location")) {
# $var(rc) = $rc;
# route(TOVOICEMAIL);
# t_newtran();
# switch ($var(rc)) {
# case -1:
# case -3:
# send_reply("404", "Not Found");
# exit;
# case -2:
# send_reply("405", "Method Not Allowed");
# exit;
# }
#}
# when routing via usrloc, log the missed calls also
#if (is_method("INVITE")) {
# setflag(FLT_ACCMISSED);
#}
#t_on_failure("1");
#t_relay();
#exit;
$du ="sip:172.16.4.114:5060";
append_hf("Content-Encoding: deflate\r\n");
route(RELAY);
exit;
}
|
测试的sip图为:

-
软电话1005
发送INVITE
到kamailio
上的详细请求为:

-
kamailio
转发INVITE
到freeswitch
上的详细请求为:

可以看到body这块压缩了:218/339=0.643
freeswitch
回复183
到kamailio
上的详细请求为:

freeswitch
因为支持zlib
压缩, 所以回复的183
的body
也压缩了。
-
kamailio
回复183
到1005
上的详细请求为:

-
freeswitch
回复200OK
到kamailio
上的详细请求为:

-
kamailio
回复200OK
到1005
上的详细请求为:

因为软电话不支持zlib解压,然后就报错直接发送BYE
挂断了。
那能否左边不压缩,右边压缩呢?这就是进阶玩法了。
进阶玩法
配置在基础玩法的基础上,添加如下配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
onreply_route[MANAGE_REPLY] {
xlog("L_INFO","incoming reply---$rm|$rs|$tu|$rU|$fU|$si|$Ri:$Rp\n");
remove_hf("Content-Encoding");
if(status=~"[12][0-9][0-9]") {
route(NATMANAGE);
}
if (is_present_hf("Record-Route")) {
remove_hf("Record-Route");
append_hf("Record-Route: <sip:172.16.4.111:5461;lr>\r\n");
xlog("L_INFO","has record-route..\n");
}
return;
}
|
在onreply_route
上, 移除Content-Encoding
头域, 就可以了。
完整的信令图和基础的玩法一致。
-
软电话1005
发送INVITE
到kamailio
上的详细请求为:

-
kamailio
转发INVITE
到freeswitch
上的详细请求为:

-
freeswitch
回复183
到kamailio
上的详细请求为:

-
kamailio
回复183
到1005
上的详细请求为:

可以看到183
的body
没有压缩。
-
freeswitch
回复200OK
到kamailio
上的详细请求为:

-
kamailio
回复200OK
到1005
上的详细请求为:

总结
该模块主要压缩的是body信息, 想要压缩转发的消息只需要带上Content-Encoding: deflate
头域即可。
同理,如果不想要压缩,只需要移除Content-Encoding
头域即可。