3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <pjsip-ua/sip_100rel.h>
21 #include <pjsip/sip_endpoint.h>
22 #include <pjsip/sip_event.h>
23 #include <pjsip/sip_module.h>
24 #include <pjsip/sip_transaction.h>
25 #include <pj/assert.h>
31 #include <pj/string.h>
33 #define THIS_FILE "sip_100rel.c"
36 PJ_DEF_DATA(const pjsip_method) pjsip_prack_method =
42 typedef struct dlg_data dlg_data;
47 static pj_status_t mod_100rel_load(pjsip_endpoint *endpt);
49 static void on_retransmit(pj_timer_heap_t *timer_heap,
50 struct pj_timer_entry *entry);
53 const pj_str_t tag_100rel = { "100rel", 6 };
54 const pj_str_t RSEQ = { "RSeq", 4 };
55 const pj_str_t RACK = { "RAck", 4 };
59 static struct mod_100rel
62 pjsip_endpoint *endpt;
66 NULL, NULL, /* prev, next. */
67 { "mod-100rel", 10 }, /* Name. */
69 PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */
70 &mod_100rel_load, /* load() */
74 NULL, /* on_rx_request() */
75 NULL, /* on_rx_response() */
76 NULL, /* on_tx_request. */
77 NULL, /* on_tx_response() */
78 NULL, /* on_tsx_state() */
83 /* List of pending transmission (may include the final response as well) */
84 typedef struct tx_data_list_t
86 PJ_DECL_LIST_MEMBER(struct tx_data_list_t);
92 /* Below, UAS and UAC roles are of the INVITE transaction */
95 typedef struct uas_state_t
98 pj_uint32_t rseq; /* Initialized to -1 */
99 tx_data_list_t tx_data_list;
100 unsigned retransmit_count;
101 pj_timer_entry retransmit_timer;
106 typedef struct uac_state_t
108 pj_str_t tag; /* To tag */
110 pj_uint32_t rseq; /* Initialized to -1 */
111 struct uac_state_t *next; /* next call leg */
115 /* State attached to each dialog. */
118 pjsip_inv_session *inv;
119 uas_state_t *uas_state;
120 uac_state_t *uac_state_list;
124 /*****************************************************************************
128 *****************************************************************************
130 static pj_status_t mod_100rel_load(pjsip_endpoint *endpt)
132 mod_100rel.endpt = endpt;
133 pjsip_endpt_add_capability(endpt, &mod_100rel.mod,
135 1, &pjsip_prack_method.name);
136 pjsip_endpt_add_capability(endpt, &mod_100rel.mod,
137 PJSIP_H_SUPPORTED, NULL,
143 static pjsip_require_hdr *find_req_hdr(pjsip_msg *msg)
145 pjsip_require_hdr *hreq;
147 hreq = (pjsip_require_hdr*)
148 pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL);
152 for (i=0; i<hreq->count; ++i) {
153 if (!pj_stricmp(&hreq->values[i], &tag_100rel)) {
158 if ((void*)hreq->next == (void*)&msg->hdr)
161 hreq = (pjsip_require_hdr*)
162 pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, hreq->next);
171 * Get PRACK method constant.
173 PJ_DEF(const pjsip_method*) pjsip_get_prack_method(void)
175 return &pjsip_prack_method;
182 PJ_DEF(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt)
184 if (mod_100rel.mod.id != -1)
187 return pjsip_endpt_register_module(endpt, &mod_100rel.mod);
192 * API: attach 100rel support in invite session. Called by
195 PJ_DEF(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv)
199 /* Check that 100rel module has been initialized */
200 PJ_ASSERT_RETURN(mod_100rel.mod.id >= 0, PJ_EINVALIDOP);
202 /* Create and attach as dialog usage */
203 dd = PJ_POOL_ZALLOC_T(inv->dlg->pool, dlg_data);
205 pjsip_dlg_add_usage(inv->dlg, &mod_100rel.mod, (void*)dd);
207 PJ_LOG(5,(dd->inv->dlg->obj_name, "100rel module attached"));
214 * Check if incoming response has reliable provisional response feature.
216 PJ_DEF(pj_bool_t) pjsip_100rel_is_reliable(pjsip_rx_data *rdata)
218 pjsip_msg *msg = rdata->msg_info.msg;
220 PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG, PJ_FALSE);
222 return msg->line.status.code > 100 && msg->line.status.code < 200 &&
223 rdata->msg_info.require != NULL &&
224 find_req_hdr(msg) != NULL;
229 * Create PRACK request for the incoming reliable provisional response.
231 PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv,
232 pjsip_rx_data *rdata,
233 pjsip_tx_data **p_tdata)
236 uac_state_t *uac_state = NULL;
237 const pj_str_t *to_tag = &rdata->msg_info.to->tag;
238 pjsip_transaction *tsx;
240 pjsip_generic_string_hdr *rseq_hdr;
241 pjsip_generic_string_hdr *rack_hdr;
245 pjsip_tx_data *tdata;
250 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
251 PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED);
253 tsx = pjsip_rdata_get_tsx(rdata);
254 msg = rdata->msg_info.msg;
256 /* Check our assumptions */
257 pj_assert( tsx->role == PJSIP_ROLE_UAC &&
258 tsx->method.id == PJSIP_INVITE_METHOD &&
259 msg->line.status.code > 100 &&
260 msg->line.status.code < 200);
263 /* Get the RSeq header */
264 rseq_hdr = (pjsip_generic_string_hdr*)
265 pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL);
266 if (rseq_hdr == NULL) {
267 PJ_LOG(4,(dd->inv->dlg->obj_name,
268 "Ignoring 100rel response with no RSeq header"));
269 return PJSIP_EMISSINGHDR;
271 rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue);
273 /* Find UAC state for the specified call leg */
274 uac_state = dd->uac_state_list;
276 if (pj_strcmp(&uac_state->tag, to_tag)==0)
278 uac_state = uac_state->next;
281 /* Create new UAC state if we don't have one */
282 if (uac_state == NULL) {
283 uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t);
284 uac_state->cseq = rdata->msg_info.cseq->cseq;
285 uac_state->rseq = rseq - 1;
286 pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag);
287 uac_state->next = dd->uac_state_list;
288 dd->uac_state_list = uac_state;
291 /* If this is from new INVITE transaction, reset UAC state. */
292 if (rdata->msg_info.cseq->cseq != uac_state->cseq) {
293 uac_state->cseq = rdata->msg_info.cseq->cseq;
294 uac_state->rseq = rseq - 1;
297 /* Ignore provisional response retransmission */
298 if (rseq <= uac_state->rseq) {
299 /* This should have been handled before */
302 /* Ignore provisional response with out-of-order RSeq */
303 } else if (rseq != uac_state->rseq + 1) {
304 PJ_LOG(4,(dd->inv->dlg->obj_name,
305 "Ignoring 100rel response because RSeq jump "
306 "(expecting %u, got %u)",
307 uac_state->rseq+1, rseq));
311 /* Update our RSeq */
312 uac_state->rseq = rseq;
315 status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method,
317 if (status != PJ_SUCCESS)
320 /* If this response is a forked response from a different call-leg,
321 * update the req URI (https://trac.pjsip.org/repos/ticket/1364)
323 if (pj_strcmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) {
324 const pjsip_contact_hdr *mhdr;
326 mhdr = (const pjsip_contact_hdr*)
327 pjsip_msg_find_hdr(rdata->msg_info.msg,
328 PJSIP_H_CONTACT, NULL);
329 if (!mhdr || !mhdr->uri) {
330 PJ_LOG(4,(dd->inv->dlg->obj_name,
331 "Ignoring 100rel response with no or "
332 "invalid Contact header"));
333 pjsip_tx_data_dec_ref(tdata);
336 tdata->msg->line.req.uri = (pjsip_uri*)
337 pjsip_uri_clone(tdata->pool, mhdr->uri);
340 /* Create RAck header */
342 rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf),
344 rseq, rdata->msg_info.cseq->cseq,
345 (int)tsx->method.name.slen,
346 tsx->method.name.ptr);
347 rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack);
348 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr);
358 * Send PRACK request.
360 PJ_DEF(pj_status_t) pjsip_100rel_send_prack( pjsip_inv_session *inv,
361 pjsip_tx_data *tdata)
365 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
366 PJ_ASSERT_ON_FAIL(dd != NULL,
367 {pjsip_tx_data_dec_ref(tdata); return PJSIP_ENOTINITIALIZED; });
369 return pjsip_dlg_send_request(inv->dlg, tdata,
370 mod_100rel.mod.id, (void*) dd);
376 * Notify 100rel module that the invite session has been disconnected.
378 PJ_DEF(pj_status_t) pjsip_100rel_end_session(pjsip_inv_session *inv)
382 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
386 /* Make sure we don't have pending transmission */
388 pj_assert(!dd->uas_state->retransmit_timer.id);
389 pj_assert(pj_list_empty(&dd->uas_state->tx_data_list));
396 static void parse_rack(const pj_str_t *rack,
397 pj_uint32_t *p_rseq, pj_int32_t *p_seq,
400 const char *p = rack->ptr, *end = p + rack->slen;
403 token.ptr = (char*)p;
404 while (p < end && pj_isdigit(*p))
406 token.slen = p - token.ptr;
407 *p_rseq = pj_strtoul(&token);
410 token.ptr = (char*)p;
411 while (p < end && pj_isdigit(*p))
413 token.slen = p - token.ptr;
414 *p_seq = pj_strtoul(&token);
418 p_method->ptr = (char*)p;
419 p_method->slen = end - p;
421 p_method->ptr = NULL;
426 /* Clear all responses in the transmission list */
427 static void clear_all_responses(dlg_data *dd)
431 tl = dd->uas_state->tx_data_list.next;
432 while (tl != &dd->uas_state->tx_data_list) {
433 pjsip_tx_data_dec_ref(tl->tdata);
436 pj_list_init(&dd->uas_state->tx_data_list);
441 * Handle incoming PRACK request.
443 PJ_DEF(pj_status_t) pjsip_100rel_on_rx_prack( pjsip_inv_session *inv,
444 pjsip_rx_data *rdata)
447 pjsip_transaction *tsx;
449 pjsip_generic_string_hdr *rack_hdr;
450 pjsip_tx_data *tdata;
456 tsx = pjsip_rdata_get_tsx(rdata);
457 pj_assert(tsx != NULL);
459 msg = rdata->msg_info.msg;
461 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
463 /* UAC sends us PRACK while we didn't send reliable provisional
464 * response. Respond with 400 (?)
466 const pj_str_t reason = pj_str("Unexpected PRACK");
468 status = pjsip_dlg_create_response(inv->dlg, rdata, 400,
470 if (status == PJ_SUCCESS) {
471 status = pjsip_dlg_send_response(inv->dlg, tsx, tdata);
473 return PJSIP_ENOTINITIALIZED;
476 /* Always reply with 200/OK for PRACK */
477 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata);
478 if (status == PJ_SUCCESS) {
479 status = pjsip_dlg_send_response(inv->dlg, tsx, tdata);
482 /* Ignore if we don't have pending transmission */
483 if (dd->uas_state == NULL || pj_list_empty(&dd->uas_state->tx_data_list)) {
484 PJ_LOG(4,(dd->inv->dlg->obj_name,
485 "PRACK ignored - no pending response"));
489 /* Find RAck header */
490 rack_hdr = (pjsip_generic_string_hdr*)
491 pjsip_msg_find_hdr_by_name(msg, &RACK, NULL);
493 /* RAck header not found */
494 PJ_LOG(4,(dd->inv->dlg->obj_name, "No RAck header"));
495 return PJSIP_EMISSINGHDR;
498 /* Parse RAck header */
499 parse_rack(&rack_hdr->hvalue, &rseq, &cseq, &method);
502 /* Match RAck against outgoing transmission */
503 if (rseq == dd->uas_state->tx_data_list.next->rseq &&
504 cseq == dd->uas_state->cseq)
507 * Yes this PRACK matches outgoing transmission.
509 tx_data_list_t *tl = dd->uas_state->tx_data_list.next;
511 if (dd->uas_state->retransmit_timer.id) {
512 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
513 &dd->uas_state->retransmit_timer);
514 dd->uas_state->retransmit_timer.id = PJ_FALSE;
517 /* Remove from the list */
518 if (tl != &dd->uas_state->tx_data_list) {
521 /* Destroy the response */
522 pjsip_tx_data_dec_ref(tl->tdata);
525 /* Schedule next packet */
526 dd->uas_state->retransmit_count = 0;
527 if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
528 on_retransmit(NULL, &dd->uas_state->retransmit_timer);
532 /* No it doesn't match */
533 PJ_LOG(4,(dd->inv->dlg->obj_name,
534 "Rx PRACK with no matching reliable response"));
543 * This is retransmit timer callback, called initially to send the response,
544 * and subsequently when the retransmission time elapses.
546 static void on_retransmit(pj_timer_heap_t *timer_heap,
547 struct pj_timer_entry *entry)
551 pjsip_tx_data *tdata;
555 PJ_UNUSED_ARG(timer_heap);
557 dd = (dlg_data*) entry->user_data;
559 entry->id = PJ_FALSE;
561 ++dd->uas_state->retransmit_count;
562 if (dd->uas_state->retransmit_count >= 7) {
563 /* If a reliable provisional response is retransmitted for
564 64*T1 seconds without reception of a corresponding PRACK,
565 the UAS SHOULD reject the original request with a 5xx
568 pj_str_t reason = pj_str("Reliable response timed out");
571 /* Clear all pending responses */
572 clear_all_responses(dd);
574 /* Send 500 response */
575 status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata);
576 if (status == PJ_SUCCESS) {
577 pjsip_dlg_send_response(dd->inv->dlg,
584 pj_assert(!pj_list_empty(&dd->uas_state->tx_data_list));
585 tl = dd->uas_state->tx_data_list.next;
588 pjsip_tx_data_add_ref(tdata);
589 final = tdata->msg->line.status.code >= 200;
591 if (dd->uas_state->retransmit_count == 1) {
592 pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata);
594 pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata);
598 /* This is final response, which will be retransmitted by
599 * UA layer. There's no more task to do, so clear the
600 * transmission list and bail out.
602 clear_all_responses(dd);
606 /* Schedule next retransmission */
607 if (dd->uas_state->retransmit_count < 6) {
609 delay.msec = (1 << dd->uas_state->retransmit_count) *
611 pj_time_val_normalize(&delay);
618 pjsip_endpt_schedule_timer(dd->inv->dlg->endpt,
619 &dd->uas_state->retransmit_timer,
626 /* Clone response. */
627 static pjsip_tx_data *clone_tdata(dlg_data *dd,
628 const pjsip_tx_data *src)
631 const pjsip_hdr *hsrc;
635 status = pjsip_endpt_create_tdata(dd->inv->dlg->endpt, &dst);
636 if (status != PJ_SUCCESS)
639 msg = pjsip_msg_create(dst->pool, PJSIP_RESPONSE_MSG);
641 pjsip_tx_data_add_ref(dst);
643 /* Duplicate status line */
644 msg->line.status.code = src->msg->line.status.code;
645 pj_strdup(dst->pool, &msg->line.status.reason,
646 &src->msg->line.status.reason);
648 /* Duplicate all headers */
649 hsrc = src->msg->hdr.next;
650 while (hsrc != &src->msg->hdr) {
651 pjsip_hdr *h = (pjsip_hdr*) pjsip_hdr_clone(dst->pool, hsrc);
652 pjsip_msg_add_hdr(msg, h);
656 /* Duplicate message body */
658 msg->body = pjsip_msg_body_clone(dst->pool, src->msg->body);
660 PJ_LOG(5,(dd->inv->dlg->obj_name,
661 "Reliable response %s created",
662 pjsip_tx_data_get_info(dst)));
668 /* Check if any pending response in transmission list has SDP */
669 static pj_bool_t has_sdp(dlg_data *dd)
673 tl = dd->uas_state->tx_data_list.next;
674 while (tl != &dd->uas_state->tx_data_list) {
675 if (tl->tdata->msg->body)
684 /* Send response reliably */
685 PJ_DEF(pj_status_t) pjsip_100rel_tx_response(pjsip_inv_session *inv,
686 pjsip_tx_data *tdata)
688 pjsip_cseq_hdr *cseq_hdr;
689 pjsip_generic_string_hdr *rseq_hdr;
690 pjsip_require_hdr *req_hdr;
693 pjsip_tx_data *old_tdata;
696 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG,
697 PJSIP_ENOTRESPONSEMSG);
699 status_code = tdata->msg->line.status.code;
701 /* 100 response doesn't need PRACK */
702 if (status_code == 100)
703 return pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
706 /* Get the 100rel data attached to this dialog */
707 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id];
708 PJ_ASSERT_RETURN(dd != NULL, PJ_EINVALIDOP);
712 * We need to clone tdata because we may need to keep it in our
713 * retransmission list, while the original dialog may modify it
714 * if it wants to send another response.
717 tdata = clone_tdata(dd, old_tdata);
718 pjsip_tx_data_dec_ref(old_tdata);
721 /* Get CSeq header, and make sure this is INVITE response */
722 cseq_hdr = (pjsip_cseq_hdr*)
723 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
724 PJ_ASSERT_RETURN(cseq_hdr != NULL, PJ_EBUG);
725 PJ_ASSERT_RETURN(cseq_hdr->method.id == PJSIP_INVITE_METHOD,
728 /* Remove existing Require header */
729 req_hdr = find_req_hdr(tdata->msg);
731 pj_list_erase(req_hdr);
734 /* Remove existing RSeq header */
735 rseq_hdr = (pjsip_generic_string_hdr*)
736 pjsip_msg_find_hdr_by_name(tdata->msg, &RSEQ, NULL);
738 pj_list_erase(rseq_hdr);
740 /* Different treatment for provisional and final response */
741 if (status_code/100 == 2) {
743 /* RFC 3262 Section 3: UAS Behavior:
745 The UAS MAY send a final response to the initial request
746 before having received PRACKs for all unacknowledged
747 reliable provisional responses, unless the final response
748 is 2xx and any of the unacknowledged reliable provisional
749 responses contained a session description. In that case,
750 it MUST NOT send a final response until those provisional
751 responses are acknowledged.
754 if (dd->uas_state && has_sdp(dd)) {
755 /* Yes we have transmitted 1xx with SDP reliably.
756 * In this case, must queue the 2xx response.
760 tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
762 tl->rseq = (pj_uint32_t)-1;
763 pj_list_push_back(&dd->uas_state->tx_data_list, tl);
765 /* Will send later */
768 PJ_LOG(4,(dd->inv->dlg->obj_name,
769 "2xx response will be sent after PRACK"));
771 } else if (dd->uas_state) {
773 RFC 3262 Section 3: UAS Behavior:
775 If the UAS does send a final response when reliable
776 responses are still unacknowledged, it SHOULD NOT
777 continue to retransmit the unacknowledged reliable
778 provisional responses, but it MUST be prepared to
779 process PRACK requests for those outstanding
783 PJ_LOG(4,(dd->inv->dlg->obj_name,
784 "No SDP sent so far, sending 2xx now"));
786 /* Cancel the retransmit timer */
787 if (dd->uas_state->retransmit_timer.id) {
788 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
789 &dd->uas_state->retransmit_timer);
790 dd->uas_state->retransmit_timer.id = PJ_FALSE;
793 /* Clear all pending responses (drop 'em) */
794 clear_all_responses(dd);
796 /* And transmit the 2xx response */
797 status=pjsip_dlg_send_response(inv->dlg,
798 inv->invite_tsx, tdata);
801 /* We didn't send any reliable provisional response */
803 /* Transmit the 2xx response */
804 status=pjsip_dlg_send_response(inv->dlg,
805 inv->invite_tsx, tdata);
808 } else if (status_code >= 300) {
811 RFC 3262 Section 3: UAS Behavior:
813 If the UAS does send a final response when reliable
814 responses are still unacknowledged, it SHOULD NOT
815 continue to retransmit the unacknowledged reliable
816 provisional responses, but it MUST be prepared to
817 process PRACK requests for those outstanding
821 /* Cancel the retransmit timer */
822 if (dd->uas_state && dd->uas_state->retransmit_timer.id) {
823 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt,
824 &dd->uas_state->retransmit_timer);
825 dd->uas_state->retransmit_timer.id = PJ_FALSE;
827 /* Clear all pending responses (drop 'em) */
828 clear_all_responses(dd);
831 /* And transmit the 2xx response */
832 status=pjsip_dlg_send_response(inv->dlg,
833 inv->invite_tsx, tdata);
837 * This is provisional response.
843 /* Create UAS state if we don't have one */
844 if (dd->uas_state == NULL) {
845 dd->uas_state = PJ_POOL_ZALLOC_T(inv->dlg->pool,
847 dd->uas_state->cseq = cseq_hdr->cseq;
848 dd->uas_state->rseq = pj_rand() % 0x7FFF;
849 pj_list_init(&dd->uas_state->tx_data_list);
850 dd->uas_state->retransmit_timer.user_data = dd;
851 dd->uas_state->retransmit_timer.cb = &on_retransmit;
854 /* Check that CSeq match */
855 PJ_ASSERT_RETURN(cseq_hdr->cseq == dd->uas_state->cseq,
858 /* Add Require header */
859 req_hdr = pjsip_require_hdr_create(tdata->pool);
861 req_hdr->values[0] = tag_100rel;
862 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr);
864 /* Add RSeq header */
865 pj_ansi_snprintf(rseq_str, sizeof(rseq_str), "%u",
866 dd->uas_state->rseq);
867 rseq = pj_str(rseq_str);
868 rseq_hdr = pjsip_generic_string_hdr_create(tdata->pool,
870 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)rseq_hdr);
872 /* Create list entry for this response */
873 tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t);
875 tl->rseq = dd->uas_state->rseq++;
877 /* Add to queue if there's pending response, otherwise
878 * transmit immediately.
880 if (!pj_list_empty(&dd->uas_state->tx_data_list)) {
882 int code = tdata->msg->line.status.code;
884 /* Will send later */
885 pj_list_push_back(&dd->uas_state->tx_data_list, tl);
888 PJ_LOG(4,(dd->inv->dlg->obj_name,
889 "Reliable %d response enqueued (%d pending)",
890 code, pj_list_size(&dd->uas_state->tx_data_list)));
893 pj_list_push_back(&dd->uas_state->tx_data_list, tl);
895 dd->uas_state->retransmit_count = 0;
896 on_retransmit(NULL, &dd->uas_state->retransmit_timer);