📄 nua_stack.c
字号:
}/* ---------------------------------------------------------------------- *//**@internal * * @class nua_client_request * * Each handle has a queue of client-side requests; if a request is pending, * a new request from API is added to the queue. After the request is * complete, it is removed from the queue and destroyed by the default. The * exception is the client requests bound to a dialog usage: they are saved * and re-used when the dialog usage is refreshed (and sometimes when the * usage is terminated). * * The client request is subclassed and its behaviour modified using virtual * function table in #nua_client_methods_t. * * The first three methods (crm_template(), crm_init(), crm_send()) are * called when the request is sent first time. * * The crm_template() is called if a template request message is needed (for * example, in case of unregister, unsubscribe and unpublish, the template * message is taken from the request establishing the usage). * * The crm_init() is called when the template message and dialog leg has * been created and populated by the tags procided by the application. Its * parameters msg and sip are pointer to the template request message that * is saved in the nua_client_request::cr_msg field. * * The crm_send() is called with a copy of the template message that has * been populated with all the fields included in the request, including * @CSeq and @MaxForwards. The crm_send() function, such as * nua_publish_client_request(), usually calls nua_base_client_trequest() that * then creates the nta-level transaction. * * The response to the request is processed by crm_check_restart(), which * modifies and restarts the request when needed (e.g., when negotiating * expiration time). After the request has been suitably modified, e.g., the * expiration time has been increased, the restart function calls * nua_client_restart(), which restarts the request and relays the * intermediate response to the application with nua_client_restart() and * crm_report(). * * The final responses are processed by crm_recv() and and preliminary ones * by crm_preliminary(). Both functions call nua_base_client_response() after * method-specific processing. * * The nua_base_client_response() relays the response to the application with * nua_client_restart() and crm_report(). * * @par Terminating Dialog Usages and Dialogs * * The response can be marked as terminating with nua_client_terminating(). * When a terminating request completes the dialog usage is removed and the * dialog is destroyed (unless there is an another active usage). */static int nua_client_request_try(nua_client_request_t *cr);static int nua_client_request_sendmsg(nua_client_request_t *cr, msg_t *msg, sip_t *sip);static void nua_client_restart_after(su_root_magic_t *magic, su_timer_t *timer, nua_client_request_t *cr);/**Create a client request. * * @retval 0 if request is pending * @retval > 0 if error event has been sent * @retval < 0 upon an error */int nua_client_create(nua_handle_t *nh, int event, nua_client_methods_t const *methods, tagi_t const * const tags){ su_home_t *home = nh->nh_home; nua_client_request_t *cr; sip_method_t method; char const *name; method = methods->crm_method, name = methods->crm_method_name; if (!name) { tagi_t const *t = tl_find_last(tags, nutag_method); if (t) name = (char const *)t->t_value; } cr = su_zalloc(home, sizeof *cr + methods->crm_extra); if (!cr) { return nua_stack_event(nh->nh_nua, nh, NULL, event, NUA_ERROR_AT(__FILE__, __LINE__), NULL); } cr->cr_methods = methods; cr->cr_event = event; cr->cr_method = method; cr->cr_method_name = name; cr->cr_contactize = methods->crm_flags.target_refresh; cr->cr_dialog = methods->crm_flags.create_dialog; cr->cr_auto = 1; if (su_msg_is_non_null(nh->nh_nua->nua_signal)) { nua_event_data_t *e = su_msg_data(nh->nh_nua->nua_signal)->ee_data; if (tags == e->e_tags && event == e->e_event) { cr->cr_auto = 0; if (tags) { nua_move_signal(cr->cr_signal, nh->nh_nua->nua_signal); if (cr->cr_signal) { /* Steal reference from signal */ cr->cr_owner = e->e_nh, e->e_nh = NULL; cr->cr_tags = tags; } } } } if (cr->cr_owner == NULL) cr->cr_owner = nua_handle_ref(nh); if (tags && cr->cr_tags == NULL) cr->cr_tags = tl_tlist(nh->nh_home, TAG_NEXT(tags)); if (nua_client_request_queue(cr)) return 0; return nua_client_init_request(cr);}int nua_client_tcreate(nua_handle_t *nh, int event, nua_client_methods_t const *methods, tag_type_t tag, tag_value_t value, ...){ int retval; ta_list ta; ta_start(ta, tag, value); retval = nua_client_create(nh, event, methods, ta_args(ta)); ta_end(ta); return retval;}int nua_client_request_queue(nua_client_request_t *cr){ int queued = 0; nua_client_request_t **queue = &cr->cr_owner->nh_ds->ds_cr; assert(cr->cr_prev == NULL && cr->cr_next == NULL); cr->cr_status = 0; if (cr->cr_method != sip_method_invite && cr->cr_method != sip_method_cancel) { while (*queue) { if ((*queue)->cr_method == sip_method_invite || (*queue)->cr_method == sip_method_cancel) break; queue = &(*queue)->cr_next; queued = 1; } } else { while (*queue) { queue = &(*queue)->cr_next; if (cr->cr_method == sip_method_invite) queued = 1; } } if ((cr->cr_next = *queue)) cr->cr_next->cr_prev = &cr->cr_next; cr->cr_prev = queue, *queue = cr; return queued;}nua_client_request_t *nua_client_request_remove(nua_client_request_t *cr){ if (cr->cr_prev) if ((*cr->cr_prev = cr->cr_next)) cr->cr_next->cr_prev = cr->cr_prev; cr->cr_prev = NULL, cr->cr_next = NULL; return cr;}void nua_client_request_complete(nua_client_request_t *cr){ nua_client_request_remove(cr); if (cr && cr->cr_methods->crm_complete) cr->cr_methods->crm_complete(cr);}void nua_client_request_destroy(nua_client_request_t *cr){ nua_handle_t *nh; if (cr == NULL) return; nua_client_request_complete(cr); nh = cr->cr_owner; nua_destroy_signal(cr->cr_signal); nua_client_bind(cr, NULL); if (cr->cr_msg) msg_destroy(cr->cr_msg); cr->cr_msg = NULL, cr->cr_sip = NULL; if (cr->cr_orq) nta_outgoing_destroy(cr->cr_orq); cr->cr_orq = NULL; if (cr->cr_timer) su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL; if (cr->cr_target) su_free(nh->nh_home, cr->cr_target); su_free(nh->nh_home, cr); nua_handle_unref(nh);}/** Bind client request to a dialog usage */ int nua_client_bind(nua_client_request_t *cr, nua_dialog_usage_t *du){ assert(cr); if (cr == NULL) return -1; if (du == NULL) { if (cr->cr_usage && cr->cr_usage->du_cr == cr) cr->cr_usage->du_cr = NULL; cr->cr_usage = NULL; return 0; } if (du->du_cr && cr != du->du_cr) { /* This should never happen (but it does): assert(!nua_client_is_queued(du->du_cr)); */ if (nua_client_is_queued(du->du_cr)) return -1; if (nua_client_is_reporting(du->du_cr)) { du->du_cr->cr_usage = NULL; du->du_cr = NULL; } else nua_client_request_destroy(du->du_cr); } du->du_cr = cr, cr->cr_usage = du; return 0;}/**Initialize client request for sending. * * This function is called only first time the request is sent. * * @retval 0 if request is pending * @retval >=1 if error event has been sent */int nua_client_init_request(nua_client_request_t *cr){ nua_handle_t *nh = cr->cr_owner; nua_t *nua = nh->nh_nua; nua_dialog_state_t *ds = nh->nh_ds; msg_t *msg = NULL; sip_t *sip; url_string_t const *url = NULL; tagi_t const *t; int has_contact = 0; int error = 0; if (!cr->cr_method_name) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL); if (cr->cr_msg) return nua_client_request_try(cr); cr->cr_answer_recv = 0, cr->cr_offer_sent = 0; cr->cr_offer_recv = 0, cr->cr_answer_sent = 0; cr->cr_terminated = 0, cr->cr_graceful = 0; nua_stack_init_handle(nua, nh, cr->cr_tags); if (cr->cr_method == sip_method_cancel) { if (cr->cr_methods->crm_init) { error = cr->cr_methods->crm_init(cr, NULL, NULL, cr->cr_tags); if (error) return error; } if (cr->cr_methods->crm_send) return cr->cr_methods->crm_send(cr, NULL, NULL, cr->cr_tags); else return nua_base_client_request(cr, NULL, NULL, cr->cr_tags); } if (!cr->cr_methods->crm_template || cr->cr_methods->crm_template(cr, &msg, cr->cr_tags) == 0) msg = nua_client_request_template(cr); sip = sip_object(msg); if (!sip) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (nh->nh_tags) { for (t = nh->nh_tags; t; t = t_next(t)) { if (t->t_tag == siptag_contact || t->t_tag == siptag_contact_str) has_contact = 1; else if (t->t_tag == nutag_url) url = (url_string_t const *)t->t_value; } } /**@par Populating SIP Request Message with Tagged Arguments * * The tagged arguments can be used to pass values for any SIP headers * to the stack. When the INVITE message (or any other SIP message) is * created, the tagged values saved with nua_handle() are used first, * next the tagged values given with the operation (nua_invite()) are * added. * * When multiple tags for the same header are specified, the behaviour * depends on the header type. If only a single header field can be * included in a SIP message, the latest non-NULL value is used, e.g., * @Subject. However, if the SIP header can consist of multiple lines or * header fields separated by comma, e.g., @Accept, all the tagged * values are concatenated. * * However, if a tag value is #SIP_NONE (-1 casted as a void pointer), * the values from previous tags are ignored. */ for (t = cr->cr_tags; t; t = t_next(t)) { if (t->t_tag == siptag_contact || t->t_tag == siptag_contact_str) has_contact = 1; else if (t->t_tag == nutag_url) url = (url_string_t const *)t->t_value; else if (t->t_tag == nutag_dialog) { cr->cr_dialog = t->t_value > 1; cr->cr_contactize = t->t_value >= 1; } else if (t->t_tag == nutag_auth && t->t_value) { /* XXX ignoring errors */ if (nh->nh_auth) auc_credentials(&nh->nh_auth, nh->nh_home, (char *)t->t_value); } } if (cr->cr_method == sip_method_register && url == NULL) url = (url_string_t const *)NH_PGET(nh, registrar); if ((t = cr->cr_tags)) { if (sip_add_tagis(msg, sip, &t) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); } /** * Now, the target URI for the request needs to be determined. * * For initial requests, values from tags are used. If NUTAG_URL() is * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given, * it is used as target URI. If neither is given, the complete request * line already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR() * is used. At this point, the target URI is stored in the request line, * together with method name and protocol version ("SIP/2.0"). The * initial dialog information is also created: @CallID, @CSeq headers * are generated, if they do not exist, and a tag is added to the @From * header. */ if (!ds->ds_leg) { if (ds->ds_remote_tag && ds->ds_remote_tag[0] && sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (sip->sip_from == NULL && sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (sip->sip_to == NULL && cr->cr_method == sip_method_register && sip_add_dup_as(msg, sip, sip_to_class, (sip_header_t *)sip->sip_from) < 0) { return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); } if (cr->cr_dialog) { ds->ds_leg = nta_leg_tcreate(nua->nua_nta, nua_stack_process_request, nh, SIPTAG_CALL_ID(sip->sip_call_id), SIPTAG_FROM(sip->sip_from), SIPTAG_TO(sip->sip_to), SIPTAG_CSEQ(sip->sip_cseq), TAG_END()); if (!ds->ds_leg) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (!sip->sip_from->a_tag && sip_from_tag(msg_home(msg), sip->sip_from, nta_leg_tag(ds->ds_leg, NULL)) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); } } else { if (ds->ds_route) url = NULL; } if (url && nua_client_set_target(cr, (url_t *)url) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); cr->cr_has_contact = has_contact; if (cr->cr_methods->crm_init) { error = cr->cr_methods->crm_init(cr, msg, sip, cr->cr_tags); if (error < -1) msg = NULL; if (error < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (error != 0) return error; } cr->cr_msg = msg; cr->cr_sip = sip; return nua_client_request_try(cr);}msg_t *nua_client_request_template(nua_client_request_t *cr){ nua_handle_t *nh = cr->cr_owner; nua_t *nua = nh->nh_nua; nua_dialog_state_t *ds = nh->nh_ds; msg_t *msg = nta_msg_create(nua->nua_nta, 0); sip_t *sip = sip_object(msg); if (!sip) return NULL; if (nh->nh_tags) { tagi_t const *t = nh->nh_tags; /* Use the From header from the dialog. From is always first tag in the handle */ if (ds->ds_leg && t->t_tag == siptag_from) t++; /* When the INVITE message (or any other SIP message) is * created, the tagged values saved with nua_handle() are used first. */ sip_add_tagis(msg, sip, &t); } return msg;}/** Restart the request message. * * A restarted request has not been completed successfully. * * @retval 0 if request is pending * @retval >=1 if error event has been sent
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -