[asterisk-commits] file: branch file/pimp_sip_registration r383558 - /team/file/pimp_sip_registr...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Mar 21 20:26:16 CDT 2013
Author: file
Date: Thu Mar 21 20:26:13 2013
New Revision: 383558
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383558
Log:
Incorporate more feedback.
Modified:
team/file/pimp_sip_registration/res/res_sip_outbound_registration.c
Modified: team/file/pimp_sip_registration/res/res_sip_outbound_registration.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_registration/res/res_sip_outbound_registration.c?view=diff&rev=383558&r1=383557&r2=383558
==============================================================================
--- team/file/pimp_sip_registration/res/res_sip_outbound_registration.c (original)
+++ team/file/pimp_sip_registration/res/res_sip_outbound_registration.c Thu Mar 21 20:26:13 2013
@@ -29,6 +29,10 @@
#include "asterisk/res_sip.h"
#include "asterisk/module.h"
+#include "asterisk/taskprocessor.h"
+
+/*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */
+#define REREGISTER_BUFFER_TIME 10
/*! \brief Various states that an outbound registration may be in */
enum sip_outbound_registration_status {
@@ -40,22 +44,34 @@
SIP_REGISTRATION_REJECTED_TEMPORARY,
/*! \brief Registration was rejected, permanently */
SIP_REGISTRATION_REJECTED_PERMANENT,
+ /*! \brief Registration has been stopped */
+ SIP_REGISTRATION_STOPPED,
};
-/*! \brief Outbound registration state information */
-struct sip_outbound_registration_state {
+/*! \brief Outbound registration client state information (persists for lifetime of regc) */
+struct sip_outbound_registration_client_state {
/*! \brief Current status of this registration */
enum sip_outbound_registration_status status;
/*! \brief Outbound registration client */
pjsip_regc *client;
/*! \brief Timer entry for retrying on temporal responses */
- pj_timer_entry *timer;
+ pj_timer_entry timer;
/*! \brief Current number of retries */
unsigned int retries;
/*! \brief Maximum number of retries permitted */
unsigned int max_retries;
/*! \brief Interval at which retries should occur for temporal responses */
unsigned int retry_interval;
+ /*! \brief Serializer for stuff and things */
+ struct ast_taskprocessor *serializer;
+ /*! \brief Registration should be destroyed after completion of transaction */
+ unsigned int destroy:1;
+};
+
+/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
+struct sip_outbound_registration_state {
+ /*! \brief Client state information */
+ struct sip_outbound_registration_client_state *client_state;
};
/*! \brief Outbound registration information */
@@ -85,22 +101,122 @@
struct sip_outbound_registration_state *state;
};
-/*! \brief Callback function for outbound registration attempt or destruction */
-static void sip_outbound_registration_attempt_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
-{
+/*! \brief Helper function which cancels the timer on a client */
+static void cancel_registration(struct sip_outbound_registration_client_state *client_state)
+{
+ if (!client_state->timer.id) {
+ return;
+ }
+
+ if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) {
+ /* The timer was successfully cancelled, drop the refcount of client_state */
+ ao2_ref(client_state, -1);
+ }
+
+ client_state->timer.id = 0;
+}
+
+/*! \brief Callback function for registering */
+static int handle_client_registration(void *data)
+{
+ RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
pjsip_tx_data *tdata;
- if (entry->id == 1) {
- if (pjsip_regc_register(entry->user_data, PJ_TRUE, &tdata) != PJ_SUCCESS) {
- return;
+ cancel_registration(client_state);
+
+ if ((client_state->status == SIP_REGISTRATION_STOPPED) ||
+ (pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS)) {
+ return 0;
+ }
+
+ /* Due to the registration the callback may now get called, so bump the ref count */
+ ao2_ref(client_state, +1);
+ if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) {
+ ao2_ref(client_state, -1);
+ pjsip_tx_data_dec_ref(tdata);
+ }
+
+ return 0;
+}
+
+/*! \brief Timer callback function, used just for registrations */
+static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
+{
+ RAII_VAR(struct sip_outbound_registration_client_state *, client_state, entry->user_data, ao2_cleanup);
+
+ ao2_ref(client_state, +1);
+ if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
+ ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n");
+ ao2_ref(client_state, -1);
+ }
+
+ entry->id = 0;
+}
+
+/*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
+static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds)
+{
+ pj_time_val delay = { .sec = seconds, };
+
+ cancel_registration(client_state);
+ client_state->timer.id = 1;
+
+ ao2_ref(client_state, +1);
+ if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) {
+ ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n");
+ ao2_ref(client_state, -1);
+ }
+}
+
+/*! \brief Callback function for unregistering (potentially) and destroying state */
+static int handle_client_state_destruction(void *data)
+{
+ RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
+ pjsip_regc_info info;
+
+ cancel_registration(client_state);
+
+ pjsip_regc_get_info(client_state->client, &info);
+
+ if (info.is_busy == PJ_TRUE) {
+ /* If a client transaction is in progress we defer until it is complete */
+ client_state->destroy = 1;
+ return 0;
+ }
+
+ if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
+ pjsip_tx_data *tdata;
+
+ if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
+ pjsip_regc_send(client_state->client, tdata);
}
- pjsip_regc_send(entry->user_data, tdata);
- } else if (entry->id == 2) {
- if (pjsip_regc_unregister(entry->user_data, &tdata) == PJ_SUCCESS) {
- pjsip_regc_send(entry->user_data, tdata);
- }
- pjsip_regc_destroy(entry->user_data);
- }
+ }
+
+ pjsip_regc_destroy(client_state->client);
+
+ client_state->status = SIP_REGISTRATION_STOPPED;
+
+ return 0;
+}
+
+/*! \brief Structure for registration response */
+struct registration_response {
+ /*! \brief Response code for the registration attempt */
+ int code;
+ /*! \brief Expiration time for registration */
+ int expiration;
+ /*! \brief Retry-After value */
+ int retry_after;
+ /*! \brief Outbound registration client state */
+ struct sip_outbound_registration_client_state *client_state;
+};
+
+/*! \brief Registration response structure destructor */
+static void registration_response_destroy(void *obj)
+{
+ struct registration_response *response = obj;
+
+ ao2_cleanup(response->client_state);
}
/* \brief Helper funtion which determines if a response code is temporal or not */
@@ -119,39 +235,77 @@
}
}
-/*! \brief Callback function for outbound registration client */
-static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
-{
- struct sip_outbound_registration_state *state = param->token;
+/*! \brief Callback function for handling a response to a registration attempt */
+static int handle_registration_response(void *data)
+{
+ RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
pjsip_regc_info info;
char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE];
- pjsip_regc_get_info(param->regc, &info);
+ if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
+ return 0;
+ }
+
+ pjsip_regc_get_info(response->client_state->client, &info);
ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
- if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
- /* Registration was accepted, yahoo! */
- state->status = SIP_REGISTRATION_REGISTERED;
- state->retries = 0;
- } else if (state->retry_interval && sip_outbound_registration_is_temporal(param->code)) {
- if (state->retries == state->max_retries) {
- state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+ if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) {
+ /* If the registration went fine simply reschedule registration for the future */
+ response->client_state->status = SIP_REGISTRATION_REGISTERED;
+ response->client_state->retries = 0;
+ schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME);
+ } else if (response->retry_after) {
+ /* If we have been instructed to retry after a period of time, schedule it as such */
+ response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+ schedule_registration(response->client_state, response->retry_after);
+ ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on registration attempt to '%s', instructed to retry in '%d'\n",
+ response->code, server_uri, client_uri, response->retry_after);
+ } else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code)) {
+ if (response->client_state->retries == response->client_state->max_retries) {
+ /* If we received enough temporal responses to exceed our maximum give up permanently */
+ response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n",
server_uri, client_uri);
} else {
- pj_time_val delay = { .sec = state->retry_interval, };
-
- state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
- state->retries++;
- pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), state->timer, &delay);
+ /* On the other hand if we can still try some more do so */
+ response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY;
+ response->client_state->retries++;
+ schedule_registration(response->client_state, response->client_state->retry_interval);
ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on registration attempt to '%s', retrying in '%d' seconds\n",
- param->code, server_uri, client_uri, state->retry_interval);
+ response->code, server_uri, client_uri, response->client_state->retry_interval);
}
} else {
- state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
+ /* Finally if there's no hope of registering give up */
+ response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT;
ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n",
- param->code, server_uri, client_uri);
+ response->code, server_uri, client_uri);
+ }
+
+ /* If deferred destruction is in use see if we need to destroy now */
+ if (response->client_state->destroy) {
+ handle_client_state_destruction(response->client_state);
+ }
+
+ return 0;
+}
+
+/*! \brief Callback function for outbound registration client */
+static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
+{
+ RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
+ struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy);
+ struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL);
+
+ response->code = param->code;
+ response->expiration = param->expiration;
+ response->retry_after = retry_after ? retry_after->ivalue : 0;
+ response->client_state = client_state;
+ ao2_ref(response->client_state, +1);
+
+ if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) {
+ ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n");
+ ao2_cleanup(response);
}
}
@@ -159,19 +313,23 @@
static void sip_outbound_registration_state_destroy(void *obj)
{
struct sip_outbound_registration_state *state = obj;
- pj_time_val delay = { .sec = 1, };
-
- if (!state->client) {
+
+ if (!state->client_state) {
return;
}
- if (state->status != SIP_REGISTRATION_UNREGISTERED && state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
- pjsip_endpt_cancel_timer(ast_sip_get_pjsip_endpoint(), state->timer);
- state->timer->id = 2;
- pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), state->timer, &delay);
- } else {
- pjsip_regc_destroy(state->client);
- }
+ if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) {
+ ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n");
+ ao2_ref(state->client_state, -1);
+ }
+}
+
+/*! \brief Destructor function for client registration state */
+static void sip_outbound_registration_client_state_destroy(void *obj)
+{
+ struct sip_outbound_registration_client_state *client_state = obj;
+
+ ast_taskprocessor_unreference(client_state->serializer);
}
/*! \brief Allocator function for registration state */
@@ -179,20 +337,23 @@
{
struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy);
- if (!state) {
- return NULL;
- }
-
- if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state, sip_outbound_registration_response_cb, &state->client) != PJ_SUCCESS) ||
- !(state->timer = PJ_POOL_ZALLOC_T(pjsip_regc_get_pool(state->client), struct pj_timer_entry))) {
+ if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) {
ao2_cleanup(state);
return NULL;
}
- state->status = SIP_REGISTRATION_UNREGISTERED;
- state->timer->id = 1;
- state->timer->user_data = state->client;
- state->timer->cb = sip_outbound_registration_attempt_cb;
+ if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, sip_outbound_registration_response_cb, &state->client_state->client) != PJ_SUCCESS) ||
+ !(state->client_state->serializer = ast_sip_create_serializer())) {
+ /* This is on purpose, normal operation will have it be deallocated within the serializer */
+ pjsip_regc_destroy(state->client_state->client);
+ ao2_cleanup(state->client_state);
+ ao2_cleanup(state);
+ return NULL;
+ }
+
+ state->client_state->status = SIP_REGISTRATION_UNREGISTERED;
+ state->client_state->timer.user_data = state->client_state;
+ state->client_state->timer.cb = sip_outbound_registration_timer_cb;
return state;
}
@@ -324,7 +485,7 @@
}
}
- pjsip_regc_set_transport(applied->state->client, &selector);
+ pjsip_regc_set_transport(applied->state->client_state->client, &selector);
if (!ast_strlen_zero(applied->outbound_proxy)) {
pjsip_route_hdr route_set, *route;
@@ -333,24 +494,24 @@
pj_list_init(&route_set);
- pj_strdup2_with_null(pjsip_regc_get_pool(applied->state->client), &tmp, applied->outbound_proxy);
- if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(applied->state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
+ pj_strdup2_with_null(pjsip_regc_get_pool(applied->state->client_state->client), &tmp, applied->outbound_proxy);
+ if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(applied->state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) {
return -1;
}
pj_list_push_back(&route_set, route);
- pjsip_regc_set_route_set(applied->state->client, &route_set);
+ pjsip_regc_set_route_set(applied->state->client_state->client, &route_set);
}
pj_cstr(&server_uri, applied->server_uri);
- if (sip_dialog_create_contact(pjsip_regc_get_pool(applied->state->client), &contact_uri, S_OR(applied->contact_user, "s"), &server_uri, &selector)) {
+ if (sip_dialog_create_contact(pjsip_regc_get_pool(applied->state->client_state->client), &contact_uri, S_OR(applied->contact_user, "s"), &server_uri, &selector)) {
return -1;
}
pj_cstr(&client_uri, applied->client_uri);
- if (pjsip_regc_init(applied->state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, applied->expiration) != PJ_SUCCESS) {
+ if (pjsip_regc_init(applied->state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, applied->expiration) != PJ_SUCCESS) {
return -1;
}
@@ -361,17 +522,14 @@
static int sip_outbound_registration_perform(void *obj, void *arg, int flags)
{
struct sip_outbound_registration *registration = obj;
- pj_time_val delay = { .sec = 1, };
-
- registration->state->retry_interval = registration->retry_interval;
- registration->state->max_retries = registration->max_retries;
- registration->state->retries = 0;
-
- pjsip_regc_update_expires(registration->state->client, registration->expiration);
-
- /* Cancel retry attept if present and try a new registration */
- pjsip_endpt_cancel_timer(ast_sip_get_pjsip_endpoint(), registration->state->timer);
- pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), registration->state->timer, &delay);
+
+ registration->state->client_state->retry_interval = registration->retry_interval;
+ registration->state->client_state->max_retries = registration->max_retries;
+ registration->state->client_state->retries = 0;
+
+ pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration);
+
+ schedule_registration(registration->state->client_state, (ast_random() % 10) + 1);
return 0;
}
More information about the asterisk-commits
mailing list