9 | | The SIP redirection support in PJSUA-LIB is implemented as subset of generic multiple call targeting support. Multiple call targeting is a neat new feature in PJSUA-LIB that allows a single outgoing call to have multiple targets, which will be called sequentially until one of the target picks up the call. Some use cases of this feature are as follows. |
10 | | |
11 | | === Redirection === |
12 | | |
13 | | Redirection (by means of SIP 3xx response) is a natural use case of multiple call targeting. In this scenario, the original INVITE is rejected with redirection response (SIP 300-399 code) containing one or more targets (Contact header). Application then may instruct the call to follow the redirection, and the library will send a fresh INVITE request to the new target. This second INVITE may be redirected too, and once again application may instruct the call to follow the new target. And so on, the process continues until one of the target picks up the call, or all targets have been tried but none of them can accept the call, or the call has reached it's maximum number of targets allowed in a single call. |
14 | | |
15 | | The process of retrying new targets will happen automatically without application having to make a new call (although it may do that if it wants to), and all invite requests belong to the same call id. That said, application still has the full control over accepting the redirection instruction. It may reject the redirection, it may accept it, it may accept only partial list of targets, and it may delay the acceptance if it wants to ask for user permission first. |
| 9 | The redirected call handling is implemented by ticket #10. The objective statements of this work are: |
| 10 | - must be implemented in PJSIP level rather than PJSUA-LIB level |
| 11 | - must allow application to accept or reject the redirection request on a per-target basis (by means of callback) |
| 12 | - must allow application to defer the decision to accept or reject the redirection request (for example, to ask for user confirmation to accept or reject the target) |
| 13 | - the subsequent INVITE requests sent after the 3xx response must have the same To, From, and Call-ID as used by the original INVITE. While RFC 3261 only put this at RECOMMENDED strength (see section 8.1.3.4), in practice there are many servers out there that relies on this property. |
24 | | So you dial Perry's URI, and also configure the (same) call with my URI as alternate target. If Perry picks up the phone, good, you can offload your rants to him. If he doesn't pick up the phone, the call will go to its alternate target, which is me (as long as Perry doesn't also redirect the call to his voice mail). Of course this is bad news for me, but you, the customer, is happy, and that's the main thing. |
| 22 | {{{ |
| 23 | void (*on_redirected)(pjsip_inv_session *inv, const pjsip_uri *target, |
| 24 | pjsip_redirect_op *cmd, const pjsip_event *e); |
| 25 | }}} |
| 26 | |
| 27 | Application '''MUST''' implement this callback if it wants the invite session to manage redirection. If this callback is not implemented, the invite session will be disconnected upon receipt of 3xx response. |
| 28 | |
| 29 | If this callback is implemented, the default behavior (that is when the callback contains no function body) is to follow the redirection. |
| 30 | |
| 31 | This callback is called when the invite session is being redirected to a new target. If there are multiple targets, then the callback will be called for each target. This function is called on these events: |
| 32 | - a 3xx response containing usable Contact header(s) is received |
| 33 | - a 4xx or 5xx response to subsequent INVITE request is received, and the invite session has at least one more valid target to try. This happens for example when multiple Contact headers (alternate targets) are received for the original INVITE, and 4xx/5xx response is received for the second INVITE (to the first alternate target). The callback then will be called before the invite session tries to send the third INVITE to the second alternate target. |
31 | | {{{ |
32 | | PJ_DECL(pj_status_t) pjsua_call_manage_target(pjsua_call_id call_id, |
33 | | const pj_str_t *target, |
34 | | pjsip_status_code tgt_code, |
35 | | const pj_str_t *referer, |
36 | | float qvalue, |
37 | | const pjsua_msg_data *msg_data); |
38 | | }}} |
| 43 | ''cmd'':: |
| 44 | Tells the invite session about what to do with this redirection. The default value is to accept the redirection. Application can change this to the following valid values: |
| 45 | - ''PJSIP_REDIRECT_ACCEPT'': immediately accept the redirection (default value). The session will immediately resend INVITE request to the target upon return of this callback. |
| 46 | - ''PJSIP_REDIRECT_REJECT'': immediately reject this target. The session will continue retrying with next target if present (and it will call this callback again for the next target), or immediately disconnect the call if there is no more target to try (notifying the ''on_state_changed()'' callback as usual). |
| 47 | - ''PJSIP_REDIRECT_STOP'': stop the whole redirection process and immediately disconnect the call, regardless on whether there is other target to try or not. |
| 48 | - ''PJSIP_REDIRECT_PENDING'': set to this value if no decision can be made immediately (for example to request confirmation from user). Application then MUST call ''pjsip_inv_process_redirect()'' (see below) to either accept or reject the redirection upon getting user decision. |
40 | | The parameters of the function are as follows: |
41 | | |
42 | | ''call_id'':: |
43 | | The call ID of an outgoing call which status is not disconnected yet. |
44 | | ''target'':: |
45 | | Target URI. |
46 | | ''tgt_code'':: |
47 | | Operation to be applied to the target: |
48 | | - value 100-199: the target will be added, but it will not be called until it's code is set to 2xx. |
49 | | - value 200-299: the target will be added and called as soon as possible. |
50 | | - value 300-699: the target will be removed from the list, if it's present. |
51 | | ''referer'':: |
52 | | Optionally specify the URI of the referer, that is the URI of the original target which returned redirection response containing this target. This referer URI is used to calculate the priority of this new target. If referer is not used, the target priority will be calculated from the qvalue parameter instead. |
53 | | ''qvalue'':: |
54 | | Priority value, from 0 to 1. Target with lower priority number will be called first. If application specify zero, then this target will be selected first for the next call, and if 1, this target will be called last. |
55 | | ''msg_data'':: |
56 | | Optional list of headers etc to be added to outgoing call to this target. |
| 50 | ''e'':: |
| 51 | The event that triggers this callback to be called. This could be of type {{{PJSIP_EVENT_TSX_STATE}}} containing receipt of 3xx, 4xx, or 5xx response, or any other type depending on what is passed to {{{pjsip_inv_process_redirect()}}} below, or of type {{{PJSIP_EVENT_USER}}} if this callback is called by {{{pjsip_inv_process_redirect()}}} below and the event argument to that function is NULL, or NULL. |
| 72 | ==== Reject Redirection to This Target ==== |
| 73 | |
| 74 | Set the ''cmd'' argument of the callback to ''PJSIP_REDIRECT_REJECT'' to reject the redirection to this target. If there is another target to try, then the callback will be called again with this next target, otherwise the invite session will be disconnected immediately. |
| 75 | |
| 76 | ==== Stop Redirection ==== |
| 77 | |
| 78 | Set the ''cmd'' argument of the callback to ''PJSIP_REDIRECT_STOP'' to stop the redirection process and disconnect the call immediately, regardless of whether there are more targets to try. |
| 79 | |
| 80 | ==== Defer the Decision ==== |
| 81 | |
| 82 | Set the ''cmd'' argument of the callback to ''PJSIP_REDIRECT_PENDING'' to tell the invite session that a decision cannot be made that this time (for example to ask for user approval), and the application will notify the invite session about the decision later. |
| 83 | |
| 84 | Once the application decides what to do with the redirection, it '''MUST''' call {{{pjsip_inv_process_redirect()}}} function to notify the session about this. It may accept or reject the target, or stop the redirection altogether by setting the appropriate value to the ''cmd'' argument. It must not set ''PJSIP_REDIRECT_PENDING'' to this argument. |
| 85 | |
| 86 | Failure to call {{{pjsip_inv_process_redirect()}}} will cause the invite session to be kept alive indefinitely until the library is shutdown. |
| 87 | |
| 88 | |
| 89 | === PJSUA-LIB === |
| 90 | |
| 91 | Similar callback and function were added to PJSUA-LIB: |
| 92 | |
| 93 | {{{ |
| 94 | void (*on_call_redirected)(pjsua_call_id call_id, const pjsip_uri *target, |
| 95 | pjsip_redirect_op *cmd, const pjsip_event *e); |
| 96 | |
| 97 | PJ_DECL(pj_status_t) pjsua_call_process_redirect(pjsua_call_id call_id, |
| 98 | pjsip_redirect_op cmd); |
| 99 | }}} |
| 100 | |
| 101 | Please see the PJSIP section above on how to use this feature. |
| 102 | |
| 103 | |
| 104 | === pjsua === |
| 105 | |
| 106 | The following additions were made to pjsua: |
| 107 | |
| 108 | - new command line argument "{{{--accept-redirect=N}}}", with valid values for N: |
| 109 | - 0: reject/stop, |
| 110 | - 1: follow automatically (default), |
| 111 | - 2: ask |
| 112 | - the default behavior is to follow redirection automatically |
| 113 | - when {{{--accept-redirect}}} is set to 2 (ask), user can enter 'Ra', 'Rr', or 'Rd' to accept, reject, or stop/disconnect the redirection. |
| 114 | |
| 115 | |
| 116 | == Open Issues == |
| 117 | |
| 118 | === 3xx Response after 183 === |
| 119 | |
| 120 | When 3xx response is received after 183 with SDP response is received, the invite session will treat the SDP answer in the ''subsequent'' INVITE as an offer rather than answer. This is because the SDP has been negotiated in the previous INVITE offer/answer. |
| 121 | |
| 122 | While it is uncommon to send 183 before 3xx, this is a bug. |
| 123 | |
| 124 | === Library shutdown with a pending redirection causes assertion failure === |
| 125 | |
| 126 | Assertion will be triggered in the invite session when the library is shutdown while the invite session is still waiting for {{{pjsip_inv_process_redirect}}} function to be called. |
| 127 | |
| 128 | While this is caused by application not following the requirement to call {{{pjsip_inv_process_redirect()}}}, nevertheless the library should be robust enough to handle this, so this will be fixed. |