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", # 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", "sip:bob@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,sip:bob@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> 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
- 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 To unsubscribe send an email to sr-users-leave@lists.kamailio.org Important: keep the mailing list in the recipients, do not reply only to the sender!