| From 0ed41eb5fd0e4192e1b7dc374f819d17aef3e805 Mon Sep 17 00:00:00 2001 |
| From: George Joseph <gtjoseph@users.noreply.github.com> |
| Date: Tue, 21 Dec 2021 19:32:22 -0700 |
| Subject: [PATCH] sip_inv: Additional multipart support (#2919) (#2920) |
| |
| --- |
| pjsip/include/pjsip-ua/sip_inv.h | 108 ++++++++++- |
| pjsip/src/pjsip-ua/sip_inv.c | 240 ++++++++++++++++++++----- |
| pjsip/src/test/inv_offer_answer_test.c | 103 ++++++++++- |
| 3 files changed, 394 insertions(+), 57 deletions(-) |
| |
| --- a/pjsip/include/pjsip-ua/sip_inv.h |
| +++ b/pjsip/include/pjsip-ua/sip_inv.h |
| @@ -451,11 +451,11 @@ struct pjsip_inv_session |
| |
| |
| /** |
| - * This structure represents SDP information in a pjsip_rx_data. Application |
| - * retrieve this information by calling #pjsip_rdata_get_sdp_info(). This |
| + * This structure represents SDP information in a pjsip_(rx|tx)_data. Application |
| + * retrieve this information by calling #pjsip_get_sdp_info(). This |
| * mechanism supports multipart message body. |
| */ |
| -typedef struct pjsip_rdata_sdp_info |
| +typedef struct pjsip_sdp_info |
| { |
| /** |
| * Pointer and length of the text body in the incoming message. If |
| @@ -475,7 +475,15 @@ typedef struct pjsip_rdata_sdp_info |
| */ |
| pjmedia_sdp_session *sdp; |
| |
| -} pjsip_rdata_sdp_info; |
| +} pjsip_sdp_info; |
| + |
| +/** |
| + * For backwards compatibility and completeness, |
| + * pjsip_rdata_sdp_info and pjsip_tdata_sdp_info |
| + * are typedef'd to pjsip_sdp_info. |
| + */ |
| +typedef pjsip_sdp_info pjsip_rdata_sdp_info; |
| +typedef pjsip_sdp_info pjsip_tdata_sdp_info; |
| |
| |
| /** |
| @@ -1046,6 +1054,44 @@ PJ_DECL(pj_status_t) pjsip_create_sdp_bo |
| pjsip_msg_body **p_body); |
| |
| /** |
| + * This is a utility function to create a multipart body with the |
| + * SIP body as the first part. |
| + * |
| + * @param pool Pool to allocate memory. |
| + * @param sdp SDP session to be put in the SIP message body. |
| + * @param p_body Pointer to receive SIP message body containing |
| + * the SDP session. |
| + * |
| + * @return PJ_SUCCESS on success. |
| + */ |
| +PJ_DECL(pj_status_t) pjsip_create_multipart_sdp_body( pj_pool_t *pool, |
| + pjmedia_sdp_session *sdp, |
| + pjsip_msg_body **p_body); |
| + |
| +/** |
| + * Retrieve SDP information from a message body. Application should |
| + * prefer to use this function rather than parsing the SDP manually since |
| + * this function supports multipart message body. |
| + * |
| + * This function will only parse the SDP once, the first time it is called |
| + * on the same message. Subsequent call on the same message will just pick |
| + * up the already parsed SDP from the message. |
| + * |
| + * @param pool Pool to allocate memory. |
| + * @param body The message body. |
| + * @param msg_media_type From the rdata or tdata Content-Type header, if available. |
| + * If NULL, the content_type from the body will be used. |
| + * @param search_media_type The media type to search for. |
| + * If NULL, "application/sdp" will be used. |
| + * |
| + * @return The SDP info. |
| + */ |
| +PJ_DECL(pjsip_sdp_info*) pjsip_get_sdp_info(pj_pool_t *pool, |
| + pjsip_msg_body *body, |
| + pjsip_media_type *msg_media_type, |
| + const pjsip_media_type *search_media_type); |
| + |
| +/** |
| * Retrieve SDP information from an incoming message. Application should |
| * prefer to use this function rather than parsing the SDP manually since |
| * this function supports multipart message body. |
| @@ -1061,6 +1107,60 @@ PJ_DECL(pj_status_t) pjsip_create_sdp_bo |
| PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata); |
| |
| |
| +/** |
| + * Retrieve SDP information from an incoming message. Application should |
| + * prefer to use this function rather than parsing the SDP manually since |
| + * this function supports multipart message body. |
| + * |
| + * This function will only parse the SDP once, the first time it is called |
| + * on the same message. Subsequent call on the same message will just pick |
| + * up the already parsed SDP from the message. |
| + * |
| + * @param rdata The incoming message. |
| + * @param search_media_type The SDP media type to search for. |
| + * If NULL, "application/sdp" will be used. |
| + * |
| + * @return The SDP info. |
| + */ |
| +PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info2( |
| + pjsip_rx_data *rdata, |
| + const pjsip_media_type *search_media_type); |
| + |
| +/** |
| + * Retrieve SDP information from an outgoing message. Application should |
| + * prefer to use this function rather than parsing the SDP manually since |
| + * this function supports multipart message body. |
| + * |
| + * This function will only parse the SDP once, the first time it is called |
| + * on the same message. Subsequent call on the same message will just pick |
| + * up the already parsed SDP from the message. |
| + * |
| + * @param tdata The outgoing message. |
| + * |
| + * @return The SDP info. |
| + */ |
| +PJ_DECL(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info(pjsip_tx_data *tdata); |
| + |
| +/** |
| + * Retrieve SDP information from an outgoing message. Application should |
| + * prefer to use this function rather than parsing the SDP manually since |
| + * this function supports multipart message body. |
| + * |
| + * This function will only parse the SDP once, the first time it is called |
| + * on the same message. Subsequent call on the same message will just pick |
| + * up the already parsed SDP from the message. |
| + * |
| + * @param tdata The outgoing message. |
| + * @param search_media_type The SDP media type to search for. |
| + * If NULL, "application/sdp" will be used. |
| + * |
| + * @return The SDP info. |
| + */ |
| +PJ_DECL(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info2( |
| + pjsip_tx_data *tdata, |
| + const pjsip_media_type *search_media_type); |
| + |
| + |
| PJ_END_DECL |
| |
| /** |
| --- a/pjsip/src/pjsip-ua/sip_inv.c |
| +++ b/pjsip/src/pjsip-ua/sip_inv.c |
| @@ -118,6 +118,8 @@ static pj_status_t handle_timer_response |
| static pj_bool_t inv_check_secure_dlg(pjsip_inv_session *inv, |
| pjsip_event *e); |
| |
| +static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len); |
| + |
| static void (*inv_state_handler[])( pjsip_inv_session *inv, pjsip_event *e) = |
| { |
| &inv_on_state_null, |
| @@ -956,66 +958,170 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uac |
| return PJ_SUCCESS; |
| } |
| |
| -PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata) |
| -{ |
| - pjsip_rdata_sdp_info *sdp_info; |
| - pjsip_msg_body *body = rdata->msg_info.msg->body; |
| - pjsip_ctype_hdr *ctype_hdr = rdata->msg_info.ctype; |
| - pjsip_media_type app_sdp; |
| +PJ_DEF(pjsip_sdp_info*) pjsip_get_sdp_info(pj_pool_t *pool, |
| + pjsip_msg_body *body, |
| + pjsip_media_type *msg_media_type, |
| + const pjsip_media_type *search_media_type) |
| +{ |
| + pjsip_sdp_info *sdp_info; |
| + pjsip_media_type search_type; |
| + pjsip_media_type multipart_mixed; |
| + pjsip_media_type multipart_alternative; |
| + pjsip_media_type *msg_type; |
| + pj_status_t status; |
| |
| - sdp_info = (pjsip_rdata_sdp_info*) |
| - rdata->endpt_info.mod_data[mod_inv.mod.id]; |
| - if (sdp_info) |
| - return sdp_info; |
| + sdp_info = PJ_POOL_ZALLOC_T(pool, |
| + pjsip_sdp_info); |
| |
| - sdp_info = PJ_POOL_ZALLOC_T(rdata->tp_info.pool, |
| - pjsip_rdata_sdp_info); |
| PJ_ASSERT_RETURN(mod_inv.mod.id >= 0, sdp_info); |
| - rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info; |
| |
| - pjsip_media_type_init2(&app_sdp, "application", "sdp"); |
| + if (!body) { |
| + return sdp_info; |
| + } |
| |
| - if (body && ctype_hdr && |
| - pj_stricmp(&ctype_hdr->media.type, &app_sdp.type)==0 && |
| - pj_stricmp(&ctype_hdr->media.subtype, &app_sdp.subtype)==0) |
| + if (msg_media_type) { |
| + msg_type = msg_media_type; |
| + } else { |
| + if (body->content_type.type.slen == 0) { |
| + return sdp_info; |
| + } |
| + msg_type = &body->content_type; |
| + } |
| + |
| + if (!search_media_type) { |
| + pjsip_media_type_init2(&search_type, "application", "sdp"); |
| + } else { |
| + pj_memcpy(&search_type, search_media_type, sizeof(search_type)); |
| + } |
| + |
| + pjsip_media_type_init2(&multipart_mixed, "multipart", "mixed"); |
| + pjsip_media_type_init2(&multipart_alternative, "multipart", "alternative"); |
| + |
| + if (pjsip_media_type_cmp(msg_type, &search_type, PJ_FALSE) == 0) |
| { |
| - sdp_info->body.ptr = (char*)body->data; |
| - sdp_info->body.slen = body->len; |
| - } else if (body && ctype_hdr && |
| - pj_stricmp2(&ctype_hdr->media.type, "multipart")==0 && |
| - (pj_stricmp2(&ctype_hdr->media.subtype, "mixed")==0 || |
| - pj_stricmp2(&ctype_hdr->media.subtype, "alternative")==0)) |
| + /* |
| + * If the print_body function is print_sdp, we know that |
| + * body->data is a pjmedia_sdp_session object and came from |
| + * a tx_data. If not, it's the text representation of the |
| + * sdp from an rx_data. |
| + */ |
| + if (body->print_body == print_sdp) { |
| + sdp_info->sdp = body->data; |
| + } else { |
| + sdp_info->body.ptr = (char*)body->data; |
| + sdp_info->body.slen = body->len; |
| + } |
| + } else if (pjsip_media_type_cmp(&multipart_mixed, msg_type, PJ_FALSE) == 0 || |
| + pjsip_media_type_cmp(&multipart_alternative, msg_type, PJ_FALSE) == 0) |
| { |
| - pjsip_multipart_part *part; |
| + pjsip_multipart_part *part; |
| + part = pjsip_multipart_find_part(body, &search_type, NULL); |
| + if (part) { |
| + if (part->body->print_body == print_sdp) { |
| + sdp_info->sdp = part->body->data; |
| + } else { |
| + sdp_info->body.ptr = (char*)part->body->data; |
| + sdp_info->body.slen = part->body->len; |
| + } |
| + } |
| + } |
| |
| - part = pjsip_multipart_find_part(body, &app_sdp, NULL); |
| - if (part) { |
| - sdp_info->body.ptr = (char*)part->body->data; |
| - sdp_info->body.slen = part->body->len; |
| - } |
| + /* |
| + * If the body was already a pjmedia_sdp_session, we can just |
| + * return it. If not and there wasn't a text representation |
| + * of the sdp either, we can also just return. |
| + */ |
| + if (sdp_info->sdp || !sdp_info->body.ptr) { |
| + return sdp_info; |
| } |
| |
| - if (sdp_info->body.ptr) { |
| - pj_status_t status; |
| - status = pjmedia_sdp_parse(rdata->tp_info.pool, |
| - sdp_info->body.ptr, |
| - sdp_info->body.slen, |
| - &sdp_info->sdp); |
| - if (status == PJ_SUCCESS) |
| - status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE); |
| + /* |
| + * If the body was the text representation of teh SDP, we need |
| + * to parse it to create a pjmedia_sdp_session object. |
| + */ |
| + status = pjmedia_sdp_parse(pool, |
| + sdp_info->body.ptr, |
| + sdp_info->body.slen, |
| + &sdp_info->sdp); |
| + if (status == PJ_SUCCESS) |
| + status = pjmedia_sdp_validate2(sdp_info->sdp, PJ_FALSE); |
| |
| - if (status != PJ_SUCCESS) { |
| - sdp_info->sdp = NULL; |
| - PJ_PERROR(1,(THIS_FILE, status, |
| - "Error parsing/validating SDP body")); |
| - } |
| + if (status != PJ_SUCCESS) { |
| + sdp_info->sdp = NULL; |
| + PJ_PERROR(1, (THIS_FILE, status, |
| + "Error parsing/validating SDP body")); |
| + } |
| + |
| + sdp_info->sdp_err = status; |
| + |
| + return sdp_info; |
| +} |
| + |
| +PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info2( |
| + pjsip_rx_data *rdata, |
| + const pjsip_media_type *search_media_type) |
| +{ |
| + pjsip_media_type *msg_media_type = NULL; |
| + pjsip_rdata_sdp_info *sdp_info; |
| |
| - sdp_info->sdp_err = status; |
| + if (rdata->endpt_info.mod_data[mod_inv.mod.id]) { |
| + return (pjsip_rdata_sdp_info *)rdata->endpt_info.mod_data[mod_inv.mod.id]; |
| + } |
| + |
| + /* |
| + * rdata should have a Content-Type header at this point but we'll |
| + * make sure. |
| + */ |
| + if (rdata->msg_info.ctype) { |
| + msg_media_type = &rdata->msg_info.ctype->media; |
| } |
| + sdp_info = pjsip_get_sdp_info(rdata->tp_info.pool, |
| + rdata->msg_info.msg->body, |
| + msg_media_type, |
| + search_media_type); |
| + rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info; |
| |
| return sdp_info; |
| } |
| |
| +PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata) |
| +{ |
| + return pjsip_rdata_get_sdp_info2(rdata, NULL); |
| +} |
| + |
| +PJ_DEF(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info2( |
| + pjsip_tx_data *tdata, |
| + const pjsip_media_type *search_media_type) |
| +{ |
| + pjsip_ctype_hdr *ctype_hdr = NULL; |
| + pjsip_media_type *msg_media_type = NULL; |
| + pjsip_tdata_sdp_info *sdp_info; |
| + |
| + if (tdata->mod_data[mod_inv.mod.id]) { |
| + return (pjsip_tdata_sdp_info *)tdata->mod_data[mod_inv.mod.id]; |
| + } |
| + /* |
| + * tdata won't usually have a Content-Type header at this point |
| + * but we'll check just the same, |
| + */ |
| + ctype_hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTENT_TYPE, NULL); |
| + if (ctype_hdr) { |
| + msg_media_type = &ctype_hdr->media; |
| + } |
| + |
| + sdp_info = pjsip_get_sdp_info(tdata->pool, |
| + tdata->msg->body, |
| + msg_media_type, |
| + search_media_type); |
| + tdata->mod_data[mod_inv.mod.id] = sdp_info; |
| + |
| + return sdp_info; |
| +} |
| + |
| +PJ_DEF(pjsip_tdata_sdp_info*) pjsip_tdata_get_sdp_info(pjsip_tx_data *tdata) |
| +{ |
| + return pjsip_tdata_get_sdp_info2(tdata, NULL); |
| +} |
| |
| /* |
| * Verify incoming INVITE request. |
| @@ -1740,13 +1846,55 @@ PJ_DEF(pj_status_t) pjsip_create_sdp_bod |
| return PJ_SUCCESS; |
| } |
| |
| +static pjsip_multipart_part* create_sdp_part(pj_pool_t *pool, pjmedia_sdp_session *sdp) |
| +{ |
| + pjsip_multipart_part *sdp_part; |
| + pjsip_media_type media_type; |
| + |
| + pjsip_media_type_init2(&media_type, "application", "sdp"); |
| + |
| + sdp_part = pjsip_multipart_create_part(pool); |
| + PJ_ASSERT_RETURN(sdp_part != NULL, NULL); |
| + |
| + sdp_part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); |
| + PJ_ASSERT_RETURN(sdp_part->body != NULL, NULL); |
| + |
| + pjsip_media_type_cp(pool, &sdp_part->body->content_type, &media_type); |
| + |
| + sdp_part->body->data = sdp; |
| + sdp_part->body->clone_data = clone_sdp; |
| + sdp_part->body->print_body = print_sdp; |
| + |
| + return sdp_part; |
| +} |
| + |
| +PJ_DEF(pj_status_t) pjsip_create_multipart_sdp_body(pj_pool_t *pool, |
| + pjmedia_sdp_session *sdp, |
| + pjsip_msg_body **p_body) |
| +{ |
| + pjsip_media_type media_type; |
| + pjsip_msg_body *multipart; |
| + pjsip_multipart_part *sdp_part; |
| + |
| + pjsip_media_type_init2(&media_type, "multipart", "mixed"); |
| + multipart = pjsip_multipart_create(pool, &media_type, NULL); |
| + PJ_ASSERT_RETURN(multipart != NULL, PJ_ENOMEM); |
| + |
| + sdp_part = create_sdp_part(pool, sdp); |
| + PJ_ASSERT_RETURN(sdp_part != NULL, PJ_ENOMEM); |
| + pjsip_multipart_add_part(pool, multipart, sdp_part); |
| + *p_body = multipart; |
| + |
| + return PJ_SUCCESS; |
| +} |
| + |
| static pjsip_msg_body *create_sdp_body(pj_pool_t *pool, |
| const pjmedia_sdp_session *c_sdp) |
| { |
| pjsip_msg_body *body; |
| pj_status_t status; |
| |
| - status = pjsip_create_sdp_body(pool, |
| + status = pjsip_create_sdp_body(pool, |
| pjmedia_sdp_session_clone(pool, c_sdp), |
| &body); |
| |
| @@ -2069,6 +2217,7 @@ static pj_status_t inv_check_sdp_in_inco |
| ) |
| ) |
| { |
| + pjsip_sdp_info *tdata_sdp_info; |
| const pjmedia_sdp_session *reoffer_sdp = NULL; |
| |
| PJ_LOG(4,(inv->obj_name, "Received %s response " |
| @@ -2077,14 +2226,15 @@ static pj_status_t inv_check_sdp_in_inco |
| (st_code/10==18? "early" : "final" ))); |
| |
| /* Retrieve original SDP offer from INVITE request */ |
| - reoffer_sdp = (const pjmedia_sdp_session*) |
| - tsx->last_tx->msg->body->data; |
| + tdata_sdp_info = pjsip_tdata_get_sdp_info(tsx->last_tx); |
| + reoffer_sdp = tdata_sdp_info->sdp; |
| |
| /* Feed the original offer to negotiator */ |
| status = pjmedia_sdp_neg_modify_local_offer2(inv->pool_prov, |
| inv->neg, |
| inv->sdp_neg_flags, |
| reoffer_sdp); |
| + |
| if (status != PJ_SUCCESS) { |
| PJ_LOG(1,(inv->obj_name, "Error updating local offer for " |
| "forked 2xx/18x response (err=%d)", status)); |
| --- a/pjsip/src/test/inv_offer_answer_test.c |
| +++ b/pjsip/src/test/inv_offer_answer_test.c |
| @@ -137,6 +137,7 @@ typedef struct inv_test_param_t |
| pj_bool_t need_established; |
| unsigned count; |
| oa_t oa[4]; |
| + pj_bool_t multipart_body; |
| } inv_test_param_t; |
| |
| typedef struct inv_test_t |
| @@ -257,6 +258,17 @@ static void on_media_update(pjsip_inv_se |
| } |
| } |
| |
| + /* Special handling for standard offer/answer */ |
| + if (inv_test.param.count == 1 && |
| + inv_test.param.oa[0] == OFFERER_UAC && |
| + inv_test.param.need_established) |
| + { |
| + jobs[job_cnt].type = ESTABLISH_CALL; |
| + jobs[job_cnt].who = PJSIP_ROLE_UAS; |
| + job_cnt++; |
| + TRACE_((THIS_FILE, " C+++")); |
| + } |
| + |
| pj_assert(job_cnt <= PJ_ARRAY_SIZE(jobs)); |
| } |
| } |
| @@ -333,6 +345,15 @@ static pj_bool_t on_rx_request(pjsip_rx_ |
| NULL, &tdata); |
| pj_assert(status == PJ_SUCCESS); |
| |
| + /* Use multipart body, if configured */ |
| + if (sdp && inv_test.param.multipart_body) { |
| + status = pjsip_create_multipart_sdp_body( |
| + tdata->pool, |
| + pjmedia_sdp_session_clone(tdata->pool, sdp), |
| + &tdata->msg->body); |
| + } |
| + pj_assert(status == PJ_SUCCESS); |
| + |
| status = pjsip_inv_send_msg(inv_test.uas, tdata); |
| pj_assert(status == PJ_SUCCESS); |
| |
| @@ -426,6 +447,7 @@ static int perform_test(inv_test_param_t |
| sdp = NULL; |
| |
| status = pjsip_inv_create_uac(dlg, sdp, inv_test.param.inv_option, &inv_test.uac); |
| + //inv_test.uac->create_multipart = param->multipart_body; |
| PJ_ASSERT_RETURN(status==PJ_SUCCESS, -20); |
| |
| TRACE_((THIS_FILE, " Sending INVITE %s offer", (sdp ? "with" : "without"))); |
| @@ -436,8 +458,17 @@ static int perform_test(inv_test_param_t |
| status = pjsip_inv_invite(inv_test.uac, &tdata); |
| PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30); |
| |
| + /* Use multipart body, if configured */ |
| + if (sdp && param->multipart_body) { |
| + status = pjsip_create_multipart_sdp_body( |
| + tdata->pool, |
| + pjmedia_sdp_session_clone(tdata->pool, sdp), |
| + &tdata->msg->body); |
| + } |
| + PJ_ASSERT_RETURN(status==PJ_SUCCESS, -40); |
| + |
| status = pjsip_inv_send_msg(inv_test.uac, tdata); |
| - PJ_ASSERT_RETURN(status==PJ_SUCCESS, -30); |
| + PJ_ASSERT_RETURN(status==PJ_SUCCESS, -50); |
| |
| /* |
| * Wait until test completes |
| @@ -525,13 +556,14 @@ static inv_test_param_t test_params[] = |
| 200/INVITE (answer) <-- |
| ACK --> |
| */ |
| -#if 0 |
| +#if 1 |
| { |
| "Standard INVITE with offer", |
| 0, |
| PJ_TRUE, |
| 1, |
| - { OFFERER_UAC } |
| + { OFFERER_UAC }, |
| + PJ_FALSE |
| }, |
| |
| { |
| @@ -539,7 +571,25 @@ static inv_test_param_t test_params[] = |
| PJSIP_INV_REQUIRE_100REL, |
| PJ_TRUE, |
| 1, |
| - { OFFERER_UAC } |
| + { OFFERER_UAC }, |
| + PJ_FALSE |
| + }, |
| + { |
| + "Standard INVITE with offer, with Multipart", |
| + 0, |
| + PJ_TRUE, |
| + 1, |
| + { OFFERER_UAC }, |
| + PJ_TRUE |
| + }, |
| + |
| + { |
| + "Standard INVITE with offer, with 100rel, with Multipart", |
| + PJSIP_INV_REQUIRE_100REL, |
| + PJ_TRUE, |
| + 1, |
| + { OFFERER_UAC }, |
| + PJ_TRUE |
| }, |
| #endif |
| |
| @@ -555,7 +605,8 @@ static inv_test_param_t test_params[] = |
| 0, |
| PJ_TRUE, |
| 1, |
| - { OFFERER_UAS } |
| + { OFFERER_UAS }, |
| + PJ_FALSE |
| }, |
| |
| { |
| @@ -563,7 +614,25 @@ static inv_test_param_t test_params[] = |
| PJSIP_INV_REQUIRE_100REL, |
| PJ_TRUE, |
| 1, |
| - { OFFERER_UAS } |
| + { OFFERER_UAS }, |
| + PJ_FALSE |
| + }, |
| + { |
| + "INVITE with no offer, with Multipart", |
| + 0, |
| + PJ_TRUE, |
| + 1, |
| + { OFFERER_UAS }, |
| + PJ_TRUE |
| + }, |
| + |
| + { |
| + "INVITE with no offer, with 100rel, with Multipart", |
| + PJSIP_INV_REQUIRE_100REL, |
| + PJ_TRUE, |
| + 1, |
| + { OFFERER_UAS }, |
| + PJ_TRUE |
| }, |
| #endif |
| |
| @@ -584,14 +653,24 @@ static inv_test_param_t test_params[] = |
| 0, |
| PJ_TRUE, |
| 2, |
| - { OFFERER_UAC, OFFERER_UAC } |
| + { OFFERER_UAC, OFFERER_UAC }, |
| + PJ_FALSE |
| + }, |
| + { |
| + "INVITE and UPDATE by UAC, with Multipart", |
| + 0, |
| + PJ_TRUE, |
| + 2, |
| + { OFFERER_UAC, OFFERER_UAC }, |
| + PJ_TRUE |
| }, |
| { |
| "INVITE and UPDATE by UAC, with 100rel", |
| PJSIP_INV_REQUIRE_100REL, |
| PJ_TRUE, |
| 2, |
| - { OFFERER_UAC, OFFERER_UAC } |
| + { OFFERER_UAC, OFFERER_UAC }, |
| + PJ_FALSE |
| }, |
| #endif |
| |
| @@ -617,6 +696,14 @@ static inv_test_param_t test_params[] = |
| 4, |
| { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS } |
| }, |
| + { |
| + "INVITE and many UPDATE by UAC and UAS, with Multipart", |
| + 0, |
| + PJ_TRUE, |
| + 4, |
| + { OFFERER_UAC, OFFERER_UAS, OFFERER_UAC, OFFERER_UAS }, |
| + PJ_TRUE |
| + }, |
| |
| }; |
| |