Commit 092bc951 authored by jehan's avatar jehan
Browse files

Refresher is now responsible for managing dialogs for outgoing subscription

parent 77d350a9
......@@ -584,6 +584,9 @@ typedef struct listener_ctx{
#define BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_TRANSACTION(t,callback,event) \
BELLE_SIP_PROVIDER_INVOKE_LISTENERS((t)->is_internal?(t)->provider->internal_listeners:(t)->provider->listeners,callback,event)
#define BELLE_SIP_PROVIDER_INVOKE_LISTENERS_FOR_DIALOG(d,callback,event) \
BELLE_SIP_PROVIDER_INVOKE_LISTENERS((d)->is_internal?(d)->provider->internal_listeners:(d)->provider->listeners,callback,event)
#define BELLE_SIP_PROVIDER_INVOKE_LISTENERS(listeners,callback,event) \
BELLE_SIP_INVOKE_LISTENERS_ARG((listeners),belle_sip_listener_t,callback,(event))
......@@ -816,6 +819,7 @@ struct belle_sip_dialog{
unsigned char needs_ack;
unsigned char is_expired;
unsigned char pending_trans_checking_enabled; /*use to disabled pending transaction check at request creation (testing)*/
unsigned char is_internal; /*Internal dialogs are those created by refreshers. */
};
belle_sip_dialog_t *belle_sip_dialog_new(belle_sip_transaction_t *t);
......
......@@ -554,7 +554,21 @@ void belle_sip_channel_parse_stream(belle_sip_channel_t *obj, int end_of_stream)
static void belle_sip_channel_process_stream(belle_sip_channel_t *obj, int eos){
belle_sip_channel_parse_stream(obj,eos);
if (obj->incoming_messages) notify_incoming_messages(obj);
if (obj->incoming_messages) {
if (obj->simulated_recv_return == 1500) {
belle_sip_list_t *elem;
for(elem=obj->incoming_messages;elem!=NULL;elem=elem->next){
belle_sip_message_t *msg=(belle_sip_message_t*)elem->data;
char* dump = belle_sip_message_to_string(msg);
belle_sip_message("Silently discarding incoming message [%.50s...] on channel [%p]",dump, obj);
belle_sip_free(dump);
}
belle_sip_list_free_with_data(obj->incoming_messages,belle_sip_object_unref);
obj->incoming_messages=NULL;
} else {
notify_incoming_messages(obj);
}
}
}
static int belle_sip_channel_process_read_data(belle_sip_channel_t *obj){
......
......@@ -112,7 +112,7 @@ struct belle_sip_channel{
belle_sip_list_t* incoming_messages;
belle_sip_source_t *inactivity_timer;
uint64_t last_recv_time;
int simulated_recv_return; /* used to simulate network error. 0= no data (disconnected) >0= do nothing -1= network error*/
int simulated_recv_return; /* used to simulate network error. 0= no data (disconnected) >0= do nothing -1= network error, 1500 special number to silently discard incoming buffer*/
unsigned long bg_task_id;
unsigned long recv_bg_task_id;
unsigned char force_close; /* when channel is intentionnaly disconnected, in order to prevent looping notifications*/
......
......@@ -680,7 +680,7 @@ void belle_sip_provider_add_dialog(belle_sip_provider_t *prov, belle_sip_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_PROVIDER_INVOKE_LISTENERS_FOR_DIALOG(ev->dialog,process_dialog_terminated,ev);
belle_sip_object_unref(ev->dialog);
belle_sip_free(ev);
}
......
......@@ -39,6 +39,8 @@ struct belle_sip_refresher {
belle_sip_refresher_listener_t listener;
belle_sip_source_t* timer;
belle_sip_client_transaction_t* transaction;
belle_sip_request_t* first_acknoleged_request; /*store first request sucessfully acknoleged, usefull to re-build a dialog iff needed*/
belle_sip_dialog_t* dialog; /*Cannot rely on transaction to store dialog because of belle_sip_transaction_reset_dialog*/
char* realm;
int target_expires;
int obtained_expires;
......@@ -52,6 +54,7 @@ struct belle_sip_refresher {
timer_purpose_t timer_purpose;
unsigned char manual;
};
static void set_or_update_dialog(belle_sip_refresher_t* refresher, belle_sip_dialog_t* dialog);
static int set_expires_from_trans(belle_sip_refresher_t* refresher);
static int timer_cb(void *user_data, unsigned int events) ;
......@@ -95,10 +98,15 @@ static void schedule_timer(belle_sip_refresher_t* refresher) {
static void process_dialog_terminated(belle_sip_listener_t *user_ctx, const belle_sip_dialog_terminated_event_t *event){
belle_sip_refresher_t* refresher=(belle_sip_refresher_t*)user_ctx;
belle_sip_dialog_t *dialog =belle_sip_dialog_terminated_event_get_dialog(event);
if (event->is_expired){
belle_sip_warning("Refresher [%p]: forced to stop because dialog has expired.", refresher);
belle_sip_refresher_stop_internal(refresher, 0);
if (refresher && refresher->transaction && dialog != belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(refresher->transaction)))
return; /*not for me*/
if (refresher->state == started) {
belle_sip_warning("Refresher [%p] still started but receiving unexpected dialog deleted event on [%p], retrying",refresher,dialog);
retry_later_on_io_error(refresher);
if (refresher->listener) refresher->listener(refresher,refresher->user_data,503, "io error");
}
}
......@@ -202,6 +210,7 @@ static void process_response_event(belle_sip_listener_t *user_ctx, const belle_s
if (refresher && (client_transaction !=refresher->transaction))
return; /*not for me*/
set_or_update_dialog(refresher,belle_sip_response_event_get_dialog(event));
/*success case:*/
if (response_code>=200 && response_code<300){
refresher->auth_failures=0;
......@@ -240,6 +249,8 @@ static void process_response_event(belle_sip_listener_t *user_ctx, const belle_s
}
if (refresher->state==started) {
if (!refresher->first_acknoleged_request)
belle_sip_object_ref(refresher->first_acknoleged_request = request);
if (is_contact_address_acurate(refresher,request)) {
schedule_timer(refresher); /*re-arm timer*/
} else {
......@@ -340,6 +351,8 @@ static void destroy(belle_sip_refresher_t *refresher){
refresher->transaction=NULL;
if (refresher->realm) belle_sip_free(refresher->realm);
if (refresher->auth_events) refresher->auth_events=belle_sip_list_free_with_data(refresher->auth_events,(void (*)(void*))belle_sip_auth_event_destroy);
if (refresher->first_acknoleged_request) belle_sip_object_unref(refresher->first_acknoleged_request);
if (refresher->dialog) belle_sip_object_unref(refresher->dialog);
}
BELLE_SIP_IMPLEMENT_INTERFACE_BEGIN(belle_sip_refresher_t,belle_sip_listener_t)
......@@ -376,7 +389,7 @@ static int unfilled_auth_info(const void* info,const void* userptr) {
static int belle_sip_refresher_refresh_internal(belle_sip_refresher_t* refresher, int expires, int auth_mandatory, belle_sip_list_t** auth_infos, belle_sip_uri_t *requri) {
belle_sip_request_t*old_request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(refresher->transaction));
belle_sip_response_t*old_response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(refresher->transaction));
belle_sip_dialog_t* dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(refresher->transaction));
belle_sip_dialog_t* dialog = refresher->dialog;
belle_sip_client_transaction_t* client_transaction;
belle_sip_request_t* request;
belle_sip_header_expires_t* expires_header;
......@@ -413,44 +426,65 @@ static int belle_sip_refresher_refresh_internal(belle_sip_refresher_t* refresher
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_AUTHORIZATION);
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_PROXY_AUTHORIZATION);
}
} else if (dialog && belle_sip_dialog_get_state(dialog)==BELLE_SIP_DIALOG_CONFIRMED) {
if (belle_sip_dialog_request_pending(dialog)){
belle_sip_message("Cannot refresh now, there is a pending request in the dialog.");
return -1;
}
request=belle_sip_dialog_create_request_from(dialog,old_request);
if (strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0) {
belle_sip_header_content_type_t *content_type;
/*put expire header*/
if (!(expires_header = belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t))) {
expires_header = belle_sip_header_expires_new();
belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header));
} else {
switch (belle_sip_dialog_get_state(dialog)) {
case BELLE_SIP_DIALOG_CONFIRMED: {
if (belle_sip_dialog_request_pending(dialog)){
belle_sip_message("Cannot refresh now, there is a pending request in the dialog.");
return -1;
}
request=belle_sip_dialog_create_request_from(dialog,old_request);
if (strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0) {
belle_sip_header_content_type_t *content_type;
/*put expire header*/
if (!(expires_header = belle_sip_message_get_header_by_type(request,belle_sip_header_expires_t))) {
expires_header = belle_sip_header_expires_new();
belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header));
}
if ((content_type = belle_sip_message_get_header_by_type(request, belle_sip_header_content_type_t))
&& strcasecmp("application", belle_sip_header_content_type_get_type(content_type)) == 0
&& strcasecmp("resource-lists+xml", belle_sip_header_content_type_get_subtype(content_type)) == 0) {
/*rfc5367
3.2. Subsequent SUBSCRIBE Requests
...
At this point, there are no semantics associated with resource-list
bodies in subsequent SUBSCRIBE requests (although future extensions
can define them). Therefore, UACs SHOULD NOT include resource-list
bodies in subsequent SUBSCRIBE requests to a resource list server.
*/
belle_sip_message("Removing body, content type and content length for refresher [%p]",refresher);
belle_sip_message_set_body(BELLE_SIP_MESSAGE(request), NULL, 0);
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_TYPE);
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_LENGTH);
}
}
belle_sip_provider_add_authorization(prov,request,old_response,NULL,auth_infos,refresher->realm);
break;
}
if ((content_type = belle_sip_message_get_header_by_type(request, belle_sip_header_content_type_t))
&& strcasecmp("application", belle_sip_header_content_type_get_type(content_type)) == 0
&& strcasecmp("resource-lists+xml", belle_sip_header_content_type_get_subtype(content_type)) == 0) {
/*rfc5367
3.2. Subsequent SUBSCRIBE Requests
...
At this point, there are no semantics associated with resource-list
bodies in subsequent SUBSCRIBE requests (although future extensions
can define them). Therefore, UACs SHOULD NOT include resource-list
bodies in subsequent SUBSCRIBE requests to a resource list server.
*/
belle_sip_message("Removing body, content type and content length for refresher [%p]",refresher);
belle_sip_message_set_body(BELLE_SIP_MESSAGE(request), NULL, 0);
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_TYPE);
belle_sip_message_remove_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_CONTENT_LENGTH);
case BELLE_SIP_DIALOG_TERMINATED: {
if (refresher->first_acknoleged_request) {
char tmp[11];
belle_sip_message("Dialog [%p] is in state terminated, recreating a new one for refresher [%p]",dialog,refresher);
request = refresher->first_acknoleged_request;
belle_sip_header_cseq_set_seq_number(belle_sip_message_get_header_by_type(request,belle_sip_header_cseq_t)
,20);
belle_sip_parameters_remove_parameter(BELLE_SIP_PARAMETERS(belle_sip_message_get_header_by_type(request,belle_sip_header_to_t)),"tag");
belle_sip_header_call_id_set_call_id( belle_sip_message_get_header_by_type(request,belle_sip_header_call_id_t)
, belle_sip_random_token(tmp,sizeof(tmp)));
break;
} /*else nop, error case*/
}
default: {
belle_sip_error("Unexpected dialog state [%s] for dialog [%p], cannot refresh [%s]"
,belle_sip_dialog_state_to_string(belle_sip_dialog_get_state(dialog))
,dialog
,belle_sip_request_get_method(old_request));
return -1;
}
}
belle_sip_provider_add_authorization(prov,request,old_response,NULL,auth_infos,refresher->realm);
} else {
belle_sip_error("Unexpected dialog state [%s] for dialog [%p], cannot refresh [%s]"
,belle_sip_dialog_state_to_string(belle_sip_dialog_get_state(dialog))
,dialog
,belle_sip_request_get_method(old_request));
return -1;
}
if (auth_mandatory && auth_infos && belle_sip_list_find_custom(*auth_infos, unfilled_auth_info, NULL)) {
......@@ -481,7 +515,12 @@ static int belle_sip_refresher_refresh_internal(belle_sip_refresher_t* refresher
client_transaction = belle_sip_provider_create_client_transaction(prov,request);
client_transaction->base.is_internal=1;
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),refresher);
if (request == refresher->first_acknoleged_request) { /*request is now ref by transaction so no need to keepo it*/
belle_sip_object_unref(refresher->first_acknoleged_request);
refresher->first_acknoleged_request = NULL;
}
switch (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(refresher->transaction))) {
case BELLE_SIP_TRANSACTION_INIT:
case BELLE_SIP_TRANSACTION_CALLING:
......@@ -678,6 +717,9 @@ belle_sip_refresher_t* belle_sip_refresher_new(belle_sip_client_transaction_t* t
belle_sip_object_ref(transaction);
refresher->retry_after=DEFAULT_RETRY_AFTER;
if (belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(transaction))) {
set_or_update_dialog(refresher, belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(transaction)));
}
belle_sip_provider_add_internal_sip_listener(transaction->base.provider,BELLE_SIP_LISTENER(refresher), is_register);
if (set_expires_from_trans(refresher)==-1){
belle_sip_error("Unable to extract refresh value from transaction [%p]",transaction);
......@@ -732,3 +774,18 @@ void belle_sip_refresher_enable_manual_mode(belle_sip_refresher_t *refresher, in
char *belle_sip_refresher_get_public_uri(belle_sip_refresher_t* refresher) {
return belle_sip_channel_get_public_ip_port(refresher->transaction->base.channel);
}
static void set_or_update_dialog(belle_sip_refresher_t* refresher, belle_sip_dialog_t* dialog) {
if (refresher->dialog!=dialog){
belle_sip_message("refresher [%p] : set_or_update_dialog() current=[%p] new=[%p]",refresher,refresher->dialog,dialog);
if (refresher->dialog){
belle_sip_object_unref(refresher->dialog);
}
if (dialog) {
belle_sip_object_ref(dialog);
/*make sure dialog is internal now*/
dialog->is_internal = TRUE;
}
refresher->dialog=dialog;
}
}
......@@ -66,6 +66,7 @@ static void transaction_destroy(belle_sip_transaction_t *t){
if (t->channel) belle_sip_object_unref(t->channel);
if (t->branch_id) belle_sip_free(t->branch_id);
belle_sip_transaction_set_dialog(t,NULL);
belle_sip_message("Transaction [%p] deleted",t);
}
......@@ -412,8 +413,8 @@ int belle_sip_client_transaction_send_request_to(belle_sip_client_transaction_t
/*this request was created by belle_sip_dialog_create_queued_request().*/
if (belle_sip_dialog_request_pending(dialog) || dialog->queued_ct!=NULL){
/*it cannot be sent immediately, queue the transaction into dialog*/
belle_sip_message("belle_sip_client_transaction_send_request(): transaction [%p], cannot send request now because dialog is busy"
" or other transactions are queued, so queuing into dialog.",t);
belle_sip_message("belle_sip_client_transaction_send_request(): transaction [%p], cannot send request now because dialog [%p] is busy"
" or other transactions are queued, so queuing into dialog.",t, dialog);
belle_sip_dialog_queue_client_transaction(dialog,t);
return 0;
}
......
......@@ -50,9 +50,11 @@ typedef enum auth_mode {
typedef struct _status {
int twoHundredOk;
int fourHundredOne;
int fourHundredEightyOne;
int refreshOk;
int refreshKo;
int dialogTerminated;
int fiveHundredTree;
}status_t;
typedef struct endpoint {
......@@ -318,10 +320,14 @@ static void belle_sip_refresher_listener (belle_sip_refresher_t* refresher
endpoint_t* endpoint = (endpoint_t*)user_pointer;
BELLESIP_UNUSED(refresher);
belle_sip_message("belle_sip_refresher_listener [%i] reason [%s]",status_code,reason_phrase);
if (status_code >=300)
endpoint->stat.refreshKo++;
switch (status_code) {
case 200:endpoint->stat.refreshOk++; break;
case 481:endpoint->stat.fourHundredEightyOne++;break;
case 503:endpoint->stat.fiveHundredTree++;break;
default:
endpoint->stat.refreshKo++;
/*nop*/
break;
}
if (endpoint->stat.refreshKo==1 && endpoint->transiant_network_failure) {
......@@ -506,6 +512,8 @@ static void subscribe_base(int with_resource_lists) {
belle_sip_uri_t *dest_uri;
belle_sip_refresher_t* refresher;
belle_sip_header_contact_t* contact=belle_sip_header_contact_new();
belle_sip_dialog_t * client_dialog;
int dummy = 0;
uint64_t begin;
uint64_t end;
memset(&client_callbacks,0,sizeof(belle_sip_listener_callbacks_t));
......@@ -559,7 +567,7 @@ static void subscribe_base(int with_resource_lists) {
belle_sip_client_transaction_send_request(trans);
BC_ASSERT_TRUE_FATAL(wait_for(server->stack,client->stack,&client->stat.twoHundredOk,1,1000));
/*maybe dialog should be automatically created*/
BC_ASSERT_PTR_NOT_NULL_FATAL(belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(trans)));
BC_ASSERT_PTR_NOT_NULL_FATAL(client_dialog = belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(trans)));
refresher = belle_sip_client_transaction_create_refresher(trans);
belle_sip_object_unref(trans);
......@@ -570,12 +578,21 @@ static void subscribe_base(int with_resource_lists) {
end = belle_sip_time_ms();
BC_ASSERT_TRUE(end-begin>=3000);
BC_ASSERT_TRUE(end-begin<5000);
belle_sip_stack_set_send_error(client->stack, 1500);
BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.fiveHundredTree,1,4000));
belle_sip_stack_set_send_error(client->stack, 0);
wait_for(server->stack,client->stack, &dummy, 1, 1000);
BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.refreshOk,4,4000));
BC_ASSERT_EQUAL(client->stat.dialogTerminated, 0, int, "%i");
BC_ASSERT_NOT_EQUAL(client_dialog, belle_sip_transaction_get_dialog(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher))),void*,"%p"); /*make sure dialog has changed*/
/*unsubscribe twice to make sure refresh operation can be safely cascaded*/
belle_sip_refresher_refresh(refresher,0);
belle_sip_refresher_refresh(refresher,0);
belle_sip_refresher_stop(refresher);
BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&client->stat.dialogTerminated,1,4000));
BC_ASSERT_TRUE(wait_for(server->stack,client->stack,&server->stat.dialogTerminated,1,4000));
belle_sip_object_unref(refresher);
......
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