Commit c480cd26 authored by Pekka Pessi's avatar Pekka Pessi
Browse files

nua_subnotref.c: using dialog usage refresher and shutdown functions.

Now actually retrying after retry-after (either in NOTIFY or in a [3456]XX
response to SUBSCRIBE). Not waiting for final NOTIFY after we terminate
subscription. Wait only when trying to fetch event state.

darcs-hash:20060817183932-65a35-4a6b8eb17638881ac021eb66b0e4298ae7cdeb53.gz
parent a4a91bf6
......@@ -51,12 +51,14 @@
#include "nua_stack.h"
/* ---------------------------------------------------------------------- */
/* Subcribe event usage */
/* Subcriber event usage */
struct event_usage
{
enum nua_substate eu_substate; /**< Subscription state */
sip_time_t eu_expires; /**< Proposed expiration time */
unsigned eu_notified; /**< Number of NOTIFYs received */
unsigned eu_final_wait; /**< Waiting for final NOTIFY */
};
static char const *nua_subscribe_usage_name(nua_dialog_usage_t const *du);
......@@ -66,6 +68,11 @@ static int nua_subscribe_usage_add(nua_handle_t *nh,
static void nua_subscribe_usage_remove(nua_handle_t *nh,
nua_dialog_state_t *ds,
nua_dialog_usage_t *du);
static void nua_subscribe_usage_refresh(nua_handle_t *,
nua_dialog_usage_t *,
sip_time_t);
static int nua_subscribe_usage_shutdown(nua_handle_t *,
nua_dialog_usage_t *);
static nua_usage_class const nua_subscribe_usage[1] = {
{
......@@ -73,6 +80,9 @@ static nua_usage_class const nua_subscribe_usage[1] = {
nua_subscribe_usage_add,
nua_subscribe_usage_remove,
nua_subscribe_usage_name,
NULL,
nua_subscribe_usage_refresh,
nua_subscribe_usage_shutdown
}};
static char const *nua_subscribe_usage_name(nua_dialog_usage_t const *du)
......@@ -100,7 +110,7 @@ void nua_subscribe_usage_remove(nua_handle_t *nh,
}
/* ---------------------------------------------------------------------- */
/* Notify event usage */
/* Notifier side event usage */
static char const *nua_notify_usage_name(nua_dialog_usage_t const *du);
static int nua_notify_usage_add(nua_handle_t *nh,
......@@ -145,9 +155,6 @@ void nua_notify_usage_remove(nua_handle_t *nh,
/* ====================================================================== */
/* SUBSCRIBE */
static void
refresh_subscribe(nua_handle_t *nh, nua_dialog_usage_t *, sip_time_t now),
terminate_subscription(nua_handle_t *nh, nua_dialog_usage_t *, sip_time_t now);
static int process_response_to_subscribe(nua_handle_t *nh,
nta_outgoing_t *orq,
sip_t const *sip);
......@@ -175,7 +182,7 @@ nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
/* We can re-use existing INVITE handle */
nh->nh_special = nua_r_subscribe;
msg = nua_creq_msg(nua, nh, cr, cr->cr_retry_count,
msg = nua_creq_msg(nua, nh, cr, 0,
SIP_METHOD_SUBSCRIBE,
NUTAG_USE_DIALOG(1),
NUTAG_ADD_CONTACT(1),
......@@ -186,21 +193,32 @@ nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
sip_event_t *o = sip->sip_event;
if (e != nua_r_subscribe) { /* Unsubscribe */
sip_add_make(msg, sip, sip_expires_class, "0");
du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, o);
if (du == NULL && o == NULL) {
du = nua_dialog_usage_get(nh->nh_ds, nua_subscribe_usage, NONE);
if (du && du->du_event)
sip_add_dup(msg, sip, (sip_header_t *)du->du_event);
}
if (du) {
/* Embryonic subscription is just a placeholder */
eu = nua_dialog_usage_private(du);
if (eu->eu_substate == nua_substate_terminated ||
eu->eu_substate == nua_substate_embryonic) {
nua_dialog_usage_remove(nh, nh->nh_ds, du);
msg_destroy(msg);
return UA_EVENT3(e, SIP_200_OK,
NUTAG_SUBSTATE(nua_substate_terminated),
TAG_END());
}
}
}
else
/* We allow here SUBSCRIBE without event */
du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, o);
}
/* Store supported features (eventlist) */
/* Store message template with supported features (eventlist) */
if (du && sip) {
if (du->du_msg)
msg_destroy(du->du_msg);
......@@ -211,6 +229,8 @@ nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
process_response_to_subscribe, nh, NULL,
msg,
TAG_IF(e != nua_r_subscribe,
SIPTAG_EXPIRES_STR("0")),
SIPTAG_END(), TAG_NEXT(tags));
eu = nua_dialog_usage_private(du);
......@@ -221,7 +241,7 @@ nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
if (du == NULL)
;
else if (du->du_ready)
substate = eu->eu_substate; /* We already */
substate = eu->eu_substate; /* No change in subscription state */
else
nua_dialog_usage_remove(nh, nh->nh_ds, du);
......@@ -231,7 +251,7 @@ nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
NUTAG_SUBSTATE(substate), TAG_END());
}
du->du_pending = NULL;
nua_dialog_usage_no_refresh(du); /* during SUBSCRIBE transaction */
du->du_terminating = e != nua_r_subscribe; /* Unsubscribe or destroy */
if (du->du_terminating)
......@@ -244,6 +264,8 @@ nua_stack_subscribe(nua_t *nua, nua_handle_t *nh, nua_event_t e,
[Event] packages MUST also define a
default "Expires" value to be used if none is specified. */
eu->eu_expires = 3600;
eu->eu_final_wait = 0;
if (sip->sip_expires && sip->sip_expires->ex_delta == 0)
du->du_terminating = 1;
......@@ -294,22 +316,28 @@ static int process_response_to_subscribe(nua_handle_t *nh,
delta = sip_contact_expires(NULL, sip->sip_expires, sip->sip_date,
eu->eu_expires, now);
if (!win_messenger_enable)
nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
if (win_messenger_enable && !nua_dialog_is_established(nh->nh_ds)) {
/* Notify from messanger does not match with dialog tag */
nh->nh_ds->ds_remote_tag = su_strdup(nh->nh_home, "");
}
nua_dialog_uac_route(nh, nh->nh_ds, sip, 1);
nua_dialog_store_peer_info(nh, nh->nh_ds, sip);
if (delta > 0) {
nua_dialog_usage_set_refresh(du, delta);
du->du_pending = refresh_subscribe;
}
else {
else if (!eu->eu_notified) {
/* This is a fetch: subscription was really terminated
but we wait 32 seconds for NOTIFY. */
delta = 64 * NTA_SIP_T1 / 1000;
if (win_messenger_enable)
/* Wait 4 minutes for NOTIFY from Messenger */
du->du_refresh = now + 4 * 60;
else
/* Wait 32 seconds for NOTIFY */
du->du_refresh = now + 64 * NTA_SIP_T1 / 1000;
du->du_pending = terminate_subscription;
delta = 4 * 60; /* Wait 4 minutes for NOTIFY from Messenger */
eu->eu_final_wait = 1;
nua_dialog_usage_refresh_range(du, delta, delta);
}
}
else /* if (status >= 300) */ {
......@@ -350,97 +378,127 @@ static int process_response_to_subscribe(nua_handle_t *nh,
}
/** Refresh subscription */
void
refresh_subscribe(nua_handle_t *nh, nua_dialog_usage_t *du, sip_time_t now)
static void nua_subscribe_usage_refresh(nua_handle_t *nh,
nua_dialog_usage_t *du,
sip_time_t now)
{
nua_t *nua = nh->nh_nua;
nua_client_request_t *cr = nh->nh_cr;
nua_event_t e;
struct event_usage *eu = nua_dialog_usage_private(du);
msg_t *msg;
sip_t *sip;
if (cr->cr_msg) { /* Already doing something */
/* Delay of 5 .. 15 seconds */
du->du_refresh = sip_now() + su_randint(5, 15);
du->du_pending = refresh_subscribe;
assert(eu);
if (eu->eu_final_wait) {
/* Did not receive NOTIFY for fetch... */
sip_event_t const *o = du->du_event;
char const *id = o ? o->o_id : NULL;
SU_DEBUG_3(("nua(%p): fetch event %s%s%s timeouts\n",
nh, o ? o->o_type : "(empty)",
id ? "; id=" : "", id ? id : ""));
nua_stack_event(nh->nh_nua, nh, NULL,
nua_i_notify, 408, "Fetch Timeouts without NOTIFY",
NUTAG_SUBSTATE(nua_substate_terminated),
SIPTAG_EVENT(o),
TAG_END());
nua_dialog_usage_remove(nh, nh->nh_ds, du);
return;
}
if (now > 0)
e = nua_r_subscribe;
else
e = nua_r_destroy, du->du_terminating = 1;
cr->cr_msg = msg_copy(du->du_msg);
if (du->du_terminating) /* No need to refresh. */
return;
{
/* Remove initial route */
sip_t *sip = sip_object(cr->cr_msg);
if (sip->sip_route)
msg_header_remove(cr->cr_msg, (msg_pub_t*)sip, (void *)sip->sip_route);
if (cr->cr_msg) {
/* Already doing something, delay 5..15 seconds? */
if (cr->cr_usage != du)
nua_dialog_usage_refresh_range(du, 5, 15);
return;
}
cr->cr_msg = msg_copy(du->du_msg);
msg = nua_creq_msg(nua, nh, cr, 1,
SIP_METHOD_SUBSCRIBE,
NUTAG_USE_DIALOG(1),
NUTAG_ADD_CONTACT(1),
TAG_IF(du->du_terminating,
SIPTAG_EXPIRES_STR("0")),
/* If dialog is established, remove initial route */
TAG_IF(nua_dialog_is_established(nh->nh_ds),
SIPTAG_ROUTE(NONE)),
TAG_END());
sip = sip_object(msg);
cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
process_response_to_subscribe, nh, NULL,
msg,
SIPTAG_END(), TAG_NEXT(NULL));
if (!cr->cr_orq) {
struct event_usage *eu = nua_dialog_usage_private(du);
if (du->du_terminating)
nua_dialog_usage_remove(nh, nh->nh_ds, du);
msg_destroy(msg);
UA_EVENT3(e, NUA_INTERNAL_ERROR,
NUTAG_SUBSTATE(eu->eu_substate), TAG_END());
if (cr->cr_orq) {
cr->cr_usage = du;
cr->cr_event = nua_r_subscribe;
return;
}
cr->cr_usage = du;
cr->cr_event = e;
if (du->du_terminating)
nua_dialog_usage_remove(nh, nh->nh_ds, du);
else /* Try again later? */
nua_dialog_usage_refresh_range(du, 5, 15);
msg_destroy(msg);
UA_EVENT3(nua_r_subscribe, NUA_INTERNAL_ERROR,
NUTAG_SUBSTATE(eu->eu_substate),
TAG_END());
}
/** Terminate subscription when we have not received a NOTIFY */
static void
terminate_subscription(nua_handle_t *nh,
nua_dialog_usage_t *du,
sip_time_t now)
/** Terminate subscription */
static int nua_subscribe_usage_shutdown(nua_handle_t *nh,
nua_dialog_usage_t *du)
{
sip_event_t const *o = NULL;
char const *id;
nua_t *nua = nh->nh_nua;
nua_client_request_t *cr = nh->nh_cr;
struct event_usage *eu = nua_dialog_usage_private(du);
msg_t *msg;
if (!du) {
SU_DEBUG_1(("nua(%p): terminate_subscription() without usage to remove\n",
nh));
return;
}
assert(eu);
o = du->du_event;
id = o ? o->o_id : NULL;
SU_DEBUG_3(("nua(%p): terminate_subscription() with event %s%s%s\n",
nh, o ? o->o_type : "(empty)",
id ? "; id=" : "", id ? id : ""));
if (du->du_terminating)
return 100; /* ...in progress */
if (cr->cr_msg)
/* XXX - already doing something else? */
return 100;
nua_stack_event(nh->nh_nua, nh, NULL,
nua_i_notify, 408, "Early Subscription Timeouts without NOTIFY",
NUTAG_SUBSTATE(nua_substate_terminated),
SIPTAG_EVENT(o),
TAG_END());
cr->cr_msg = msg_copy(du->du_msg);
msg = nua_creq_msg(nua, nh, cr, 1,
SIP_METHOD_SUBSCRIBE,
NUTAG_USE_DIALOG(1),
NUTAG_ADD_CONTACT(1),
SIPTAG_EXPIRES_STR("0"),
/* If dialog is established, remove initial route */
TAG_IF(nua_dialog_is_established(nh->nh_ds),
SIPTAG_ROUTE(NONE)),
TAG_END());
cr->cr_orq = nta_outgoing_mcreate(nua->nua_nta,
process_response_to_subscribe, nh, NULL,
msg,
SIPTAG_END(), TAG_NEXT(NULL));
if (cr->cr_orq) {
cr->cr_usage = du;
cr->cr_event = nua_r_destroy;
return 100;
}
/* Too bad. */
nua_dialog_usage_remove(nh, nh->nh_ds, du);
msg_destroy(msg);
return 200;
}
/** @internal Process incoming SUBSCRIBE. */
int nua_stack_process_subsribe(nua_t *nua,
nua_handle_t *nh,
......@@ -548,8 +606,11 @@ static int process_response_to_notify(nua_handle_t *nh,
nta_outgoing_t *orq,
sip_t const *sip);
int
nua_stack_notify(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags)
/**@internal Send NOTIFY. */
int nua_stack_notify(nua_t *nua,
nua_handle_t *nh,
nua_event_t e,
tagi_t const *tags)
{
struct nua_client_request *cr = nh->nh_cr;
nua_dialog_usage_t *du = NULL;
......@@ -642,12 +703,12 @@ nua_stack_notify(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags
else
du->du_refresh = now + expires;
ss = sip_subscription_state_format(msg_home(msg), "%s;expires=%lu",
substate, expires);
substate, expires);
}
else {
du->du_refresh = now;
ss = sip_subscription_state_make(msg_home(msg), "terminated; "
"reason=noresource");
"reason=noresource");
}
msg_header_insert(msg, (void *)sip, (void *)ss);
......@@ -713,7 +774,8 @@ int nua_stack_process_notify(nua_t *nua,
assert(nh);
if (/* XXX - support forking of subscriptions?... */
ds->ds_remote_tag && sip && sip->sip_from->a_tag &&
ds->ds_remote_tag && ds->ds_remote_tag[0] &&
sip && sip->sip_from->a_tag &&
strcmp(ds->ds_remote_tag, sip->sip_from->a_tag)) {
sip_contact_t const *m = NULL;
sip_warning_t *w = NULL, w0[1];
......@@ -742,23 +804,22 @@ int nua_stack_process_notify(nua_t *nua,
eu = nua_dialog_usage_private(du); assert(eu);
eu->eu_notified++;
if (subs == NULL) {
/* Do some compatibility stuff here */
unsigned long delta = 3600;
unsigned long delta;
sip_subscription_state_init(subs = ss0);
if (sip->sip_expires)
delta = sip->sip_expires->ex_delta;
else
delta = eu->eu_expires;
delta = sip->sip_expires ? sip->sip_expires->ex_delta : eu->eu_expires;
if (delta == 0)
subs->ss_substate = "terminated";
else
subs->ss_substate = "active";
if (delta > 0) {
if (delta > 0 && sip->sip_expires) {
snprintf(expires, sizeof expires, "%lu", delta);
subs->ss_expires = expires;
}
......@@ -772,13 +833,15 @@ int nua_stack_process_notify(nua_t *nua,
if (str0casecmp(subs->ss_reason, why = "deactivated") == 0) {
eu->eu_substate = nua_substate_embryonic;
retry = 0;
retry = 0; /* retry immediately */
}
else if (str0casecmp(subs->ss_reason, why = "probation") == 0) {
eu->eu_substate = nua_substate_embryonic;
retry = 30;
if (subs->ss_retry_after)
retry = strtoul(subs->ss_retry_after, NULL, 10);
if (retry > 3600)
retry = 3600;
}
else
why = subs->ss_reason;
......@@ -786,10 +849,13 @@ int nua_stack_process_notify(nua_t *nua,
else if (strcasecmp(subs->ss_substate, what = "pending") == 0)
eu->eu_substate = nua_substate_pending;
else /* if (strcasecmp(subs->ss_substate, "active") == 0) */ {
/* Any extended state is considered as active */
what = subs->ss_substate ? subs->ss_substate : "active";
/* XXX - any extended state is considered as active */
eu->eu_substate = nua_substate_active;
}
if (du->du_terminating)
retry = -1;
response = nh_make_response(nua, nh, irq, SIP_200_OK,
SIPTAG_ALLOW(NH_PGET(nh, allow)),
......@@ -804,10 +870,13 @@ int nua_stack_process_notify(nua_t *nua,
else
nta_incoming_treply(irq, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
if (eu->eu_substate == nua_substate_terminated && retry > 0)
eu->eu_substate = nua_substate_embryonic;
nua_stack_event(nh->nh_nua, nh, nta_incoming_getrequest(irq),
nua_i_notify, SIP_200_OK,
NUTAG_SUBSTATE(eu->eu_substate),
TAG_END());
nua_i_notify, SIP_200_OK,
NUTAG_SUBSTATE(eu->eu_substate),
TAG_END());
nta_incoming_destroy(irq), irq = NULL;
......@@ -815,27 +884,34 @@ int nua_stack_process_notify(nua_t *nua,
nh, what, why ? why : ""));
if (eu->eu_substate == nua_substate_terminated) {
du->du_refresh = 0, du->du_pending = NULL;
if (du != nh->nh_cr->cr_usage)
nua_dialog_usage_remove(nh, nh->nh_ds, du);
else
nua_dialog_usage_no_refresh(du);
}
else if (eu->eu_substate == nua_substate_embryonic) {
if (retry != -1 && !du->du_terminating) {
if (retry >= 0) {
/* Try to subscribe again */
/* XXX - how to handle dialog ?? */
nua_dialog_usage_set_refresh(du, retry);
du->du_pending = refresh_subscribe;
nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */
nua_dialog_usage_refresh_range(du, retry, retry + 5);
}
else if (du != nh->nh_cr->cr_usage)
nua_dialog_usage_remove(nh, nh->nh_ds, du);
else
nua_dialog_usage_no_refresh(du);
}
else if (subs->ss_expires) {
sip_time_t delta = strtoul(subs->ss_expires, NULL, 10);
else if (du->du_terminating) {
nua_dialog_usage_no_refresh(du);
}
else {
sip_time_t delta;
if (subs->ss_expires)
delta = strtoul(subs->ss_expires, NULL, 10);
else
delta = eu->eu_expires;
if (!du->du_terminating) {
nua_dialog_usage_set_refresh(du, delta);
du->du_pending = refresh_subscribe;
}
nua_dialog_usage_set_refresh(du, delta);
}
return 0;
......
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