上一节,我们讲了mod_rtc
。mod_rtc
是一个纯媒体的模块,目的是为了支持WebRTC。然而,任何的通信都需要一定的信令支持,mod_verto
就是配合mod_rtc
的信令模块。
众所周知,WebRTC从诞生的第一天起就只定义了媒体的交互和传输,而把信令留给大家自己实现,以便有更大的自由度。最初,大部分WebRTC的例子都是基于GAE的,但在电信的VoIP领域,SIP还是占统治地位的,因而,包括FreeSWITCH在内,又有一些SIP代理和软交换设备实现了配合WebRTC使用的SIP信令,这但是SIP over WebSocket。FreeSWITCH对SIP over WebSocket的支持是直接扩展了Sofia-SIP协议栈。
但无论如何,虽然SIP与传统的VoIP协议如H323相比,脱离了老式的电信信令思维,采用了类似HTTP协议的文本协议,但,它从电信领域诞生的基因决定了它还是很难融入互联网,退一步讲,人们还是认为SIP通信专业性太强了,SIP就是SIP,互联网就是互联网。
单从协议内容角度讲,SIP对于浏览器尤其是对于移动浏览器来说,还是有些庞大了。而基于文本的SIP协议解析起来对浏览器来说,即使不是一种负担,也不是非常的优雅。对浏览器来说,最适合的数据格式是JSON已是不争的事实。
我们很高兴地看到,FreeSWITCH团队开放了mod_verto
。它采用了JSON及JSON-RPC相关的信令协议,非常优雅的与mod_rtc
相配合,将热闹的互连网与冷冰冰的SIP通信结合在了一起。也就是说,FreeSWITCH不再是互联网从业者眼里专业的运动员,而跟MySQL,Apache一样,可以实实在在的融入互联网了。
在WebRTC设计之初,就非常重视安全问题,因而,一切都是加密的,不管是在媒体层还是在信令层,这是个好事,唯一比较麻烦的是,对于没有耐心的实践者来说,你在跑通mod_verto
前要设置好你的Web服务器以及证书。
除了Endpoint功能外,mod_verto
其实还自己带了一个HTTP/HTTPS服务器。这是一个隐藏选项,下面,我们看一下其配置:
vhosts>
<vhost domain="localhost">
<param name="alias" value="seven.local freeswitch.org"/>
<param name="root" value="/usr/local/freeswitch/htdocs"/>
<param name="script_root" value="/usr/local/freeswitch/scripts"/>
<param name="index" value="index.html"/>
<<!--
<param name="auth-realm" value="FreeSWITCH"/>
<param name="auth-user" value="freeswitch"/>
<param name="auth-pass" value="rocks"/>
-->
rewrites>
<rule expression="^/api" value="/my_custom_api.lua"/>
<rule expression="^/channels" value="/rest.lua"/>
<rewrites>
</vhost> </
在上述配置中,alias
其实没什么用,备以后扩展;root
要指向Web服务的根目录;index
是默认的首页;auth-
相关的是HTTP
Basic认证;rewrites
会重写请求地址,比如把请求重定向到一个Lua脚本进行处理(类似传统的CGI)。
把上述配置加到Verto的Profile部分,就可以有一个内置的小型的HTTP服务器了。
下面的Lua脚本可以提供REST风格的API,读者请参考 https://freeswitch.org/confluence/display/FREESWITCH/Restful 自行解析:
--[[
Restful by Seven Du.
GET /channels
GET /channels/uuid
POST /channels
PUT /channels/uuid
DELETE /channels/uuid
DELETE /channels
]]
function headers()
stream:write("HTTP/1.0 200 OK\r\n")
if (accept == "application/json") then
stream:write("Content-Type: application/json\r\n")
else
stream:write("Content-Type: text/plain\r\n")
end
stream:write("\r\n")
end
-- print(env:serialize())
api = freeswitch.API()
method = env:getHeader("Request-Method")
http_uri = env:getHeader("HTTP-Request-URI")
http_query = env:getHeader("HTTP-QUERY")
accept = env:getHeader("Accept")
uuid = string.sub(http_uri, "11") -- remove /channels/ from uri
freeswitch.consoleLog("ERR", "[" .. method .. "]\n")
-- freeswitch.consoleLog("ERR", http_uri .. "\n")
-- freeswitch.consoleLog("ERR", http_query .. "\n")
if (method == "GET") then
if not (uuid == "") then
format = ""
if accept == "application/json" then
format = " json"
end
ret = api:execute("uuid_dump", uuid .. format)
else
format = ""
if (accept == "application/json") then
format = " as json"
end
ret = api:execute("show", "channels" .. format)
end
elseif (method == "POST") then
dest = env:getHeader("destNumber")
app = "echo"
dialstr = "user/" .. dest .. " &" .. app
print(dialstr)
ret = api:execute("originate", dialstr)
elseif method == "PUT" then
if action == "nomedia" then
cmd = "uuid_media"
arg = uuid .. " off"
elseif action == "media" then
cmd = "uuid_media"
arg = uuid .. " on"
elseif action == "hold" then
cmd = "uuid_hold"
arg = "on " .. uuid
elseif action == "media" then
cmd = "uuid_media"
arg = "off" .. uuid
end
ret = api:execute(cmd, arg)
elseif method == "DELETE" then
if not (uuid == "") then
cmd = "uuid_kill"
arg = uuid
else
cmd = "hupall"
arg = ""
end
ret = api:execute(cmd, arg)
end
()
headersstream:write(ret .. "\n")
使用Curl测试RESTfulAPI的例子如下:
curl -0 localhost:8081/channels
curl -0 -XPOST -d "destNumber=1000" localhost:8081/channels
curl -0 localhost:8081/channels/1f0802b7-3568-4eb6-b372-182861b56d9b
curl -0 -H "Accept: application/json" localhost:8081/channels/1f0802b7-3568-4eb6-b372-182861b56d9b
curl -0 -XDELETE localhost:8081/channels
关于本模块更详细的信息,请参阅:https://confluence.freeswitch.org/display/FREESWITCH`mod_verto` 。