📄 sip_transaction.c
字号:
tsx->timeout_timer._timer_id = -1;
tsx->timeout_timer.user_data = tsx;
tsx->timeout_timer.cb = &tsx_timer_callback;
status = pj_mutex_create_recursive(pool, tsx->obj_name, &tsx->mutex);
if (status != PJ_SUCCESS) {
pjsip_endpt_release_pool(mod_tsx_layer.endpt, pool);
return status;
}
*p_tsx = tsx;
return PJ_SUCCESS;
}
/* Destroy transaction. */
static pj_status_t tsx_destroy( pjsip_transaction *tsx )
{
struct tsx_lock_data *lck;
/* Decrement transport reference counter. */
if (tsx->transport) {
pjsip_transport_dec_ref( tsx->transport );
tsx->transport = NULL;
}
/* Decrement reference counter in transport selector */
pjsip_tpselector_dec_ref(&tsx->tp_sel);
/* Free last transmitted message. */
if (tsx->last_tx) {
pjsip_tx_data_dec_ref( tsx->last_tx );
tsx->last_tx = NULL;
}
/* Cancel timeout timer. */
if (tsx->timeout_timer._timer_id != -1) {
pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
tsx->timeout_timer._timer_id = -1;
}
/* Cancel retransmission timer. */
if (tsx->retransmit_timer._timer_id != -1) {
pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
tsx->retransmit_timer._timer_id = -1;
}
/* Clear some pending flags. */
tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED | TSX_HAS_PENDING_SEND);
/* Refuse to destroy transaction if it has pending resolving. */
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_DESTROY;
tsx->tsx_user = NULL;
PJ_LOG(4,(tsx->obj_name, "Will destroy later because transport is "
"in progress"));
return PJ_EBUSY;
}
/* Clear TLS, so that mutex will not be unlocked */
lck = pj_thread_local_get(pjsip_tsx_lock_tls_id);
while (lck) {
if (lck->tsx == tsx) {
lck->is_alive = 0;
}
lck = lck->prev;
}
pj_mutex_destroy(tsx->mutex);
PJ_LOG(5,(tsx->obj_name, "Transaction destroyed!"));
pjsip_endpt_release_pool(tsx->endpt, tsx->pool);
return PJ_SUCCESS;
}
/*
* Callback when timer expires.
*/
static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
{
pjsip_event event;
pjsip_transaction *tsx = entry->user_data;
struct tsx_lock_data lck;
PJ_UNUSED_ARG(theap);
PJ_LOG(5,(tsx->obj_name, "%s timer event",
(entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit":"Timeout")));
if (entry->id == TSX_TIMER_RETRANSMISSION) {
PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
} else {
PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
}
/* Dispatch event to transaction. */
lock_tsx(tsx, &lck);
(*tsx->state_handler)(tsx, &event);
unlock_tsx(tsx, &lck);
}
/*
* Set transaction state, and inform TU about the transaction state change.
*/
static void tsx_set_state( pjsip_transaction *tsx,
pjsip_tsx_state_e state,
pjsip_event_id_e event_src_type,
void *event_src )
{
pjsip_tsx_state_e prev_state = tsx->state;
/* New state must be greater than previous state */
pj_assert(state >= tsx->state);
PJ_LOG(5, (tsx->obj_name, "State changed from %s to %s, event=%s",
state_str[tsx->state], state_str[state],
pjsip_event_str(event_src_type)));
/* Change state. */
tsx->state = state;
/* Update the state handlers. */
if (tsx->role == PJSIP_ROLE_UAC) {
tsx->state_handler = tsx_state_handler_uac[state];
} else {
tsx->state_handler = tsx_state_handler_uas[state];
}
/* Before informing TU about state changed, inform TU about
* rx event.
*/
if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user) {
pjsip_rx_data *rdata = event_src;
pj_assert(rdata != NULL);
if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG &&
tsx->tsx_user->on_rx_response)
{
(*tsx->tsx_user->on_rx_response)(rdata);
}
}
/* Inform TU about state changed. */
if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) {
pjsip_event e;
PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src,
prev_state);
(*tsx->tsx_user->on_tsx_state)(tsx, &e);
}
/* When the transaction is terminated, release transport, and free the
* saved last transmitted message.
*/
if (state == PJSIP_TSX_STATE_TERMINATED) {
pj_time_val timeout = {0, 0};
/* Reschedule timeout timer to destroy this transaction. */
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_DESTROY;
} else {
/* Cancel timeout timer. */
if (tsx->timeout_timer._timer_id != -1) {
pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
tsx->timeout_timer._timer_id = -1;
}
pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
&timeout);
}
} else if (state == PJSIP_TSX_STATE_DESTROYED) {
/* Unregister transaction. */
mod_tsx_layer_unregister_tsx(tsx);
/* Destroy transaction. */
tsx_destroy(tsx);
}
}
/*
* Create, initialize, and register UAC transaction.
*/
PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user,
pjsip_tx_data *tdata,
pjsip_transaction **p_tsx)
{
pjsip_transaction *tsx;
pjsip_msg *msg;
pjsip_cseq_hdr *cseq;
pjsip_via_hdr *via;
pjsip_host_info dst_info;
struct tsx_lock_data lck;
pj_status_t status;
/* Validate arguments. */
PJ_ASSERT_RETURN(tdata && tdata->msg && p_tsx, PJ_EINVAL);
PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
PJSIP_ENOTREQUESTMSG);
/* Method MUST NOT be ACK! */
PJ_ASSERT_RETURN(tdata->msg->line.req.method.id != PJSIP_ACK_METHOD,
PJ_EINVALIDOP);
/* Keep shortcut */
msg = tdata->msg;
/* Make sure CSeq header is present. */
cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
if (!cseq) {
pj_assert(!"CSeq header not present in outgoing message!");
return PJSIP_EMISSINGHDR;
}
/* Create transaction instance. */
status = tsx_create( tsx_user, &tsx);
if (status != PJ_SUCCESS)
return status;
/* Lock transaction. */
lock_tsx(tsx, &lck);
/* Role is UAC. */
tsx->role = PJSIP_ROLE_UAC;
/* Save method. */
pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
/* Save CSeq. */
tsx->cseq = cseq->cseq;
/* Generate Via header if it doesn't exist. */
via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
if (via == NULL) {
via = pjsip_via_hdr_create(tdata->pool);
pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
}
/* Generate branch parameter if it doesn't exist. */
if (via->branch_param.slen == 0) {
pj_str_t tmp;
via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
PJSIP_RFC3261_BRANCH_LEN);
tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN + 2;
*(tmp.ptr-2) = 80; *(tmp.ptr-1) = 106;
pj_generate_unique_string( &tmp );
/* Save branch parameter. */
tsx->branch = via->branch_param;
} else {
/* Copy branch parameter. */
pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
}
/* Generate transaction key. */
create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
PJSIP_ROLE_UAC, &tsx->method,
&via->branch_param);
/* Calculate hashed key value. */
#ifdef PRECALC_HASH
tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr,
tsx->transaction_key.slen);
#endif
PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
tsx->transaction_key.ptr));
/* Begin with State_Null.
* Manually set-up the state becase we don't want to call the callback.
*/
tsx->state = PJSIP_TSX_STATE_NULL;
tsx->state_handler = &tsx_on_state_null;
/* Save the message. */
tsx->last_tx = tdata;
pjsip_tx_data_add_ref(tsx->last_tx);
/* Determine whether reliable transport should be used initially.
* This will be updated whenever transport has changed.
*/
status = pjsip_get_request_dest(tdata, &dst_info);
if (status != PJ_SUCCESS) {
tsx_destroy(tsx);
return status;
}
tsx->is_reliable = (dst_info.flag & PJSIP_TRANSPORT_RELIABLE);
/* Register transaction to hash table. */
status = mod_tsx_layer_register_tsx(tsx);
if (status != PJ_SUCCESS) {
pj_assert(!"Bug in branch_param generator (i.e. not unique)");
tsx_destroy(tsx);
return status;
}
/* Unlock transaction and return. */
unlock_tsx(tsx, &lck);
PJ_LOG(5,(tsx->obj_name, "Transaction created for %s",
pjsip_tx_data_get_info(tdata)));
*p_tsx = tsx;
return PJ_SUCCESS;
}
/*
* Create, initialize, and register UAS transaction.
*/
PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user,
pjsip_rx_data *rdata,
pjsip_transaction **p_tsx)
{
pjsip_transaction *tsx;
pjsip_msg *msg;
pj_str_t *branch;
pjsip_cseq_hdr *cseq;
pj_status_t status;
struct tsx_lock_data lck;
/* Validate arguments. */
PJ_ASSERT_RETURN(rdata && rdata->msg_info.msg && p_tsx, PJ_EINVAL);
/* Keep shortcut to message */
msg = rdata->msg_info.msg;
/* Make sure this is a request message. */
PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG, PJSIP_ENOTREQUESTMSG);
/* Make sure method is not ACK */
PJ_ASSERT_RETURN(msg->line.req.method.id != PJSIP_ACK_METHOD,
PJ_EINVALIDOP);
/* Make sure CSeq header is present. */
cseq = rdata->msg_info.cseq;
if (!cseq)
return PJSIP_EMISSINGHDR;
/* Make sure Via header is present. */
if (rdata->msg_info.via == NULL)
return PJSIP_EMISSINGHDR;
/* Check that method in CSeq header match request method.
* Reference: PROTOS #1922
*/
if (pjsip_method_cmp(&msg->line.req.method,
&rdata->msg_info.cseq->method) != 0)
{
PJ_LOG(4,(THIS_FILE, "Error: CSeq header contains different "
"method than the request line"));
return PJSIP_EINVALIDHDR;
}
/*
* Create transaction instance.
*/
status = tsx_create( tsx_user, &tsx);
if (status != PJ_SUCCESS)
return status;
/* Lock transaction. */
lock_tsx(tsx, &lck);
/* Role is UAS */
tsx->role = PJSIP_ROLE_UAS;
/* Save method. */
pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
/* Save CSeq */
tsx->cseq = cseq->cseq;
/* Get transaction key either from branch for RFC3261 message, or
* create transaction key.
*/
status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key,
PJSIP_ROLE_UAS, &tsx->method, rdata);
if (status != PJ_SUCCESS) {
tsx_destroy(tsx);
return status;
}
/* Calculate hashed key value. */
#ifdef PRECALC_HASH
tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr,
tsx->transaction_key.slen);
#endif
/* Duplicate branch parameter for transaction. */
branch = &rdata->msg_info.via->branch_param;
pj_strdup(tsx->pool, &tsx->branch, branch);
PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
tsx->transaction_key.ptr));
/* Begin with state NULL.
* Manually set-up the state becase we don't want to call the callback.
*/
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 );
if (status != PJ_SUCCESS) {
tsx_destroy(tsx);
return status;
}
/* If it's decided that we should use current transport, keep the
* transport.
*/
if (tsx->res_addr.transport) {
tsx->transport = tsx->res_addr.transport;
pjsip_transport_add_ref(tsx->transport);
pj_memcpy(&tsx->addr, &tsx->res_addr.addr, tsx->res_addr.addr_len);
tsx->addr_len = tsx->res_addr.addr_len;
tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
}
/* Register the transaction. */
status = mod_tsx_layer_register_tsx(tsx);
if (status != PJ_SUCCESS) {
tsx_destroy(tsx);
return status;
}
/* Put this transaction in rdata's mod_data. */
rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;
/* Unlock transaction and return. */
unlock_tsx(tsx, &lck);
PJ_LOG(5,(tsx->obj_name, "Transaction created for %s",
pjsip_rx_data_get_info(rdata)));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -