<!-- Kamailio Pull Request Template -->
<!-- IMPORTANT: - for detailed contributing guidelines, read: https://github.com/kamailio/kamailio/blob/master/.github/CONTRIBUTING.md - pull requests must be done to master branch, unless they are backports of fixes from master branch to a stable branch - backports to stable branches must be done with 'git cherry-pick -x ...' - code is contributed under BSD for core and main components (tm, sl, auth, tls) - code is contributed GPLv2 or a compatible license for the other components - GPL code is contributed with OpenSSL licensing exception -->
#### Pre-Submission Checklist <!-- Go over all points below, and after creating the PR, tick all the checkboxes that apply --> <!-- All points should be verified, otherwise, read the CONTRIBUTING guidelines from above--> <!-- If you're unsure about any of these, don't hesitate to ask on sr-dev mailing list --> - [x] Commit message has the format required by CONTRIBUTING guide - [x] Commits are split per component (core, individual modules, libs, utils, ...) - [x] Each component has a single commit (if not, squash them into one commit) - [x] No commits to README files for modules (changes must be done to docbook files in `doc/` subfolder, the README file is autogenerated)
#### Type Of Change - [ ] Small bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds new functionality) - [x] Breaking change (fix or feature that would change existing functionality)
#### Checklist: <!-- Go over all points below, and after creating the PR, tick the checkboxes that apply --> - [ ] PR should be backported to stable branches - [ ] Tested changes locally - [ ] Related to issue #XXXX (replace XXXX with an open issue number)
#### Description <!-- Describe your changes in detail -->
You can view, comment on, or merge this pull request online at:
https://github.com/kamailio/kamailio/pull/2721
-- Commit Summary --
* tm: t_suspend.c - do not t_continue with reply if suspended
-- File Changes --
M src/modules/tm/t_suspend.c (6)
-- Patch Links --
https://github.com/kamailio/kamailio/pull/2721.patch https://github.com/kamailio/kamailio/pull/2721.diff
In order to explain why this is needed, I've prepared a new test scenario: https://github.com/kamailio/kamailio-tests/pull/19
This sees an HTTP request being performed in a reply route, using `http_async_client` and with suspension of the transaction. Upon landing into the HTTP reply route, the transaction is suspended for a second time while a new `http_async_query()` is performed.
Without the change in this PR, the suspension during the second query, even though it's logged as happened, doesn't have effect on `t_continue_helper()` which continues processing the reply. The reply is so sent out and when the HTTP reply route is reached afterwards the transaction exists but it's already in terminated state.
An example of .cfg (as used in the example test scenario):
``` request_route { if (is_request()) { if (is_method("INVITE")) { t_newtran(); t_on_reply("INVITE_REPLY"); t_relay("127.0.0.1", "5080"); exit; } else if (is_method("ACK")) { t_relay("127.0.0.1", "5080"); exit; } } drop; }
onreply_route[INVITE_REPLY] { xlog("L_INFO", "================ INVITE_REPLY ==================\n"); if (status=~"200") { http_async_query("http://127.0.0.1:8080/", "HTTP_REPLY1"); } }
route[HTTP_REPLY1] { xlog("L_INFO", "================ HTTP_REPLY1 ==================\n"); if ($http_ok) { xlog("L_INFO", "HTTP GET REPLY1: $http_rs - Response: $http_rb\n"); http_async_query("http://127.0.0.1:8080/", "HTTP_REPLY2"); sl_send_reply("200", "OK"); exit; } else { xlog("L_ERR", "HTTP error: $http_err\n"); } }
route[HTTP_REPLY2] { xlog("L_INFO", "================ HTTP_REPLY2 ================== DONE\n"); } ```
The debug log before the change:
``` 6(32) DEBUG: tm [t_lookup.c:1612]: t_lookup_ident_filter(): transaction found 6(32) DEBUG: http_async_client [async_http.c:235]: async_http_cb(): resuming transaction (44930:409999179) 6(32) DEBUG: tm [t_lookup.c:1612]: t_lookup_ident_filter(): transaction found 6(32) DEBUG: tm [t_suspend.c:364]: t_continue_helper(): continuing from a suspended reply - resetting the suspend branch flag
6(32) INFO: <script>: ================ HTTP_REPLY1 ================== 6(32) INFO: <script>: HTTP GET REPLY1: 200 - Response:
Now it calls async_send_query():
6(32) DEBUG: tm [t_suspend.c:113]: t_suspend(): this is a suspend on reply - setting msg flag to SUSPEND 6(32) DEBUG: tm [t_lookup.c:1034]: t_check_msg(): msg (0x7f48b3cd6b60) id=2/27 global id=2/27 T start=0x7f48b3cd34d8 6(32) DEBUG: tm [t_lookup.c:1109]: t_check_msg(): T (0x7f48b3cd34d8) already found for msg (0x7f48b3cd6b60)! 6(32) DEBUG: tm [t_suspend.c:121]: t_suspend(): found a a match with branch id [0] - cloning reply message to t->uac[branch].reply 6(32) DEBUG: tm [t_suspend.c:133]: t_suspend(): saving transaction data
6(32) DEBUG: http_async_client [async_http.c:469]: async_send_query(): transaction suspended [44930:409999179]
(index and label are as expected)
6(32) DEBUG: http_async_client [async_http.c:625]: async_push_query(): query sent [http://127.0.0.1:8080/] (0x7f48b3cdfa08) to worker 1
Here it should have suspended, but it's actually continuing in the reply route:
6(32) DEBUG: tm [t_suspend.c:408]: t_continue_helper(): restoring previous environment 6(32) DEBUG: tm [t_suspend.c:435]: t_continue_helper(): t is not local - relaying reply with status code: [200] ```
@giavac pushed 1 commit.
bdd602c8c10367de1be097912c9d3bc359a94676 tm: t_suspend.c - unlock t_continue and reset flag if suspended again
After more testing, in particular with multiple http_async_client workers, I've pushed some changes to ensure t_continue is not locked and the state correctly handled if the route execution suspends again the transaction. I've also moved the changes to _after_ resetting the fake environment, in order to clean it.
The reset of faked env was something I wanted to ask about, but I didn't comment before because I didn't get the chance to look more at the code.
I think the t_suspend()/t_continue() were somehow limited to be used only once for a request or a reply, not to suspend again from a t_continue(). It does not mean that it has to stay like this, but your commit seems to affect re-suspending a reply. Have you looked also at re-suspending requests? It may just works, because request processing does not imply any forwarding of the message after config route execution, like happens for replies.
If the commit makes it coherent (i.e., re-suspending for requests and replies has similar behaviour), then the PR can be merged if no other comments.
Thanks Daniel,
Have you looked also at re-suspending requests? It may just works, because request processing does not imply any forwarding of the message after config route execution, like happens for replies.
Yes, re-suspending requests just works, and used it in the past.
If the commit makes it coherent (i.e., re-suspending for requests and replies has similar behaviour), then the PR can be merged if no other comments.
Yes, with these changes re-suspending a reply becomes possible and similar to re-suspending a request.
I'm adding a test case with requests suspended twice in `kamailio-tests`.
Will leave this open a little longer to give me the chance to do more testing and for further comments.
OK.
@giavac pushed 1 commit.
a8f52fd86a59f8f411ebc063f09eb54cb3047b6c tm: t_reply.c - received_reply() done if suspended after route
I've been testing a scenario where processing the HTTP requests took longer enough for SIP 200 retransmissions to be triggered. In that case, `reply_received()` checks if the message was marked as suspended, but that works only if the onreply route calls `t_suspend()`. The retransmission was dropped instead, so I had to look into the transaction's T_ASYNC_SUSPENDED flag. The latest commit solves this, and should be safe for normal usages. I need to test more edge cases, will update.
Merged #2721 into master.
I haven't seen any issues with these changes, so proceeded with the merge into master.