📄 sip_transaction.c
字号:
*p_tsx = tsx;
return PJ_SUCCESS;
}
/*
* Bind transaction to a specific transport/listener.
*/
PJ_DEF(pj_status_t) pjsip_tsx_set_transport(pjsip_transaction *tsx,
const pjsip_tpselector *sel)
{
struct tsx_lock_data lck;
/* Must be UAC transaction */
PJ_ASSERT_RETURN(tsx && sel, PJ_EINVAL);
/* Start locking the transaction. */
lock_tsx(tsx, &lck);
/* Decrement reference counter of previous transport selector */
pjsip_tpselector_dec_ref(&tsx->tp_sel);
/* Copy transport selector structure .*/
pj_memcpy(&tsx->tp_sel, sel, sizeof(*sel));
/* Increment reference counter */
pjsip_tpselector_add_ref(&tsx->tp_sel);
/* Unlock transaction. */
unlock_tsx(tsx, &lck);
return PJ_SUCCESS;
}
/*
* Set the UAC absolute transaction timeout.
*/
PJ_DEF(pj_status_t) pjsip_tsx_set_uac_timeout(pjsip_transaction *tsx,
unsigned msec_time,
pjsip_tsx_timeout_policy policy)
{
PJ_ASSERT_RETURN(tsx && tsx->role==PJSIP_ROLE_UAC &&
tsx->state==PJSIP_TSX_STATE_NULL, PJ_EINVALIDOP);
tsx->msec_timeout = msec_time;
tsx->timeout_policy = policy;
return PJ_SUCCESS;
}
/*
* Set transaction status code and reason.
*/
static void tsx_set_status_code(pjsip_transaction *tsx,
int code, const pj_str_t *reason)
{
tsx->status_code = code;
if (reason)
pj_strdup(tsx->pool, &tsx->status_text, reason);
else
tsx->status_text = *pjsip_get_status_text(code);
}
/*
* Forcely terminate transaction.
*/
PJ_DEF(pj_status_t) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
{
struct tsx_lock_data lck;
PJ_LOG(5,(tsx->obj_name, "Request to terminate transaction"));
PJ_ASSERT_RETURN(tsx != NULL, PJ_EINVAL);
PJ_ASSERT_RETURN(code >= 200, PJ_EINVAL);
if (tsx->state == PJSIP_TSX_STATE_TERMINATED)
return PJ_SUCCESS;
lock_tsx(tsx, &lck);
tsx_set_status_code(tsx, code, NULL);
tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, PJSIP_EVENT_USER, NULL);
unlock_tsx(tsx, &lck);
return PJ_SUCCESS;
}
/*
* This function is called by TU to send a message.
*/
PJ_DEF(pj_status_t) pjsip_tsx_send_msg( pjsip_transaction *tsx,
pjsip_tx_data *tdata )
{
pjsip_event event;
struct tsx_lock_data lck;
pj_status_t status;
if (tdata == NULL)
tdata = tsx->last_tx;
PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP);
PJ_LOG(5,(tsx->obj_name, "Sending %s in state %s",
pjsip_tx_data_get_info(tdata),
state_str[tsx->state]));
PJSIP_EVENT_INIT_TX_MSG(event, tdata);
/* Dispatch to transaction. */
lock_tsx(tsx, &lck);
/* Set transport selector to tdata */
pjsip_tx_data_set_transport(tdata, &tsx->tp_sel);
/* Dispatch to state handler */
status = (*tsx->state_handler)(tsx, &event);
unlock_tsx(tsx, &lck);
/* Only decrement reference counter when it returns success.
* (This is the specification from the .PDF design document).
*/
if (status == PJ_SUCCESS) {
pjsip_tx_data_dec_ref(tdata);
}
return status;
}
/*
* This function is called by endpoint when incoming message for the
* transaction is received.
*/
PJ_DEF(void) pjsip_tsx_recv_msg( pjsip_transaction *tsx,
pjsip_rx_data *rdata)
{
pjsip_event event;
struct tsx_lock_data lck;
pj_status_t status;
PJ_LOG(5,(tsx->obj_name, "Incoming %s in state %s",
pjsip_rx_data_get_info(rdata), state_str[tsx->state]));
/* Put the transaction in the rdata's mod_data. */
rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx;
/* Init event. */
PJSIP_EVENT_INIT_RX_MSG(event, rdata);
/* Dispatch to transaction. */
lock_tsx(tsx, &lck);
status = (*tsx->state_handler)(tsx, &event);
unlock_tsx(tsx, &lck);
}
/* Callback called by send message framework */
static void send_msg_callback( pjsip_send_state *send_state,
pj_ssize_t sent, pj_bool_t *cont )
{
pjsip_transaction *tsx = send_state->token;
struct tsx_lock_data lck;
lock_tsx(tsx, &lck);
if (sent > 0) {
/* Successfully sent! */
pj_assert(send_state->cur_transport != NULL);
if (tsx->transport != send_state->cur_transport) {
/* Update transport. */
if (tsx->transport) {
pjsip_transport_dec_ref(tsx->transport);
tsx->transport = NULL;
}
tsx->transport = send_state->cur_transport;
pjsip_transport_add_ref(tsx->transport);
/* Update remote address. */
tsx->addr_len = send_state->addr.entry[send_state->cur_addr].addr_len;
pj_memcpy(&tsx->addr,
&send_state->addr.entry[send_state->cur_addr].addr,
tsx->addr_len);
/* Update is_reliable flag. */
tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
}
/* Clear pending transport flag. */
tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
/* Mark that we have resolved the addresses. */
tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER;
/* Pending destroy? */
if (tsx->transport_flag & TSX_HAS_PENDING_DESTROY) {
tsx_set_state( tsx, PJSIP_TSX_STATE_DESTROYED,
PJSIP_EVENT_UNKNOWN, NULL );
unlock_tsx(tsx, &lck);
return;
}
/* Need to transmit a message? */
if (tsx->transport_flag & TSX_HAS_PENDING_SEND) {
tsx->transport_flag &= ~(TSX_HAS_PENDING_SEND);
tsx_send_msg(tsx, tsx->last_tx);
}
/* Need to reschedule retransmission? */
if (tsx->transport_flag & TSX_HAS_PENDING_RESCHED) {
tsx->transport_flag &= ~(TSX_HAS_PENDING_RESCHED);
/* Only update when transport turns out to be unreliable. */
if (!tsx->is_reliable) {
tsx_resched_retransmission(tsx);
}
}
} else {
/* Failed to send! */
pj_assert(sent != 0);
/* If transaction is using the same transport as the failed one,
* release the transport.
*/
if (send_state->cur_transport==tsx->transport &&
tsx->transport != NULL)
{
pjsip_transport_dec_ref(tsx->transport);
tsx->transport = NULL;
}
if (!*cont) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_str_t err;
tsx->transport_err = -sent;
err =pj_strerror(-sent, errmsg, sizeof(errmsg));
PJ_LOG(2,(tsx->obj_name,
"Failed to send %s! err=%d (%s)",
pjsip_tx_data_get_info(send_state->tdata), -sent,
errmsg));
/* Clear pending transport flag. */
tsx->transport_flag &= ~(TSX_HAS_PENDING_TRANSPORT);
/* Mark that we have resolved the addresses. */
tsx->transport_flag |= TSX_HAS_RESOLVED_SERVER;
/* Terminate transaction, if it's not already terminated. */
tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
if (tsx->state != PJSIP_TSX_STATE_TERMINATED &&
tsx->state != PJSIP_TSX_STATE_DESTROYED)
{
tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
PJSIP_EVENT_TRANSPORT_ERROR, send_state->tdata);
}
} else {
char errmsg[PJ_ERR_MSG_SIZE];
PJ_LOG(2,(tsx->obj_name,
"Temporary failure in sending %s, "
"will try next server. Err=%d (%s)",
pjsip_tx_data_get_info(send_state->tdata), -sent,
pj_strerror(-sent, errmsg, sizeof(errmsg)).ptr));
}
}
unlock_tsx(tsx, &lck);
}
/* Transport callback. */
static void transport_callback(void *token, pjsip_tx_data *tdata,
pj_ssize_t sent)
{
if (sent < 0) {
pjsip_transaction *tsx = token;
struct tsx_lock_data lck;
char errmsg[PJ_ERR_MSG_SIZE];
pj_str_t err;
tsx->transport_err = -sent;
err = pj_strerror(-sent, errmsg, sizeof(errmsg));
PJ_LOG(2,(tsx->obj_name, "Transport failed to send %s! Err=%d (%s)",
pjsip_tx_data_get_info(tdata), -sent, errmsg));
lock_tsx(tsx, &lck);
/* Dereference transport. */
pjsip_transport_dec_ref(tsx->transport);
tsx->transport = NULL;
/* Terminate transaction. */
tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
PJSIP_EVENT_TRANSPORT_ERROR, tdata );
unlock_tsx(tsx, &lck);
}
}
/*
* Send message to the transport.
*/
static pj_status_t tsx_send_msg( pjsip_transaction *tsx,
pjsip_tx_data *tdata)
{
pj_status_t status = PJ_SUCCESS;
PJ_ASSERT_RETURN(tsx && tdata, PJ_EINVAL);
/* Send later if transport is still pending. */
if (tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) {
tsx->transport_flag |= TSX_HAS_PENDING_SEND;
return PJ_SUCCESS;
}
/* If we have the transport, send the message using that transport.
* Otherwise perform full transport resolution.
*/
if (tsx->transport) {
status = pjsip_transport_send( tsx->transport, tdata, &tsx->addr,
tsx->addr_len, tsx,
&transport_callback);
if (status == PJ_EPENDING)
status = PJ_SUCCESS;
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
PJ_LOG(2,(tsx->obj_name,
"Error sending %s: Err=%d (%s)",
pjsip_tx_data_get_info(tdata), status,
pj_strerror(status, errmsg, sizeof(errmsg)).ptr));
/* On error, release transport to force using full transport
* resolution procedure.
*/
if (tsx->transport) {
pjsip_transport_dec_ref(tsx->transport);
tsx->transport = NULL;
}
tsx->addr_len = 0;
tsx->res_addr.transport = NULL;
tsx->res_addr.addr_len = 0;
} else {
return PJ_SUCCESS;
}
}
/* We are here because we don't have transport, or we failed to send
* the message using existing transport. If we haven't resolved the
* server before, then begin the long process of resolving the server
* and send the message with possibly new server.
*/
pj_assert(status != PJ_SUCCESS || tsx->transport == NULL);
/* If we have resolved the server, we treat the error as permanent error.
* Terminate transaction with transport error failure.
*/
if (tsx->transport_flag & TSX_HAS_RESOLVED_SERVER) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_str_t err;
if (status == PJ_SUCCESS) {
pj_assert(!"Unexpected status!");
status = PJ_EUNKNOWN;
}
/* We have resolved the server!.
* Treat this as permanent transport error.
*/
err = pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(2,(tsx->obj_name,
"Transport error, terminating transaction. "
"Err=%d (%s)",
status, errmsg));
tsx_set_status_code(tsx, PJSIP_SC_TSX_TRANSPORT_ERROR, &err);
tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
PJSIP_EVENT_TRANSPORT_ERROR, NULL );
return status;
}
/* Must add reference counter because the send request functions
* decrement the reference counter.
*/
pjsip_tx_data_add_ref(tdata);
/* Begin resolving destination etc to send the message. */
if (tdata->msg->type == PJSIP_REQUEST_MSG) {
tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
status = pjsip_endpt_send_request_stateless(tsx->endpt, tdata, tsx,
&send_msg_callback);
if (status == PJ_EPENDING)
status = PJ_SUCCESS;
if (status != PJ_SUCCESS)
pjsip_tx_data_dec_ref(tdata);
/* Check if transaction is terminated. */
if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED)
status = tsx->transport_err;
} else {
tsx->transport_flag |= TSX_HAS_PENDING_TRANSPORT;
status = pjsip_endpt_send_response( tsx->endpt, &tsx->res_addr,
tdata, tsx,
&send_msg_callback);
if (status == PJ_EPENDING)
status = PJ_SUCCESS;
if (status != PJ_SUCCESS)
pjsip_tx_data_dec_ref(tdata);
/* Check if transaction is terminated. */
if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TERMINATED)
status = tsx->transport_err;
}
return status;
}
/*
* Retransmit last message sent.
*/
static void tsx_resched_retransmission( pjsip_transaction *tsx )
{
pj_time_val timeout;
int msec_time;
pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0);
msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
if (tsx->role == PJSIP_ROLE_UAC) {
/* Retransmission for non-INVITE transaction caps-off at T2 */
if (msec_time>PJSIP_T2_TIMEOUT && tsx->method.id!=PJSIP_INVITE_METHOD)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -