*
the solution needs to be stateless
Why? This seems to be arbitrary. Suppose you wanted to retrieve the data via SIP rather
than http: You'd be using a transaction. There's no magic here, and the benefit
of http_async_client is that you're trading off holding the the process with an
increased usage in shared memory.
Regarding the re-transmission of the 3xx, the likely reason is that you don't have a
Contact: header in the reply, or that the rURI of the ACK doesn't match the Contact,
etc. These are the typical reasons any ACK doesn't get matched.
Regarding an example, I added an example for a 300 reply to my previous example project
https://github.com/whosgonna/kamailio_http_async/tree/reply300
The difference from the main branch is:
1.
300 reply is sent
2.
The reply has a contact header (which a 3xx should have, and is probably why your ACK is
failing)
3.
Added a new sipp testing scenario file for handling a 300, using the received Contact as
the ACK ruri.
Kaufman
Senior Voice Engineer
E: bkaufman(a)bcmone.com
SIP.US Client Support: 800.566.9810 | SIPTRUNK Client Support: 800.250.6510 |
Flowroute Client Support: 855.356.9768
[img]<https://www.sip.us>
[img]<https://www.siptrunk.com>
[img]<https://www.flowroute.com>
________________________________
From: Sergio Charrua via sr-users <sr-users(a)lists.kamailio.org>
Sent: Sunday, December 22, 2024 6:52 AM
To: Henning Westerholt <hw(a)gilawa.com>
Cc: Kamailio (SER) - Users Mailing List <sr-users(a)lists.kamailio.org>rg>; Alexis
Fidalgo <alzrck(a)gmail.com>om>; Sergio Charrua <sergio.charrua(a)voip.pt>
Subject: [SR-Users] Re: Transactions help
CAUTION: This email originated from outside the organization. Do not click links or open
attachments unless you recognize the sender and know the content is safe.
Hi Henning,
Main reason is that the solution needs to be stateless, so, in my understanding, TM module
would turn everything stateful... Am I wrong?
This way I still end up with a stateless redirect server...
Anyway, I will give it a try. Any advice you can share?
Atenciosamente / Kind Regards / Cordialement / Un saludo,
Sérgio Charrua
On Sun, Dec 22, 2024 at 9:13 AM Henning Westerholt
<hw@gilawa.com<mailto:hw@gilawa.com>> wrote:
Hello,
I have not reviewed your cfg in detail. But I am wondering why you go this way of adding
additional complexity (e.g. using a htable, use special message sending functions)? The
easiest approach would be to use the http_async_client module in a way it was designed in
the first place, with the tm module and transactions.
Cheers,
Henning
From: Sergio Charrua via sr-users
<sr-users@lists.kamailio.org<mailto:sr-users@lists.kamailio.org>>
Sent: Sonntag, 22. Dezember 2024 00:34
To: Kamailio (SER) - Users Mailing List
<sr-users@lists.kamailio.org<mailto:sr-users@lists.kamailio.org>>
Cc: Alexis Fidalgo <alzrck@gmail.com<mailto:alzrck@gmail.com>>; Sergio Charrua
<sergio.charrua@voip.pt<mailto:sergio.charrua@voip.pt>>
Subject: [SR-Users] Re: Transactions help
i'm also doing some tests on my side, but in stateless mode and I came to an
interesting approach (but requires a lot more testing...).
The idea is to have http_async_client running in a stateless proxy, and the way I did it
is:
- receive the INVITE
- keep the message buffer in a htable, with key call-id
...
$var(call_id) = $ci;
$sht(stateless_calls=>$var(call_id)) = $mb;
...
- send the async http request, specifying the response route handler
in the route that handles the http response, I do:
- check if the JSON response received from HTTP includes the call-id value (this is
mandatory for the whole solution to work)
- if the call-id is present, get the value from htable with something like:
...
$var(saved_msg) = $sht(stateless_calls=>$var(call_id));
...
- then use $var(saved_msg) to create a new message buffer with msg_set_buffer(
$var(saved_msg) ); (from textopsx module)
- modify the contact header with values found in JSON object returned from the HTTP
response
- send_reply("300", "Multiple Choices");
But msg_set_buffer is not working as I would expect: not sure why, but the Via header now
includes a ;received=127.0.0.1 value (it is not present in the original header), which
results in the following SIP flow
SBC ----> INVITE ----> Kamailio
SBC <---- 100 Trying <---- Kamailio
Kamailio ----> 300 Multi Choice ----> 127.0.0.1
(but the SIP 300 Multi Choice is not the message buffer constructed in the
RELAY_API_RESPONSE route)
This is the code I ended up so far.... If you, or anyone, manage to make this work, please
share within this thread (I will do the same too)
####### Modules Section ########
# Load stateless reply module
loadmodule "sl.so"
loadmodule "tm.so"
# Load PV module (pseudo-variables)
loadmodule "pv.so"
# Load http_async_client module
loadmodule "http_async_client.so"
# Load htable module (for storing data)
loadmodule "htable.so"
# Load JSON module (used if you parse JSON in responses)
loadmodule "jansson.so"
loadmodule "xlog.so"
loadmodule "textops.so"
loadmodule "textopsx.so"
# Other modules as needed...
# loadmodule "xlog.so"
# loadmodule "textopsx.so"
# ...
####### Module Parameters ########
# http_async_client parameters
#modparam("http_async_client", "http_retry", 0) # No retries
modparam("http_async_client", "tcp_keepalive", 1)
modparam("http_async_client", "workers", 4) # Number of async
workers
modparam("http_async_client", "connection_timeout", 200)
# htable module parameters - define a table for storing requests
modparam("htable", "htable",
"stateless_calls=>size=8;autoexpire=3600;")
# ^ 'stateless_calls' is just an example table name; it stores data for up to 1
hour, but should be reduced to seconds....
####### Request Routing Section ########
include_file "includes/handle_options.cfg"
# Main request route
request_route {
if (is_method("ACK") ) { #&& t_check_trans() ){
exit;
}
route(HANDLE_OPTIONS);
# Handle INVITE
if (is_method("INVITE")) {
if (is_method("INVITE")) {
send_reply("100","Trying");
}
# A unique key to identify this call; typically use $ci (Call-ID)
$var(call_id) = $ci;
xlog("L_INFO", "[INVITE] Received INVITE with Call-ID:
$var(call_id)\n");
# ---------------------------------------------------------
# 1) Store the SIP Message in our HTable so we can respond later
# Key: the Call-ID
# Value: the entire SIP message buffer ($mb) + maybe some headers
# ---------------------------------------------------------
$sht(stateless_calls=>$var(call_id)) = $mb;
xlog("L_INFO","[INVITE] $mb \n");
# ---------------------------------------------------------
# 2) Generate JSON payload and Trigger asynchronous HTTP request
#
# We send the Call-ID so the server can return it in the response
#
# ---------------------------------------------------------
jansson_set("string","from" , $hdr(From) ,
"$var(post)");
jansson_set("string","to" , $hdr(To) ,
"$var(post)");
jansson_set("string","r-uri" , $ru ,
"$var(post)");
jansson_set("string","contact" , $hdr(Contact) ,
"$var(post)");
jansson_set("string","call-id" , $ci ,
"$var(post)");
if ( is_present_hf("Identity") )
jansson_set("string","identity" ,
$hdr(Identity) , "$var(post)");
if ( is_present_hf("P-Identity-Bypass") )
jansson_set("string","p-identity-bypass" ,
$hdr(P-Identity-Bypass) , "$var(post)");
if ( is_present_hf("P-Asserted-Identity") )
jansson_set("string","p-asserted-identity" ,
$hdr(P-Asserted-Identity) , "$var(post)");
if ( is_present_hf("P-STSH-UC") )
jansson_set("string","p-stsh-uc" ,
$hdr(P-STSH-UC) , "$var(post)");
jansson_set("string", "request-time" , $xavp(requestTime),
"$var(post)");
$http_req(all) = $null;
$http_req(suspend) = 0
$http_req(method) = "POST";
$http_req(hdr) = "Content-Type: application/json";
$http_req(suspend) = 0;
$http_req(body) = $var(post);
xlog("L_INFO", "HANDLE_STIRSHAKEN - JSON object to POST :\n
$var(post)\n");
xlog("L_INFO", "HANDLE_STIRSHAKEN - HTTP_STSH_QUERY URL =
$var(REQUEST_URL)\n");
http_async_query(
"[http://IP:PORT/ire/v1/stsh]http://IP:PORT/ire/v1/stsh", #
Endpoint
"RELAY_API_RESPONSE" # Callback route
);
# ---------------------------------------------------------
# 3) Since we are 'stateless', do NOT send a reply yet, do NOT forward.
# We simply exit; the code in event_route will handle the final reply.
# ---------------------------------------------------------
xlog("L_INFO", "[INVITE] Asynchronous HTTP call triggered, exit
now.\n");
exit;
}
# For other methods or default:
sl_send_reply("405","Method Not Allowed");
exit;
}
#
# event_route for asynchronous HTTP responses
#
route[RELAY_API_RESPONSE] {
xlog("L_INFO", "[HTTP-ASYNC] HTTP response received!\n");
xlog("L_INFO", "[HTTP-ASYNC] Response body: $(rb)\n");
$var(rtjson) = "";
$var(size) = "";
$var(REQUEST_URL) = "";
# 1) Parse the JSON (assuming you get something like: { "call_id":
"xyz", "contacts":
["sip:alice@1.2.3.4<mailto:sip%3Aalice@1.2.3.4>",
"sip:bob@1.2.3.5<mailto:sip%3Abob@1.2.3.5>"] } )
# This is an example; adapt to your actual response format.
jansson_xdecode($http_rb, "json");
$var(call_id) = $xavp(json=>call-id); # $(rb{s.json, call_id});
xlog("L_INFO", "[HTTP-ASYNC] call_id from HTTP response:
$var(call_id)\n");
if ($var(call_id) == "") {
xlog("L_ERR", "[HTTP-ASYNC] No call_id in HTTP response, cannot
correlate!\n");
return;
}
# 2) Retrieve the original SIP message from the HTable
$var(saved_msg) = $sht(stateless_calls=>$var(call_id));
if ($var(saved_msg) == $null) {
xlog("L_ERR", "[HTTP-ASYNC] No stored message found for
call_id=$var(call_id).\n");
return;
}
# 3) Now we must 're-inject' or 're-process' that saved SIP message in
Kamailio's context
# so we can send a stateless reply.
# We can do this by dynamically building the response.
#
# the typical approach is:
# - Use 'msg_set_buffer' to load the saved message buffer into context
# - Then call 'sl_send_reply()' with the desired code and reason
msg_set_buffer( $var(saved_msg) );
append_to_reply("Contact: $xavp(json=>contact)\r\n");
# 4) Construct and send the 300 Multiple Choices.
# We can add new Contacts by using `append_hf("Contact: ...\r\n")`.
# In a perfect scenario, you'd parse the array properly. For demonstration,
# let's assume $var(contacts) =
"sip:alice@1.2.3.4<mailto:sip%3Aalice@1.2.3.4>,sip:bob@1.2.3.5<mailto:sip%3Abob@1.2.3.5>"
# Add the 300 code and reason
send_reply("300", "Multiple Choices");
# 5) Clean up from the HTable
sht_rm("stateless_calls",$var(call_id));
# Done
xlog("L_INFO", "[HTTP-ASYNC] 300 Multiple Choices sent for
call_id=$var(call_id).\n");
}
Atenciosamente / Kind Regards / Cordialement / Un saludo,
Sérgio Charrua
On Sat, Dec 21, 2024 at 5:31 PM Alexis Fidalgo via sr-users
<sr-users@lists.kamailio.org<mailto:sr-users@lists.kamailio.org>> wrote:
Hello, after the discussion regarding http and http_async, I started some labs to make a
comparison and see what’s the trade off and the associated cost.
Main problem (to me) is im not clear on how transactions behave. Im running some fast
tests now before I start to deploy the final scenarios and
1. A new transaction is created before the http_async call (as the module example shows
for http_async_query function
2. The http_async_query call is executed, receives a response and the HTTP_REPLY route for
http_async_query is executed.
3. As we need to redirect the call, im adding Contact information with append_to_reply and
then calling t_reply(302,”Redirect)
Up to here INVITE is received, a 100 is answered and then the 302 with the expected
Contact information (collected and built from the http_async_query response), problem is
that the 302 is retransmitted 3 more times.
So, im not even sure that im doing the correct actions to process the call this way. Is
there a document/book/reference I can read to understand transactions better?
By now I can not handle the call to be processed correctly (without the retransmissions)
and finish the transaction the correct way.
Any help, link, guide, will be appreciated.
Regards.
__________________________________________________________
Kamailio - Users Mailing List - Non Commercial Discussions --
sr-users@lists.kamailio.org<mailto:sr-users@lists.kamailio.org>
To unsubscribe send an email to
sr-users-leave@lists.kamailio.org<mailto:sr-users-leave@lists.kamailio.org>
Important: keep the mailing list in the recipients, do not reply only to the sender!