Skip to content

Commit

Permalink
Added support for Content-Disposition: no-cancel
Browse files Browse the repository at this point in the history
As per RFC3841, section 9.1, the TM module may be instructed not to cancel all ongoing branches when a 2xx reply is received. It will keep the pending branches ongoing until (1) all branches will receive a final reply or (2) the transactionhits the timeout.

This is work sponsored by Harris Corporation (https://www.harris.com)
  • Loading branch information
bogdan-iancu committed Mar 27, 2018
1 parent 5bb438a commit 4747da5
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 16 deletions.
2 changes: 2 additions & 0 deletions modules/tm/h_table.h
Expand Up @@ -199,6 +199,8 @@ struct totag_elem {
#define T_NO_NEW_BRANCHES_FLAG (1<<8)
/* transaction must forward the REASON header (for CANCEL) */
#define T_CANCEL_REASON_FLAG (1<<9)
/* transaction must accept multiple 200 OK (different branches) */
#define T_MULTI_200OK_FLAG (1<<10)

/* transaction UAC's flags */
/* is the UAC pending for CANCEL ? */
Expand Down
12 changes: 10 additions & 2 deletions modules/tm/t_cancel.c
Expand Up @@ -66,8 +66,16 @@ void cancel_uacs( struct cell *t, branch_bm_t cancel_bm )

/* cancel pending client transactions, if any */
for( i=0 ; i<t->nr_of_outgoings ; i++ )
if (cancel_bm & (1<<i))
cancel_branch(t, i);
if (cancel_bm & (1<<i)) {
/* any reply actually received on this branch */
if (t->uac[i].last_received!=0) {
/* send a cancel out */
cancel_branch(t, i);
} else {
/* set flag to catch the delaied replies */
t->uac[i].flags |= T_UAC_TO_CANCEL_FLAG;
}
}
}


Expand Down
8 changes: 1 addition & 7 deletions modules/tm/t_cancel.h
Expand Up @@ -55,20 +55,14 @@ char *build_cancel(struct cell *Trans,unsigned int branch,

inline static short should_cancel_branch( struct cell *t, int b )
{
int last_received;

last_received = t->uac[b].last_received;
/* cancel only if provisional received and no one else
attempted to cancel yet */
if ( t->uac[b].local_cancel.buffer.s==NULL ) {
if ( last_received>=100 && last_received<200 ) {
if ( t->uac[b].last_received<200 ) {
/* we'll cancel -- label it so that no one else
(e.g. another 200 branch) will try to do the same */
t->uac[b].local_cancel.buffer.s=BUSY_BUFFER;
return 1;
} else if (last_received==0) {
/* set flag to catch the delaied replies */
t->uac[b].flags |= T_UAC_TO_CANCEL_FLAG;
}
}
return 0;
Expand Down
24 changes: 24 additions & 0 deletions modules/tm/t_funcs.c
Expand Up @@ -47,6 +47,7 @@
#include "../../hash_func.h"
#include "../../dset.h"
#include "../../mem/mem.h"
#include "../../parser/parse_list_hdr.h"
#include "t_funcs.h"
#include "t_fwd.h"
#include "t_msgbuilder.h"
Expand Down Expand Up @@ -176,6 +177,26 @@ static int kill_transaction( struct cell *trans )
}


int tm_has_request_disponsition_no_cancel(struct sip_msg *msg)
{
str RD_hdr_name = {"Request-Disposition",19};
str opt = {"no-cancel",9};
struct hdr_field *hdr;

/* be sure all SIP headers are parsed */
if (parse_headers(msg, HDR_EOH_F, 0) < 0) {
LM_ERR("failed to parse SIP headers while looking for "
"Request-Disposition\n");
return -1;
}

hdr = get_header_by_name( msg, RD_hdr_name.s, RD_hdr_name.len);
if (hdr==NULL)
return -1;

return list_hdr_has_option( hdr, &opt);
}


int t_relay_to( struct sip_msg *p_msg , struct proxy_l *proxy, int flags)
{
Expand Down Expand Up @@ -229,6 +250,9 @@ int t_relay_to( struct sip_msg *p_msg , struct proxy_l *proxy, int flags)
if (flags&TM_T_REPLY_repl_FLAG) t->flags|=T_IS_LOCAL_FLAG;
if (flags&TM_T_REPLY_nodnsfo_FLAG) t->flags|=T_NO_DNS_FAILOVER_FLAG;
if (flags&TM_T_REPLY_reason_FLAG) t->flags|=T_CANCEL_REASON_FLAG;
if ((flags&TM_T_REPLY_do_cancel_dis_FLAG) &&
tm_has_request_disponsition_no_cancel(p_msg)==0 )
t->flags|=T_MULTI_200OK_FLAG;

/* now go ahead and forward ... */
ret=t_forward_nonack( t, p_msg, proxy, 0/*no reset*/, 0/*unlocked*/);
Expand Down
13 changes: 8 additions & 5 deletions modules/tm/t_funcs.h
Expand Up @@ -66,11 +66,12 @@ extern int noisy_ctimer;


/* t_reply_to flags */
#define TM_T_REPLY_repl_FLAG (1<<0)
#define TM_T_REPLY_not_used (1<<1)
#define TM_T_REPLY_noerr_FLAG (1<<2)
#define TM_T_REPLY_nodnsfo_FLAG (1<<3)
#define TM_T_REPLY_reason_FLAG (1<<4)
#define TM_T_REPLY_repl_FLAG (1<<0)
#define TM_T_REPLY_not_used (1<<1)
#define TM_T_REPLY_noerr_FLAG (1<<2)
#define TM_T_REPLY_nodnsfo_FLAG (1<<3)
#define TM_T_REPLY_reason_FLAG (1<<4)
#define TM_T_REPLY_do_cancel_dis_FLAG (1<<5)


/* send a private buffer: utilize a retransmission structure
Expand Down Expand Up @@ -183,5 +184,7 @@ void cleanup_localcancel_timers( struct cell *t );
int t_relay_to( struct sip_msg *p_msg, struct proxy_l *proxy, int replicate);


int tm_has_request_disponsition_no_cancel(struct sip_msg *msg);

#endif

49 changes: 48 additions & 1 deletion modules/tm/t_reply.c
Expand Up @@ -793,6 +793,20 @@ static inline int t_pick_branch( struct cell *t, int *res_code, int *do_cancel)
}


/* Quick and harmless test to see if the transaction still has
pending branches */
static inline int tran_is_completed( struct cell *t )
{
int i;

for( i=t->first_branch ; i<t->nr_of_outgoings ; i++ )
if ( t->uac[i].last_received<200 )
return 0;

return 1;
}


/* This is the neurological point of reply processing -- called
* from within a REPLY_LOCK, t_should_relay_response decides
* how a reply shall be processed and how transaction state is
Expand Down Expand Up @@ -820,6 +834,31 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
out
*/
LM_DBG("T_code=%d, new_code=%d\n", Trans->uas.status,new_code);

/* a final reply after 200 OK on a transaction with multi branch 200 OK */
if ( is_invite(Trans) && new_code>=200
&& Trans->flags&T_MULTI_200OK_FLAG
&& Trans->uas.status>=200 && Trans->uas.status<300) {
*should_store=0;
picked_branch=-1;
Trans->uac[branch].last_received=new_code;
if (new_code>=300) {
/* negative reply, we simply discard (no relay, no save) */
*should_relay = -1;
return RPS_DISCARDED;
}
/* 2xx reply - is this the last branch to complete?? */
if (tran_is_completed(Trans)) {
/* last branch gets also a 200OK, no more pending branches */
*should_relay = branch;
return RPS_COMPLETED;
} else {
/* 200 OK, but still having ongoing branches */
*should_relay = branch;
return RPS_RELAY;
}
}

inv_through=new_code>=200 && new_code<300 && is_invite(Trans);
/* if final response sent out, allow only INVITE 2xx */
if ( Trans->uas.status >= 200 ) {
Expand Down Expand Up @@ -847,7 +886,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
#ifdef EXTRA_DEBUG
/* don't report on retransmissions */
if (Trans->uac[branch].last_received==new_code) {
LM_DBG("final reply retransmission\n");
LM_DBG("final rely retransmission\n");
} else
/* if you FR-timed-out, faked a local 408 and 487 came, don't
* report on it either */
Expand Down Expand Up @@ -972,6 +1011,14 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
*should_store=0;
*should_relay= new_code==100? -1 : branch;
if (new_code>=200 ) {
/* if a a multi-200OK transaction, prevent the transaction
completion if we still have pending branches */
if (Trans->flags&T_MULTI_200OK_FLAG) {
if (tran_is_completed(Trans))
return RPS_COMPLETED;
else
return RPS_RELAY;
}
which_cancel( Trans, cancel_bitmap );
return RPS_COMPLETED;
} else return RPS_PROVISIONAL;
Expand Down
4 changes: 3 additions & 1 deletion modules/tm/t_reply.h
Expand Up @@ -44,7 +44,9 @@ enum rps {
/* transaction completed */
RPS_COMPLETED,
/* provisional reply not affecting transaction state */
RPS_PROVISIONAL
RPS_PROVISIONAL,
/* just relay the reply*/
RPS_RELAY
};

extern char tm_tags[TOTAG_VALUE_LEN];
Expand Down
3 changes: 3 additions & 0 deletions modules/tm/tm.c
Expand Up @@ -1330,6 +1330,9 @@ static int w_t_relay( struct sip_msg *p_msg , char *proxy, char *flags)
t->flags|=T_NO_DNS_FAILOVER_FLAG;
if (((int)(long)flags)&TM_T_REPLY_reason_FLAG)
t->flags|=T_CANCEL_REASON_FLAG;
if ( (((int)(long)flags)&TM_T_REPLY_do_cancel_dis_FLAG) &&
tm_has_request_disponsition_no_cancel(p_msg)==0 )
t->flags|=T_MULTI_200OK_FLAG;

/* update the transaction only if in REQUEST route; for other types
of routes we do not want to inherit the local changes */
Expand Down

0 comments on commit 4747da5

Please sign in to comment.