Commit c9442a2a authored by Simon Morlat's avatar Simon Morlat

refactor transaction handling and implement INVITE client transaction

parent f06db4bc
......@@ -71,6 +71,8 @@ void belle_sip_message_add_header(belle_sip_message_t *msg, belle_sip_header_t*
void belle_sip_message_add_headers(belle_sip_message_t *message, const belle_sip_list_t *header_list);
void belle_sip_message_set_header(belle_sip_message_t *msg, belle_sip_header_t* header);
char *belle_sip_message_to_string(belle_sip_message_t *msg);
const char* belle_sip_message_get_body(belle_sip_message_t *msg);
void belle_sip_message_set_body(belle_sip_message_t *msg,char* body,unsigned int size);
......
......@@ -390,6 +390,7 @@ struct _belle_sip_header {
void belle_sip_header_set_next(belle_sip_header_t* header,belle_sip_header_t* next);
belle_sip_header_t* belle_sip_header_get_next(const belle_sip_header_t* headers);
void belle_sip_util_copy_headers(belle_sip_message_t *orig, belle_sip_message_t *dest, const char*header, int multiple);
void belle_sip_header_init(belle_sip_header_t* obj);
/*class parameters*/
......@@ -507,14 +508,19 @@ struct belle_sip_client_transaction{
BELLE_SIP_DECLARE_CUSTOM_VPTR_BEGIN(belle_sip_client_transaction_t,belle_sip_transaction_t)
void (*send_request)(belle_sip_client_transaction_t *);
int (*on_response)(belle_sip_client_transaction_t *obj, belle_sip_response_t *resp);
void (*on_response)(belle_sip_client_transaction_t *obj, belle_sip_response_t *resp);
BELLE_SIP_DECLARE_CUSTOM_VPTR_END
void belle_sip_client_transaction_init(belle_sip_client_transaction_t *obj, belle_sip_provider_t *prov, belle_sip_request_t *req);
int belle_sip_client_transaction_add_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp);
void belle_sip_client_transaction_add_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp);
void belle_sip_client_transaction_notify_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp);
struct belle_sip_ict{
belle_sip_client_transaction_t base;
belle_sip_source_t *timer_A;
belle_sip_source_t *timer_B;
belle_sip_source_t *timer_D;
belle_sip_request_t *ack;
};
typedef struct belle_sip_ict belle_sip_ict_t;
......
......@@ -538,4 +538,19 @@ char * belle_sip_random_token(char *ret, size_t size){
}
void belle_sip_util_copy_headers(belle_sip_message_t *orig, belle_sip_message_t *dest, const char*header, int multiple){
const belle_sip_list_t *elem;
elem=belle_sip_message_get_headers(orig,header);
for (;elem!=NULL;elem=elem->next){
belle_sip_header_t *ref_header=(belle_sip_header_t*)elem->data;
if (ref_header){
ref_header=(belle_sip_header_t*)belle_sip_object_clone((belle_sip_object_t*)ref_header);
if (!multiple){
belle_sip_message_set_header(dest,ref_header);
break;
}else
belle_sip_message_add_header(dest,ref_header);
}
}
}
......@@ -23,15 +23,132 @@ static void ict_destroy(belle_sip_ict_t *obj){
}
static void on_ict_terminate(belle_sip_ict_t *obj){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
if (obj->timer_A){
belle_sip_transaction_stop_timer(base,obj->timer_A);
belle_sip_object_unref(obj->timer_A);
obj->timer_A=NULL;
}
if (obj->timer_B){
belle_sip_transaction_stop_timer(base,obj->timer_B);
belle_sip_object_unref(obj->timer_B);
obj->timer_B=NULL;
}
if (obj->timer_D){
belle_sip_transaction_stop_timer(base,obj->timer_D);
belle_sip_object_unref(obj->timer_D);
obj->timer_D=NULL;
}
if (obj->ack){
belle_sip_object_unref(obj->ack);
obj->ack=NULL;
}
}
static belle_sip_request_t *make_ack(belle_sip_ict_t *obj, belle_sip_response_t *resp){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
if (obj->ack==NULL){
obj->ack=belle_sip_request_new();
belle_sip_request_set_method(obj->ack,"ACK");
belle_sip_request_set_uri(obj->ack,belle_sip_request_get_uri(base->request));
belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,"via",FALSE);
belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,"call-id",FALSE);
belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,"from",FALSE);
belle_sip_util_copy_headers((belle_sip_message_t*)base->request,(belle_sip_message_t*)obj->ack,"route",TRUE);
belle_sip_message_add_header((belle_sip_message_t*)obj->ack,
(belle_sip_header_t*)belle_sip_header_cseq_create(
belle_sip_header_cseq_get_seq_number((belle_sip_header_cseq_t*)belle_sip_message_get_header((belle_sip_message_t*)base->request,"cseq")),
"CANCEL"));
}
belle_sip_util_copy_headers((belle_sip_message_t*)resp,(belle_sip_message_t*)obj->ack,"to",FALSE);
return obj->ack;
}
static int ict_on_timer_D(belle_sip_ict_t *obj){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
if (base->state==BELLE_SIP_TRANSACTION_COMPLETED){
belle_sip_transaction_terminate(base);
}
return BELLE_SIP_STOP;
}
static void ict_on_response(belle_sip_ict_t *obj, belle_sip_response_t *resp){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
int code=belle_sip_response_get_status_code(resp);
switch (base->state){
case BELLE_SIP_TRANSACTION_CALLING:
base->state=BELLE_SIP_TRANSACTION_PROCEEDING;
/* no break*/
case BELLE_SIP_TRANSACTION_PROCEEDING:
if (code>=300){
base->state=BELLE_SIP_TRANSACTION_COMPLETED;
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)make_ack(obj,resp));
obj->timer_D=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_D,obj,32000);
belle_sip_transaction_start_timer(base,obj->timer_D);
}else if (code>=200){
belle_sip_transaction_terminate(base);
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
}
break;
case BELLE_SIP_TRANSACTION_COMPLETED:
if (code>=300 && obj->ack){
belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)obj->ack);
}
break;
default:
break;
}
}
static int ict_on_timer_A(belle_sip_ict_t *obj){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
switch(base->state){
case BELLE_SIP_TRANSACTION_CALLING:
{
/*reset the timer to twice the previous value, and retransmit */
unsigned int prev_timeout=belle_sip_source_get_timeout(obj->timer_A);
belle_sip_source_set_timeout(obj->timer_A,2*prev_timeout);
belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->request);
}
break;
default:
break;
}
return BELLE_SIP_CONTINUE;
}
static int ict_on_response(belle_sip_ict_t *obj, belle_sip_response_t *resp){
return 0;
static int ict_on_timer_B(belle_sip_ict_t *obj){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
switch (base->state){
case BELLE_SIP_TRANSACTION_CALLING:
belle_sip_transaction_notify_timeout(base);
belle_sip_transaction_terminate(base);
break;
default:
break;
}
return BELLE_SIP_STOP;
}
static void ict_send_request(belle_sip_ict_t *obj){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base);
base->state=BELLE_SIP_TRANSACTION_CALLING;
belle_sip_channel_queue_message(base->channel,(belle_sip_message_t*)base->request);
if (!belle_sip_channel_is_reliable(base->channel)){
obj->timer_A=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_A,obj,cfg->T1);
belle_sip_transaction_start_timer(base,obj->timer_A);
}
obj->timer_B=belle_sip_timeout_source_new((belle_sip_source_func_t)ict_on_timer_B,obj,cfg->T1*64);
belle_sip_transaction_start_timer(base,obj->timer_B);
}
......@@ -49,7 +166,7 @@ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR(belle_sip_ict_t)={
(void (*)(belle_sip_transaction_t*))on_ict_terminate
},
(void (*)(belle_sip_client_transaction_t*))ict_send_request,
(int (*)(belle_sip_client_transaction_t*,belle_sip_response_t*))ict_on_response
(void (*)(belle_sip_client_transaction_t*,belle_sip_response_t*))ict_on_response
}
};
......
......@@ -128,6 +128,13 @@ void belle_sip_message_add_headers(belle_sip_message_t *message, const belle_sip
}
}
void belle_sip_message_set_header(belle_sip_message_t *msg, belle_sip_header_t* header){
headers_container_t *headers_container=get_or_create_container(msg,belle_sip_header_get_name(header));
belle_sip_object_ref(header);
headers_container->header_list=belle_sip_list_free_with_data(headers_container->header_list,belle_sip_object_unref);
headers_container->header_list=belle_sip_list_append(headers_container->header_list,header);
}
const belle_sip_list_t* belle_sip_message_get_headers(belle_sip_message_t *message,const char* header_name) {
headers_container_t* headers_container = belle_sip_headers_container_get(message,header_name);
return headers_container ? headers_container->header_list:NULL;
......
......@@ -27,45 +27,42 @@ static int nict_on_timer_K(belle_sip_nict_t *obj){
return BELLE_SIP_STOP;
}
static void nict_set_completed(belle_sip_nict_t *obj){
static void nict_set_completed(belle_sip_nict_t *obj, belle_sip_response_t *resp){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
const belle_sip_timer_config_t *cfg=belle_sip_transaction_get_timer_config(base);
base->state=BELLE_SIP_TRANSACTION_COMPLETED;
if (obj->timer_K) belle_sip_fatal("Should never happen.");
obj->timer_K=belle_sip_timeout_source_new((belle_sip_source_func_t)nict_on_timer_K,obj,
belle_sip_channel_is_reliable(base->channel) ? 0 : cfg->T4);
/*comment: we can indeed setup a timer to fire in 0 seconds so that the process_response notification arrives before
* the transaction_terminated notification*/
belle_sip_transaction_start_timer(base,obj->timer_K);
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
if (!belle_sip_channel_is_reliable(base->channel)){
obj->timer_K=belle_sip_timeout_source_new((belle_sip_source_func_t)nict_on_timer_K,obj,cfg->T4);
belle_sip_transaction_start_timer(base,obj->timer_K);
}else belle_sip_transaction_terminate(base);
}
static int nict_on_response(belle_sip_nict_t *obj, belle_sip_response_t *resp){
static void nict_on_response(belle_sip_nict_t *obj, belle_sip_response_t *resp){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)obj;
int code=belle_sip_response_get_status_code(resp);
int pass=0; /*whether response should be passed to upper layer*/
switch(base->state){
case BELLE_SIP_TRANSACTION_TRYING:
if (code<200){
base->state=BELLE_SIP_TRANSACTION_PROCEEDING;
pass=1;
belle_sip_client_transaction_notify_response((belle_sip_client_transaction_t*)obj,resp);
}
else {
nict_set_completed(obj);
pass=1;
nict_set_completed(obj,resp);
}
break;
case BELLE_SIP_TRANSACTION_PROCEEDING:
if (code>=200){
nict_set_completed(obj);
pass=1;
nict_set_completed(obj,resp);
}
break;
default:
break;
}
return pass;
}
static void nict_on_terminate(belle_sip_nict_t *obj){
......@@ -153,7 +150,7 @@ BELLE_SIP_INSTANCIATE_CUSTOM_VPTR(belle_sip_nict_t)={
(void (*)(belle_sip_transaction_t *))nict_on_terminate
},
(void (*)(belle_sip_client_transaction_t*))nict_send_request,
(int (*)(belle_sip_client_transaction_t*,belle_sip_response_t*))nict_on_response
(void (*)(belle_sip_client_transaction_t*,belle_sip_response_t*))nict_on_response
}
};
......
......@@ -37,16 +37,26 @@ static void belle_sip_provider_dispatch_message(belle_sip_provider_t *prov, bell
event.dialog=NULL;
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov,process_request_event,&event);
}else{
belle_sip_response_event_t event;
int pass=1;
event.source=prov;
event.client_transaction=belle_sip_provider_find_matching_client_transaction(prov,(belle_sip_response_t*)msg);
event.dialog=NULL;
event.response=(belle_sip_response_t*)msg;
if (event.client_transaction){
pass=belle_sip_client_transaction_add_response(event.client_transaction,event.response);
belle_sip_client_transaction_t *t;
t=belle_sip_provider_find_matching_client_transaction(prov,(belle_sip_response_t*)msg);
/*
* If a transaction is found, pass it to the transaction and let it decide what to do.
* Else notifies directly.
*/
if (t){
/*since the add_response may indirectly terminate the transaction, we need to guarantee the transaction is not freed
* until full completion*/
belle_sip_object_ref(t);
belle_sip_client_transaction_add_response(t,(belle_sip_response_t*)msg);
belle_sip_object_unref(t);
}else{
belle_sip_response_event_t event;
event.source=prov;
event.client_transaction=NULL;
event.dialog=NULL;
event.response=(belle_sip_response_t*)msg;
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov,process_response_event,&event);
}
if (pass) BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov,process_response_event,&event);
}
belle_sip_object_unref(msg);
}
......@@ -243,7 +253,14 @@ void belle_sip_provider_send_response(belle_sip_provider_t *p, belle_sip_respons
/*private provider API*/
void belle_sip_provider_set_transaction_terminated(belle_sip_provider_t *p, belle_sip_transaction_t *t){
if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(t,belle_sip_client_transaction_t)){
belle_sip_transaction_terminated_event_t ev;
BELLE_SIP_OBJECT_VPTR(t,belle_sip_transaction_t)->on_terminate(t);
ev.source=t->provider;
ev.transaction=t;
ev.is_server_transaction=BELLE_SIP_IS_INSTANCE_OF(t,belle_sip_server_transaction_t);
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(t->provider,process_transaction_terminated,&ev);
if (!ev.is_server_transaction){
belle_sip_provider_remove_client_transaction(p,(belle_sip_client_transaction_t*)t);
}
}
......
......@@ -140,18 +140,6 @@ void belle_sip_server_transaction_send_response(belle_sip_server_transaction_t *
*/
static void clone_headers(belle_sip_message_t *orig, belle_sip_message_t *dest, const char*header, int multiple){
const belle_sip_list_t *elem;
elem=belle_sip_message_get_headers(orig,header);
for (;elem!=NULL;elem=elem->next){
belle_sip_header_t *ref_header=(belle_sip_header_t*)elem->data;
if (ref_header){
belle_sip_message_add_header(dest,
(belle_sip_header_t*)belle_sip_object_clone((belle_sip_object_t*)ref_header));
}
if (!multiple) break; /*just one*/
}
}
belle_sip_request_t * belle_sip_client_transaction_create_cancel(belle_sip_client_transaction_t *t){
belle_sip_message_t *orig=(belle_sip_message_t*)t->base.request;
......@@ -168,11 +156,11 @@ belle_sip_request_t * belle_sip_client_transaction_create_cancel(belle_sip_clien
req=belle_sip_request_new();
belle_sip_request_set_method(req,"CANCEL");
belle_sip_request_set_uri(req,(belle_sip_uri_t*)belle_sip_object_clone((belle_sip_object_t*)belle_sip_request_get_uri((belle_sip_request_t*)orig)));
clone_headers(orig,(belle_sip_message_t*)req,"via",FALSE);
clone_headers(orig,(belle_sip_message_t*)req,"call-id",FALSE);
clone_headers(orig,(belle_sip_message_t*)req,"from",FALSE);
clone_headers(orig,(belle_sip_message_t*)req,"to",FALSE);
clone_headers(orig,(belle_sip_message_t*)req,"route",TRUE);
belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"via",FALSE);
belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"call-id",FALSE);
belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"from",FALSE);
belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"to",FALSE);
belle_sip_util_copy_headers(orig,(belle_sip_message_t*)req,"route",TRUE);
belle_sip_message_add_header((belle_sip_message_t*)req,
(belle_sip_header_t*)belle_sip_header_cseq_create(
belle_sip_header_cseq_get_seq_number((belle_sip_header_cseq_t*)belle_sip_message_get_header(orig,"cseq")),
......@@ -204,15 +192,24 @@ void belle_sip_client_transaction_send_request(belle_sip_client_transaction_t *t
}else belle_sip_error("belle_sip_client_transaction_send_request(): no channel available");
}
int belle_sip_client_transaction_add_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp){
void belle_sip_client_transaction_notify_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp){
belle_sip_transaction_t *base=(belle_sip_transaction_t*)t;
int pass=BELLE_SIP_OBJECT_VPTR(t,belle_sip_client_transaction_t)->on_response(t,resp);
if (pass){
if (base->prov_response)
belle_sip_object_unref(base->prov_response);
base->prov_response=(belle_sip_response_t*)belle_sip_object_ref(resp);
}
return pass;
belle_sip_response_event_t event;
if (base->prov_response)
belle_sip_object_unref(base->prov_response);
base->prov_response=(belle_sip_response_t*)belle_sip_object_ref(resp);
event.source=base->provider;
event.client_transaction=t;
event.dialog=NULL;
event.response=(belle_sip_response_t*)resp;
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(base->provider,process_response_event,&event);
}
void belle_sip_client_transaction_add_response(belle_sip_client_transaction_t *t, belle_sip_response_t *resp){
BELLE_SIP_OBJECT_VPTR(t,belle_sip_client_transaction_t)->on_response(t,resp);
}
static void client_transaction_destroy(belle_sip_client_transaction_t *t ){
......
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