[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