Commit e08de77a authored by Pekka Pessi's avatar Pekka Pessi

nua session timer: refactored implementation, using timer values recommended by RFC 4028.

The problem and crash with session timers was reported by Fabio Margarido.

Now following RFC 4028 more closely. There are test cases for UAS and UAC
refreshing the session with INVITE and UPDATE, and a test case where UPDATE
is filtered away until non-refreshing party sends BYE.

The re-INVITE/UPDATE refresh is sent around middle of expiration time (e.g.,
if expiration time is 3600 seconds, it is sent 1795..1805 seconds after the
previous refresh).

The non-refreshing party now sends a BYE request before two thirds of
session expiration time has elapsed without session refresh request.

darcs-hash:20070411174513-65a35-229d09a2e5371b5a88c9430f80da637b4c2bb6be.gz
parent 4ffae050
......@@ -149,9 +149,10 @@ int nua_stack_set_defaults(nua_handle_t *nh,
NHP_SET(nhp, auto_ack, 1);
NHP_SET(nhp, invite_timeout, 120);
NHP_SET(nhp, session_timer, 1800);
nhp->nhp_session_timer = 1800;
nhp->nhp_refresher = nua_no_refresher;
NHP_SET(nhp, min_se, 120);
NHP_SET(nhp, refresher, nua_no_refresher);
NHP_SET(nhp, update_refresh, 0);
NHP_SET(nhp, message_enable, 1);
......@@ -1503,6 +1504,11 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e,
#define TIF(TAG, pref) \
TAG_IF(nhp->nhp_set.nhb_##pref, TAG(nhp->nhp_##pref))
/* Include tag in the list returned to user
* if it has been earlier set (by user) returning default parameters */
#define TIFD(TAG, pref) \
TAG_IF(nh == dnh || nhp->nhp_set.nhb_##pref, TAG(nhp->nhp_##pref))
/* Include string tag made out of SIP header
* if it has been earlier set (by user) */
#define TIF_STR(TAG, pref) \
......@@ -1540,9 +1546,9 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e,
TIF(NUTAG_AUTOACK, auto_ack),
TIF(NUTAG_INVITE_TIMER, invite_timeout),
TIF(NUTAG_SESSION_TIMER, session_timer),
TIFD(NUTAG_SESSION_TIMER, session_timer),
TIF(NUTAG_MIN_SE, min_se),
TIF(NUTAG_SESSION_REFRESHER, refresher),
TIFD(NUTAG_SESSION_REFRESHER, refresher),
TIF(NUTAG_UPDATE_REFRESH, update_refresh),
TIF(NUTAG_ENABLEMESSAGE, message_enable),
......
......@@ -226,4 +226,9 @@ typedef struct nua_handle_preferences
(NHP_ISSET((nh)->nh_prefs, pref) && \
(nh)->nh_nua->nua_dhandle->nh_prefs != (nh)->nh_prefs)
/* Check if preference has been set by applicationx */
#define NUA_PISSET(nua, nh, pref) \
(NHP_ISSET((nua)->nua_dhandle->nh_prefs, pref) || \
((nh) && NHP_ISSET((nh)->nh_prefs, pref)))
#endif /* NUA_PARAMS_H */
This diff is collapsed.
......@@ -494,40 +494,48 @@ SOFIAPUBVAR tag_typedef_t nutag_invite_timer_ref;
/**Default session timer in seconds.
*
* Set default session timer in seconds when using session timer extension.
* The value given here is the proposed session expiration time in seconds.
* Note that the session timer extension is ponly used
* Set default value for session timer in seconds when the session timer
* extension is used. The tag value is the proposed session expiration time
* in seconds, the session is refreshed twice during the expiration time.
*
* @par Sending INVITE and UPDATE Requests
*
* If NUTAG_SESSION_TIMER() is used with non-zero value, the value is
* used in the @SessionExpires header included in the INVITE or UPDATE
* requests. The intermediate proxies or the ultimate destination can lower
* the interval in @SessionExpires header. If the value is too low, they can
* If NUTAG_SESSION_TIMER() is used with non-zero value, the value is used
* in the @SessionExpires header included in the INVITE or UPDATE requests.
* The intermediate proxies or the ultimate destination can lower the
* interval in @SessionExpires header. If the value is too low, they can
* reject the request with the status code <i>422 Session Timer Too
* Small</i>. When Re-INVITE will be sent in given intervals. In that case,
* @b nua retries the request automatically.
*
* @par Returning Response to the INVITE and UPDATE Requests
* Small</i>. In that case, @b nua increases the value of @SessionExpires
* header and retries the request automatically.
*
* @par Returning a Response to the INVITE and UPDATE Requests
*
* The NUTAG_SESSION_TIMER() value is also used when sending the final
* response to the INVITE or UPDATE requests. If the NUTAG_SESSION_TIMER()
* value is 0 or the value in the @SessionExpires header of the requeast is
* value is 0 or the value in the @SessionExpires header of the request is
* lower than the value in NUTAG_SESSION_TIMER(), the value from the
* incoming @SessionExpires header is used. However, if the value in
* @SessionExpires is lower than the minimal acceptable session expiration
* interval specified with the tag NUTAG_MIN_SE() the request is
* automatically rejected with <i>422 Session Timer Too Small</i>.
*
* @par Refreshes
*
* After the initial INVITE request, the SIP session is refreshed at the
* intervals indicated by the @SessionExpires header returned in the 2XX
* response. The party indicated with the "refresher" parameter of the
* @SessionExpires header sends a re-INVITE requests (or an UPDATE
* request if NUTAG_UPDATE_REFRESH(1) parameter tag has been set).
*
* @par When to Use NUTAG_SESSION_TIMER()?
*
* The session time extension is enabled ("timer" feature tag is included in
* @Supported header) but not activated by default (no @SessionExpires
* header is included in the requests or responses by default). Using
* non-zero value with NUTAG_SESSION_TIMER() activates it. When the
* extension is activated, @nua refreshes the call state by sending periodic
* re-INVITE or UPDATE requests unless the remote end indicated that it will
* take care of refreshes.
* non-zero value with NUTAG_SESSION_TIMER() or NUTAG_SESSION_REFRESHER()
* activates it. When the extension is activated, @nua refreshes the call
* state by sending periodic re-INVITE or UPDATE requests unless the remote
* end indicated that it will take care of refreshes.
*
* The session timer extension is mainly useful for proxies or back-to-back
* user agents that keep call state. The call state is "soft" meaning that
......@@ -535,6 +543,10 @@ SOFIAPUBVAR tag_typedef_t nutag_invite_timer_ref;
* will be destroyed. An ordinary user-agent can also make use of session
* timer if it cannot get any activity feedback from RTP or other media.
*
* @note The session timer extension is used only if the feature
* tag "timer" is listed in the @Supported header, set by NUTAG_SUPPORTED(),
* SIPTAG_SUPPORTED(), or SIPTAG_SUPPORTED_STR() tags.
*
* @par Used with
* nua_invite(), nua_update(), nua_respond() \n
* nua_set_params() or nua_set_hparams() \n
......@@ -553,8 +565,8 @@ SOFIAPUBVAR tag_typedef_t nutag_invite_timer_ref;
* Corresponding tag taking reference parameter is NUTAG_SESSION_TIMER_REF()
*
* @sa NUTAG_SUPPORTED(), NUTAG_MIN_SE(), NUTAG_SESSION_REFRESHER(),
* nua_invite(), #nua_r_invite, #nua_i_invite, nua_update(), #nua_r_update,
* #nua_i_update,
* nua_invite(), #nua_r_invite, #nua_i_invite, nua_respond(),
* nua_update(), #nua_r_update, #nua_i_update,
* NUTAG_UPDATE_REFRESH(), @RFC4028, @SessionExpires, @MinSE
*/
#define NUTAG_SESSION_TIMER(x) nutag_session_timer, tag_uint_v((x))
......
......@@ -205,6 +205,8 @@ int test_basic_call_1(struct context *ctx)
sip_replaces_t *repa, *repb;
nua_handle_t *nh;
static int once = 0;
if (print_headings)
printf("TEST NUA-3.1: Basic call\n");
......@@ -272,6 +274,10 @@ int test_basic_call_1(struct context *ctx)
TEST_1(sip->sip_contact);
TEST_S(sip->sip_contact->m_display, "Bob");
TEST_S(sip->sip_contact->m_url->url_user, "b+b");
if (!once) {
/* The session expiration is not used by default. */
TEST_1(sip->sip_session_expires == NULL);
}
/* Test that B uses application-specific contact */
if (ctx->proxy_tests)
TEST_1(sip->sip_contact->m_url->url_user);
......@@ -293,6 +299,10 @@ int test_basic_call_1(struct context *ctx)
TEST_1(sip->sip_contact);
TEST_S(sip->sip_contact->m_display, "Alice");
TEST_S(sip->sip_contact->m_url->url_user, "a+a");
if (!once++) {
/* The Session-Expires header is not used by default. */
TEST_1(sip->sip_session_expires == NULL);
}
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(is_offer_recv(e->data->e_tags));
......
......@@ -22,7 +22,7 @@
*
*/
/**@CFILE test_nua_re_invite.c
/**@CFILE test_session_timer.c
* @brief NUA-8 tests: Session timer, UPDATE
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
......@@ -40,11 +40,13 @@
#elif HAVE_FUNCTION
#define __func__ __FUNCTION__
#else
#define __func__ "test_call_hold"
#define __func__ "test_session_timer"
#endif
/* ======================================================================== */
static size_t remove_update(void *a, void *message, size_t len);
int test_session_timer(struct context *ctx)
{
BEGIN();
......@@ -69,22 +71,6 @@ int test_session_timer(struct context *ctx)
|--INVITE->| |
|<--422----| |
|---ACK--->| |
| |
|-------INVITE------>|
|<-------422---------|
|--------ACK-------->|
| |
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
| |
|<------200 OK-------|
|--------ACK-------->|
| |
|<-------BYE---------|
|-------200 OK-------|
| |
*/
......@@ -94,8 +80,15 @@ int test_session_timer(struct context *ctx)
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
/* We negotiate session timer of 6 second */
/* Disable session timer from proxy */
test_proxy_set_session_timer(ctx->p, 0, 0);
nua_set_params(ctx->b.nua,
NUTAG_SESSION_REFRESHER(nua_any_refresher),
NUTAG_MIN_SE(1),
NUTAG_SESSION_TIMER(6),
NTATAG_SIP_T1X64(8000),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
......@@ -159,23 +152,132 @@ int test_session_timer(struct context *ctx)
if (print_headings)
printf("TEST NUA-8.1.1: PASSED\n");
if (ctx->expensive) {
nua_set_hparams(a_call->nh,
NUTAG_SUPPORTED("timer"),
NUTAG_MIN_SE(1),
NUTAG_SESSION_TIMER(5),
TAG_END());
run_a_until(ctx, nua_r_set_params, until_final_response);
if (print_headings)
printf("TEST NUA-8.1.2: Wait for refresh using INVITE\n");
run_ab_until(ctx, -1, until_ready, -1, until_ready);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-8.1.2: PASSED\n");
nua_set_hparams(b_call->nh,
NUTAG_UPDATE_REFRESH(1),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
if (print_headings)
printf("TEST NUA-8.1.3: Wait for refresh using UPDATE\n");
run_ab_until(ctx, -1, until_ready, -1, until_ready);
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_update);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-8.1.3: PASSED\n");
if (ctx->nat) {
struct nat_filter *f;
if (print_headings)
printf("TEST NUA-8.1.4: filter UPDATE, wait until session expires\n");
f = test_nat_add_filter(ctx->nat, remove_update, NULL, nat_inbound);
TEST_1(f);
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_bye);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
test_nat_remove_filter(ctx->nat, f);
if (print_headings)
printf("TEST NUA-8.1.4: PASSED\n");
}
}
if (b_call->nh) {
if (print_headings)
printf("TEST NUA-8.1.9: Terminate first session timer call\n");
BYE(b, b_call, b_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
free_events_in_list(ctx, a->events);
free_events_in_list(ctx, b->events);
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-8.1.9: PASSED\n");
}
/*
| |
|-------INVITE------>|
|<-------422---------|
|--------ACK-------->|
| |
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
| |
|<------200 OK-------|
|--------ACK-------->|
| |
|<-------BYE---------|
|-------200 OK-------|
| |
*/
if (print_headings)
printf("TEST NUA-8.1.2: Session timers negotiation\n");
printf("TEST NUA-8.2: Session timers negotiation\n");
nua_set_hparams(b_call->nh,
NUTAG_AUTOANSWER(0),
NUTAG_MIN_SE(120),
TAG_END());
test_proxy_set_session_timer(ctx->p, 180, 90);
nua_set_params(a->nua,
NUTAG_SUPPORTED("timer"),
TAG_END());
run_a_until(ctx, nua_r_set_params, until_final_response);
nua_set_params(b->nua,
NUTAG_AUTOANSWER(0),
NUTAG_MIN_SE(120),
NTATAG_SIP_T1X64(2000),
TAG_END());
run_b_until(ctx, nua_r_set_params, until_final_response);
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to),
TAG_END()));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
SIPTAG_SUPPORTED_STR("100rel, timer"),
NUTAG_SESSION_TIMER(15),
NUTAG_MIN_SE(5),
NUTAG_SESSION_TIMER(4),
NUTAG_MIN_SE(3),
TAG_END());
run_ab_until(ctx, -1, until_ready, -1, accept_call);
......@@ -252,10 +354,10 @@ int test_session_timer(struct context *ctx)
free_events_in_list(ctx, b->events);
if (print_headings)
printf("TEST NUA-8.1: PASSED\n");
printf("TEST NUA-8.2: PASSED\n");
if (print_headings)
printf("TEST NUA-8.2: use UPDATE\n");
printf("TEST NUA-8.3: UPDATE with session timer headers\n");
UPDATE(b, b_call, b_call->nh, TAG_END());
run_ab_until(ctx, -1, until_ready, -1, until_ready);
......@@ -313,7 +415,20 @@ int test_session_timer(struct context *ctx)
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-8.2: PASSED\n");
printf("TEST NUA-8.3: PASSED\n");
END();
}
static
size_t remove_update(void *a, void *message, size_t len)
{
(void)a;
if (strncmp("UPDATE ", message, 7) == 0) {
return 0;
}
return len;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment