Commit d3137b81 authored by jehan's avatar jehan

refresher recover from some errors

parent 4da27241
......@@ -44,13 +44,13 @@ BELLESIP_EXPORT belle_sip_request_t *belle_sip_dialog_create_request(belle_sip_d
*/
BELLESIP_EXPORT belle_sip_request_t * belle_sip_dialog_create_request_from(belle_sip_dialog_t *obj, const belle_sip_request_t *initial_req);
void belle_sip_dialog_delete(belle_sip_dialog_t *dialog);
BELLESIP_EXPORT void belle_sip_dialog_delete(belle_sip_dialog_t *dialog);
BELLESIP_EXPORT void *belle_sip_dialog_get_application_data(const belle_sip_dialog_t *dialog);
BELLESIP_EXPORT void belle_sip_dialog_set_application_data(belle_sip_dialog_t *dialog, void *data);
const char *belle_sip_dialog_get_dialog_id(const belle_sip_dialog_t *dialog);
BELLESIP_EXPORT const char *belle_sip_dialog_get_dialog_id(const belle_sip_dialog_t *dialog);
BELLESIP_EXPORT const belle_sip_header_call_id_t *belle_sip_dialog_get_call_id(const belle_sip_dialog_t *dialog);
......@@ -69,13 +69,13 @@ BELLESIP_EXPORT const char *belle_sip_dialog_get_local_tag(const belle_sip_dialo
BELLESIP_EXPORT const char *belle_sip_dialog_get_remote_tag(const belle_sip_dialog_t *dialog);
const belle_sip_header_address_t *belle_sip_dialog_get_remote_target(belle_sip_dialog_t *dialog);
BELLESIP_EXPORT const belle_sip_header_address_t *belle_sip_dialog_get_remote_target(belle_sip_dialog_t *dialog);
const belle_sip_list_t* belle_sip_dialog_get_route_set(belle_sip_dialog_t *dialog);
BELLESIP_EXPORT const belle_sip_list_t* belle_sip_dialog_get_route_set(belle_sip_dialog_t *dialog);
BELLESIP_EXPORT belle_sip_dialog_state_t belle_sip_dialog_get_state(const belle_sip_dialog_t *dialog);
/*
* return the dialog state before last transition. Can be usefull to detect early avorted dialogs
* return the dialog state before last transition. Can be useful to detect early avorted dialogs
* @param dialog
* @returns state
* */
......@@ -84,12 +84,17 @@ BELLESIP_EXPORT belle_sip_dialog_state_t belle_sip_dialog_get_previous_state(con
BELLESIP_EXPORT int belle_sip_dialog_is_server(const belle_sip_dialog_t *dialog);
int belle_sip_dialog_is_secure(const belle_sip_dialog_t *dialog);
BELLESIP_EXPORT int belle_sip_dialog_is_secure(const belle_sip_dialog_t *dialog);
BELLESIP_EXPORT void belle_sip_dialog_send_ack(belle_sip_dialog_t *dialog, belle_sip_request_t *request);
void belle_sip_dialog_terminate_on_bye(belle_sip_dialog_t *dialog, int val);
BELLESIP_EXPORT void belle_sip_dialog_terminate_on_bye(belle_sip_dialog_t *dialog, int val);
/*
* Give access to the last transaction processed by a dialog. Can be useful to get reason code for dialog terminated before reaching established state
* @param dialog
* @return last transaction
*/
BELLESIP_EXPORT belle_sip_transaction_t* belle_sip_dialog_get_last_transaction(const belle_sip_dialog_t *dialog);
BELLE_SIP_END_DECLS
#endif
......
......@@ -58,4 +58,14 @@ BELLESIP_EXPORT int belle_sip_refresher_refresh(belle_sip_refresher_t* refresher
*/
BELLESIP_EXPORT int belle_sip_refresher_get_expires(const belle_sip_refresher_t* refresher);
/**
* returns delay in ms after which the refresher will retry in case of recoverable error (I.E 408, 480, 503, 504, io error);
*/
BELLESIP_EXPORT int belle_sip_refresher_get_retry_after(const belle_sip_refresher_t* refresher);
/**
* Delay in ms after which the refresher will retry in case of recoverable error (I.E 408, 480, 503, 504, io error);
*/
BELLESIP_EXPORT void belle_sip_refresher_set_retry_after(belle_sip_refresher_t* refresher, int delay_ms);
#endif /* REFRESHER_HELPER_H_ */
......@@ -704,6 +704,7 @@ struct belle_sip_dialog{
int is_secure:1;
int terminate_on_bye:1;
int needs_ack:1;
belle_sip_transaction_t* last_transaction;
};
belle_sip_dialog_t *belle_sip_dialog_new(belle_sip_transaction_t *t);
......@@ -711,7 +712,7 @@ belle_sip_dialog_t * belle_sip_provider_create_dialog_internal(belle_sip_provide
/*returns 1 if message belongs to the dialog, 0 otherwise */
int _belle_sip_dialog_match(belle_sip_dialog_t *obj, const char *call_id, const char *local_tag, const char *remote_tag);
int belle_sip_dialog_match(belle_sip_dialog_t *obj, belle_sip_message_t *msg, int as_uas);
int belle_sip_dialog_update(belle_sip_dialog_t *obj,belle_sip_request_t *req, belle_sip_response_t *resp, int as_uas);
int belle_sip_dialog_update(belle_sip_dialog_t *obj,belle_sip_transaction_t* transaction, int as_uas);
void belle_sip_dialog_check_ack_sent(belle_sip_dialog_t*obj);
int belle_sip_dialog_handle_ack(belle_sip_dialog_t *obj, belle_sip_request_t *ack);
......
......@@ -303,7 +303,7 @@ static void belle_sip_resolver_context_destroy(belle_sip_resolver_context_t *ctx
if (ctx->R)
dns_res_close(ctx->R);
if (ctx->hosts)
free(ctx->hosts);
dns_hosts_release(ctx->hosts);
if (ctx->resconf)
free(ctx->resconf);
}
......
......@@ -41,6 +41,8 @@ static void belle_sip_dialog_uninit(belle_sip_dialog_t *obj){
belle_sip_object_unref(obj->last_out_invite);
if (obj->last_out_ack)
belle_sip_object_unref(obj->last_out_ack);
if (obj->last_transaction)
belle_sip_object_unref(obj->last_transaction);
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sip_dialog_t);
......@@ -315,9 +317,15 @@ static void belle_sip_dialog_stop_200Ok_retrans(belle_sip_dialog_t *obj){
/*
* return 0 if message should be delivered to the next listener, otherwise, its a retransmision, just keep it
* */
int belle_sip_dialog_update(belle_sip_dialog_t *obj,belle_sip_request_t *req, belle_sip_response_t *resp, int as_uas){
int belle_sip_dialog_update(belle_sip_dialog_t *obj,belle_sip_transaction_t* transaction, int as_uas){
int code;
int is_retransmition=FALSE;
belle_sip_request_t *req=belle_sip_transaction_get_request(transaction);
belle_sip_response_t *resp=belle_sip_transaction_get_response(transaction);
if (obj->last_transaction) belle_sip_object_unref(obj->last_transaction);
obj->last_transaction=transaction;
belle_sip_object_ref(obj->last_transaction);
/*first update local/remote cseq*/
if (as_uas) {
belle_sip_header_cseq_t* cseq=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_cseq_t);
......@@ -707,3 +715,6 @@ belle_sip_dialog_t* belle_sip_provider_find_dialog(const belle_sip_provider_t *p
}
return NULL;
}
belle_sip_transaction_t* belle_sip_dialog_get_last_transaction(const belle_sip_dialog_t *dialog) {
return dialog->last_transaction;
}
......@@ -402,13 +402,20 @@ void belle_sip_provider_add_dialog(belle_sip_provider_t *prov, belle_sip_dialog_
prov->dialogs=belle_sip_list_prepend(prov->dialogs,belle_sip_object_ref(dialog));
}
static void notify_dialog_terminated(belle_sip_dialog_terminated_event_t* ev) {
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(((belle_sip_provider_t*)ev->source)->listeners,process_dialog_terminated,ev);
belle_sip_object_unref(ev->dialog);
belle_sip_free(ev);
}
void belle_sip_provider_remove_dialog(belle_sip_provider_t *prov, belle_sip_dialog_t *dialog){
belle_sip_dialog_terminated_event_t ev;
ev.source=prov;
ev.dialog=dialog;
belle_sip_dialog_terminated_event_t* ev=belle_sip_malloc(sizeof(belle_sip_dialog_terminated_event_t));
ev->source=prov;
ev->dialog=dialog;
prov->dialogs=belle_sip_list_remove(prov->dialogs,dialog);
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_dialog_terminated,&ev);
belle_sip_object_unref(dialog);
belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(prov->stack)
,(belle_sip_callback_t) notify_dialog_terminated
, ev);
}
belle_sip_client_transaction_t *belle_sip_provider_create_client_transaction(belle_sip_provider_t *prov, belle_sip_request_t *req){
......
......@@ -19,6 +19,8 @@
#include "belle_sip_internal.h"
#include "belle-sip/refresher-helper.h"
#define DEFAULT_RETRY_AFTER 60000
struct belle_sip_refresher {
belle_sip_object_t obj;
belle_sip_refresher_listener_t listener;
......@@ -29,7 +31,30 @@ struct belle_sip_refresher {
belle_sip_listener_callbacks_t listener_callbacks;
belle_sip_listener_t *sip_listener;
void* user_data;
int retry_after;
};
static int set_expires_from_trans(belle_sip_refresher_t* refresher);
static int timer_cb(void *user_data, unsigned int events) ;
static void schedule_timer_at(belle_sip_refresher_t* refresher,int delay) {
if (delay>0) {
if (refresher->timer){
belle_sip_main_loop_remove_source(belle_sip_stack_get_main_loop(refresher->transaction->base.provider->stack),refresher->timer);
belle_sip_object_unref(refresher->timer);
}
refresher->timer=belle_sip_timeout_source_new(timer_cb,refresher,delay);
belle_sip_object_set_name((belle_sip_object_t*)refresher->timer,"Refresher timeout");
belle_sip_main_loop_add_source(belle_sip_stack_get_main_loop(refresher->transaction->base.provider->stack),refresher->timer);
refresher->started=1;
}
}
static void schedule_timer(belle_sip_refresher_t* refresher) {
schedule_timer_at(refresher,refresher->expires*900);
}
static void process_dialog_terminated(void *user_ctx, const belle_sip_dialog_terminated_event_t *event){
/*nop*/
......@@ -40,25 +65,24 @@ static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *e
if (belle_sip_object_is_instance_of(BELLE_SIP_OBJECT(belle_sip_io_error_event_get_source(event)),BELLE_SIP_TYPE_ID(belle_sip_client_transaction_t))) {
client_transaction=BELLE_SIP_CLIENT_TRANSACTION(belle_sip_io_error_event_get_source(event));
if (!refresher || (refresher && (!refresher->started || client_transaction !=refresher->transaction )))
if (!refresher || (refresher && ((!refresher->started && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) != BELLE_SIP_TRANSACTION_TRYING)
|| client_transaction !=refresher->transaction )))
return; /*not for me or no longuer involved*/
/*first stop timer if any*/
belle_sip_refresher_stop(refresher);
schedule_timer_at(refresher,refresher->retry_after);
if (refresher->listener) refresher->listener(refresher,refresher->user_data,503, "io error");
return;
} else if (belle_sip_object_is_instance_of(BELLE_SIP_OBJECT(belle_sip_io_error_event_get_source(event)),BELLE_SIP_TYPE_ID(belle_sip_provider_t))) {
/*something went wrong on this provider, checking if my channel is still up*/
if ((refresher->started /*refresher started or trying to refresh */
|| belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) == BELLE_SIP_TRANSACTION_TRYING
|| belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) == BELLE_SIP_TRANSACTION_TRYING)
&& (belle_sip_channel_get_state(refresher->transaction->base.channel) == BELLE_SIP_CHANNEL_DISCONNECTED
if (refresher->started /*refresher started or trying to refresh */
&& belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction)) == BELLE_SIP_TRANSACTION_TERMINATED /*else we are notified by transaction error*/
&& (belle_sip_channel_get_state(refresher->transaction->base.channel) == BELLE_SIP_CHANNEL_DISCONNECTED
||belle_sip_channel_get_state(refresher->transaction->base.channel) == BELLE_SIP_CHANNEL_ERROR)) {
belle_sip_message("refresher [%p] has channel [%p] in state [%s], reporting error"
,refresher
,refresher->transaction->base.channel
,belle_sip_channel_state_to_string(belle_sip_channel_get_state(refresher->transaction->base.channel)));
belle_sip_refresher_stop(refresher);
schedule_timer_at(refresher,refresher->retry_after);
if (refresher->listener) refresher->listener(refresher,refresher->user_data,503, "io error");
}
return;
......@@ -67,21 +91,7 @@ static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *e
}
}
static int set_expires_from_trans(belle_sip_refresher_t* refresher);
static int timer_cb(void *user_data, unsigned int events) ;
static void schedule_timer(belle_sip_refresher_t* refresher) {
if (refresher->expires>0) {
if (refresher->timer){
belle_sip_main_loop_remove_source(belle_sip_stack_get_main_loop(refresher->transaction->base.provider->stack),refresher->timer);
belle_sip_object_unref(refresher->timer);
}
refresher->timer=belle_sip_timeout_source_new(timer_cb,refresher,refresher->expires*900);
belle_sip_object_set_name((belle_sip_object_t*)refresher->timer,"Refresher timeout");
belle_sip_main_loop_add_source(belle_sip_stack_get_main_loop(refresher->transaction->base.provider->stack),refresher->timer);
}
}
static void process_response_event(void *user_ctx, const belle_sip_response_event_t *event){
belle_sip_client_transaction_t* client_transaction = belle_sip_response_event_get_client_transaction(event);
......@@ -105,6 +115,12 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even
belle_sip_refresher_refresh(refresher,refresher->expires); /*authorization is supposed to be available immediately*/
return;
}
case 408:
case 480:
case 503:
case 504:
schedule_timer_at(refresher,refresher->retry_after);
break;
default:
break;
}
......@@ -118,8 +134,7 @@ static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *eve
if (refresher && (client_transaction !=refresher->transaction))
return; /*not for me*/
/*first stop timer if any*/
belle_sip_refresher_stop(refresher);
belle_sip_refresher_refresh(refresher,refresher->expires); /*re-arm timer imediatly*/
if (refresher->listener) refresher->listener(refresher,refresher->user_data,408, "timeout");
return;
}
......@@ -349,6 +364,7 @@ belle_sip_refresher_t* belle_sip_refresher_new(belle_sip_client_transaction_t* t
refresher = (belle_sip_refresher_t*)belle_sip_object_new(belle_sip_refresher_t);
refresher->transaction=transaction;
belle_sip_object_ref(transaction);
refresher->retry_after=DEFAULT_RETRY_AFTER;
refresher->listener_callbacks.process_response_event=process_response_event;
refresher->listener_callbacks.process_timeout=process_timeout;
refresher->listener_callbacks.process_io_error=process_io_error;
......@@ -365,3 +381,11 @@ belle_sip_refresher_t* belle_sip_refresher_new(belle_sip_client_transaction_t* t
int belle_sip_refresher_get_expires(const belle_sip_refresher_t* refresher) {
return refresher->expires;
}
int belle_sip_refresher_get_retry_after(const belle_sip_refresher_t* refresher){
return refresher->retry_after;
}
void belle_sip_refresher_set_retry_after(belle_sip_refresher_t* refresher, int delay_ms) {
refresher->retry_after=delay_ms;
}
......@@ -96,6 +96,10 @@ int belle_sip_transaction_state_is_transient(const belle_sip_transaction_state_t
}
void belle_sip_transaction_terminate(belle_sip_transaction_t *t){
t->state=BELLE_SIP_TRANSACTION_TERMINATED;
belle_sip_message("%s%s %s transaction [%p] terminated" ,BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_client_transaction_t)?"Client":"Server"
,t->is_internal?" internal":""
,belle_sip_request_get_method(belle_sip_transaction_get_request(t))
,t);
BELLE_SIP_OBJECT_VPTR(t,belle_sip_transaction_t)->on_terminate(t);
belle_sip_provider_set_transaction_terminated(t->provider,t);
}
......@@ -188,7 +192,7 @@ void belle_sip_server_transaction_send_response(belle_sip_server_transaction_t *
base->last_response=resp;
}
if (dialog)
belle_sip_dialog_update(dialog,base->request,resp,TRUE);
belle_sip_dialog_update(dialog,BELLE_SIP_TRANSACTION(t),TRUE);
}
static void server_transaction_notify(belle_sip_server_transaction_t *t, belle_sip_request_t *req, belle_sip_dialog_t *dialog){
......@@ -355,7 +359,7 @@ void belle_sip_client_transaction_notify_response(belle_sip_client_transaction_t
dialog=belle_sip_provider_create_dialog_internal(t->base.provider,BELLE_SIP_TRANSACTION(t),FALSE);
}
if (dialog && belle_sip_dialog_update(dialog,base->request,resp,FALSE)) {
if (dialog && belle_sip_dialog_update(dialog,BELLE_SIP_TRANSACTION(t),FALSE)) {
/* retransmition, just return*/
belle_sip_message("[%p] is a200 ok retransmition on dialog [%p], skiping",resp,dialog);
return;
......
......@@ -42,6 +42,7 @@ typedef struct _stat {
int twoHundredOk;
int fourHundredOne;
int refreshOk;
int refreshKo;
}stat_t;
typedef struct endpoint {
......@@ -60,6 +61,8 @@ typedef struct endpoint {
unsigned char unreconizable_contact;
int connection_family;
int register_count;
int transiant_network_failure;
belle_sip_refresher_t* refresher;
} endpoint_t;
......@@ -272,7 +275,16 @@ static void belle_sip_refresher_listener ( const belle_sip_refresher_t* refreshe
belle_sip_message("belle_sip_refresher_listener [%i] reason [%s]",status_code,reason_phrase);
switch (status_code) {
case 200:endpoint->stat.refreshOk++; break;
default: break;
default:
endpoint->stat.refreshKo++;
break;
}
if (endpoint->stat.refreshKo==1 && endpoint->transiant_network_failure) {
belle_sip_stack_set_send_error(endpoint->stack,0);
} else if (endpoint->stat.refreshOk==1 && endpoint->transiant_network_failure) {
/*generate a network failure*/
belle_sip_refresher_set_retry_after(endpoint->refresher,100);
belle_sip_stack_set_send_error(endpoint->stack,-1);
}
}
......@@ -359,7 +371,7 @@ static void register_base(endpoint_t* client,endpoint_t *server) {
belle_sip_client_transaction_send_request(trans);
CU_ASSERT_TRUE_FATAL(wait_for(server->stack,client->stack,&client->stat.twoHundredOk,1,1000));
}
refresher = belle_sip_client_transaction_create_refresher(trans);
client->refresher= refresher = belle_sip_client_transaction_create_refresher(trans);
CU_ASSERT_TRUE_FATAL(refresher!=NULL);
belle_sip_object_unref(trans);
belle_sip_refresher_set_listener(refresher,belle_sip_refresher_listener,client);
......@@ -489,7 +501,26 @@ static void register_expires_header_digest(void) {
static void register_expires_in_contact_header_digest_auth(void) {
register_test_with_param(1,digest_auth);
}
static void register_with_failure(void) {
belle_sip_listener_callbacks_t client_callbacks;
belle_sip_listener_callbacks_t server_callbacks;
endpoint_t* client,*server;
memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t));
memset(&server_callbacks,0,sizeof(belle_sip_listener_callbacks_t));
client_callbacks.process_response_event=client_process_response_event;
client_callbacks.process_auth_requested=client_process_auth_requested;
server_callbacks.process_request_event=server_process_request_event;
client = create_udp_endpoint(3452,&client_callbacks);
server = create_udp_endpoint(6788,&server_callbacks);
server->expire_in_contact=1;
server->unreconizable_contact=1;
server->auth=digest;
client->transiant_network_failure=1;
register_base(client,server);
CU_ASSERT_EQUAL(client->stat.refreshKo,1);
destroy_endpoint(client);
destroy_endpoint(server);
}
static void register_with_unrecognizable_contact(void) {
belle_sip_listener_callbacks_t client_callbacks;
belle_sip_listener_callbacks_t server_callbacks;
......@@ -575,6 +606,7 @@ test_t refresher_tests[] = {
{ "REGISTER Expires in Contact", register_expires_in_contact },
{ "REGISTER Expires header digest", register_expires_header_digest },
{ "REGISTER Expires in Contact digest auth", register_expires_in_contact_header_digest_auth },
{ "REGISTER with failure", register_with_failure },
{ "SUBSCRIBE", subscribe_test },
{ "REGISTER with unrecognizable Contact", register_with_unrecognizable_contact },
{ "REGISTER UDP from ipv6 to ipv4", register_test_ipv6_to_ipv4 },
......
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