Finished invite session UAS implementation

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@160 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2006-02-08 22:44:25 +00:00
parent 1519e57fd7
commit 3899823631
12 changed files with 474 additions and 262 deletions

View File

@ -48,6 +48,15 @@ typedef struct pjsip_dlg_party
} pjsip_dlg_party;
/**
* Dialog state.
*/
enum pjsip_dialog_state
{
PJSIP_DIALOG_STATE_NULL,
PJSIP_DIALOG_STATE_ESTABLISHED,
};
/**
* This structure describes the dialog structure.
*/
@ -67,7 +76,7 @@ struct pjsip_dialog
void *dlg_set;
/* Dialog's session properties. */
pj_bool_t established;/**< Dialog is established? */
enum pjsip_dialog_state state; /**< Dialog state. */
pjsip_uri *target; /**< Current target. */
pjsip_dlg_party local; /**< Local party info. */
pjsip_dlg_party remote; /**< Remote party info. */

View File

@ -124,7 +124,6 @@ struct pjsip_event
struct
{
pjsip_tx_data *tdata; /**< The transmit data buffer. */
pjsip_transaction *tsx; /**< The transaction. */
} tx_msg;
@ -139,7 +138,6 @@ struct pjsip_event
struct
{
pjsip_rx_data *rdata; /**< The receive data buffer. */
pjsip_transaction *tsx; /**< The transaction. */
} rx_msg;
/** User event. */
@ -178,20 +176,18 @@ struct pjsip_event
/**
* Init tx msg event.
*/
#define PJSIP_EVENT_INIT_TX_MSG(event,ptsx,ptdata) \
#define PJSIP_EVENT_INIT_TX_MSG(event,ptdata) \
do { \
(event).type = PJSIP_EVENT_TX_MSG; \
(event).body.tx_msg.tsx = ptsx; \
(event).body.tx_msg.tdata = ptdata; \
} while (0)
/**
* Init rx msg event.
*/
#define PJSIP_EVENT_INIT_RX_MSG(event,ptsx,prdata) \
#define PJSIP_EVENT_INIT_RX_MSG(event,prdata) \
do { \
(event).type = PJSIP_EVENT_RX_MSG; \
(event).body.rx_msg.tsx = ptsx; \
(event).body.rx_msg.rdata = prdata; \
} while (0)

View File

@ -175,7 +175,9 @@ PJ_DECL(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user,
/**
* Create, initialize, and register a new transaction as UAS from the
* specified incoming request in \c rdata.
* specified incoming request in \c rdata. After calling this function,
* application MUST call #pjsip_tsx_recv_msg() so that transaction
* moves from state NULL.
*
* @param tsx_user Module to be registered as transaction user of the new
* transaction, which will receive notification from the
@ -189,6 +191,24 @@ PJ_DECL(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
pjsip_rx_data *rdata,
pjsip_transaction **p_tsx );
/**
* Call this function to manually feed a message to the transaction.
* For UAS transaction, application MUST call this function after
* UAS transaction has been created.
*
* This function SHOULD only be called to pass initial request message
* to UAS transaction. Before this function returns, on_tsx_state()
* callback of the transaction user will be called. If response message
* is passed to this function, then on_rx_response() will also be called
* before on_tsx_state().
*
* @param tsx The transaction.
* @param rdata The message.
*/
PJ_DECL(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx,
pjsip_rx_data *rdata);
/**
* Transmit message in tdata with this transaction. It is possible to
* pass NULL in tdata for UAC transaction, which in this case the last

View File

@ -133,6 +133,10 @@ typedef pjsip_module pjsip_user_agent;
*/
typedef struct pjsip_dialog pjsip_dialog;
/**
* Dialog state (sip_dialog.h).
*/
enum pjsip_dialog_state pjsip_dialog_state;
/**
* Transaction role.

View File

@ -108,6 +108,21 @@ static pj_status_t mod_inv_unload(void)
return PJ_SUCCESS;
}
/*
* Set session state.
*/
void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state,
pjsip_event *e)
{
inv->state = state;
if (mod_inv.cb.on_state_changed)
(*mod_inv.cb.on_state_changed)(inv, e);
if (inv->state == PJSIP_INV_STATE_DISCONNECTED)
pjsip_dlg_dec_session(inv->dlg);
}
/*
* Send ACK for 2xx response.
*/
@ -145,11 +160,16 @@ static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata)
static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata)
{
pjsip_method *method;
pjsip_dialog *dlg;
pjsip_inv_session *inv;
/* Only wants to receive request from a dialog. */
if (pjsip_rdata_get_dlg(rdata) == NULL)
dlg = pjsip_rdata_get_dlg(rdata);
if (dlg == NULL)
return PJ_FALSE;
inv = dlg->mod_data[mod_inv.mod.id];
/* Report to dialog that we handle INVITE, CANCEL, BYE, ACK.
* If we need to send response, it will be sent in the state
* handlers.
@ -158,12 +178,23 @@ static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata)
if (method->id == PJSIP_INVITE_METHOD ||
method->id == PJSIP_CANCEL_METHOD ||
method->id == PJSIP_ACK_METHOD ||
method->id == PJSIP_BYE_METHOD)
{
return PJ_TRUE;
}
/* On receipt ACK request, when state is CONNECTING,
* move state to CONFIRMED.
*/
if (method->id == PJSIP_ACK_METHOD && inv &&
inv->state == PJSIP_INV_STATE_CONFIRMED)
{
pjsip_event event;
PJSIP_EVENT_INIT_RX_MSG(event, rdata);
inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &event);
}
return PJ_FALSE;
}
@ -702,7 +733,7 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg,
inv->pool = dlg->pool;
inv->role = PJSIP_ROLE_UAS;
inv->state = PJSIP_INV_STATE_INCOMING;
inv->state = PJSIP_INV_STATE_NULL;
inv->dlg = dlg;
inv->options = options;
@ -1066,19 +1097,6 @@ PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv,
}
void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state,
pjsip_event *e)
{
inv->state = state;
if (mod_inv.cb.on_state_changed)
(*mod_inv.cb.on_state_changed)(inv, e);
if (inv->state == PJSIP_INV_STATE_DISCONNECTED)
pjsip_dlg_dec_session(inv->dlg);
}
/*
* Respond to incoming CANCEL request.
*/
@ -1165,6 +1183,52 @@ static void inv_respond_incoming_bye( pjsip_inv_session *inv,
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
/*
* Respond to BYE request.
*/
static void inv_handle_bye_response( pjsip_inv_session *inv,
pjsip_transaction *tsx,
pjsip_rx_data *rdata,
pjsip_event *e )
{
pj_status_t status;
if (e->body.tsx_state.type != PJSIP_EVENT_RX_MSG) {
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
return;
}
/* Handle 401/407 challenge. */
if (tsx->status_code == 401 || tsx->status_code == 407) {
pjsip_tx_data *tdata;
status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess,
rdata,
tsx->last_tx,
&tdata);
if (status != PJ_SUCCESS) {
/* Does not have proper credentials.
* End the session anyway.
*/
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
} else {
/* Re-send BYE. */
status = pjsip_inv_send_msg(inv, tdata, NULL );
}
} else {
/* End the session. */
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
}
/*
* State NULL is before anything is sent/received.
*/
@ -1197,6 +1261,11 @@ static void inv_on_state_null( pjsip_inv_session *inv, pjsip_event *e)
case PJSIP_TSX_STATE_TRYING:
inv_set_state(inv, PJSIP_INV_STATE_INCOMING, e);
break;
case PJSIP_TSX_STATE_PROCEEDING:
inv_set_state(inv, PJSIP_INV_STATE_INCOMING, e);
if (tsx->status_code > 100)
inv_set_state(inv, PJSIP_INV_STATE_EARLY, e);
break;
default:
pj_assert(!"Unexpected state");
}
@ -1463,6 +1532,8 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e)
switch (tsx->state) {
case PJSIP_TSX_STATE_CONFIRMED:
if (tsx->status_code/100 == 2)
inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, e);
break;
case PJSIP_TSX_STATE_TERMINATED:
@ -1495,7 +1566,18 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e)
inv_respond_incoming_bye( inv, tsx, e->body.tsx_state.src.rdata, e );
} else if (tsx->method.id == PJSIP_BYE_METHOD &&
tsx->role == PJSIP_ROLE_UAC &&
tsx->state == PJSIP_TSX_STATE_COMPLETED)
{
/*
* Outgoing BYE
*/
inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e);
}
}
/*
@ -1513,39 +1595,15 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
tsx->role == PJSIP_ROLE_UAC &&
tsx->state == PJSIP_TSX_STATE_COMPLETED)
{
/*
* Outgoing BYE.
* Outgoing BYE
*/
pj_status_t status;
/* Handle 401/407 challenge. */
if (tsx->status_code == 401 || tsx->status_code == 407) {
pjsip_tx_data *tdata;
status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess,
e->body.tsx_state.src.rdata,
tsx->last_tx,
&tdata);
if (status != PJ_SUCCESS) {
/* Does not have proper credentials.
* End the session anyway.
*/
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
} else {
/* Re-send BYE. */
status = pjsip_inv_send_msg(inv, tdata, NULL );
}
} else {
/* End the session. */
if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e);
else
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
}
else if (tsx->method.id == PJSIP_BYE_METHOD &&

View File

@ -74,6 +74,7 @@ static pj_status_t create_dialog( pjsip_user_agent *ua,
pj_sprintf(dlg->obj_name, "dlg%p", dlg);
dlg->ua = ua;
dlg->endpt = endpt;
dlg->state = PJSIP_DIALOG_STATE_NULL;
status = pj_mutex_create_recursive(pool, "dlg%p", &dlg->mutex);
if (status != PJ_SUCCESS)
@ -374,6 +375,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua,
PJ_TODO(DIALOG_APP_TIMER);
/* Feed the first request to the transaction. */
pjsip_tsx_recv_msg(tsx, rdata);
/* Done. */
*p_dlg = dlg;
return PJ_SUCCESS;
@ -407,6 +411,11 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG,
PJSIP_ENOTRESPONSEMSG);
/* Status code MUST be 1xx (but not 100), or 2xx */
status = rdata->msg_info.msg->line.status.code;
PJ_ASSERT_RETURN( (status/100==1 && status!=100) ||
(status/100==2), PJ_EBUG);
/* To tag must present in the response. */
PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen != 0, PJSIP_EMISSINGTAG);
@ -444,6 +453,15 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
/* Initial role is UAC. */
dlg->role = PJSIP_ROLE_UAC;
/* Dialog state depends on the response. */
status = rdata->msg_info.msg->line.status.code/100;
if (status == 1 || status == 2)
dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED;
else {
pj_assert(!"Invalid status code");
dlg->state = PJSIP_DIALOG_STATE_NULL;
}
/* Secure? */
dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target);
@ -462,7 +480,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg,
r = r->next;
}
/* Init client authentication session. */
/* Clone client authentication session. */
status = pjsip_auth_clt_clone(dlg->pool, &dlg->auth_sess,
&first_dlg->auth_sess);
if (status != PJ_SUCCESS)
@ -849,6 +867,71 @@ on_error:
return status;
}
/* Add standard headers for certain types of response */
static void dlg_beautify_response(pjsip_dialog *dlg,
int st_code,
pjsip_tx_data *tdata)
{
pjsip_cseq_hdr *cseq;
int st_class;
const pjsip_hdr *c_hdr;
pjsip_hdr *hdr;
cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg);
pj_assert(cseq != NULL);
st_class = st_code / 100;
/* Contact, Allow, Supported header. */
if (pjsip_method_creates_dialog(&cseq->method)) {
/* Add Contact header for 1xx, 2xx, 3xx and 485 response. */
if (st_class==2 || st_class==3 || (st_class==1 && st_code != 100) ||
st_code==485)
{
/* Add contact header only if one is not present. */
if (pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL) == 0) {
hdr = pjsip_hdr_clone(tdata->pool, dlg->local.contact);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
}
/* Add Allow header in 2xx and 405 response. */
if ((st_class==2 || st_code==405) &&
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ALLOW, NULL)==NULL)
{
c_hdr = pjsip_endpt_get_capability(dlg->endpt,
PJSIP_H_ALLOW, NULL);
if (c_hdr) {
hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
}
/* Add Supported header in 2xx response. */
if (st_class==2 &&
pjsip_msg_find_hdr(tdata->msg, PJSIP_H_SUPPORTED, NULL)==NULL)
{
c_hdr = pjsip_endpt_get_capability(dlg->endpt,
PJSIP_H_SUPPORTED, NULL);
if (c_hdr) {
hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
}
}
/* Add To tag in all responses except 100 */
if (st_code != 100) {
pjsip_to_hdr *to;
to = PJSIP_MSG_TO_HDR(tdata->msg);
pj_assert(to != NULL);
to->tag = dlg->local.info->tag;
}
}
/*
* Create response.
@ -860,11 +943,12 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
pjsip_tx_data **p_tdata)
{
pj_status_t status;
pjsip_cseq_hdr *cseq;
pjsip_tx_data *tdata;
int st_class;
/* Create generic response. */
/* Create generic response.
* This will initialize response's Via, To, From, Call-ID, CSeq
* and Record-Route headers from the request.
*/
status = pjsip_endpt_create_response(dlg->endpt,
rdata, st_code, st_text, &tdata);
if (status != PJ_SUCCESS)
@ -873,90 +957,7 @@ PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
/* Lock the dialog. */
pj_mutex_lock(dlg->mutex);
/* Special treatment for 2xx response to request that establishes
* dialog.
*
* RFC 3261 Section 12.1.1
*
* When a UAS responds to a request with a response that establishes
* a dialog (such as a 2xx to INVITE):
* - MUST copy all Record-Route header field values from the request
* into the response (including the URIs, URI parameters, and any
* Record-Route header field parameters, whether they are known or
* unknown to the UAS) and MUST maintain the order of those values.
* - The Contact header field contains an address where the UAS would
* like to be contacted for subsequent requests in the dialog.
*
* Also from Table 3, page 119.
*/
cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg);
pj_assert(cseq != NULL);
st_class = st_code / 100;
if (cseq->cseq == dlg->remote.first_cseq &&
(st_class==1 || st_class==2) && st_code != 100)
{
pjsip_hdr *rr, *hdr;
/* Duplicate Record-Route header from the request. */
rr = (pjsip_hdr*) rdata->msg_info.record_route;
while (rr) {
hdr = pjsip_hdr_clone(tdata->pool, rr);
pjsip_msg_add_hdr(tdata->msg, hdr);
rr = rr->next;
if (rr == &rdata->msg_info.msg->hdr)
break;
rr = pjsip_msg_find_hdr(rdata->msg_info.msg,
PJSIP_H_RECORD_ROUTE, rr);
}
}
/* Contact header. */
if (pjsip_method_creates_dialog(&cseq->method)) {
/* Add Contact header for 1xx, 2xx, 3xx and 485 response. */
if (st_class==2 || st_class==3 || (st_class==1 && st_code != 100) ||
st_code==485)
{
/* Add contact header. */
pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, dlg->local.contact);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
/* Add Allow header in 2xx and 405 response. */
if (st_class==2 || st_code==405) {
const pjsip_hdr *c_hdr;
c_hdr = pjsip_endpt_get_capability(dlg->endpt,
PJSIP_H_ALLOW, NULL);
if (c_hdr) {
pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
}
/* Add Supported header in 2xx response. */
if (st_class==2) {
const pjsip_hdr *c_hdr;
c_hdr = pjsip_endpt_get_capability(dlg->endpt,
PJSIP_H_SUPPORTED, NULL);
if (c_hdr) {
pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr);
pjsip_msg_add_hdr(tdata->msg, hdr);
}
}
}
/* Add To tag in all responses except 100 */
if (st_code != 100 && rdata->msg_info.to->tag.slen == 0) {
pjsip_to_hdr *to;
to = PJSIP_MSG_TO_HDR(tdata->msg);
pj_assert(to != NULL);
to->tag = dlg->local.info->tag;
}
dlg_beautify_response(dlg, st_code, tdata);
/* Unlock the dialog. */
pj_mutex_unlock(dlg->mutex);
@ -980,6 +981,9 @@ PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg,
PJSIP_ENOTRESPONSEMSG);
PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL);
pj_mutex_lock(dlg->mutex);
/* Replace status code and reason */
tdata->msg->line.status.code = st_code;
if (st_text) {
pj_strdup(tdata->pool, &tdata->msg->line.status.reason, st_text);
@ -987,6 +991,17 @@ PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg,
tdata->msg->line.status.reason = *pjsip_get_status_text(st_code);
}
dlg_beautify_response(dlg, st_code, tdata);
/* Must add reference counter, since tsx_send_msg() will decrement it */
pjsip_tx_data_add_ref(tdata);
/* Force to re-print message. */
pjsip_tx_data_invalidate_msg(tdata);
pj_mutex_unlock(dlg->mutex);
return PJ_SUCCESS;
}
@ -1057,18 +1072,22 @@ PJ_DEF(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg,
void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata )
{
pj_status_t status;
pjsip_transaction *tsx;
pjsip_transaction *tsx = NULL;
unsigned i;
/* Lock the dialog. */
pj_mutex_lock(dlg->mutex);
/* Check CSeq */
if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq) {
if (rdata->msg_info.cseq->cseq <= dlg->remote.cseq &&
rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD &&
rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD)
{
/* Invalid CSeq.
* Respond statelessly with 500 (Internal Server Error)
*/
pj_mutex_unlock(dlg->mutex);
pj_assert(pjsip_rdata_get_tsx(rdata) == NULL);
pjsip_endpt_respond_stateless(dlg->endpt,
rdata, 500, NULL, NULL, NULL);
return;
@ -1078,14 +1097,16 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata )
dlg->remote.cseq = rdata->msg_info.cseq->cseq;
/* Create UAS transaction for this request. */
status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx);
PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;});
if (pjsip_rdata_get_tsx(rdata) == NULL) {
status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx);
PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;});
/* Put this dialog in the transaction data. */
tsx->mod_data[dlg->ua->id] = dlg;
/* Put this dialog in the transaction data. */
tsx->mod_data[dlg->ua->id] = dlg;
/* Add transaction count. */
++dlg->tsx_count;
/* Add transaction count. */
++dlg->tsx_count;
}
/* Report the request to dialog usages. */
for (i=0; i<dlg->usage_cnt; ++i) {
@ -1100,28 +1121,9 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata )
break;
}
if (i==dlg->usage_cnt) {
pjsip_tx_data *tdata;
PJ_LOG(4,(dlg->obj_name,
"%s is unhandled by dialog usages. "
"Dialog will response with 500 (Internal Server Error)",
pjsip_rx_data_get_info(rdata)));
status = pjsip_endpt_create_response(dlg->endpt,
rdata,
PJSIP_SC_INTERNAL_SERVER_ERROR,
NULL, &tdata);
if (status == PJ_SUCCESS)
status = pjsip_tsx_send_msg(tsx, tdata);
if (status != PJ_SUCCESS) {
char errmsg[PJSIP_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(dlg->obj_name,"Error sending %s: %s",
pjsip_tx_data_get_info(tdata), errmsg));
pjsip_tsx_terminate(tsx, 500);
}
}
/* Feed the first request to the transaction. */
if (tsx)
pjsip_tsx_recv_msg(tsx, rdata);
on_return:
/* Unlock dialog. */
@ -1142,25 +1144,25 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
/* Check that rdata already has dialog in mod_data. */
pj_assert(pjsip_rdata_get_dlg(rdata) == dlg);
/* Update the remote tag if it is different. */
if (pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag) != 0) {
pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag);
/* No need to update remote's tag_hval since its never used. */
}
/* Keep the response's status code */
res_code = rdata->msg_info.msg->line.status.code;
/* When we receive response that establishes dialog, update the route
* set and dialog target.
/* When we receive response that establishes dialog, update To tag,
* route set and dialog target.
*/
if (!dlg->established &&
if (dlg->state == PJSIP_DIALOG_STATE_NULL &&
pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) &&
(res_code > 100 && res_code < 300) &&
rdata->msg_info.to->tag.slen)
{
pjsip_hdr *hdr, *end_hdr;
pjsip_contact_hdr *contact;
/* Update To tag. */
pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag);
/* No need to update remote's tag_hval since its never used. */
/* RFC 3271 Section 12.1.2:
* The route set MUST be set to the list of URIs in the Record-Route
* header field from the response, taken in reverse order and
@ -1169,9 +1171,6 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
* empty set. This route set, even if empty, overrides any pre-existing
* route set for future requests in this dialog.
*/
pjsip_hdr *hdr, *end_hdr;
pjsip_contact_hdr *contact;
pj_list_init(&dlg->route_set);
end_hdr = &rdata->msg_info.msg->hdr;
@ -1194,7 +1193,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata )
dlg->target = dlg->remote.contact->uri;
}
dlg->established = 1;
dlg->state = PJSIP_DIALOG_STATE_ESTABLISHED;
}
/* Update remote target (again) when receiving 2xx response messages
@ -1251,6 +1250,9 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg,
if (tsx->state == PJSIP_TSX_STATE_TERMINATED)
--dlg->tsx_count;
/* Increment session to prevent usages from destroying dialog. */
++dlg->sess_count;
/* Pass to dialog usages. */
for (i=0; i<dlg->usage_cnt; ++i) {
@ -1260,6 +1262,9 @@ void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg,
(*dlg->usage[i]->on_tsx_state)(tsx, e);
}
/* Decrement temporary session. */
--dlg->sess_count;
if (tsx->state == PJSIP_TSX_STATE_TERMINATED && dlg->tsx_count == 0 &&
dlg->sess_count == 0)
{

View File

@ -170,8 +170,6 @@ static void tsx_resched_retransmission( pjsip_transaction *tsx );
static pj_status_t tsx_retransmit( pjsip_transaction *tsx, int resched);
static int tsx_send_msg( pjsip_transaction *tsx,
pjsip_tx_data *tdata);
static void tsx_on_rx_msg( pjsip_transaction *tsx,
pjsip_rx_data *rdata );
/* State handlers for UAC, indexed by state */
@ -697,12 +695,12 @@ static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata)
/* Race condition!
* Transaction may gets deleted before we have chance to lock it
* in tsx_on_rx_msg().
* in pjsip_tsx_recv_msg().
*/
PJ_TODO(FIX_RACE_CONDITION_HERE);
/* Pass the message to the transaction. */
tsx_on_rx_msg(tsx, rdata );
pjsip_tsx_recv_msg(tsx, rdata );
return PJ_TRUE;
}
@ -745,12 +743,12 @@ static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata)
/* Race condition!
* Transaction may gets deleted before we have chance to lock it
* in tsx_on_rx_msg().
* in pjsip_tsx_recv_msg().
*/
PJ_TODO(FIX_RACE_CONDITION_HERE);
/* Pass the message to the transaction. */
tsx_on_rx_msg(tsx, rdata );
pjsip_tsx_recv_msg(tsx, rdata );
return PJ_TRUE;
}
@ -965,12 +963,7 @@ static void tsx_set_state( pjsip_transaction *tsx,
pj_assert(rdata != NULL);
if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG &&
tsx->tsx_user->on_rx_request)
{
(*tsx->tsx_user->on_rx_request)(rdata);
} else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG &&
if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG &&
tsx->tsx_user->on_rx_response)
{
(*tsx->tsx_user->on_rx_response)(rdata);
@ -1239,11 +1232,11 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
tsx->transaction_key.ptr));
/* Begin with state TRYING.
/* Begin with state NULL.
* Manually set-up the state becase we don't want to call the callback.
*/
tsx->state = PJSIP_TSX_STATE_TRYING;
tsx->state_handler = &tsx_on_state_trying;
tsx->state = PJSIP_TSX_STATE_NULL;
tsx->state_handler = &tsx_on_state_null;
/* Get response address. */
status = pjsip_get_response_addr( tsx->pool, rdata, &tsx->res_addr );
@ -1327,7 +1320,7 @@ PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx,
pjsip_tx_data_get_info(tdata),
state_str[tsx->state]));
PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata);
PJSIP_EVENT_INIT_TX_MSG(event, tdata);
/* Dispatch to transaction. */
lock_tsx(tsx, &lck);
@ -1349,7 +1342,8 @@ PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx,
* This function is called by endpoint when incoming message for the
* transaction is received.
*/
static void tsx_on_rx_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata)
PJ_DEF(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx,
pjsip_rx_data *rdata)
{
pjsip_event event;
struct tsx_lock_data lck;
@ -1362,7 +1356,7 @@ static void tsx_on_rx_msg( pjsip_transaction *tsx, pjsip_rx_data *rdata)
rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;
/* Init event. */
PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata);
PJSIP_EVENT_INIT_RX_MSG(event, rdata);
/* Dispatch to transaction. */
lock_tsx(tsx, &lck);
@ -1716,11 +1710,12 @@ static pj_status_t tsx_on_state_null( pjsip_transaction *tsx,
if (tsx->role == PJSIP_ROLE_UAS) {
/* UAS doesn't have STATE_NULL.
* State has moved from NULL after transaction is initialized.
*/
pj_assert(!"Bug bug bug!!");
return PJ_EBUG;
/* Set state to Trying. */
pj_assert(event->type == PJSIP_EVENT_RX_MSG &&
event->body.rx_msg.rdata->msg_info.msg->type ==
PJSIP_REQUEST_MSG);
tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, PJSIP_EVENT_RX_MSG,
event->body.rx_msg.rdata);
} else {
pjsip_tx_data *tdata;

View File

@ -474,9 +474,14 @@ static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata)
pj_str_t *from_tag;
pjsip_dialog *dlg;
/* Optimized path: bail out early if request doesn't have To tag */
if (rdata->msg_info.to->tag.slen == 0)
/* Optimized path: bail out early if request is not CANCEL and it doesn't
* have To tag
*/
if (rdata->msg_info.to->tag.slen == 0 &&
rdata->msg_info.msg->line.req.method.id != PJSIP_CANCEL_METHOD)
{
return PJ_FALSE;
}
/* Lock user agent before looking up the dialog hash table. */
pj_mutex_lock(mod_ua.mutex);
@ -624,17 +629,15 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata)
// rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq)
if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) {
//pj_str_t *to_tag = &rdata->msg_info.to->tag;
int st_code = rdata->msg_info.msg->line.status.code;
pj_str_t *to_tag = &rdata->msg_info.to->tag;
/* Must hold UA mutex before accessing dialog set. */
pj_mutex_lock(mod_ua.mutex);
dlg = dlg_set->dlg_list.next;
/* Forking handling is temporarily disabled. */
PJ_TODO(UA_LAYER_HANDLE_FORKING);
#if 0
while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) {
/* If there is dialog with no remote tag (i.e. dialog has not
@ -652,9 +655,12 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata)
}
/* If no dialog with matching remote tag is found, this must be
* a forked response.
* a forked response. Respond to this ONLY when response is non-100
* provisional response OR a 2xx response.
*/
if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) {
if (dlg == (pjsip_dialog*)&dlg_set->dlg_list &&
((st_code/100==1 && st_code!=100) || st_code/100==2))
{
/* Report to application about forked condition.
* Application can either create a dialog or ignore the response.
*/
@ -677,8 +683,16 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata)
return PJ_TRUE;
}
} else if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) {
/* For 100 or non-2xx response which has different To tag,
* pass the response to the first dialog.
*/
dlg = dlg_set->dlg_list.next;
}
#endif
/* Done with the dialog set. */
pj_mutex_unlock(mod_ua.mutex);

View File

@ -1303,6 +1303,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_respond( pjsip_endpoint *endpt,
return status;
}
/* Feed the request to the transaction. */
pjsip_tsx_recv_msg(tsx, rdata);
/* Send the message. */
status = pjsip_tsx_send_msg(tsx, tdata);
if (status != PJ_SUCCESS) {

View File

@ -18,15 +18,9 @@
*/
#include "pjsua.h"
#include "getopt.h"
#include <stdlib.h>
/* For debugging, disable threading. */
//#define NO_WORKER_THREAD
#ifdef NO_WORKER_THREAD
#include <conio.h>
#endif
#define THIS_FILE "main.c"
static pjsip_inv_session *inv_session;
@ -68,35 +62,45 @@ static void ui_help(void)
puts("");
puts("Console keys:");
puts(" m Make a call");
puts(" a Answer incoming call");
puts(" h Hangup current call");
puts(" q Quit");
puts("");
fflush(stdout);
}
static pj_bool_t input(const char *title, char *buf, pj_size_t len)
{
char *p;
printf("%s (empty to cancel): ", title); fflush(stdout);
fgets(buf, len, stdin);
/* Remove trailing newlines. */
for (p=buf; ; ++p) {
if (*p=='\r' || *p=='\n') *p='\0';
else if (!*p) break;
}
if (!*buf)
return PJ_FALSE;
return PJ_TRUE;
}
static void ui_console_main(void)
{
char keyin[10];
char buf[128];
char *p;
pjsip_inv_session *inv;
//ui_help();
for (;;) {
#ifdef NO_WORKER_THREAD
pj_time_val timeout = { 0, 10 };
pjsip_endpt_handle_events (pjsua.endpt, &timeout);
if (kbhit())
fgets(keyin, sizeof(keyin), stdin);
#else
ui_help();
fgets(keyin, sizeof(keyin), stdin);
#endif
fgets(buf, sizeof(buf), stdin);
switch (keyin[0]) {
switch (buf[0]) {
case 'm':
if (inv_session != NULL) {
@ -106,23 +110,9 @@ static void ui_console_main(void)
}
#if 1
printf("Enter URL to call: "); fflush(stdout);
fgets(buf, sizeof(buf), stdin);
if (buf[0]=='\r' || buf[0]=='\n') {
/* Cancelled. */
puts("<cancelled>");
fflush(stdout);
continue;
}
/* Remove trailing newlines. */
for (p=buf; ; ++p) {
if (*p=='\r' || *p=='\n') *p='\0';
else if (!*p) break;
}
/* Make call! : */
if (!input("Enter URL to call", buf, sizeof(buf)))
continue;
pjsua_invite(buf, &inv);
#else
@ -132,6 +122,33 @@ static void ui_console_main(void)
break;
case 'a':
if (inv_session == NULL || inv_session->role != PJSIP_ROLE_UAS ||
inv_session->state >= PJSIP_INV_STATE_CONNECTING)
{
puts("No pending incoming call");
fflush(stdout);
continue;
} else {
pj_status_t status;
pjsip_tx_data *tdata;
if (!input("Answer with code (100-699)", buf, sizeof(buf)))
continue;
status = pjsip_inv_answer(inv_session, atoi(buf), NULL, NULL,
&tdata);
if (status == PJ_SUCCESS)
status = pjsip_inv_send_msg(inv_session, tdata, NULL);
if (status != PJ_SUCCESS)
pjsua_perror("Unable to create/send response", status);
}
break;
case 'h':
if (inv_session == NULL) {
@ -672,11 +689,6 @@ int main(int argc, char *argv[])
pjsua_default();
#ifdef NO_WORKER_THREAD
pjsua.thread_cnt = 0;
#endif
/* Initialize pjsua (to create pool etc).
*/

View File

@ -101,8 +101,99 @@ void pjsua_perror(const char *title, pj_status_t status)
*/
static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
{
PJ_UNUSED_ARG(rdata);
PJ_TODO(IMPLEMENT_UAS);
pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
pjsip_msg *msg = rdata->msg_info.msg;
/*
* Handle incoming INVITE outside dialog.
*/
if (dlg == NULL && tsx == NULL &&
msg->line.req.method.id == PJSIP_INVITE_METHOD)
{
pj_status_t status;
pjsip_tx_data *response = NULL;
unsigned options = 0;
/* Verify that we can handle the request. */
status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
pjsua.endpt, &response);
if (status != PJ_SUCCESS) {
/*
* No we can't handle the incoming INVITE request.
*/
if (response) {
pjsip_response_addr res_addr;
pjsip_get_response_addr(response->pool, rdata, &res_addr);
pjsip_endpt_send_response(pjsua.endpt, &res_addr, response,
NULL, NULL);
} else {
/* Respond with 500 (Internal Server Error) */
pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
NULL, NULL);
}
} else {
/*
* Yes we can handle the incoming INVITE request.
*/
pjsip_inv_session *inv;
pjmedia_sdp_session *answer;
/* Create dummy SDP answer: */
status = pjmedia_sdp_parse(pjsua.pool, PJSUA_DUMMY_SDP_ANSWER,
pj_native_strlen(PJSUA_DUMMY_SDP_ANSWER),
&answer);
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
NULL, NULL);
return PJ_TRUE;
}
/* Create dialog: */
status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
&pjsua.contact_uri, &dlg);
if (status != PJ_SUCCESS)
return PJ_TRUE;
/* Create invite session: */
status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
if (status != PJ_SUCCESS) {
status = pjsip_dlg_create_response( dlg, rdata, 500, NULL,
&response);
if (status == PJ_SUCCESS)
status = pjsip_dlg_send_response(dlg,
pjsip_rdata_get_tsx(rdata),
response);
return PJ_TRUE;
}
/* Answer with 100 (using the dialog, not invite): */
status = pjsip_dlg_create_response(dlg, rdata, 100, NULL, &response);
if (status == PJ_SUCCESS)
status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), response);
}
/* This INVITE request has been handled. */
return PJ_TRUE;
}
return PJ_FALSE;
}
@ -121,7 +212,6 @@ static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
{
PJ_UNUSED_ARG(rdata);
PJ_TODO(IMPLEMENT_UAS);
return PJ_FALSE;
}

View File

@ -709,6 +709,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
test_complete = -110;
return PJ_TRUE;
}
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
send_response(rdata, tsx, status_code);
@ -749,6 +750,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
test_complete = -120;
return PJ_TRUE;
}
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
@ -801,6 +803,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
return PJ_TRUE;
}
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
if (pj_strcmp2(&branch_param, TEST4_BRANCH_ID) == 0) {
@ -882,6 +885,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
return PJ_TRUE;
}
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
if (pj_strcmp2(&branch_param, TEST7_BRANCH_ID) == 0) {
@ -966,6 +970,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
return PJ_TRUE;
}
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
send_response(rdata, tsx, TEST9_STATUS_CODE);
@ -1076,6 +1081,7 @@ static pj_bool_t on_rx_message(pjsip_rx_data *rdata)
return PJ_TRUE;
}
pjsip_tsx_recv_msg(tsx, rdata);
save_key(tsx);
schedule_send_response(rdata, &tsx_key, code1, 1000);