This sometimes works when the subset of functionality actually needed is very small. But generally speaking, it ends up being something kind of like, though not exactly:

https://thedailywtf.com/articles/the_inner-platform_effect


The emphasis here would be on the aspect of such a system wherein it is a poorly-performing and naive subset of the thing it is meant to replace.

One common example in the Kamailio world is efforts to keep dialog state using htable, or by other means. This works for the modal, well-behaved dialog, but the life cycle of dialogs is very diverse and there are many edge-cases in the way a dialog can come to an end. Race conditions are common, as well. It is wisest to simply use the mature reference implementation.

Mature is a key word. If I remember correctly, it took close to a decade to shake all the problems out of the dialog module. SIP stacks themselves take many years to mature. There are lots of SIP implementations out there, but only a handful are good and production-worthy. 

TM is always the right tool, and a homegrown transaction state model is always the wrong approach. At the very least, this is so statistically valid as to be reasonably guaranteed.

-- Alex

Sent from mobile, apologies for brevity and errors.

On Dec 23, 2024, at 8:35 PM, Henning Westerholt via sr-users <sr-users@lists.kamailio.org> wrote:



Hello Sergio,

 

but you are using a htable to actually keep a state. So, your solution would be not stateless in the end. You are simply not using the tm module to keep the state, but by another means.

 

Cheers,

 

Henning

 

 

From: Sergio Charrua <sergio.charrua@voip.pt>
Sent: Sonntag, 22. Dezember 2024 13:53
To: Henning Westerholt <hw@gilawa.com>
Cc: Kamailio (SER) - Users Mailing List <sr-users@lists.kamailio.org>; Alexis Fidalgo <alzrck@gmail.com>
Subject: Re: [SR-Users] Re: Transactions help

 

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:13AM 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",  # 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!