tm: Number of fixes in code and documentation for serial forking.
authorJan Janak <jan@ryngle.com>
Sat, 17 Oct 2009 22:39:31 +0000 (00:39 +0200)
committerJan Janak <jan@ryngle.com>
Sat, 17 Oct 2009 22:42:11 +0000 (00:42 +0200)
This patch improves the serial forking related code and documentation.
The original code suffers from shortcommits, some of which were result
of the migration to the new core.

The function that decodes destination sets encoded in AVPs has been
improved, because the original version did not handle properly strings
with missing elements. In such case the original implementation was
likely to overwrite memory, because it did not check the return value
of strchr properly. The new implementation tries to handle this
situation. It continues parsing as long as it can, it only requires
that the request-uri string is present, all other fields are made
optional and their variables are properly initialized if their values
cannot be found in the AVP.

There was a bug in the implementation of fr_inv_timer_next modparam in
the original version, changes to the parameter value were ignored by
the serial forking code. This was reported by Andrei and is now fixed
by this commit. The parameter fr_inv_timer_next can now be configured
at runtime with the configuration framework. Its value is in
milliseconds and unlike fr_inv_timer, this timer cannot be configured
separately for individual branches.

Obsolete definition of INV_FR_TIME_OUT_FIRST has been removed. That
macro is not used anywhere in the code, thus it is not needed.

There were several places where LM_DBG printed a string and relied on
the string being zero terminated, this may or may not be true in the
future and this patch uses the macro STR_FMT where appropriate, which
is safer.

Function t_next_contacts now checks if the function decode_branch_info
really returned values for the dst_uri and the path vector. If not then
it calls reset_dst_uri and reset_path_vector. Previous version of the
code crashed sip-router, this is likely due to the merge and updates in
the sip-router core.

We now use t_set_fr in t_next_contacts for setting the fr_inv_timer
value to fr_inv_timer_next. This is much more efficient than creating
AVPs with new timer values. Also the new value of the timer is now
taken from a variable in the configuration framework, instead of just a
regular global variable configured through modparam. This way we can
adjust the value of the timer on the fly. Configuring it through
modparam is, of course, possible too.

The value of of fr_inv_timer_next is now in milliseconds, instead of
seconds. That's the only possibly incompatible change. However, this is
consistent with all other timers in tm module, it is more efficient and
it offers better granularity.

A missing call to destroy_avp has been added to t_next_contacts, in the
code which is executed when no transaction exists. There, the avp
should also be destroyed if all values have been exhausted and none of
them had Q_FLAG set. This is a corner case which should not happen
under normal circumstances, because that situation only happens if all
branches have the same q value. Such AVP would not have been created by
t_load_contacts and therefore t_next_contacts should not be called, but
this bug may be triggered if someone uses t_next_contacts in an
unexpected way and it is probably better to have it fixed.

Also the code which restores the value of fr_inv_timer at the end of
t_next_contacts did not work properly. This patch fixes that. It first
tries to retrieve a value configured with t_set_fr, but that is not
guaranteed to succeed. After that it also tries the timer AVP and
finally the configuration framework. The configuration framework always
yields a value, so we can always restore the timer value, but we may
fail to restore individual transaction timer values set by t_set_fr. If
that fails then the global value from the configuration framework is
used. This is documented as a shortcomming in the README and in the
code.

In addition to code changes this patch also expands documentation on
functions t_load_contacts and t_next_contacts, describing their
operation in more detail. Also the format of the contacts AVP is now
documented.

Finally, there is a whole new section in the README which describes
how serial/parallel forking can be achieved with t_load_contacts and
t_next_contacts and provides a number of examples.

modules/tm/README
modules/tm/config.c
modules/tm/config.h
modules/tm/doc/functions.xml
modules/tm/doc/params.xml
modules/tm/doc/tm.xml
modules/tm/t_serial.c
modules/tm/tm.c

index 89d2281..e580668 100644 (file)
@@ -15,106 +15,6 @@ Juha Heinanen
    Revision $Revision$ $Date$
      __________________________________________________________________
 
-   1.1. Overview
-   1.2. Known Issues
-   1.3. Parameters
-
-        1.3.1. fr_timer (integer)
-        1.3.2. fr_inv_timer (integer)
-        1.3.3. max_inv_lifetime (integer)
-        1.3.4. max_noninv_lifetime (integer)
-        1.3.5. wt_timer (integer)
-        1.3.6. delete_timer (integer)
-        1.3.7. retr_timer1 (integer)
-        1.3.8. retr_timer2 (integer)
-        1.3.9. noisy_ctimer (integer)
-        1.3.10. restart_fr_on_each_reply (integer)
-        1.3.11. auto_inv_100 (integer)
-        1.3.12. auto_inv_100_reason (string)
-        1.3.13. unix_tx_timeout (integer)
-        1.3.14. aggregate_challenges (integer)
-        1.3.15. reparse_invite (integer)
-        1.3.16. ac_extra_hdrs (string)
-        1.3.17. blst_503 (integer)
-        1.3.18. blst_503_def_timeout (integer)
-        1.3.19. blst_503_min_timeout (integer)
-        1.3.20. blst_503_max_timeout (integer)
-        1.3.21. blst_methods_add (unsigned integer)
-        1.3.22. blst_methods_lookup (unsigned integer)
-        1.3.23. cancel_b_method (integer)
-        1.3.24. reparse_on_dns_failover (integer)
-        1.3.25. on_sl_reply (string)
-        1.3.26. fr_inv_timer_next (integer)
-        1.3.27. contacts_avp (string)
-        1.3.28. fr_timer_avp (string)
-        1.3.29. fr_inv_timer_avp (string)
-        1.3.30. unmatched_cancel (string)
-        1.3.31. ruri_matching (integer)
-        1.3.32. via1_matching (integer)
-        1.3.33. pass_provisional_replies (integer)
-        1.3.34. default_code (integer)
-        1.3.35. default_reason (string)
-        1.3.36. disable_6xx_block (integer)
-
-   1.4. Functions
-
-        1.4.1. t_relay_to_udp(ip, port), t_relay_to_udp(),
-                t_relay_to_tcp(ip, port) t_relay_to_tcp()
-                t_relay_to_tls(ip, port) t_relay_to_tls()
-                t_relay_to_sctp(ip, port) t_relay_to_sctp()
-
-        1.4.2. t_relay() t_relay(host, port)
-        1.4.3. t_on_failure(failure_route)
-        1.4.4. t_on_reply(onreply_route)
-        1.4.5. t_on_branch(branch_route)
-        1.4.6. append_branch()
-        1.4.7. t_newtran()
-        1.4.8. t_reply(code, reason_phrase)
-        1.4.9. t_lookup_request()
-        1.4.10. t_retransmit_reply()
-        1.4.11. t_release()
-        1.4.12. t_forward_nonack() t_forward_nonack(ip, port)
-                t_forward_nonack_udp(ip, port) t_forward_nonack_tcp(ip,
-                port) t_forward_nonack_tls(ip, port)
-                t_forward_nonack_sctp(ip, port)
-
-        1.4.13. t_set_fr(fr_inv_timeout [, fr_timeout])
-        1.4.14. t_reset_fr()
-        1.4.15. t_set_max_lifetime(inv_lifetime, noninv_lifetime)
-        1.4.16. t_reset_max_lifetime()
-        1.4.17. t_set_retr(retr_t1_interval, retr_t2_interval)
-        1.4.18. t_reset_retr()
-        1.4.19. t_set_auto_inv_100(0|1)
-        1.4.20. t_branch_timeout()
-        1.4.21. t_branch_replied()
-        1.4.22. t_any_timeout()
-        1.4.23. t_any_replied()
-        1.4.24. t_grep_status("code")
-        1.4.25. t_is_canceled()
-        1.4.26. t_is_expired()
-        1.4.27. t_relay_cancel()
-        1.4.28. t_lookup_cancel(), t_lookup_cancel(1)
-        1.4.29. t_drop_replies()
-        1.4.30. t_save_lumps()
-        1.4.31. t_load_contacts()
-        1.4.32. t_next_contacts()
-        1.4.33. t_check_trans()
-        1.4.34. t_set_disable_6xx(0|1)
-        1.4.35. t_set_disable_failover(0|1)
-
-   1.5. TM Module API
-
-        1.5.1. Defines
-        1.5.2. Functions
-
-              1.5.2.1. register_tmcb(cb_type, cb_func)
-              1.5.2.2. load_tm(*import_structure)
-              1.5.2.3. int t_suspend(struct sip_msg *msg, unsigned int
-                      *hash_index, unsigned int *label)
-
-              1.5.2.4. int t_continue(unsigned int hash_index, unsigned
-                      int label, struct action *route)
-
 1.1. Overview
 
    TM module enables stateful processing of SIP transactions. The main use
@@ -170,7 +70,179 @@ Note
    implemented in the TMX module: "modules_k/tmx". Check it to see if what
    you are looking for is there.
 
-1.2. Known Issues
+1.2. Serial Forking Based on Q Value
+
+   A single SIP INVITE request may be forked to multiple destinations. We
+   call the set of all such destinations a destination set. Individual
+   elements within the destination sets are called branches. The script
+   writer can add URIs to the destination set from the configuration file,
+   or they can be loaded from the user location database, each registered
+   contact then becomes one branch in the destination set.
+
+   The default behavior of the tm module, if it encounters a SIP message
+   with multiple branches in the destination set, it to forward the SIP
+   message to all the branches in parallel. That means it sends the
+   message to all the branch destinations before it waits for replies from
+   any of them. This is the default behavior if you call t_relay() and
+   similar functions without anything else.
+
+   Another approach of handling multiple branches in a destination set it
+   serial forking. When configured to do serial forking, the server takes
+   the first branch out of the destination set, forwards the message to
+   its destination and waits for a reply or timeout. Only after a reply
+   has been received or the timeout occurred, the server takes another
+   destination from the destination set and tries again, until it receives
+   a positive final reply or until all branches from the destination set
+   have been tried.
+
+   Yet another, more sophisticated, way of handling multiple branches is
+   combined serial/parallel forking, where individual branches within the
+   destination set are assigned priorities. The order in which individual
+   branches are tried is then determined by their relative priority within
+   the destination set. Branches can be tried sequentially in the
+   descending priority order and all branches that have the same priority
+   can be tried in parallel. Such combined serial/parallel forking can be
+   achieved in the tm module with the help of functions t_load_contacts()
+   and t_next_contacts().
+
+   Every branch in the destination set is assigned a priority number, also
+   known as the q value. The q value is a floating point number in a range
+   0 to 1.0. The higher the q value number, the more priority is the
+   particular branch in the destination set is given. Branches with q
+   value 1.0 have maximum priority, such branches should be always tried
+   first in serial forking. Branches with q value 0 have the lowest
+   priority and they should by tried after all other branches with higher
+   priority in the destination set.
+
+   As an example, consider the following simple configuration file. When
+   the server receives an INVITE, it creates four branches for it with
+   usernames A through D and then forwards the request using t_relay():
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com");
+  append_branch("sip:c@example.com");
+  append_branch("sip:d@example.com");
+
+  t_relay();
+  break;
+}
+
+   With this configuratin the server forwards the request to all four
+   branches at once, performing parallel forking described above. We did
+   not set the q value for individual branches in this example but we can
+   do that by slightly modifying the arguments given to append_branch():
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com", "0.5");
+  append_branch("sip:c@example.com", "0.5");
+  append_branch("sip:d@example.com", "1.0");
+
+  t_relay();
+  break;
+}
+
+   Here we assigned q value 0.5 to branches B and C and q value 1.0 to
+   branch D. We did not specify any q value for branch A and in that case
+   it is assumed that its q value is the lowest from all branches within
+   the destination set. If you try to run this example again, you will
+   figure out that nothing changed, t_relay() still forward the message to
+   all branches in parallel.
+
+   We now want to implement the combined serial/parallel forking. Branch D
+   should be tried first, because its q value is 1.0. Branches B and C
+   should be tried in parallel, but only after D finishes. Branch A should
+   be tried after B and C finished, because its q value (the default) is
+   the lowest of all. To do that, we need to introduce two new functions
+   into our example and one tm module parameter:
+modparam("tm", "contacts_avp", "tm_contacts");
+
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com", "0.5");
+  append_branch("sip:c@example.com", "0.5");
+  append_branch("sip:d@example.com", "1.0");
+
+  t_load_contacts();
+
+  t_next_contacts();
+  t_relay();
+  break;
+}
+
+   First of all, the tm module parameter is mandatory if the two new
+   functions are used. Function t_load_contacts() takes all branches from
+   the destination set, sorts them according to their q values and stores
+   them in the AVP configured in the modparam. The function also clears
+   the destination set, which means that it removes all branches
+   configured before with seturi() and append_branch().
+
+   Function t_next_contacts() takes the AVP created by the previous
+   function and extract the branches with highest q values from it. In our
+   example it is branch D. That branch is then put back into the
+   destination set and when the script finally reaches t_relay(), the
+   destination set only contains branch D and the request will be
+   forwarded there.
+
+   We achieved the first step of serial forking, but this is not
+   sufficient. Now we also need to forward to other branches with lower
+   priority values when branch D finishes. To do that, we need to extend
+   the configuration file again and introduce a failure_route section:
+modparam("tm", "contacts_avp", "tm_contacts");
+
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com", "0.5");
+  append_branch("sip:c@example.com", "0.5");
+  append_branch("sip:d@example.com", "1.0");
+
+  t_load_contacts();
+
+  t_next_contacts();
+  t_on_failure("serial");
+  t_relay();
+  break;
+}
+
+failure_route["serial"]
+{
+  if (!t_next_contacts()) {
+    exit;
+  }
+
+  t_on_failure("serial");
+  t_relay();
+}
+
+   The failure_route section will be executed when branch D finishes. It
+   executes t_next_contacts() again and this time the function retrieves
+   branches B and C from the AVP and adds them to the destination set.
+   Here we need to check the return value of the function, because a
+   negative value indicates that there were no more branches, in that case
+   the failure_route should just terminate and forward the response from
+   branch D upstream.
+
+   If t_next_contact() returns a positive value then we have more new
+   branches to try and we need to setup the failure_route again and call
+   t_relay(). In our example the request will now be forwarded to branches
+   B and C in paralell, because they were both added to the destination
+   set by t_next_contacts() at the same time.
+
+   When branches B and C finish, the failure_route block is executed
+   again, this time t_next_contacts() puts the final branch A into the
+   destination set and t_relay() forwards the request there.
+
+   And that's the whole example, we achieved combined serial/parallel
+   forking based on the q value of individual branches. In real-world
+   configuration files the script writer would need to check the return
+   value of all functions and also configure some additional parameters,
+   such as fr_inv_timer_next and restart_fr_on_each_reply. Also the
+   destination set would not be configured directly in the configuration
+   file, but can be retrieved from the user location database, for
+   example. In that case registered contacts will be stored in the
+   destination set as branches and their q values (provided by UAs) will
+   be used.
+
+1.3. Known Issues
 
      * Possibly, performance could be improved by not parsing non-INVITEs,
        as they do not be replied with 100, and do not result in
@@ -185,13 +257,19 @@ Note
      * t_replicate should be done more cleanly--Vias, Routes, etc. should
        be removed from a message prior to replicating it (well, does not
        matter any longer so much as there is a new replication module).
+     * Function t_next_contacts should restore the value of timer
+       fr_inv_timer when there are no more branches to be processed
+       serially. The function can restore the timer properly if it has
+       been configured through an AVP or with the config framework, but
+       may fail to restore timer values configured for individual
+       transactions with t_set_fr.
 
-1.3. Parameters
+1.4. Parameters
 
    Revision History
    Revision $Revision$ $Date$
 
-1.3.1. fr_timer (integer)
+1.4.1. fr_timer (integer)
 
    Timer which hits if no final reply for a request or ACK for a negative
    INVITE reply arrives (in milliseconds).
@@ -205,7 +283,7 @@ Note
 modparam("tm", "fr_timer", 10000)
 ...
 
-1.3.2. fr_inv_timer (integer)
+1.4.2. fr_inv_timer (integer)
 
    Timer which hits if no final reply for an INVITE arrives after a
    provisional message was received (in milliseconds).
@@ -222,7 +300,7 @@ modparam("tm", "fr_timer", 10000)
 modparam("tm", "fr_inv_timer", 180000)
 ...
 
-1.3.3. max_inv_lifetime (integer)
+1.4.3. max_inv_lifetime (integer)
 
    Maximum time an INVITE transaction is allowed to be active (in
    milliseconds). After this interval has passed from the transaction
@@ -258,7 +336,7 @@ modparam("tm", "fr_inv_timer", 180000)
 modparam("tm", "max_inv_lifetime", 150000)
 ...
 
-1.3.4. max_noninv_lifetime (integer)
+1.4.4. max_noninv_lifetime (integer)
 
    Maximum time a non-INVITE transaction is allowed to be active (in
    milliseconds). After this interval has passed from the transaction
@@ -288,7 +366,7 @@ modparam("tm", "max_inv_lifetime", 150000)
 modparam("tm", "max_inv_lifetime", 30000)
 ...
 
-1.3.5. wt_timer (integer)
+1.4.5. wt_timer (integer)
 
    Time for which a transaction stays in memory to absorb delayed messages
    after it completed (in milliseconds); also, when this timer hits,
@@ -303,7 +381,7 @@ modparam("tm", "max_inv_lifetime", 30000)
 modparam("tm", "wt_timer", 1000)
 ...
 
-1.3.6. delete_timer (integer)
+1.4.6. delete_timer (integer)
 
    Time after which a to-be-deleted transaction currently ref-ed by a
    process will be tried to be deleted again (in milliseconds).
@@ -318,7 +396,7 @@ modparam("tm", "wt_timer", 1000)
 modparam("tm", "delete_timer", 100)
 ...
 
-1.3.7. retr_timer1 (integer)
+1.4.7. retr_timer1 (integer)
 
    Initial retransmission period (in milliseconds).
 
@@ -329,7 +407,7 @@ modparam("tm", "delete_timer", 100)
 modparam("tm", "retr_timer1", 1000)
 ...
 
-1.3.8. retr_timer2 (integer)
+1.4.8. retr_timer2 (integer)
 
    Maximum retransmission period (in milliseconds). The retransmission
    interval starts with retr_timer1 and increases until it reaches this
@@ -342,7 +420,7 @@ modparam("tm", "retr_timer1", 1000)
 modparam("tm", "retr_timer2", 2000)
 ...
 
-1.3.9. noisy_ctimer (integer)
+1.4.9. noisy_ctimer (integer)
 
    If set, INVITE transactions that time-out (FR INV timer) will be always
    replied. If it's not set, the transaction has only one branch and no
@@ -361,7 +439,7 @@ modparam("tm", "retr_timer2", 2000)
 modparam("tm", "noisy_ctimer", 1)
 ...
 
-1.3.10. restart_fr_on_each_reply (integer)
+1.4.10. restart_fr_on_each_reply (integer)
 
    If set (default), the fr_inv_timer for an INVITE transaction will be
    restarted for each provisional reply received (rfc3261 mandated
@@ -383,7 +461,7 @@ modparam("tm", "noisy_ctimer", 1)
 modparam("tm", "restart_fr_on_each_reply", 0)
 ...
 
-1.3.11. auto_inv_100 (integer)
+1.4.11. auto_inv_100 (integer)
 
    If set (default) tm will automatically send and 100 reply to INVITEs.
 
@@ -403,7 +481,7 @@ modparam("tm", "restart_fr_on_each_reply", 0)
 modparam("tm", "auto_inv_100", 0)
 ...
 
-1.3.12. auto_inv_100_reason (string)
+1.4.12. auto_inv_100_reason (string)
 
    Set reason text of the automatically send 100 to an INVITE.
 
@@ -416,7 +494,7 @@ modparam("tm", "auto_inv_100", 0)
 modparam("tm", "auto_inv_100_reason", "Trying")
 ...
 
-1.3.13. unix_tx_timeout (integer)
+1.4.13. unix_tx_timeout (integer)
 
    Unix socket transmission timeout, in milliseconds.
 
@@ -431,7 +509,7 @@ modparam("tm", "auto_inv_100_reason", "Trying")
 modparam("tm", "unix_tx_timeout", 250)
 ...
 
-1.3.14. aggregate_challenges (integer)
+1.4.14. aggregate_challenges (integer)
 
    If set (default), the final reply is a 401 or a 407 and more then one
    branch received a 401 or 407, then all the WWW-Authenticate and
@@ -448,7 +526,7 @@ modparam("tm", "unix_tx_timeout", 250)
 modparam("tm", "aggregate_challenges", 0)
 ...
 
-1.3.15. reparse_invite (integer)
+1.4.15. reparse_invite (integer)
 
    If set (default), the CANCEL and negative ACK requests are constructed
    from the INVITE message which was sent out instead of building them
@@ -474,7 +552,7 @@ modparam("tm", "aggregate_challenges", 0)
 modparam("tm", "reparse_invite", 0)
 ...
 
-1.3.16. ac_extra_hdrs (string)
+1.4.16. ac_extra_hdrs (string)
 
    Header fields prefixed by this parameter value are included in the
    CANCEL and negative ACK messages if they were present in the outgoing
@@ -492,7 +570,7 @@ modparam("tm", "reparse_invite", 0)
 modparam("tm", "ac_extra_hdrs", "myfavoriteheaders-")
 ...
 
-1.3.17. blst_503 (integer)
+1.4.17. blst_503 (integer)
 
    If set and the blacklist support is enabled, every 503 reply source is
    added to the blacklist. The initial blacklist timeout (or ttl) depends
@@ -510,7 +588,7 @@ modparam("tm", "ac_extra_hdrs", "myfavoriteheaders-")
 modparam("tm", "blst_503", 1)
 ...
 
-1.3.18. blst_503_def_timeout (integer)
+1.4.18. blst_503_def_timeout (integer)
 
    Blacklist interval in seconds for a 503 reply with no Retry-After
    header. See also blst_503, blst_503_min_timeout and
@@ -525,7 +603,7 @@ modparam("tm", "blst_503", 1)
 modparam("tm", "blst_503_def_timeout", 120)
 ...
 
-1.3.19. blst_503_min_timeout (integer)
+1.4.19. blst_503_min_timeout (integer)
 
    Minimum blacklist interval in seconds for a 503 reply with a
    Retry-After header. It will be used if the Retry-After value is
@@ -539,7 +617,7 @@ modparam("tm", "blst_503_def_timeout", 120)
 modparam("tm", "blst_503_min_timeout", 30)
 ...
 
-1.3.20. blst_503_max_timeout (integer)
+1.4.20. blst_503_max_timeout (integer)
 
    Maximum blacklist interval in seconds for a 503 reply with a
    Retry-After header. It will be used if the Retry-After value is
@@ -553,7 +631,7 @@ modparam("tm", "blst_503_min_timeout", 30)
 modparam("tm", "blst_503_max_timeout", 604800)
 ...
 
-1.3.21. blst_methods_add (unsigned integer)
+1.4.21. blst_methods_add (unsigned integer)
 
    Bitmap of method types that trigger blacklisting on transaction
    timeouts. (This setting has no effect on blacklisting because of send
@@ -578,7 +656,7 @@ modparam("tm", "blst_503_max_timeout", 604800)
 modparam("tm", "blst_methods_add", 33)
 ...
 
-1.3.22. blst_methods_lookup (unsigned integer)
+1.4.22. blst_methods_lookup (unsigned integer)
 
    Bitmap of method types that are looked-up in the blacklist before
    statefull forwarding. See also blst_methods_add
@@ -592,7 +670,7 @@ modparam("tm", "blst_methods_add", 33)
 modparam("tm", "blst_methods_lookup", 1)
 ...
 
-1.3.23. cancel_b_method (integer)
+1.4.23. cancel_b_method (integer)
 
    Method used when attempting to CANCEL an unreplied transaction branch
    (a branch where no reply greater the 99 was received). The possible
@@ -630,7 +708,7 @@ modparam("tm", "blst_methods_lookup", 1)
 modparam("tm", "cancel_b_method", 1)
 ...
 
-1.3.24. reparse_on_dns_failover (integer)
+1.4.24. reparse_on_dns_failover (integer)
 
    If set to 1, the SIP message after a DNS failover is constructed from
    the outgoing message buffer of the failed branch instead of from the
@@ -658,7 +736,7 @@ modparam("tm", "cancel_b_method", 1)
 modparam("tm", "reparse_on_dns_failover", 0)
 ...
 
-1.3.25. on_sl_reply (string)
+1.4.25. on_sl_reply (string)
 
    Sets reply route block, to which control is passed when a reply is
    received that has no associated transaction. The reply is passed to the
@@ -675,27 +753,56 @@ onreply_route["stateless_replies"] {
         return 0;
 }
 
-1.3.26. fr_inv_timer_next (integer)
-
-   Value of the Final Response timeout for INVITE transactions to be used
-   during serial forwarding:
-
-   Function t_next_contacts() sets fr_inv_timer to fr_inv_timer_next value
-   if, after t_next_contacts() is called, there are still lower qvalue
-   contacts available, and to fr_inv_timer value if there are not.
-
-   Default value is 30.
+1.4.26. fr_inv_timer_next (integer)
+
+   This parameter can be used to configure an alternative value for the
+   fr_inv_timer timer. This alternative value is used in place of
+   fr_inv_timer when serial forking takes place. It is used for all
+   branches during serial forking except the last one. The last branch (or
+   a set of parallel branches) use the original value from fr_inv_timer
+   again.
+
+   The purpose of the timer is to allow an administrator to configure a
+   shorter version of fr_inv_timer that is used only when serial forking
+   takes place. Forwarding branches one after another is much more time
+   consuming, because every serial branch has to wait for the result of
+   the previous one. That can take up to the value of fr_inv_timer and
+   this timer is configured to two minutes by default. Hence, if you have
+   three serial branches then completing the transaction can take six
+   minutes with default timer values.
+
+   In practise, the transaction will be terminated sooner, because the
+   timer max_inv_lifetime hits after three minutes. Thus, some of the
+   serial branches might not be forwarded at all. And this is exactly what
+   fr_inv_timer_next is for. You can configure the timer to a shorter
+   value to ensure that all serial branches are tried before the timer
+   max_inv_lifetime hits.
+
+   Note that if there is only one branch or if the current serial branch
+   is the last one (i.e. no more serial forking takes place after this
+   branch is finished) then fr_inv_timer_next is not used, instead the
+   branch uses the longer fr_inv_timer.
+
+   Function t_next_contacts() sets fr_inv_timer to fr_inv_timer_next if
+   serial forking takes place and there is more than one serial branch.
+
+   The administrator can configure the value of this timer using the
+   configuration framework on the fly. But unlike fr_inv_timer, it is not
+   possible to configure the value of this timer on per-transaction basis.
+
+   The value of this timer is to be specified in milliseconds. The default
+   value is 30000ms.
 
    Example 26. Set fr_inv_timer_next parameter
 ...
-modparam("tm", "fr_inv_timer_next", 10)
+modparam("tm", "fr_inv_timer_next", 10000)
 ...
 
-1.3.27. contacts_avp (string)
+1.4.27. contacts_avp (string)
 
-   Internal AVP that t_load_contacts() function uses to store contacts of
-   the destination set and that t_next_contacts() function uses to restore
-   those contacts.
+   This is the name or Id of an AVP that t_load_contacts() function uses
+   to store contacts of the destination set and that t_next_contacts()
+   function uses to restore those contacts.
 
    Default value is "NULL" (t_load_contacts()/t_next_contacts() functions
    are disabled).
@@ -705,7 +812,7 @@ modparam("tm", "fr_inv_timer_next", 10)
 modparam("tm", "contacts_avp", "$avp(i:25)")
 ...
 
-1.3.28. fr_timer_avp (string)
+1.4.28. fr_timer_avp (string)
 
    The value of fr_timer timer can be overriden on per-transaction basis.
    The administrator can provide a value to be used for a particular
@@ -734,7 +841,7 @@ Note
 modparam("tm", "fr_timer_avp", "i:708")
 ...
 
-1.3.29. fr_inv_timer_avp (string)
+1.4.29. fr_inv_timer_avp (string)
 
    The value of fr_inv_timer timer can be overriden on per-transaction
    basis. The administrator can provide a value to be used for a
@@ -764,7 +871,7 @@ Note
 modparam("tm", "fr_inv_timer_avp", "my_fr_inv_timer")
 ...
 
-1.3.30. unmatched_cancel (string)
+1.4.30. unmatched_cancel (string)
 
    This parameter selects between forwarding CANCELs that do not match any
    transaction statefully (0, default value), statelessly (1) or dropping
@@ -783,7 +890,7 @@ modparam("tm", "fr_inv_timer_avp", "my_fr_inv_timer")
 modparam("tm", "unmatched_cancel", "2")
 ...
 
-1.3.31. ruri_matching (integer)
+1.4.31. ruri_matching (integer)
 
    If set it will also try to match the request uri when doing pre-3261
    transaction matching (the via branch parameter does not contain the
@@ -802,7 +909,7 @@ modparam("tm", "unmatched_cancel", "2")
 modparam("tm", "ruri_matching", 1)
 ...
 
-1.3.32. via1_matching (integer)
+1.4.32. via1_matching (integer)
 
    If set it will also try to match the topmost via when doing pre-3261
    transaction matching (the via branch parameter does not contain the
@@ -821,7 +928,7 @@ modparam("tm", "ruri_matching", 1)
 modparam("tm", "via1_matching", 1)
 ...
 
-1.3.33. pass_provisional_replies (integer)
+1.4.33. pass_provisional_replies (integer)
 
    If set, TMCB_LOCAL_REPONSE_OUT tm registered callbacks will be called
    also for provisional replies.
@@ -836,7 +943,7 @@ modparam("tm", "via1_matching", 1)
 modparam("tm", "pass_provisional_replies", 1)
 ...
 
-1.3.34. default_code (integer)
+1.4.34. default_code (integer)
 
    Default response code sent by t_reply() if it cannot retrieve its
    parameters (e.g. inexistent avp). Valid values are between 400 and 699.
@@ -851,7 +958,7 @@ modparam("tm", "pass_provisional_replies", 1)
 modparam("tm", "default_code", 501)
 ...
 
-1.3.35. default_reason (string)
+1.4.35. default_reason (string)
 
    Default SIP reason phrase sent by t_reply() if it cannot retrieve its
    parameters (e.g. inexistent avp).
@@ -866,7 +973,7 @@ modparam("tm", "default_code", 501)
 modparam("tm", "default_reason", "Unknown reason")
 ...
 
-1.3.36. disable_6xx_block (integer)
+1.4.36. disable_6xx_block (integer)
 
    If set tm will treat all the 6xx replies like normal replies (warning:
    this would be non-rfc conformant behaviour).
@@ -890,12 +997,12 @@ modparam("tm", "default_reason", "Unknown reason")
 modparam("tm", "disable_6xx_block", 1)
 ...
 
-1.4. Functions
+1.5. Functions
 
    Revision History
    Revision $Revision$ $Date$
 
-1.4.1. t_relay_to_udp(ip, port), t_relay_to_udp(), t_relay_to_tcp(ip, port)
+1.5.1.  t_relay_to_udp(ip, port), t_relay_to_udp(), t_relay_to_tcp(ip, port)
 t_relay_to_tcp() t_relay_to_tls(ip, port) t_relay_to_tls()
 t_relay_to_sctp(ip, port) t_relay_to_sctp()
 
@@ -923,7 +1030,7 @@ else
         t_relay_to_tcp(); # relay to msg. uri, but over tcp
 ...
 
-1.4.2. t_relay() t_relay(host, port)
+1.5.2.  t_relay() t_relay(host, port)
 
    Relay a message statefully either to the destination indicated in the
    current URI (if called without any parameters) or to the specified host
@@ -951,7 +1058,7 @@ if (!t_relay())
 };
 ...
 
-1.4.3. t_on_failure(failure_route)
+1.5.3.  t_on_failure(failure_route)
 
    Sets failure routing block, to which control is passed after a
    transaction completed with a negative result but before sending a final
@@ -988,7 +1095,7 @@ failure_route[1] {
    See test/onr.cfg for a more complex example of combination of serial
    with parallel forking.
 
-1.4.4. t_on_reply(onreply_route)
+1.5.4.  t_on_reply(onreply_route)
 
    Sets the reply routing block, to which control is passed when a reply
    for the current transaction is received. Note that the set of commands
@@ -1018,7 +1125,7 @@ es');
         }
 }
 
-1.4.5. t_on_branch(branch_route)
+1.5.5.  t_on_branch(branch_route)
 
    Sets the branch routing block, to which control is passed after forking
    (when a new branch is created). For now branch routes are intended only
@@ -1042,7 +1149,7 @@ branch_route[1] {
         }
 }
 
-1.4.6. append_branch()
+1.5.6.  append_branch()
 
    Similarly to t_fork_to, it extends destination set by a new entry. The
    difference is that current URI is taken as new entry.
@@ -1056,7 +1163,7 @@ t_fork();
 t_relay();
 ...
 
-1.4.7. t_newtran()
+1.5.7.  t_newtran()
 
    Creates a new transaction, returns a negative value on error. This is
    the only way a script can add a new transaction in an atomic way.
@@ -1072,7 +1179,7 @@ if (t_newtran()) {
 
    See test/uas.cfg for more examples.
 
-1.4.8. t_reply(code, reason_phrase)
+1.5.8.  t_reply(code, reason_phrase)
 
    Sends a stateful reply after a transaction has been established. See
    t_newtran for usage.
@@ -1086,7 +1193,7 @@ if (t_newtran()) {
 t_reply("404", "Not found");
 ...
 
-1.4.9. t_lookup_request()
+1.5.9.  t_lookup_request()
 
    Checks if a transaction exists. Returns a positive value if so,
    negative otherwise. Most likely you will not want to use it, as a
@@ -1101,7 +1208,7 @@ if (t_lookup_request()) {
 };
 ...
 
-1.4.10. t_retransmit_reply()
+1.5.10.  t_retransmit_reply()
 
    Retransmits a reply sent previously by UAS transaction.
 
@@ -1110,7 +1217,7 @@ if (t_lookup_request()) {
 t_retransmit_reply();
 ...
 
-1.4.11. t_release()
+1.5.11.  t_release()
 
    Remove transaction from memory (it will be first put on a wait timer to
    absorb delayed messages).
@@ -1120,7 +1227,7 @@ t_retransmit_reply();
 t_release();
 ...
 
-1.4.12. t_forward_nonack() t_forward_nonack(ip, port)
+1.5.12.  t_forward_nonack() t_forward_nonack(ip, port)
 t_forward_nonack_udp(ip, port) t_forward_nonack_tcp(ip, port)
 t_forward_nonack_tls(ip, port) t_forward_nonack_sctp(ip, port)
 
@@ -1135,7 +1242,7 @@ t_forward_nonack_tls(ip, port) t_forward_nonack_sctp(ip, port)
 t_forward_nonack("1.2.3.4", "5060");
 ...
 
-1.4.13. t_set_fr(fr_inv_timeout [, fr_timeout])
+1.5.13.  t_set_fr(fr_inv_timeout [, fr_timeout])
 
    Sets the fr_inv_timeout and optionally fr_timeout for the current
    transaction or for transactions created during the same script
@@ -1169,7 +1276,7 @@ branch_route[1] {
         }
 }
 
-1.4.14. t_reset_fr()
+1.5.14.  t_reset_fr()
 
    Resets the fr_inv_timer and fr_timer for the current transaction to the
    default values (set using the tm module parameters fr_inv_timer and
@@ -1188,7 +1295,7 @@ route {
 ...
 }
 
-1.4.15. t_set_max_lifetime(inv_lifetime, noninv_lifetime)
+1.5.15.  t_set_max_lifetime(inv_lifetime, noninv_lifetime)
 
    Sets the maximum lifetime for the current INVITE or non-INVITE
    transaction, or for transactions created during the same script
@@ -1217,7 +1324,7 @@ route {
                                           # INVITE and to 15s if not
 }
 
-1.4.16. t_reset_max_lifetime()
+1.5.16.  t_reset_max_lifetime()
 
    Resets the the maximum lifetime for the current INVITE or non-INVITE
    transaction to the default value (set using the tm module parameter
@@ -1236,7 +1343,7 @@ route {
 ...
 }
 
-1.4.17. t_set_retr(retr_t1_interval, retr_t2_interval)
+1.5.17.  t_set_retr(retr_t1_interval, retr_t2_interval)
 
    Sets the retr_t1_interval and retr_t2_interval for the current
    transaction or for transactions created during the same script
@@ -1282,7 +1389,7 @@ branch_route[1] {
         }
 }
 
-1.4.18. t_reset_retr()
+1.5.18.  t_reset_retr()
 
    Resets the retr_timer1 and retr_timer2 for the current transaction to
    the default values (set using the tm module parameters retr_timer1 and
@@ -1301,7 +1408,7 @@ route {
 ...
 }
 
-1.4.19. t_set_auto_inv_100(0|1)
+1.5.19.  t_set_auto_inv_100(0|1)
 
    Switch automatically sending 100 replies to INVITEs on/off on a per
    transaction basis. It overrides the auto_inv_100 value for the current
@@ -1318,7 +1425,7 @@ route {
 ...
 }
 
-1.4.20. t_branch_timeout()
+1.5.20.  t_branch_timeout()
 
    Returns true if the failure route is executed for a branch that did
    timeout. It can be used only from the failure_route.
@@ -1332,7 +1439,7 @@ failure_route[0]{
         }
 }
 
-1.4.21. t_branch_replied()
+1.5.21.  t_branch_replied()
 
    Returns true if the failure route is executed for a branch that did
    receive at least one reply in the past (the "current" reply is not
@@ -1350,7 +1457,7 @@ failure_route[0]{
         }
 }
 
-1.4.22. t_any_timeout()
+1.5.22.  t_any_timeout()
 
    Returns true if at least one of the current transactions branches did
    timeout.
@@ -1366,7 +1473,7 @@ failure_route[0]{
         }
 }
 
-1.4.23. t_any_replied()
+1.5.23.  t_any_replied()
 
    Returns true if at least one of the current transactions branches did
    receive some reply in the past. If called from a failure or onreply
@@ -1381,7 +1488,7 @@ onreply_route[0]{
         }
 }
 
-1.4.24. t_grep_status("code")
+1.5.24.  t_grep_status("code")
 
    Returns true if "code" is the final reply received (or locally
    generated) in at least one of the current transactions branches.
@@ -1395,7 +1502,7 @@ onreply_route[0]{
         }
 }
 
-1.4.25. t_is_canceled()
+1.5.25.  t_is_canceled()
 
    Returns true if the current transaction was canceled.
 
@@ -1408,7 +1515,7 @@ failure_route[0]{
         }
 }
 
-1.4.26. t_is_expired()
+1.5.26.  t_is_expired()
 
    Returns true if the current transaction has already been expired, i.e.
    the max_inv_lifetime/max_noninv_lifetime interval has already elapsed.
@@ -1422,7 +1529,7 @@ failure_route[0]{
         }
 }
 
-1.4.27. t_relay_cancel()
+1.5.27.  t_relay_cancel()
 
    Forwards the CANCEL if the corresponding INVITE transaction exists. The
    function is supposed to be used at the very beginning of the script,
@@ -1447,7 +1554,7 @@ if (method == CANCEL) {
         # do the same as for INVITEs
 }
 
-1.4.28. t_lookup_cancel(), t_lookup_cancel(1)
+1.5.28.  t_lookup_cancel(), t_lookup_cancel(1)
 
    Returns true if the corresponding INVITE transaction exists for a
    CANCEL request. The function can be called at the beginning of the
@@ -1479,7 +1586,7 @@ if (method == CANCEL) {
         # do the same as for INVITEs
 }
 
-1.4.29. t_drop_replies()
+1.5.29.  t_drop_replies()
 
    Drops all the previously received replies in failure_route block to
    make sure that none of them is picked up again. Works only if a new
@@ -1502,7 +1609,7 @@ failure_route[0]{
         }
 }
 
-1.4.30. t_save_lumps()
+1.5.30.  t_save_lumps()
 
    Forces the modifications of the processed SIP message to be saved in
    shared memory before t_relay() is called. The new branches which are
@@ -1542,14 +1649,48 @@ failure_route[1] {
         t_relay();
 }
 
-1.4.31. t_load_contacts()
-
-   Loads contacts in destination set in increasing qvalue order as values
-   of contacts_avp. If all contacts in the destination set have the same
-   qvalue, t_load_contacts() does not do anything thus minimizing
-   performance impact of serial forking capability when it is not needed.
-   Returns 1 if loading of contacts succeeded or there was nothing to do.
-   Returns -1 on error (see syslog).
+1.5.31.  t_load_contacts()
+
+   This is the first of the two functions that can be used to implement
+   serial/parallel forking based on the q value of individual branches in
+   a destination set.
+
+   The function t_load_contacts() takes all branches from the current
+   destination set and encodes them into the AVP whose name or ID is
+   configured with the parameter contacts_avp. Note that you have to
+   configure this parameter before you can use the function, the parameter
+   is set to NULL by default, which disables the function.
+
+   If the destination set contains only one branch (the Request-URI) or if
+   all branches have the same q value then the function does nothing to
+   minimize performance impact. In such case all branches should be tried
+   in parallel and that is the default mode of operation of functions like
+   t_relay(), so there is no need to create the AVP or sort the branches.
+
+   If the current destination set contains more than one branch and not
+   all branches have the same q value then the function sorts them
+   according to the increasing value of the q parameter. The resulting
+   sorted list of branches is then encoded into the AVP.
+
+   The q parameter contains a value from a range of 0 to 1.0 and it
+   expresses relative preferrence of the branch among all branches in the
+   destination set. The higher the q value the more preferrence the user
+   agent gave to the branch. Branches with higher q values will be tried
+   first when serial forking takes place.
+
+   After that the function clears all branches and you have to call
+   t_next_contacts to retrieve them sorted according to their q value.
+   Note that if you use t_load_contacts then you also have to use
+   t_next_contacts before calling t_relay.
+
+   The AVP created by the function may contain multiple values, with one
+   encoded branch per value. The first value will contain the branch with
+   the highest q value. Each value contains the Request-URI, the
+   destination URI, the path vector, the outgoing socket description and
+   branch flags. All these fields are delimited with the LF character.
+
+   The function returns 1 if loading of contacts succeeded or there was
+   nothing to do. Returns -1 on error (see syslog).
 
    This function can be used from REQUEST_ROUTE.
 
@@ -1561,22 +1702,49 @@ if (!t_load_contacts()) {
 };
 ...
 
-1.4.32. t_next_contacts()
+1.5.32.  t_next_contacts()
+
+   The function t_next_contacts is the second of the two functions that
+   can be used to implement serial/parallel forking based on the q value
+   of individual branches in a destination set.
+
+   This function takes the AVP created by t_load_contacts and extracts
+   branches with highest q value from it into the destination set when
+   called for the first time. When you call the function second time it
+   extracts branches with lower q value, and so on until all branches have
+   been extracted.
 
-   If transaction does not exist when t_next_contacts() is called,
-   replaces Request-URI with the first contacts_avp value, adds the
-   remaining contacts_avp values with the same qvalue as branches, and
-   destroys those AVPs. It does nothing if there are no contacts_avp
-   values. Returns 1 if there were no errors and -1 if an error occurred
-   (see syslog).
+   If no transaction exist when t_next_contacts() is called, this usually
+   happens when you call the function from a request route block before
+   you call t_relay, it replaces the Request-URI with the first
+   contacts_avp value, adds the remaining contacts_avp values with the
+   same q value as branches, and destroys those AVPs.
 
-   If transaction does exist when t_next_contacts() is called, adds the
+   If transaction does exist when t_next_contacts() is called, it adds the
    first contacts_avp value and all following contacts_avp values with the
-   same qvalue as new branches to request and destroys those AVPs. Returns
-   1 if new branches were successfully added and -1 on error (see syslog)
-   or if there were no more contacts_avp values.
+   same q value as new branches to request and destroys those AVPs.
+
+   When you call the function repeatedly from a failure_route branch, it
+   looks for more AVP values each time and adds branches that have same q
+   value from the AVP as additional destination set branches. It always
+   stops when it reaches a branch that has a lower q value. Used AVP
+   values are always destroyed.,
+
+   The function does nothing if there are no contact_avp values.
+
+   The function returns 1 if there were no errors and -1 if an error
+   occurred (see syslog). This function can be used from REQUEST_ROUTE and
+   FAILURE_ROUTE.
+
+   Note that if use use t_load_contacts and t_next_contacts functions then
+   you should also set the value of restart_fr_on_each_reply parameter to
+   0. If you do not do that then it can happen that a broken user agent
+   that retransmits 180 periodically will keep resetting the fr_inv_timer
+   value and serial forking never happens.
 
-   This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+   Also make sure that you configured fr_inv_timer_next with lower value,
+   especially if you expect to have many serially forked branches. See the
+   documentation of that parameter for more details.
 
    Example 68. t_next_contacts usage
 ...
@@ -1597,7 +1765,7 @@ if (!t_next_contacts()) {
 };
 ...
 
-1.4.33. t_check_trans()
+1.5.33.  t_check_trans()
 
    t_check_trans() can be used to quickly check if a message belongs or is
    related to a transaction. It behaves differently for different types of
@@ -1647,7 +1815,7 @@ if ( method == "CANCEL" && !t_check_trans())
         sl_reply("403", "cancel out of the blue forbidden");
 # note: in this example t_check_trans() can be replaced by t_lookup_cancel()
 
-1.4.34. t_set_disable_6xx(0|1)
+1.5.34.  t_set_disable_6xx(0|1)
 
    Turn off/on 6xx replies special rfc conformant handling on a per
    transaction basis. If turned off (t_set_disable_6xx("1")) 6XXs will be
@@ -1666,7 +1834,7 @@ route {
 ...
 }
 
-1.4.35. t_set_disable_failover(0|1)
+1.5.35.  t_set_disable_failover(0|1)
 
    Turn off/on dns failover on a per transaction basis.
 
@@ -1681,7 +1849,7 @@ route {
 ...
 }
 
-1.5. TM Module API
+1.6. TM Module API
 
    Revision History
    Revision $Revision$ $Date$
@@ -1729,7 +1897,7 @@ end of body
 
    or cat test/transaction.fifo > /tmp/ser_fifo
 
-1.5.1. Defines
+1.6.1. Defines
 
      * ACK_TAG enables stricter matching of acknowledgments including
        to-tags. Without it, to-tags are ignored. It is disabled by default
@@ -1748,9 +1916,9 @@ end of body
        ACK_TAG, all this complex transactions matching goes with RFC3261's
        magic cookie away anyway.
 
-1.5.2. Functions
+1.6.2. Functions
 
-1.5.2.1. register_tmcb(cb_type, cb_func)
+1.6.2.1.  register_tmcb(cb_type, cb_func)
 
    For programmatic use only--register a function to be called back on an
    event. See t_hooks.h for more details.
@@ -1759,7 +1927,7 @@ end of body
      * cb_type - Callback type.
      * cb_func - Callback function.
 
-1.5.2.2. load_tm(*import_structure)
+1.6.2.2.  load_tm(*import_structure)
 
    For programmatic use only--import exported TM functions. See the acc
    module for an example of use.
@@ -1767,7 +1935,7 @@ end of body
    Meaning of the parameters is as follows:
      * import_structure - Pointer to the import structure.
 
-1.5.2.3. int t_suspend(struct sip_msg *msg, unsigned int *hash_index,
+1.6.2.3.  int t_suspend(struct sip_msg *msg, unsigned int *hash_index,
 unsigned int *label)
 
    For programmatic use only. This function together with t_continue() can
@@ -1805,7 +1973,7 @@ unsigned int *label)
    t_suspend() should return 0 to make sure that the script processing
    does not continue.
 
-1.5.2.4. int t_continue(unsigned int hash_index, unsigned int label, struct
+1.6.2.4.  int t_continue(unsigned int hash_index, unsigned int label, struct
 action *route)
 
    For programmatic use only. This function is the pair of t_suspend(),
index 02f3e77..7effbb5 100644 (file)
@@ -50,6 +50,7 @@ struct cfg_group_tm   default_tm_cfg = {
        1,      /* via1_matching */
        FR_TIME_OUT,    /* fr_timeout */
        INV_FR_TIME_OUT,        /* fr_inv_timeout */
+       INV_FR_TIME_OUT_NEXT, /* fr_inv_timeout_next */
        WT_TIME_OUT,    /* wait_timeout */
        DEL_TIME_OUT,   /* delete_timeout */
        RETR_T1,        /* rt_t1_timeout */
@@ -92,8 +93,6 @@ struct cfg_group_tm   default_tm_cfg = {
                         * for every method except BYE by default */
        1,      /* cancel_b_method used for e2e and 6xx cancels*/
        1,      /* reparse_on_dns_failover */
-       INV_FR_TIME_OUT_NEXT, /* fr_inv_timeout_next -> for serial forking subseq.
-                                                        branches */
        0 /* disable_6xx, by default off */
 };
 
@@ -111,6 +110,8 @@ cfg_def_t   tm_cfg_def[] = {
        {"fr_inv_timer",        CFG_VAR_INT | CFG_ATOMIC,       0, 0, timer_fixup, 0,
                "timer which hits if no final reply for an INVITE arrives "
                "after a provisional message was received (in milliseconds)"},
+       {"fr_inv_timer_next",   CFG_VAR_INT,    0, 0, 0, 0,
+               "The value [ms] of fr_inv_timer for subsequent branches during serial forking."},
        {"wt_timer",            CFG_VAR_INT | CFG_ATOMIC,       0, 0, timer_fixup, 0,
                "time for which a transaction stays in memory to absorb "
                "delayed messages after it completed"},
@@ -185,8 +186,6 @@ cfg_def_t   tm_cfg_def[] = {
                "if set to 1, the SIP message after a DNS failover is "
                "constructed from the outgoing message buffer of the failed "
                "branch instead of from the received request"},
-       {"fr_inv_timer_next",   CFG_VAR_INT,    0, 0, timer_fixup, 0,
-               "The value of fr_inv_timer for subsequent branches during serial forking"},
        {"disable_6xx_block",   CFG_VAR_INT | CFG_ATOMIC,       0, 1, 0, 0,
                "if set to 1, 6xx is treated like a normal reply (breaks rfc)"},
        {0, 0, 0, 0, 0, 0}
index 20493e2..4eb850a 100644 (file)
@@ -54,7 +54,6 @@
 #define INV_FR_TIME_OUT   120000 /* ms */
 
 /*! \brief final response timers to be used for serial forwarding */
-#define INV_FR_TIME_OUT_FIRST 90000 /* ms */
 #define INV_FR_TIME_OUT_NEXT  30000 /* ms */
 
 /* WAIT timer ... tells how long state should persist in memory after
@@ -108,6 +107,7 @@ struct cfg_group_tm {
        int     via1_matching;
        unsigned int    fr_timeout;
        unsigned int    fr_inv_timeout;
+       unsigned int    fr_inv_timeout_next;
        unsigned int    wait_timeout;
        unsigned int    delete_timeout;
        unsigned int    rt_t1_timeout;
@@ -134,7 +134,6 @@ struct cfg_group_tm {
        unsigned int    tm_blst_methods_lookup;
        unsigned int    cancel_b_flags;
        int     reparse_on_dns_failover;
-       unsigned int fr_inv_timeout_next;
        int disable_6xx;
 };
 
index 77facb1..c8f09d7 100644 (file)
@@ -1031,14 +1031,58 @@ failure_route[1] {
                <function moreinfo="none">t_load_contacts()</function>
                </title>
                <para>
-                Loads contacts in destination set in increasing qvalue order as
-                values of contacts_avp. If all contacts in the destination set
-                have the same qvalue, t_load_contacts() does not do
-                anything thus 
-                minimizing performance impact of serial forking capability
-                when it is not needed. Returns 1 if loading of contacts
-                succeeded or there was nothing to do. Returns -1 on error (see
-                syslog).
+                 This is the first of the two functions that can be used to implement
+                 serial/parallel forking based on the q value of individual branches
+                 in a destination set.
+               </para>
+               <para>
+                 The function <function>t_load_contacts()</function> takes all
+                 branches from the current destination set and encodes them into the
+                 AVP whose name or ID is configured with the
+                 parameter <varname>contacts_avp</varname>. Note that you have to
+                 configure this parameter before you can use the function, the
+                 parameter is set to NULL by default, which disables the function.
+               </para>
+               <para>
+                 If the destination set contains only one branch (the Request-URI) or
+                 if all branches have the same q value then the function does nothing
+                 to minimize performance impact. In such case all branches should be
+                 tried in parallel and that is the default mode of operation of
+                 functions like <function>t_relay()</function>, so there is no need
+                 to create the AVP or sort the branches.
+               </para>
+               <para>
+                 If the current destination set contains more than one branch and not
+                 all branches have the same q value then the function sorts them
+                 according to the increasing value of the q parameter. The resulting
+                 sorted list of branches is then encoded into the AVP.
+               </para>
+               <para>
+                 The q parameter contains a value from a range of 0 to 1.0 and it
+                 expresses relative preferrence of the branch among all branches in
+                 the destination set. The higher the q value the more preferrence the
+                 user agent gave to the branch. Branches with higher q values will be
+                 tried first when serial forking takes place.
+               </para>
+               <para>
+                 After that the function clears all branches and you have to
+                 call <function>t_next_contacts</function> to retrieve them sorted
+                 according to their q value. Note that if you
+                 use <function>t_load_contacts</function> then you also have to
+                 use <function>t_next_contacts</function> before
+                 calling <function>t_relay</function>.
+               </para>
+               <para>
+                 The AVP created by the function may contain multiple values, with
+                 one encoded branch per value. The first value will contain the
+                 branch with the highest q value. Each value contains the
+                 Request-URI, the destination URI, the path vector, the outgoing
+                 socket description and branch flags. All these fields are delimited
+                 with the LF character.
+               </para>
+               <para>
+          The function returns 1 if loading of contacts succeeded or there was
+          nothing to do. Returns -1 on error (see syslog).
                </para>
                <para>
                This function can be used from REQUEST_ROUTE.
@@ -1061,24 +1105,61 @@ if (!t_load_contacts()) {
                <function moreinfo="none">t_next_contacts()</function>
                </title>
                <para>
-                If transaction does not exist when t_next_contacts() is
-                called, replaces Request-URI with the
-                first contacts_avp value, adds the remaining contacts_avp values
-                with the same qvalue as branches, and destroys those AVPs. It
-                does nothing if there are no contacts_avp values. Returns 1 if
-                there were no errors and -1 if an error occurred (see syslog).
+                 The function <function>t_next_contacts</function> is the second of
+                 the two functions that can be used to implement serial/parallel
+                 forking based on the q value of individual branches in a destination
+                 set.
+               </para>
+               <para>
+                 This function takes the AVP created
+                 by <function>t_load_contacts</function> and extracts branches with
+                 highest q value from it into the destination set when called for the
+                 first time. When you call the function second time it extracts
+                 branches with lower q value, and so on until all branches have been
+                 extracted.      
+               </para>
+               <para>
+          If no transaction exist when <function>t_next_contacts()</function>
+          is called, this usually happens when you call the function from a
+          request route block before you call <function>t_relay</function>, it
+          replaces the Request-URI with the first contacts_avp value, adds the
+          remaining contacts_avp values with the same q value as branches, and
+          destroys those AVPs. 
+               </para>
+        <para>
+          If transaction does exist when t_next_contacts() is called, it adds
+          the first <varname>contacts_avp</varname> value and all following
+          <varname>contacts_avp</varname> values with the same q value as new
+          branches to request and destroys those AVPs. 
+        </para>
+               <para>
+                 When you call the function repeatedly from a failure_route branch,
+                 it looks for more AVP values each time and adds branches that have
+                 same q value from the AVP as additional destination set branches. It
+                 always stops when it reaches a branch that has a lower q value. Used
+                 AVP values are always destroyed.,
+               </para>
+               <para>
+                 The function does nothing if there are
+                 no <varname>contact_avp</varname> values.
+               </para>
+               <para>
+          The function returns 1 if there were no errors and -1 if an error
+          occurred (see syslog). This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+               </para>
+               <para>
+                 Note that if use use <function>t_load_contacts</function>
+                 and <function>t_next_contacts</function> functions then you should
+                 also set the value of <varname>restart_fr_on_each_reply</varname>
+                 parameter to 0. If you do not do that then it can happen that a
+                 broken user agent that retransmits 180 periodically will keep
+                 resetting the fr_inv_timer value and serial forking never happens.
                </para>
-                <para>
-                If transaction does exist when t_next_contacts() is
-                called, adds the first contacts_avp value and all
-                following contacts_avp values with the 
-                same qvalue as new branches to request and destroys those AVPs.
-                Returns 1 if new branches were successfully added and -1 on
-                error (see syslog) or if there were no more contacts_avp
-                values.
-                </para>
                <para>
-               This function can be used from REQUEST_ROUTE and FAILURE_ROUTE.
+                 Also make sure that you
+                 configured <varname>fr_inv_timer_next</varname> with lower value,
+                 especially if you expect to have many serially forked branches. See
+                 the documentation of that parameter for more details.
                </para>
                <example>
                <title><function>t_next_contacts</function> usage</title>
index 9d17412..64aa346 100644 (file)
@@ -744,38 +744,77 @@ onreply_route["stateless_replies"] {
     </section>
 
        <section>
-               <title><varname>fr_inv_timer_next</varname> (integer)</title>
-               <para>
-                Value of the Final Response timeout for INVITE
-                transactions to be used during serial forwarding:
-               </para>
-               <para>
-                Function t_next_contacts() sets fr_inv_timer to
-                fr_inv_timer_next value if, after t_next_contacts() is
-                called, there are still lower qvalue contacts available,
-                and to fr_inv_timer value if there are not.
-               </para>
-               <para>
+         <title><varname>fr_inv_timer_next</varname> (integer)</title>
+         <para>
+               This parameter can be used to configure an alternative value for the
+               fr_inv_timer timer. This alternative value is used in place
+               of <varname>fr_inv_timer</varname> when serial forking takes place. It
+               is used for all branches during serial forking except the last one.
+               The last branch (or a set of parallel branches) use the original value
+               from <varname>fr_inv_timer</varname> again.
+         </para>
+         <para>
+               The purpose of the timer is to allow an administrator to configure a
+               shorter version of <varname>fr_inv_timer</varname> that is used only
+               when serial forking takes place. Forwarding branches one after another
+               is much more time consuming, because every serial branch has to wait
+               for the result of the previous one. That can take up to the value
+               of <varname>fr_inv_timer</varname> and this timer is configured to two
+               minutes by default. Hence, if you have three serial branches then
+               completing the transaction can take six minutes with default timer values.
+         </para>
+         <para>
+               In practise, the transaction will be terminated sooner, because the
+               timer <varname>max_inv_lifetime</varname> hits after three minutes.
+               Thus, some of the serial branches might not be forwarded at all. And
+               this is exactly what <varname>fr_inv_timer_next</varname> is for. You
+               can configure the timer to a shorter value to ensure that all serial
+               branches are tried before the
+               timer <varname>max_inv_lifetime</varname> hits.
+         </para>
+         <para>
+               Note that if there is only one branch or if the current serial branch
+               is the last one (i.e. no more serial forking takes place after this
+               branch is finished) then <varname>fr_inv_timer_next</varname> is not
+               used, instead the branch uses the
+               longer <varname>fr_inv_timer</varname>.
+         </para>
+         <para>
+        Function <function>t_next_contacts()</function>
+        sets <varname>fr_inv_timer</varname>
+        to <varname>fr_inv_timer_next</varname> if serial forking takes place
+        and there is more than one serial branch.
+         </para>
+         <para>
+               The administrator can configure the value of this timer using the
+               configuration framework on the fly. But
+               unlike <varname>fr_inv_timer</varname>, it is not possible to
+               configure the value of this timer on per-transaction basis.
+         </para>
+         <para>
                <emphasis>
-                       Default value is 30.
+                 The value of this timer is to be specified in milliseconds. The
+                 default value is 30000ms.
                </emphasis>
-               </para>
-               <example>
+         </para>
+         <example>
                <title>Set <varname>fr_inv_timer_next</varname> parameter</title>
                <programlisting format="linespecific">
 ...
-modparam("tm", "fr_inv_timer_next", 10)
+modparam("tm", "fr_inv_timer_next", 10000)
 ...
-</programlisting>
-               </example>
+               </programlisting>
+         </example>
        </section>
-
+       
        <section>
                <title><varname>contacts_avp</varname> (string)</title>
                <para>
-                Internal AVP that t_load_contacts() function uses to store
-                contacts of the destination set and that
-                t_next_contacts() function uses to restore those contacts.
+                 This is the name or Id of an AVP
+                that <function>t_load_contacts()</function> function uses to
+                store contacts of the destination set and that
+                <function>t_next_contacts()</function> function uses to
+                restore those contacts.
                </para>
                <para>
                <emphasis>
index 64134ff..ea12dea 100644 (file)
                it to see if what you are looking for is there.</para>
        </note>
     </section>
+
+       <section id="tm.serial_forking">
+         <title>Serial Forking Based on Q Value</title>
+         <para>
+               A single SIP INVITE request may be forked to multiple destinations. We
+               call the set of all such destinations a destination set. Individual
+               elements within the destination sets are called branches. The script
+               writer can add URIs to the destination set from the configuration
+               file, or they can be loaded from the user location database, each
+               registered contact then becomes one branch in the destination set.
+         </para>
+         <para>
+               The default behavior of the tm module, if it encounters a SIP message
+               with multiple branches in the destination set, it to forward the SIP
+               message to all the branches in parallel. That means it sends the
+               message to all the branch destinations before it waits for replies
+               from any of them. This is the default behavior if you
+               call <function>t_relay()</function> and similar functions without
+               anything else.
+         </para>
+         <para>
+               Another approach of handling multiple branches in a destination set it
+               serial forking. When configured to do serial forking, the server takes
+               the first branch out of the destination set, forwards the message to
+               its destination and waits for a reply or timeout. Only after a reply
+               has been received or the timeout occurred, the server takes another
+               destination from the destination set and tries again, until it
+               receives a positive final reply or until all branches from the
+               destination set have been tried.
+         </para>
+         <para>
+               Yet another, more sophisticated, way of handling multiple branches is
+               combined serial/parallel forking, where individual branches within the
+               destination set are assigned priorities. The order in which individual
+               branches are tried is then determined by their relative priority
+               within the destination set. Branches can be tried sequentially in the
+               descending priority order and all branches that have the same priority
+               can be tried in parallel. Such combined serial/parallel forking can be
+               achieved in the tm module with the help of
+               functions <function>t_load_contacts()</function>
+               and <function>t_next_contacts()</function>.
+         </para>
+         <para>
+               Every branch in the destination set is assigned a priority number,
+               also known as the q value. The q value is a floating point number in a
+               range 0 to 1.0. The higher the q value number, the more priority is
+               the particular branch in the destination set is given. Branches with q
+               value 1.0 have maximum priority, such branches should be always tried
+               first in serial forking. Branches with q value 0 have the lowest
+               priority and they should by tried after all other branches with higher
+               priority in the destination set.
+         </para>
+         <para>
+               As an example, consider the following simple configuration file. When
+               the server receives an INVITE, it creates four branches for it with
+               usernames A through D and then forwards the request
+               using <function>t_relay()</function>:
+         </para>
+         <programlisting format="linespecific">
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com");
+  append_branch("sip:c@example.com");
+  append_branch("sip:d@example.com");
+
+  t_relay();
+  break;
+}
+</programlisting>
+         <para>
+               With this configuratin the server forwards the request to all four
+               branches at once, performing parallel forking described above. We did
+               not set the q value for individual branches in this example but we can
+               do that by slightly modifying the arguments given
+               to <function>append_branch()</function>:
+         </para>
+         <programlisting format="linespecific">
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com", "0.5");
+  append_branch("sip:c@example.com", "0.5");
+  append_branch("sip:d@example.com", "1.0");
+
+  t_relay();
+  break;
+}       
+</programlisting>
+         <para>
+               Here we assigned q value 0.5 to branches B and C and q value 1.0 to
+               branch D. We did not specify any q value for branch A and in that case
+               it is assumed that its q value is the lowest from all branches within
+               the destination set. If you try to run this example again, you will
+               figure out that nothing changed, <function>t_relay()</function> still
+               forward the message to all branches in parallel.
+         </para>
+         <para>
+               We now want to implement the combined serial/parallel forking. Branch
+               D should be tried first, because its q value is 1.0. Branches B and C
+               should be tried in parallel, but only after D finishes. Branch A
+               should be tried after B and C finished, because its q value (the
+               default) is the lowest of all. To do that, we need to introduce two
+               new functions into our example and one tm module parameter:
+         </para>
+         <programlisting format="linespecific">
+modparam("tm", "contacts_avp", "tm_contacts");
+
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com", "0.5");
+  append_branch("sip:c@example.com", "0.5");
+  append_branch("sip:d@example.com", "1.0");
+
+  t_load_contacts();
+
+  t_next_contacts();
+  t_relay();
+  break;
+}       
+</programlisting>
+         <para>
+               First of all, the tm module parameter is mandatory if the two new
+               functions are used. Function <function>t_load_contacts()</function>
+               takes all branches from the destination set, sorts them according to
+               their q values and stores them in the AVP configured in the modparam.
+               The function also clears the destination set, which means that it
+               removes all branches configured before
+               with <function>seturi()</function>
+               and <function>append_branch()</function>.
+         </para>
+         <para>
+               Function <function>t_next_contacts()</function> takes the AVP created
+               by the previous function and extract the branches with highest q
+               values from it. In our example it is branch D. That branch is then put
+               back into the destination set and when the script finally
+               reaches <function>t_relay()</function>, the destination set only
+               contains branch D and the request will be forwarded there.
+         </para>
+         <para>
+               We achieved the first step of serial forking, but this is not
+               sufficient. Now we also need to forward to other branches with lower
+               priority values when branch D finishes. To do that, we need to extend
+               the configuration file again and introduce a failure_route section:
+               </para>
+         <programlisting format="linespecific">
+modparam("tm", "contacts_avp", "tm_contacts");
+
+route {
+  seturi("sip:a@example.com");
+  append_branch("sip:b@example.com", "0.5");
+  append_branch("sip:c@example.com", "0.5");
+  append_branch("sip:d@example.com", "1.0");
+
+  t_load_contacts();
+
+  t_next_contacts();
+  t_on_failure("serial");
+  t_relay();
+  break;
+}
+
+failure_route["serial"]
+{
+  if (!t_next_contacts()) {
+    exit;
+  }
+
+  t_on_failure("serial");
+  t_relay();
+}
+</programlisting>
+         <para>
+               The failure_route section will be executed when branch D finishes. It
+               executes <function>t_next_contacts()</function> again and this time
+               the function retrieves branches B and C from the AVP and adds them to
+               the destination set. Here we need to check the return value of the
+               function, because a negative value indicates that there were no more
+               branches, in that case the failure_route should just terminate and
+               forward the response from branch D upstream.
+         </para>
+         <para>
+               If <function>t_next_contact()</function> returns a positive value then
+               we have more new branches to try and we need to setup the
+               failure_route again and call <function>t_relay()</function>. In our
+               example the request will now be forwarded to branches B and C in
+               paralell, because they were both added to the destination set
+               by <function>t_next_contacts()</function> at the same time.
+         </para>
+         <para>
+               When branches B and C finish, the failure_route block is executed
+               again, this time <function>t_next_contacts()</function> puts the final
+               branch A into the destination set and <function>t_relay()</function>
+               forwards the request there.
+         </para>
+         <para>
+               And that's the whole example, we achieved combined serial/parallel
+               forking based on the q value of individual branches. In real-world
+               configuration files the script writer would need to check the return
+               value of all functions and also configure some additional parameters,
+               such as <varname>fr_inv_timer_next</varname>
+               and <varname>restart_fr_on_each_reply</varname>. Also the destination
+               set would not be configured directly in the configuration file, but
+               can be retrieved from the user location database, for example. In that
+               case registered contacts will be stored in the destination set as
+               branches and their q values (provided by UAs) will be used.
+         </para>
+       </section>
     
     <section id="tm.known_issues">
        <title>Known Issues</title>
                    longer so much as there is a new replication module).
                </para>
            </listitem>
+           <listitem>
+               <para>
+                   Function <function>t_next_contacts</function> should restore the
+                   value of timer <varname>fr_inv_timer</varname> when there are no
+                   more branches to be processed serially. The function can restore
+                   the timer properly if it has been configured through an AVP or
+                   with the config framework, but may fail to restore timer values
+                   configured for individual transactions
+                   with <function>t_set_fr</function>.
+               </para>
+           </listitem>
+
        </itemizedlist>
     </section>
     
index 9fa0dcf..945afcb 100644 (file)
@@ -42,9 +42,6 @@
 /* usr_avp flag for sequential forking */
 #define Q_FLAG      (1<<2)
 
-/* module parameter variable */
-int fr_inv_timer_next = INV_FR_TIME_OUT_NEXT;
-
 /* Struture where information regarding contacts is stored */
 struct contact {
     str uri;
@@ -118,72 +115,100 @@ static inline int decode_branch_info(char *info, str *uri, str *dst, str *path,
     int port, proto;
     char *pos, *at, *tmp;
 
-    pos = strchr(info, '\n');
-    uri->len = pos - info;
-    if (uri->len) {
-               uri->s = info;
-    } else {
-               uri->s = 0;
+       if (info == NULL) {
+               ERR("decode_branch_info: Invalid input string.\n");
+               return 0;
+       }
+       
+       /* Reset or return arguments to sane defaults */
+       uri->s = 0; uri->len = 0;
+       dst->s = 0; dst->len = 0;
+       path->s = 0; path->len = 0;
+       *sock = NULL;
+       *flags = 0;
+       
+       /* Make sure that we have at least a non-empty URI string, it is fine if
+        * everything else is missing, but we need at least the URI. */
+       uri->s = info; 
+       if ((pos = strchr(info, '\n')) == NULL) { 
+               uri->len = strlen(info); 
+                       /* We don't even have the URI string, this is bad, report an
+                        * error. */
+               if (uri->len == 0) goto uri_missing;
+               return 1;
     }
-    at = pos + 1;
-
-    pos = strchr(at, '\n');
-    dst->len = pos - at;
-    if (dst->len) {
-               dst->s = at;
-    } else {
-               dst->s = 0;
+       uri->len = pos - info;
+       if (uri->len == 0) goto uri_missing;
+
+       /* If we get here we have at least the branch URI, now try to parse as
+        * much as you can. All output variable have been initialized above, so it
+        * is OK if any of the fields are missing from now on. */
+    dst->s = at = pos + 1;
+    if ((pos = strchr(at, '\n')) == NULL) {
+               dst->len = strlen(dst->s);
+               return 1;
     }
-    at = pos + 1;
+       dst->len = pos - at;
 
-    pos = strchr(at, '\n');
-    path->len = pos - at;
-    if (path->len) {
-               path->s = at;
-    } else {
-               path->s = 0;
+    path->s = at = pos + 1;
+    if ((pos = strchr(at, '\n')) == NULL) {
+               path->len = strlen(path->s);
+               return 1;
     }
-    at = pos + 1;
+    path->len = pos - at;
 
-    pos = strchr(at, '\n');
-    s.len = pos - at;
-    if (s.len) {
-               s.s = at;
+    s.s = at = pos + 1;
+    if ((pos = strchr(at, '\n')) == NULL) {
+               /* No LF found, that means we take the string till the final zero
+                * termination character and pass it directly to parse_phostport
+                * without making a zero-terminated copy. */
+               tmp = s.s;
+               s.len = strlen(s.s);
+       } else {
+               /* Our string is terminated by LF, so we need to make a
+                * zero-terminated copy of the string before we pass it to
+                * parse_phostport. */
+               s.len = pos - at;
                if ((tmp = as_asciiz(&s)) == NULL) {
                        ERR("No memory left\n");
                        return 0;
                }
+       }       
+       if (s.len) {
                if (parse_phostport(tmp, &host.s, &host.len,
                                                        &port, &proto) != 0) {
-                       LM_ERR("parsing of socket info <%.*s> failed\n",  s.len, s.s);
-                       pkg_free(tmp);
+                       LM_ERR("parsing of socket info <%s> failed\n", tmp);
+                       if (pos) pkg_free(tmp);
                        return 0;
                }
-               pkg_free(tmp);
+
                *sock = grep_sock_info(&host, (unsigned short)port,
                                                           (unsigned short)proto);
                if (*sock == 0) {
-                       LM_ERR("invalid socket <%.*s>\n", s.len, s.s);
+                       LM_ERR("invalid socket <%s>\n", tmp);
+                       if (pos) pkg_free(tmp);
                        return 0;
                }
-    } else {
-               *sock = 0;
-    }
-    at = pos + 1;
+       }
+       
+       if (pos) pkg_free(tmp);
+       else return 1;
+       
+    s.s = at = pos + 1;
+    if ((pos = strchr(at, '\n')) == NULL) s.len = strlen(s.s);
+    else s.len = pos - s.s;
 
-    pos = strchr(at, '\n');
-    s.len = pos - at;
     if (s.len) {
-               s.s = at;
                if (str2int(&s, flags) != 0) {
-                       LM_ERR("failed to decode flags <%.*s>\n", s.len, s.s);
+                       LM_ERR("failed to decode flags <%.*s>\n", STR_FMT(&s));
                        return 0;
                }
-    } else {
-               *flags = 0;
     }
-
     return 1;
+
+uri_missing:
+       ERR("decode_branch_info: Cannot decode branch URI.\n");
+       return 0;
 }
 
 
@@ -349,7 +374,7 @@ rest:
                                contacts_avp, val);
                pkg_free(branch_info.s);
                LM_DBG("loaded contact <%.*s> with q_flag <%d>\n",
-                          val.s.len, val.s.s, curr->q_flag);
+                          STR_FMT(&val.s), curr->q_flag);
                curr = curr->next;
     }
 
@@ -379,6 +404,8 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
     unsigned int flags;
     struct cell *t;
        struct search_state st;
+       ticks_t orig;
+       unsigned int avp_timeout;
 
     /* Check if contacts_avp has been defined */
     if (contacts_avp.n == 0) {
@@ -405,45 +432,44 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
                        return 1;
                }
 
-               LM_DBG("next contact is <%s>\n", val.s.s);
+               LM_DBG("next contact is <%.*s>\n", STR_FMT(&val.s));
 
                if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
                        == 0) {
-                       LM_ERR("decoding of branch info <%.*s> failed\n",
-                                  val.s.len, val.s.s);
+                       LM_ERR("decoding of branch info <%.*s> failed\n", STR_FMT(&val.s));
                        destroy_avp(avp);
                        return -1;
                }
 
                /* Rewrite Request-URI */
                rewrite_uri(msg, &uri);
-               set_dst_uri(msg, &dst);
-               set_path_vector(msg, &path);
+               if (dst.s && dst.len) set_dst_uri(msg, &dst);
+               else reset_dst_uri(msg);
+               if (path.s && path.len) set_path_vector(msg, &path);
+               else reset_path_vector(msg);
                msg->force_send_socket = sock;
                setbflagsval(0, flags);
 
                if (avp->flags & Q_FLAG) {
                        destroy_avp(avp);
                        /* Set fr_inv_timer */
-                       val.n = fr_inv_timer_next;
-                       if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) {
-                               LM_ERR("setting of fr_inv_timer_avp failed\n");
+                       if (t_set_fr(msg, cfg_get(tm, tm_cfg, fr_inv_timeout_next), 0) 
+                               == -1) {
+                               ERR("Cannot set fr_inv_timer value.\n");
                                return -1;
                        }
                        return 1;
                }
-
+               
                /* Append branches until out of branches or Q_FLAG is set */
                prev = avp;
                while ((avp = search_next_avp(&st, &val))) {
                        destroy_avp(prev);
-
-                       LM_DBG("next contact is <%s>\n", val.s.s);
+                       LM_DBG("next contact is <%.*s>\n", STR_FMT(&val.s));
 
                        if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
                                == 0) {
-                               LM_ERR("decoding of branch info <%.*s> failed\n",
-                                          val.s.len, val.s.s);
+                               LM_ERR("decoding of branch info <%.*s> failed\n", STR_FMT(&val.s));
                                destroy_avp(avp);
                                return -1;
                        }
@@ -456,35 +482,30 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
 
                        if (avp->flags & Q_FLAG) {
                                destroy_avp(avp);
-                               val.n = fr_inv_timer_next;
-                               if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val)
-                                       != 0) {
-                                       LM_ERR("setting of fr_inv_timer_avp failed\n");
+                               /* Set fr_inv_timer */
+                               if (t_set_fr(msg, cfg_get(tm, tm_cfg, fr_inv_timeout_next), 0) == -1) {
+                                       ERR("Cannot set fr_inv_timer value.\n");
                                        return -1;
                                }
                                return 1;
                        }
                        prev = avp;
                }
-       
+               destroy_avp(prev);
     } else {
-       
-               /* Transaction exists => only load branches */
+                       /* Transaction exists => only load branches */
 
                /* Find first contacts_avp value */
                avp = search_first_avp(contacts_avp_type, contacts_avp, &val, &st);
                if (!avp) return -1;
 
                /* Append branches until out of branches or Q_FLAG is set */
-               prev = avp;
                do {
-
-                       LM_DBG("next contact is <%s>\n", val.s.s);
+                       LM_DBG("next contact is <%.*s>\n", STR_FMT(&val.s));
 
                        if (decode_branch_info(val.s.s, &uri, &dst, &path, &sock, &flags)
                                == 0) {
-                               LM_ERR("decoding of branch info <%.*s> failed\n",
-                                          val.s.len, val.s.s);
+                               LM_ERR("decoding of branch info <%.*s> failed\n", STR_FMT(&val.s));
                                destroy_avp(avp);
                                return -1;
                        }
@@ -503,16 +524,49 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
                        prev = avp;
                        avp = search_next_avp(&st, &val);
                        destroy_avp(prev);
-
                } while (avp);
 
-               /* Restore fr_inv_timer */
-               val.n = default_tm_cfg.fr_inv_timeout;
-               if (add_avp(fr_inv_timer_avp_type, fr_inv_timer_avp, val) != 0) {
-                       LM_ERR("setting of fr_inv_timer_avp failed\n");
-                       return -1;
+               /* If we got there then we have no more branches for subsequent serial
+                * forking and the current set is the last one. For the last set we do
+                * not use the shorter timer fr_inv_timer_next anymore, instead we use
+                * the usual fr_inv_timer.
+                *
+                * There are three places in sip-router which can contain the actual
+                * value of the fr_inv_timer. The first place is the variable
+                * use_fr_inv_timeout defined in timer.c That variable is set when the
+                * script writer calls t_set_fr in the script. Its value can only be
+                * used from within the process in which t_set_fr was called. It is
+                * not guaranteed that when we get here we are still in the same
+                * process and therefore we might not be able to restore the correct
+                * value if the script writer used t_set_fr before calling
+                * t_next_contacts. If that happens then the code below detects this
+                * and looks into the AVP or cfg framework for other value. In other
+                * words, t_next_contact does not guarantee that fr_inv_timer values
+                * configured on per-transaction basis with t_set_fr will be correctly
+                * restored.
+                *
+                * The second place is the fr_inv_timer_avp configured in modules
+                * parameters. If that AVP exists and then its value will be correctly
+                * restored by t_next_contacts. The AVP is an alternative way of
+                * configuring fr_inv_timer on per-transaction basis, it can be used
+                * interchangeably with t_set_fr. Function t_next_contacts always
+                * correctly restores the timer value configured in the AVP.
+                *
+                * Finally, if we can get the value neither from user_fr_inv_timeout
+                * nor from the AVP, we turn to the fr_inv_timeout variable in the cfg
+                * framework. This variable contains module's default and it always
+                * exists and is available. */
+               orig = (ticks_t)get_msgid_val(user_fr_inv_timeout, msg->id, int);
+               if (orig == 0) {
+                       if (!fr_inv_avp2timer(&avp_timeout)) {
+                               /* The value in the AVP is in seconds and needs to be
+                                * converted to ticks */
+                               orig = S_TO_TICKS((ticks_t)avp_timeout);
+                       } else {
+                               orig = cfg_get(tm, tm_cfg, fr_inv_timeout);
+                       }
                }
-       
+               change_fr(t, orig, 0);
     }
 
     return 1;
index 0a763b8..3140e30 100644 (file)
@@ -481,6 +481,7 @@ static param_export_t params[]={
        {"via1_matching",       PARAM_INT, &default_tm_cfg.via1_matching         },
        {"fr_timer",            PARAM_INT, &default_tm_cfg.fr_timeout            },
        {"fr_inv_timer",        PARAM_INT, &default_tm_cfg.fr_inv_timeout        },
+       {"fr_inv_timer_next",   PARAM_INT, &default_tm_cfg.fr_inv_timeout_next   },
        {"wt_timer",            PARAM_INT, &default_tm_cfg.wait_timeout          },
        {"delete_timer",        PARAM_INT, &default_tm_cfg.delete_timeout        },
        {"retr_timer1",         PARAM_INT, &default_tm_cfg.rt_t1_timeout         },
@@ -514,7 +515,6 @@ static param_export_t params[]={
        {"cancel_b_method",     PARAM_INT, &default_tm_cfg.cancel_b_flags},
        {"reparse_on_dns_failover", PARAM_INT, &default_tm_cfg.reparse_on_dns_failover},
        {"on_sl_reply",         PARAM_STRING|PARAM_USE_FUNC, fixup_on_sl_reply   },
-       {"fr_inv_timer_next",   PARAM_INT, &default_tm_cfg.fr_inv_timeout_next   },
        {"contacts_avp",        PARAM_STRING, &contacts_avp_param                },
        {"disable_6xx_block",   PARAM_INT, &default_tm_cfg.disable_6xx           },
        {0,0,0}