The aim of this document is to provide information about how to deliver a sms over HTTP and how to make extra actions before sending the actual request. In this case we’ll want to make an additional HTTP request to retrieve an authentication token and add it in subsequent requests.
Enabling functionality
For this, you will need to enable in your YateSMSC from YateMMI:
- a custom routing rule that delivers a SMS over HTTP – from YateMMI->Edit YateSMSC equipment->Routing step
- a JS script that catches the request to deliver over HTTP (from Scripts step) – from YateMMI->Edit YateSMSC equipment->Routing step
How it works
The routing rule instructs YateSMSC to route to an url starting with http:// or https://. This is done by the dispaching of a http.request message that is handled by the HTTP Client module.
A JS script will delay the http request sending the SMS (and maybe alter it later on) until it does other action (ex: can send one or more http requests before the request is actually sent).
Read also:
https://docs.yate.ro/wiki/HTTP_Client
https://docs.yate.ro/wiki/Javascript
Configuration
From YateMMI configure in your YateSMSC :
- a custom routing rule – from YateMMI->Edit SMSC equipment->Routing step
- an JS script (from Scripts step) – from YateMMI->Edit SMSC equipment->Routing step
Example rule:
[default]
^9631234.*$=http://127.0.0.1/simulate_sms_service.php;json_body=true;http_use_auth=auth_service_name;copyparams=http_use_auth;
http_method=POST
Where:
* SMSes sent to numbers starting with 9631234 will be routed over HTTP by making a POST request to http://127.0.0.1/simulate_sms_service.php.
* json_body,http_use_auth,copyparams,http_method are parameters that will be added to call.route message
* json_body=true makes http request with content type ‘application/json’
* copyparams=http_use_auth is used to instruct that use_auth must be copied to http.request message
* http_method specifies if it should be a GET or POST request; default is GET
The JS script must catch the http.request message before it is handled by our HTTP Client module. (priority<90)
Uses of catching HTTP request
Because after the call.route with url starting with http:// or https://, an http.request message is dispatched, interested parties can catch the message and do some decisions based on that.
Handling http.request messages
Below there is an example of script that:
- lets the http.request pass to HTTP Client module if no use_auth param is found in http.request message
- if use_auth is set then it assumes it needs to add authentication information to the http request
- delays the handling for http.request message until it sends the authentication request
// helper function for dispatching http.request message
// use sync=true if you need to use the request response
function customHttpRequest(url,sync,accept,oneline,extra,body,return_mess)
{
var m = new Message(‘http.request’,false,extra);
m.url = url;
m.method = ‘post’;
m.type = ‘application/x-www-form-urlencoded/text’;
m.accept = accept;
m.multiline = !oneline;
m.body = body;
if (!sync) {
m.enqueue();
return null;
}
m.wait = true;
if (!m.dispatch(true))
return null;
if (m.error)
return null;
if (!!return_mess)
return m;
return ” + m.retValue();
};
function onHttpModifier(msg)
{
// if message has use_auth empty,
// let message pass to http client module, by returning false
var acc = msg.use_auth;
if (!acc)
return false;
// if value set in use_auth is not a key in auth_info global object(initialized bellow),
// let message pass to http client module, by returning false
var acc_info = auth_info[acc];
if (!acc_info) {
Engine.debug(Engine.DebugWarn, ‘Unknown auth account \” + acc + ‘\”);
return false;
}
//verify if auth token expired
var expired = false;
if (acc_info[‘expires_in’] && acc_info[‘expires_in’]<Date.now())
expired = true;
// retrive auth token and it’s expiration time
// * if it expired or
// * if we are not already authenticated
if (!acc_info[‘key’] || expired) {
//in our case the body must have next format:
//username=${u}&password=${p}&grant_type=password
body = ‘username=’ + acc_info.username + ‘&password=’ + acc_info.password + ‘&grant_type=’ + acc_info.grant_type;
//make the extra http request to the corresponding url
//autz variable will contain the body from the response
autz = customHttpRequest(acc_info.url, true, ‘application/json’, false, acc_info, body);
if (!autz) {
Engine.debug(Engine.DebugWarn, ‘Authentication to \” + acc_info[‘url’] + ‘\’ failed.’);
msg.reason = ‘failed auth’;
return false;
}
//decode response (in our case the response has JSON format)
var autz_decoded = JSON.parse(autz);
if (undefined===autz_decoded) {
Engine.debug(Engine.DebugWarn, ‘Failed decoding JSON response \” + autz + ‘\”);
msg.reason = ‘failed autz decoding’;
return false;
}
//add the key and the expiration time in auth_info global array
acc_info[‘key’] = autz_decoded[‘access_token’];
//expires_in param is in seconds and represents the token’s lifetime
//transform it to miliseconds multipling with 1000 and add it to current timestamp to obtain the expiration time
acc_info[‘expires_in’] = Date.now() + autz_decoded[‘expires_in’]*1000;
Engine.debug(Engine.DebugInfo, ‘Got autz \” + autz + ‘\”);
}
//add the authorization token to http headers
msg.http_Authorization = ‘Bearer ‘ + acc_info[‘key’];
msg.accept = ‘application/json’;
var body = JSON.parse(msg.body);
if (undefined===body)
Engine.debug(‘Can not parse json body \” + body + ‘\”);
var new_body = {‘mobile’:body.dest, ‘message’:body.text, ‘senderId’:body.orig};
msg.body = JSON.stringify(new_body);
msg.redirect = 1;
}
//a global variable that may hold the url, token, params to auth service for one or multiple sms services
// format:
// auth_info = {
// “service_name”: {
// username:’the_username’,
// password:’the_passwd’,
// grant_type:’the_grant_type’,
// url:’http(s)://the_url’,
// key:’the_token’,
// expires:’the_expiration_unix_timestamp’
// }
// }
auth_info = { ‘auth_service_name‘:{ username:’my_username’, password:’my_password’, grant_type:’password’, url:’https://127.0.0.1 ‘ } };
// install onHttpModifier handler for http.request messages
// http client module uses priority 90, so use priority 80 to catch it first
Message.install(onHttpModifier,’http.request’,80);
The above example script, catches the http.request message before HTTP Client module, and passes it as an argument to onHttpModifier() handler function.
After onHttpModifier alters the http.request message and sends an extra http request(if necessary), message will pass to the HTTP Client module.
The HTTP Client module will make the actual request corresponding to http.request message.
Inside onHttpModifier, if http.request has use_auth property:
* depending on use_auth value
–> a POST request will be made to the corresponding url from auth_info global object to the corresponding sms service url
–> the request will have next body format: ‘username=${username}&password=${password}&grant_type=${grant_type}
* the http.request message is altered in order to:
–> add Authorization header to the actual http request
–> set header accept to application/json
–> Enable HTTP 3xx redirects and set max number of allowed redirects to one
–> set http request body
Example of http.request message being dispatched after call.route message returns an url starting with http:// or https://
Sniffed ‘call.route’ time=1605001996.394041
thread=0x1ecf6b0 ‘Engine Worker’
data=(nil)
retval='(null)’
param[‘route_type’] = ‘msg’
param[‘module’] = ‘smsc_map’
param[‘billid’] = ‘1604937235-3’
param[‘retries’] = ‘4’
param[‘caller’] = ‘YateBTS.com’
param[‘callernumplan’] = ‘unknown’
param[‘callernumtype’] = ‘alphanumeric’
param[‘called’] = ‘963123456789′
param[‘callednumplan’] = ‘isdn’
param[‘callednumtype’] = ‘international’
param[‘submit_info’] = ‘HTTP 127.0.0.1:37032’
param[‘sms_type’] = ‘deliver’
param[‘sms_more’] = ‘false’
param[‘sms_pid’] = ‘0’
param[‘sms_dcs’] = ‘0’
param[‘sms_udh’] = ‘false’
param[‘sms_udl’] = ‘4’
param[‘sms_scts’] = ‘1605001995’
Returned true ‘call.route’ delay=0.001525
thread=0x1ecf6b0 ‘Engine Worker’
data=(nil)
retval=’http://127.0.0.1/simulate_sms_service.php‘
param[‘route_type’] = ‘msg’
param[‘module’] = ‘smsc_map’
param[‘billid’] = ‘1604937235-3’
param[‘retries’] = ‘4’
param[‘caller’] = ‘YateBTS.com’
param[‘callernumplan’] = ‘unknown’
param[‘callernumtype’] = ‘alphanumeric’
param[‘called’] = ‘963123456789′
param[‘callednumplan’] = ‘isdn’
param[‘callednumtype’] = ‘international’
param[‘submit_info’] = ‘HTTP 127.0.0.1:37032’
param[‘sms_type’] = ‘deliver’
param[‘sms_more’] = ‘false’
param[‘sms_pid’] = ‘0’
param[‘sms_dcs’] = ‘0’
param[‘sms_udh’] = ‘false’
param[‘sms_udl’] = ‘4’
param[‘sms_scts’] = ‘1605001995’
param[‘handlers’] = ‘javascript:15,sip:100,regexroute:100’
param[‘json_body’] = ‘true’
param[‘http_use_auth‘] = ‘auth_service_name‘
param[‘copyparams’] = ‘http_use_auth‘
param[‘http_method’] = ‘POST’
Sniffed ‘http.request’ time=1605001996.396972
thread=0x1ecf6b0 ‘Engine Worker’
data=(nil)
retval='(null)’
param[‘body’] = ‘{“more”:false,”udhi”:false,”type”:”deliver”,”orig”:”YateBTS.com”,”pid”:0,”dcs”:0,”scts”:1605001995,”udl”:4,”ud”:”f4f29c0e”,”udh”:false,
“dest”:963123456789,”tpdu”:”040dd04137595e2e870100000211011135518004f4f29c0e”,”data”:
“f4f29c0e”,”text”:”test”,”http_use_auth“:”auth_service_name“}’
param[‘type’] = ‘application/json’
param[‘use_auth‘] = ‘auth_service_name‘
param[‘method’] = ‘POST’
param[‘url’] = ‘http://127.0.0.1/simulate_sms_service.php‘
param[‘accept’] = ‘text/plain;charset=UTF-8’
param[‘multiline’] = ‘false’
param[‘agent’] = ‘YATE/6.2.1 (yate-smsc)’
param[‘wait’] = ‘true’
Returned true ‘http.request’ delay=0.001434
thread=0x1ecf6b0 ‘Engine Worker’
data=(nil)
retval=’test response from this request’
param[‘body’] = ‘{“mobile”:963123456789,”message”:”test”,”senderId”:”YateBTS.com”}’
param[‘type’] = ‘application/json’
param[‘use_auth‘] = ‘auth_service_name‘
param[‘method’] = ‘POST’
param[‘url’] = ‘http://127.0.0.1 /simulate_sms_service.php’
param[‘accept’] = ‘application/json’
param[‘multiline’] = ‘false’
param[‘agent’] = ‘YATE/6.2.1 (yate-smsc)’
param[‘wait’] = ‘true’
param[‘handlers’] = ‘javascript:80,httpclient:90’
param[‘http_Authorization’] = ‘Bearer 916kec3jf4g7850bidah2’
param[‘redirect’] = ‘1’
param[‘xversion_http’] = ‘1.1’
param[‘code’] = ‘200’
param[‘code_text’] = ‘OK’
param[‘xcontent_length’] = ‘307’
param[‘xtype’] = ‘text/html; charset=UTF-8’
Example of sms routed to HTTP Client module and handled by JS script before sending the actual http.request
Sniffed ‘call.route’ time=1605010509.235138
thread=0x1ecf480 ‘Engine Worker’
data=(nil)
retval='(null)’
param[‘route_type’] = ‘msg’
param[‘module’] = ‘smsc_map’
param[‘billid’] = ‘1604937235-4’
param[‘retries’] = ‘4’
param[‘caller’] = ‘YateBTS.com’
param[‘callernumplan’] = ‘unknown’
param[‘callernumtype’] = ‘alphanumeric’
param[‘called’] = ‘963123456789′
param[‘callednumplan’] = ‘isdn’
param[‘callednumtype’] = ‘international’
param[‘submit_info’] = ‘HTTP 127.0.0.1:47962’
param[‘sms_type’] = ‘deliver’
param[‘sms_more’] = ‘false’
param[‘sms_pid’] = ‘0’
param[‘sms_dcs’] = ‘0’
param[‘sms_udh’] = ‘false’
param[‘sms_udl’] = ‘4’
param[‘sms_scts’] = ‘1605010508’
Returned true ‘call.route’ delay=0.001448
thread=0x1ecf480 ‘Engine Worker’
data=(nil)
retval=’http://127.0.0.1/simulate_sms_service.php‘
param[‘route_type’] = ‘msg’
param[‘module’] = ‘smsc_map’
param[‘billid’] = ‘1604937235-4’
param[‘retries’] = ‘4’
param[‘caller’] = ‘YateBTS.com’
param[‘callernumplan’] = ‘unknown’
param[‘callernumtype’] = ‘alphanumeric’
param[‘called’] = ‘963123456789′
param[‘callednumplan’] = ‘isdn’
param[‘callednumtype’] = ‘international’
param[‘submit_info’] = ‘HTTP 127.0.0.1:47962’
param[‘sms_type’] = ‘deliver’
param[‘sms_more’] = ‘false’
param[‘sms_pid’] = ‘0’
param[‘sms_dcs’] = ‘0’
param[‘sms_udh’] = ‘false’
param[‘sms_udl’] = ‘4’
param[‘sms_scts’] = ‘1605010508’
param[‘handlers’] = ‘javascript:15,sip:100,regexroute:100’
param[‘json_body’] = ‘true’
param[‘http_use_auth‘] = ‘auth_service_name‘
param[‘copyparams’] = ‘http_use_auth‘
param[‘http_method’] = ‘POST’
Sniffed ‘http.request’ time=1605010509.238623
thread=0x1ecf480 ‘Engine Worker’
data=(nil)
retval='(null)’
param[‘body’] = ‘{“more”:false,”udhi”:false,”type”:”deliver”,”orig”:”YateBTS.com”,”pid”:0,”dcs”:0,”scts”:1605010508,”udl”:4,
“ud”:”f4f29c0e”,”udh”:false,”dest”:963123456789,”tpdu”:”040dd04137595e2e870100000211014151808004f4f29c0e”,”data”:
“f4f29c0e”,”text”:”test”,”http_use_auth“:”auth_service_name“}’
param[‘type’] = ‘application/json’
param[‘use_auth‘] = ‘auth_service_name‘
param[‘method’] = ‘POST’
param[‘url’] = ‘http://127.0.0.1/simulate_sms_service.php‘
param[‘accept’] = ‘text/plain;charset=UTF-8’
param[‘multiline’] = ‘false’
param[‘agent’] = ‘YATE/6.2.1 (yate-smsc)’
param[‘wait’] = ‘true’
Sniffed ‘http.request’ time=1605010509.240414
thread=0x1ecf480 ‘Engine Worker’
data=(nil)
retval='(null)’
param[‘username’] = ‘my_username’
param[‘password’] = (hidden)
param[‘grant_type’] = ‘password’
param[‘url’] = ‘http://127.0.0.1/simulate_auth_service.php’
param[‘key’] = ‘916kec3jf4g7850bidah2’
param[‘expires_in’] = ‘1605002017889’
param[‘method’] = ‘post’
param[‘type’] = ‘application/x-www-form-urlencoded/text’
param[‘accept’] = ‘application/json’
param[‘multiline’] = ‘true’
param[‘body’] = ‘username=my_username&password=mypassword&grant_type=password’
param[‘wait’] = ‘true’
Returned true ‘http.request’ delay=0.001927
thread=0x1ecf480 ‘Engine Worker’
data=(nil)
retval='{“access_token”:”b4e3c1ki962jghad0f587″,”token_type”:”bearer”,”expires_in”:30,”userName”:”my_username”,
“.issued”:”Tue, 10 Nov 2020 12:15:09 GMT”,”.expires”:”Tue, 10 Nov 2020 12:15:39 GMT”}’
param[‘username’] = ‘my_username’
param[‘password’] = (hidden)
param[‘grant_type’] = ‘password’
param[‘url’] = ‘http://127.0.0.1/simulate_auth_service.php’
param[‘key’] = ‘916kec3jf4g7850bidah2’
param[‘expires_in’] = ‘1605002017889’
param[‘method’] = ‘post’
param[‘type’] = ‘application/x-www-form-urlencoded/text’
param[‘accept’] = ‘application/json’
param[‘multiline’] = ‘true’
param[‘body’] = ‘username=my_username&password=my_password&grant_type=password’
param[‘wait’] = ‘true’
param[‘handlers’] = ‘javascript:80,httpclient:90’
param[‘xversion_http’] = ‘1.1’
param[‘code’] = ‘200’
param[‘code_text’] = ‘OK’
param[‘xcontent_length’] = ‘184’
param[‘xtype’] = ‘text/html; charset=UTF-8’
Returned true ‘http.request’ delay=0.005290
thread=0x1ecf480 ‘Engine Worker’
data=(nil)retval=’test response for this request’
param[‘body’] = ‘{“mobile”:963123456789,”message”:”test”,”senderId”:”YateBTS.com”}’
param[‘type’] = ‘application/json’
param[‘use_auth‘] = ‘auth_service_name‘
param[‘method’] = ‘POST’
param[‘url’] = ‘http://127.0.0.1/simulate_sms_service.php‘
param[‘accept’] = ‘application/json’
param[‘multiline’] = ‘false’
param[‘agent’] = ‘YATE/6.2.1 (yate-smsc)’
param[‘wait’] = ‘true’
param[‘handlers’] = ‘javascript:80,httpclient:90’
param[‘http_Authorization’] = ‘Bearer b4e3c1ki962jghad0f587’
param[‘redirect’] = ‘1’
param[‘xversion_http’] = ‘1.1’
param[‘code’] = ‘200’
param[‘code_text’] = ‘OK’
param[‘xcontent_length’] = ‘307’
param[‘xtype’] = ‘text/html; charset=UTF-8’