SlideShare uma empresa Scribd logo
1 de 64
Baixar para ler offline
Sebastian Damm
E: damm@sipgate.de
T: @_SebastianDamm
Modular And Test Driven SIP
Routing With Lua
Who we are, what we do
• Düsseldorf based VoIP service provider (since 2004)
• Active in Germany and UK
• Full MVNO in the Telefónica network
• Private and Business customers
• VoIP and Mobile products
• Some 100k active customers
• Almost 100 million minutes each month
Of course, we use Kamailio
Of course, we use Kamailio
All over our network.
Of course, we use Kamailio
All over our network.
But we have a huge problem!
What problem?
at sipgate
• methods of software development have changed:
- Agile, fast cycles, many releases
- small modules
- unit tests, continuous deployment
- permanent refactoring of functions
What problem?
at sipgate
• methods of software development have changed:
- Agile, fast cycles, many releases
- small modules
- unit tests, continuous deployment
- permanent refactoring of functions
• telephony side:
- "organically grown" routing logic (since 2004)
- 40 developers, but only 4 people can read/write it
- learnings from software development haven't made it to this part
What problem?
Nobody wants to touch it!
What problem?
1. Readability
What problem?
1. Readability
What problem?
1. Readability
What problem?
2. Config file size
What problem?
2. Config file size
What problem?
2. Config file size
What problem?
3. Testability
What problem?
3. Testability
Untested!
What problem?
It's a mess!
But how do we fix that?
But how do we fix that?
• app_*:
- app_lua
- app_python
- app_jsdt
- app_java, app_mono, app_perl
- missing: app_ruby ;)
But how do we fix that?
• app_*:
- app_lua
- app_python
- app_jsdt
- app_java, app_mono, app_perl
- missing: app_ruby ;)
• KEMI
- lua
- python
- jsdt
- sqlang
Why Lua?
1. Speed
Interpreter Average Min Max
Native 302.28 6 3824
Lua 308.32 6 3596
Python 393.71 23 3266
All times in Microseconds.

Source: https://www.kamailio.org/wiki/devel/config-engines#interpreters_performances
Why Lua?
2. Easy to learn
• None of us had written Lua (production) code before.
• If you've written code before, you'll be able to start right away.
• Less fear of changing something within Kamailio.
Why Lua?
3. You can work test-driven
• Impossible with native Kamailio language
• Know if you broke something before you deploy it!
Standard way: one big Lua file
Still many lines of code
Standard way: one big Lua file
Still have to know Kamailio syntax all the time
function ksr_request_route()
-- per request initial checks
ksr_route_reqinit();
-- NAT detection
ksr_route_natdetect();
-- CANCEL processing
if KSR.pv.get("$rm") == "CANCEL" then
if KSR.tm.t_check_trans()>0 then
ksr_route_relay();
end
return 1;
end
-- handle requests within SIP dialogs
ksr_route_withindlg();
-- -- only initial requests (no To tag)
-- handle retransmissions
if KSR.tmx.t_precheck_trans()>0 then
KSR.tm.t_check_trans();
return 1;
end
if KSR.tm.t_check_trans()==0 then return 1 end
-- authentication
ksr_route_auth();
-- remove preloaded route headers
KSR.hdr.remove("Route");
if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then
KSR.rr.record_route();
end
-- account only INVITEs
if KSR.pv.get("$rm")=="INVITE" then
KSR.setflag(FLT_ACC); -- do accounting
end
-- dispatch requests to foreign domains
ksr_route_sipout();
-- -- requests for my local domains
-- handle registrations
ksr_route_registrar();
if KSR.pv.is_null("$rU") then
-- request with no Username in RURI
KSR.sl.sl_send_reply(484,"Address Incomplete");
return 1;
end
-- user location service
ksr_route_location();
return 1;
end
Standard way: one big Lua file
Still not testable
function ksr_request_route()
-- per request initial checks
ksr_route_reqinit();
-- NAT detection
ksr_route_natdetect();
-- CANCEL processing
if KSR.pv.get("$rm") == "CANCEL" then
if KSR.tm.t_check_trans()>0 then
ksr_route_relay();
end
return 1;
end
-- handle requests within SIP dialogs
ksr_route_withindlg();
-- -- only initial requests (no To tag)
-- handle retransmissions
if KSR.tmx.t_precheck_trans()>0 then
KSR.tm.t_check_trans();
return 1;
end
if KSR.tm.t_check_trans()==0 then return 1 end
-- authentication
ksr_route_auth();
-- remove preloaded route headers
KSR.hdr.remove("Route");
if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then
KSR.rr.record_route();
end
-- account only INVITEs
if KSR.pv.get("$rm")=="INVITE" then
KSR.setflag(FLT_ACC); -- do accounting
end
-- dispatch requests to foreign domains
ksr_route_sipout();
-- -- requests for my local domains
-- handle registrations
ksr_route_registrar();
if KSR.pv.is_null("$rU") then
-- request with no Username in RURI
KSR.sl.sl_send_reply(484,"Address Incomplete");
return 1;
end
-- user location service
ksr_route_location();
return 1;
end
How about a library?
• Encapsulate Kamailio functions in easy-to-use native functions
• Re-use often used calls
• Testable
• Fix logic errors before deploying them into production
if KSR.pv.get("$rm") == "INVITE" then
[..]
end
if kamailio.is_invite() then
[..]
end
How about a library?
Reduce the main script to a minimum
kamailio = require("kamailio")
function ksr_request_route()
kamailio.process_request()
end
So how do we get there?
• Install dependencies
- Interpreter: lua, version 5.1 (apt-get install lua-5.1)
- Testing framework: busted (apt-get install lua-busted)
• Create your library.
• Define your function.
• Write tests.
• Make the function work.
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
• Can be a directory containing multiple files

/usr/local/share/lua/5.1/kamailio/

!"" actions.lua

!"" init.lua

!"" message.lua

!"" message_state.lua

!"" nathandling.lua

!"" security.lua

#"" traffic.lua
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
• Can be a directory containing multiple files

/usr/local/share/lua/5.1/kamailio/

!"" actions.lua

!"" init.lua

!"" message.lua

!"" message_state.lua

!"" nathandling.lua

!"" security.lua

#"" traffic.lua
Create your library
• Can be just one file containing all your methods

/etc/kamailio/kamailio-functions.lua

/usr/local/share/lua/5.1/kamailio-functions.lua
• Can be a directory containing multiple files

/usr/local/share/lua/5.1/kamailio/

!"" actions.lua

!"" init.lua

!"" message.lua

!"" message_state.lua

!"" nathandling.lua

!"" security.lua

#"" traffic.lua
damm@stretch:~$ lua
Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio
> foo = require "foo"
stdin:1: module 'foo' not found:
no field package.preload['foo']
no file './foo.lua'
no file '/usr/local/share/lua/5.1/foo.lua'
no file '/usr/local/share/lua/5.1/foo/init.lua'
no file '/usr/local/lib/lua/5.1/foo.lua'
no file '/usr/local/lib/lua/5.1/foo/init.lua'
no file '/usr/share/lua/5.1/foo.lua'
no file '/usr/share/lua/5.1/foo/init.lua'
no file './foo.so'
no file '/usr/local/lib/lua/5.1/foo.so'
no file '/usr/lib/x86_64-linux-gnu/lua/5.1/foo.so'
no file '/usr/lib/lua/5.1/foo.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
stack traceback:
[C]: in function 'require'
stdin:1: in main chunk
[C]: ?
>
Create your library
traffic = require "kamailio.traffic"
message = require "kamailio.message"
security = require "kamailio.security"
rex = require "rex_pcre"
local kamailio = {}
function kamailio:process_request()
[..]
end
function kamailio:process_reply()
[..]
end
return kamailio
Create your library
traffic = require "kamailio.traffic"
message = require "kamailio.message"
security = require "kamailio.security"
rex = require "rex_pcre"
local kamailio = {}
function kamailio:process_request()
[..]
end
function kamailio:process_reply()
[..]
end
return kamailio
Write your function
kamailio.cfg
route["clir"] {
if ($rU =~ "^*31[0-9]+$") {
strip(3);
setflag(21);
} else if ($rU =~ "^*31[*#][0-9]+$") {
strip(4);
setflag(21);
} else if ($rU =~ "^*31%23[0-9]+$") {
strip(6);
setflag(21);
}
if (isflagset(21) && method=="INVITE") {
xlog("L_NOTICE", "User wants to suppress his number for this call. F=$fU T=$tU
D=$fnn");
append_hf("Privacy: idrn");
}
}
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Write your function
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Test it
spec/headers_spec.lua
require 'busted.runner'()
local headers = require "../kamailio/headers"
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" )
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Fix your tests
require 'busted.runner'()
local function init_mock(options)
-- mock global variable 'KSR'
local ksr_mock = {
pv = {
get = function(key)
if key == "$rU" then
if options.rU ~= nil then return options.rU else return "01234567" end
end
end,
sets = function(k, v) end,
},
hdr = {
append = function(header) end,
}
}
_G["KSR"] = mock(ksr_mock)
local message_mock = {
is_invite = function() return options.is_invite end
}
_G["message"] = mock(message_mock)
end
local headers = require "kamailio/headers"
[..]
Fix your tests
require 'busted.runner'()
local function init_mock(options)
-- mock global variable 'KSR'
local ksr_mock = {
pv = {
get = function(key)
if key == "$rU" then
if options.rU ~= nil then return options.rU else return "01234567" end
end
end,
sets = function(k, v) end,
},
hdr = {
append = function(header) end,
}
}
_G["KSR"] = mock(ksr_mock)
local message_mock = {
is_invite = function() return options.is_invite end
}
_G["message"] = mock(message_mock)
end
local headers = require "kamailio/headers"
[..]
Fix your tests
require 'busted.runner'()
local function init_mock(options)
-- mock global variable 'KSR'
local ksr_mock = {
pv = {
get = function(key)
if key == "$rU" then
if options.rU ~= nil then return options.rU else return "01234567" end
end
end,
sets = function(k, v) end,
},
hdr = {
append = function(header) end,
}
}
_G["KSR"] = mock(ksr_mock)
local message_mock = {
is_invite = function() return options.is_invite end
}
_G["message"] = mock(message_mock)
end
local headers = require "kamailio/headers"
[..]
Fix your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Initialize the mock
init_mock{ rU = "*31021112345", is_invite = true }
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345")
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Fix your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Initialize the mock
init_mock{ rU = "*31021112345", is_invite = true }
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345")
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Fix your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function()
-- Initialize the mock
init_mock{ rU = "*31021112345", is_invite = true }
-- Call the function
headers.suppress_cid_if_needed()
-- Now check if everything is as expected
assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345")
assert.spy(KSR.hdr.append).was.called_with("Privacy: id")
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function() .. end)
it("Caller dials *31+4921112345, non-INVITE", function() .. end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Add more tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function()
init_mock{ rU = "+4921112345", is_invite = true }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called(0)
assert.spy(KSR.hdr.append).was.called(0)
end)
it("Caller dials *31+4921112345, non-INVITE", function()
init_mock{ rU = "*31+4921112345" }
headers.suppress_cid_if_needed()
assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345")
assert.spy(KSR.hdr.append).was.called(0)
end)
end)
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([*#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Fix your code
kamailio/headers.lua
rex = require "rex_pcre"
message = require "kamailio.message"
local headers = {}
function headers.get_request_user()
return KSR.pv.get("$rU")
end
function headers.set_request_user(value)
KSR.pv.sets("$rU", value)
end
function headers.set_privacy_header()
KSR.hdr.append("Privacy: id")
end
function headers.suppress_cid_if_needed()
request_user = headers.get_request_user()
if rex.find(request_user, "^*31([*#]|%23)?") then
headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", ""))
if message.is_invite() then headers.set_privacy_header() end
end
end
return headers
Rerun your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function() .. end)
it("Caller dials *31+4921112345, non-INVITE", function() .. end)
end)
Rerun your tests
require 'busted.runner'()
local function init_mock(options) [..] end
describe("Check and enable CLIR ->", function()
it("Caller dials *31021112345", function() .. end)
it("Caller dials *31*021112345", function() .. end)
it("Caller dials *31#021112345", function() .. end)
it("Caller dials *31%23021112345", function() .. end)
it("Caller dials +4921112345", function() .. end)
it("Caller dials *31+4921112345, non-INVITE", function() .. end)
end)
Too dry?
Try it yourself:
https://github.com/sipgate/lua-kamailio
That's it
Questions?
Want to chat? Stop by!

Mais conteúdo relacionado

Mais procurados

Mais procurados (20)

Introduction to cloudforecast
Introduction to cloudforecastIntroduction to cloudforecast
Introduction to cloudforecast
 
How DSL works on Ruby
How DSL works on RubyHow DSL works on Ruby
How DSL works on Ruby
 
Kamailio - Surfing Big Waves Of SIP With Style
Kamailio - Surfing Big Waves Of SIP With StyleKamailio - Surfing Big Waves Of SIP With Style
Kamailio - Surfing Big Waves Of SIP With Style
 
FOSDEM 2017 - RTC Services With Lua and Kamailio
FOSDEM 2017 - RTC Services With Lua and KamailioFOSDEM 2017 - RTC Services With Lua and Kamailio
FOSDEM 2017 - RTC Services With Lua and Kamailio
 
Minimal MVC in JavaScript
Minimal MVC in JavaScriptMinimal MVC in JavaScript
Minimal MVC in JavaScript
 
Concurrency in Python
Concurrency in PythonConcurrency in Python
Concurrency in Python
 
The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018The Year of JRuby - RubyC 2018
The Year of JRuby - RubyC 2018
 
Devel::NYTProf v5 at YAPC::NA 201406
Devel::NYTProf v5 at YAPC::NA 201406Devel::NYTProf v5 at YAPC::NA 201406
Devel::NYTProf v5 at YAPC::NA 201406
 
Perl Memory Use 201207 (OUTDATED, see 201209 )
Perl Memory Use 201207 (OUTDATED, see 201209 )Perl Memory Use 201207 (OUTDATED, see 201209 )
Perl Memory Use 201207 (OUTDATED, see 201209 )
 
Fisl - Deployment
Fisl - DeploymentFisl - Deployment
Fisl - Deployment
 
Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0
 
Troubleshooting common oslo.messaging and RabbitMQ issues
Troubleshooting common oslo.messaging and RabbitMQ issuesTroubleshooting common oslo.messaging and RabbitMQ issues
Troubleshooting common oslo.messaging and RabbitMQ issues
 
typemap in Perl/XS
typemap in Perl/XS  typemap in Perl/XS
typemap in Perl/XS
 
Kamailio Updates - VUC 588
Kamailio Updates - VUC 588Kamailio Updates - VUC 588
Kamailio Updates - VUC 588
 
Roll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and LuaRoll Your Own API Management Platform with nginx and Lua
Roll Your Own API Management Platform with nginx and Lua
 
Kamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade TrafficKamailio - SIP Firewall for Carrier Grade Traffic
Kamailio - SIP Firewall for Carrier Grade Traffic
 
Using ngx_lua in UPYUN
Using ngx_lua in UPYUNUsing ngx_lua in UPYUN
Using ngx_lua in UPYUN
 
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
PuppetConf 2017: What's in a Name? Scaling ENC with DNS- Cameron Nicholson, A...
 
Lua tech talk
Lua tech talkLua tech talk
Lua tech talk
 
Using Kamailio for Scalability and Security
Using Kamailio for Scalability and SecurityUsing Kamailio for Scalability and Security
Using Kamailio for Scalability and Security
 

Semelhante a Kamailioworld 2018 - Modular and test driven SIP Routing with Lua

PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operations
grim_radical
 
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilitiesVorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilities
DefconRussia
 

Semelhante a Kamailioworld 2018 - Modular and test driven SIP Routing with Lua (20)

Kamailio - Unifying SIP and Web Worlds with Lua
Kamailio - Unifying SIP and Web Worlds with LuaKamailio - Unifying SIP and Web Worlds with Lua
Kamailio - Unifying SIP and Web Worlds with Lua
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operations
 
From SaltStack to Puppet and beyond...
From SaltStack to Puppet and beyond...From SaltStack to Puppet and beyond...
From SaltStack to Puppet and beyond...
 
#WeSpeakLinux Session
#WeSpeakLinux Session#WeSpeakLinux Session
#WeSpeakLinux Session
 
Ingesting hdfs intosolrusingsparktrimmed
Ingesting hdfs intosolrusingsparktrimmedIngesting hdfs intosolrusingsparktrimmed
Ingesting hdfs intosolrusingsparktrimmed
 
Drupal Efficiency - Coding, Deployment, Scaling
Drupal Efficiency - Coding, Deployment, ScalingDrupal Efficiency - Coding, Deployment, Scaling
Drupal Efficiency - Coding, Deployment, Scaling
 
Ansible - A 'crowd' introduction
Ansible - A 'crowd' introductionAnsible - A 'crowd' introduction
Ansible - A 'crowd' introduction
 
Why Managed Service Providers Should Embrace Container Technology
Why Managed Service Providers Should Embrace Container TechnologyWhy Managed Service Providers Should Embrace Container Technology
Why Managed Service Providers Should Embrace Container Technology
 
Drupal Efficiency using open source technologies from Sun
Drupal Efficiency using open source technologies from SunDrupal Efficiency using open source technologies from Sun
Drupal Efficiency using open source technologies from Sun
 
Slackware Demystified [SELF 2011]
Slackware Demystified [SELF 2011]Slackware Demystified [SELF 2011]
Slackware Demystified [SELF 2011]
 
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilitiesVorontsov, golovko   ssrf attacks and sockets. smorgasbord of vulnerabilities
Vorontsov, golovko ssrf attacks and sockets. smorgasbord of vulnerabilities
 
Logstash
LogstashLogstash
Logstash
 
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
JavaOne 2010: Top 10 Causes for Java Issues in Production and What to Do When...
 
Kamailio with Docker and Kubernetes
Kamailio with Docker and KubernetesKamailio with Docker and Kubernetes
Kamailio with Docker and Kubernetes
 
Kamailio - SIP Routing in Lua
Kamailio - SIP Routing in LuaKamailio - SIP Routing in Lua
Kamailio - SIP Routing in Lua
 
How to automate all your SEO projects
How to automate all your SEO projectsHow to automate all your SEO projects
How to automate all your SEO projects
 
Migrating to XtraDB Cluster
Migrating to XtraDB ClusterMigrating to XtraDB Cluster
Migrating to XtraDB Cluster
 
Docker
DockerDocker
Docker
 
Kafka meetup JP #3 - Engineering Apache Kafka at LINE
Kafka meetup JP #3 - Engineering Apache Kafka at LINEKafka meetup JP #3 - Engineering Apache Kafka at LINE
Kafka meetup JP #3 - Engineering Apache Kafka at LINE
 
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
Fault Tolerance in Spark: Lessons Learned from Production: Spark Summit East ...
 

Último

AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 

Último (20)

AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban
 
SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions Presentation
 
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park %in kempton park+277-882-255-28 abortion pills for sale in kempton park
%in kempton park+277-882-255-28 abortion pills for sale in kempton park
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
Generic or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisionsGeneric or specific? Making sensible software design decisions
Generic or specific? Making sensible software design decisions
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 

Kamailioworld 2018 - Modular and test driven SIP Routing with Lua

  • 1. Sebastian Damm E: damm@sipgate.de T: @_SebastianDamm Modular And Test Driven SIP Routing With Lua
  • 2. Who we are, what we do • Düsseldorf based VoIP service provider (since 2004) • Active in Germany and UK • Full MVNO in the Telefónica network • Private and Business customers • VoIP and Mobile products • Some 100k active customers • Almost 100 million minutes each month
  • 3. Of course, we use Kamailio
  • 4. Of course, we use Kamailio All over our network.
  • 5. Of course, we use Kamailio All over our network. But we have a huge problem!
  • 6. What problem? at sipgate • methods of software development have changed: - Agile, fast cycles, many releases - small modules - unit tests, continuous deployment - permanent refactoring of functions
  • 7. What problem? at sipgate • methods of software development have changed: - Agile, fast cycles, many releases - small modules - unit tests, continuous deployment - permanent refactoring of functions • telephony side: - "organically grown" routing logic (since 2004) - 40 developers, but only 4 people can read/write it - learnings from software development haven't made it to this part
  • 18. But how do we fix that?
  • 19. But how do we fix that? • app_*: - app_lua - app_python - app_jsdt - app_java, app_mono, app_perl - missing: app_ruby ;)
  • 20. But how do we fix that? • app_*: - app_lua - app_python - app_jsdt - app_java, app_mono, app_perl - missing: app_ruby ;) • KEMI - lua - python - jsdt - sqlang
  • 21. Why Lua? 1. Speed Interpreter Average Min Max Native 302.28 6 3824 Lua 308.32 6 3596 Python 393.71 23 3266 All times in Microseconds. Source: https://www.kamailio.org/wiki/devel/config-engines#interpreters_performances
  • 22. Why Lua? 2. Easy to learn • None of us had written Lua (production) code before. • If you've written code before, you'll be able to start right away. • Less fear of changing something within Kamailio.
  • 23. Why Lua? 3. You can work test-driven • Impossible with native Kamailio language • Know if you broke something before you deploy it!
  • 24. Standard way: one big Lua file Still many lines of code
  • 25. Standard way: one big Lua file Still have to know Kamailio syntax all the time function ksr_request_route() -- per request initial checks ksr_route_reqinit(); -- NAT detection ksr_route_natdetect(); -- CANCEL processing if KSR.pv.get("$rm") == "CANCEL" then if KSR.tm.t_check_trans()>0 then ksr_route_relay(); end return 1; end -- handle requests within SIP dialogs ksr_route_withindlg(); -- -- only initial requests (no To tag) -- handle retransmissions if KSR.tmx.t_precheck_trans()>0 then KSR.tm.t_check_trans(); return 1; end if KSR.tm.t_check_trans()==0 then return 1 end -- authentication ksr_route_auth(); -- remove preloaded route headers KSR.hdr.remove("Route"); if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then KSR.rr.record_route(); end -- account only INVITEs if KSR.pv.get("$rm")=="INVITE" then KSR.setflag(FLT_ACC); -- do accounting end -- dispatch requests to foreign domains ksr_route_sipout(); -- -- requests for my local domains -- handle registrations ksr_route_registrar(); if KSR.pv.is_null("$rU") then -- request with no Username in RURI KSR.sl.sl_send_reply(484,"Address Incomplete"); return 1; end -- user location service ksr_route_location(); return 1; end
  • 26. Standard way: one big Lua file Still not testable function ksr_request_route() -- per request initial checks ksr_route_reqinit(); -- NAT detection ksr_route_natdetect(); -- CANCEL processing if KSR.pv.get("$rm") == "CANCEL" then if KSR.tm.t_check_trans()>0 then ksr_route_relay(); end return 1; end -- handle requests within SIP dialogs ksr_route_withindlg(); -- -- only initial requests (no To tag) -- handle retransmissions if KSR.tmx.t_precheck_trans()>0 then KSR.tm.t_check_trans(); return 1; end if KSR.tm.t_check_trans()==0 then return 1 end -- authentication ksr_route_auth(); -- remove preloaded route headers KSR.hdr.remove("Route"); if string.find("INVITE|SUBSCRIBE", KSR.pv.get("$rm")) then KSR.rr.record_route(); end -- account only INVITEs if KSR.pv.get("$rm")=="INVITE" then KSR.setflag(FLT_ACC); -- do accounting end -- dispatch requests to foreign domains ksr_route_sipout(); -- -- requests for my local domains -- handle registrations ksr_route_registrar(); if KSR.pv.is_null("$rU") then -- request with no Username in RURI KSR.sl.sl_send_reply(484,"Address Incomplete"); return 1; end -- user location service ksr_route_location(); return 1; end
  • 27. How about a library? • Encapsulate Kamailio functions in easy-to-use native functions • Re-use often used calls • Testable • Fix logic errors before deploying them into production if KSR.pv.get("$rm") == "INVITE" then [..] end if kamailio.is_invite() then [..] end
  • 28. How about a library? Reduce the main script to a minimum kamailio = require("kamailio") function ksr_request_route() kamailio.process_request() end
  • 29. So how do we get there? • Install dependencies - Interpreter: lua, version 5.1 (apt-get install lua-5.1) - Testing framework: busted (apt-get install lua-busted) • Create your library. • Define your function. • Write tests. • Make the function work.
  • 30. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua
  • 31. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua • Can be a directory containing multiple files
 /usr/local/share/lua/5.1/kamailio/
 !"" actions.lua
 !"" init.lua
 !"" message.lua
 !"" message_state.lua
 !"" nathandling.lua
 !"" security.lua
 #"" traffic.lua
  • 32. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua • Can be a directory containing multiple files
 /usr/local/share/lua/5.1/kamailio/
 !"" actions.lua
 !"" init.lua
 !"" message.lua
 !"" message_state.lua
 !"" nathandling.lua
 !"" security.lua
 #"" traffic.lua
  • 33. Create your library • Can be just one file containing all your methods
 /etc/kamailio/kamailio-functions.lua
 /usr/local/share/lua/5.1/kamailio-functions.lua • Can be a directory containing multiple files
 /usr/local/share/lua/5.1/kamailio/
 !"" actions.lua
 !"" init.lua
 !"" message.lua
 !"" message_state.lua
 !"" nathandling.lua
 !"" security.lua
 #"" traffic.lua damm@stretch:~$ lua Lua 5.1.5 Copyright (C) 1994-2012 Lua.org, PUC-Rio > foo = require "foo" stdin:1: module 'foo' not found: no field package.preload['foo'] no file './foo.lua' no file '/usr/local/share/lua/5.1/foo.lua' no file '/usr/local/share/lua/5.1/foo/init.lua' no file '/usr/local/lib/lua/5.1/foo.lua' no file '/usr/local/lib/lua/5.1/foo/init.lua' no file '/usr/share/lua/5.1/foo.lua' no file '/usr/share/lua/5.1/foo/init.lua' no file './foo.so' no file '/usr/local/lib/lua/5.1/foo.so' no file '/usr/lib/x86_64-linux-gnu/lua/5.1/foo.so' no file '/usr/lib/lua/5.1/foo.so' no file '/usr/local/lib/lua/5.1/loadall.so' stack traceback: [C]: in function 'require' stdin:1: in main chunk [C]: ? >
  • 34. Create your library traffic = require "kamailio.traffic" message = require "kamailio.message" security = require "kamailio.security" rex = require "rex_pcre" local kamailio = {} function kamailio:process_request() [..] end function kamailio:process_reply() [..] end return kamailio
  • 35. Create your library traffic = require "kamailio.traffic" message = require "kamailio.message" security = require "kamailio.security" rex = require "rex_pcre" local kamailio = {} function kamailio:process_request() [..] end function kamailio:process_reply() [..] end return kamailio
  • 36. Write your function kamailio.cfg route["clir"] { if ($rU =~ "^*31[0-9]+$") { strip(3); setflag(21); } else if ($rU =~ "^*31[*#][0-9]+$") { strip(4); setflag(21); } else if ($rU =~ "^*31%23[0-9]+$") { strip(6); setflag(21); } if (isflagset(21) && method=="INVITE") { xlog("L_NOTICE", "User wants to suppress his number for this call. F=$fU T=$tU D=$fnn"); append_hf("Privacy: idrn"); } }
  • 37. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 38. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 39. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 40. Write your function kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 41. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 42. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 43. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 44. Test it spec/headers_spec.lua require 'busted.runner'() local headers = require "../kamailio/headers" describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with( "$rU", "021112345" ) assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 45. Fix your tests require 'busted.runner'() local function init_mock(options) -- mock global variable 'KSR' local ksr_mock = { pv = { get = function(key) if key == "$rU" then if options.rU ~= nil then return options.rU else return "01234567" end end end, sets = function(k, v) end, }, hdr = { append = function(header) end, } } _G["KSR"] = mock(ksr_mock) local message_mock = { is_invite = function() return options.is_invite end } _G["message"] = mock(message_mock) end local headers = require "kamailio/headers" [..]
  • 46. Fix your tests require 'busted.runner'() local function init_mock(options) -- mock global variable 'KSR' local ksr_mock = { pv = { get = function(key) if key == "$rU" then if options.rU ~= nil then return options.rU else return "01234567" end end end, sets = function(k, v) end, }, hdr = { append = function(header) end, } } _G["KSR"] = mock(ksr_mock) local message_mock = { is_invite = function() return options.is_invite end } _G["message"] = mock(message_mock) end local headers = require "kamailio/headers" [..]
  • 47. Fix your tests require 'busted.runner'() local function init_mock(options) -- mock global variable 'KSR' local ksr_mock = { pv = { get = function(key) if key == "$rU" then if options.rU ~= nil then return options.rU else return "01234567" end end end, sets = function(k, v) end, }, hdr = { append = function(header) end, } } _G["KSR"] = mock(ksr_mock) local message_mock = { is_invite = function() return options.is_invite end } _G["message"] = mock(message_mock) end local headers = require "kamailio/headers" [..]
  • 48. Fix your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Initialize the mock init_mock{ rU = "*31021112345", is_invite = true } -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345") assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 49. Fix your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Initialize the mock init_mock{ rU = "*31021112345", is_invite = true } -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345") assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 50. Fix your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() -- Initialize the mock init_mock{ rU = "*31021112345", is_invite = true } -- Call the function headers.suppress_cid_if_needed() -- Now check if everything is as expected assert.spy(KSR.pv.sets).was.called_with("$rU", "021112345") assert.spy(KSR.hdr.append).was.called_with("Privacy: id") end) end)
  • 51. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() .. end) it("Caller dials *31+4921112345, non-INVITE", function() .. end) end)
  • 52. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 53. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 54. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 55. Add more tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() init_mock{ rU = "+4921112345", is_invite = true } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called(0) assert.spy(KSR.hdr.append).was.called(0) end) it("Caller dials *31+4921112345, non-INVITE", function() init_mock{ rU = "*31+4921112345" } headers.suppress_cid_if_needed() assert.spy(KSR.pv.sets).was.called_with("$rU", "+4921112345") assert.spy(KSR.hdr.append).was.called(0) end) end)
  • 56. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 57. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 58. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([*#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 59. Fix your code kamailio/headers.lua rex = require "rex_pcre" message = require "kamailio.message" local headers = {} function headers.get_request_user() return KSR.pv.get("$rU") end function headers.set_request_user(value) KSR.pv.sets("$rU", value) end function headers.set_privacy_header() KSR.hdr.append("Privacy: id") end function headers.suppress_cid_if_needed() request_user = headers.get_request_user() if rex.find(request_user, "^*31([*#]|%23)?") then headers.set_request_user(rex.gsub(request_user, "^*31([*#]|%23)?", "")) if message.is_invite() then headers.set_privacy_header() end end end return headers
  • 60. Rerun your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() .. end) it("Caller dials *31+4921112345, non-INVITE", function() .. end) end)
  • 61. Rerun your tests require 'busted.runner'() local function init_mock(options) [..] end describe("Check and enable CLIR ->", function() it("Caller dials *31021112345", function() .. end) it("Caller dials *31*021112345", function() .. end) it("Caller dials *31#021112345", function() .. end) it("Caller dials *31%23021112345", function() .. end) it("Caller dials +4921112345", function() .. end) it("Caller dials *31+4921112345, non-INVITE", function() .. end) end)
  • 62. Too dry? Try it yourself: https://github.com/sipgate/lua-kamailio
  • 64. Want to chat? Stop by!