Strongly agree with Ben here. Not only does the solution not need to be stateless, but it probably shouldn't be stateless, if you want to benefit from conveniences such as http_async_client. 

Do not be susceptible to received folk wisdom and 20 year-old clichés floating around on Google. Stateless mode is essentially useless, with the exception of the truly most naive load balancers and redirect servers. 

Sent from mobile, apologies for brevity and errors.

On Dec 23, 2024, at 9:22 PM, Ben Kaufman via sr-users <sr-users@lists.kamailio.org> wrote:


  • 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 

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@bcmone.com




 

SIP.US Client Support: 800.566.9810  |  SIPTRUNK Client Support: 800.250.6510  |  Flowroute Client Support: 855.356.9768

img
img
img
 


From: Sergio Charrua via sr-users <sr-users@lists.kamailio.org>
Sent: Sunday, December 22, 2024 6:52 AM
To: Henning Westerholt <hw@gilawa.com>
Cc: Kamailio (SER) - Users Mailing List <sr-users@lists.kamailio.org>; Alexis Fidalgo <alzrck@gmail.com>; Sergio Charrua <sergio.charrua@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> 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>
Sent: Sonntag, 22. Dezember 2024 00:34
To: Kamailio (SER) - Users Mailing List <sr-users@lists.kamailio.org>
Cc: Alexis Fidalgo <alzrck@gmail.com>; Sergio Charrua <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", "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:31PM 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

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
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!

__________________________________________________________
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!