From 0cafcf8a9bbad37cb4d2b4f55400a54bba81db5f Mon Sep 17 00:00:00 2001 From: Seven Du Date: Mon, 6 Jan 2020 20:25:44 +0800 Subject: [PATCH] [core] Add global/leg variable to originate: group_confirm_timeout group_confirm_timeout is a value in seconds for the maximum time to wait for group confirmation to finish. This variable replaces group_confirm_cancel_timeout which has been deprecated. Set group_confirm_timeout=0 to disable leg_timeout during group confirm. --- src/switch_ivr_originate.c | 50 +++++++--- tests/unit/switch_ivr_originate.c | 149 +++++++++++++++++++++++++++++- 2 files changed, 185 insertions(+), 14 deletions(-) diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index 6adaba174e..170a5ed1b8 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -129,6 +129,7 @@ typedef struct { char *file; char *error_file; int confirm_timeout; + int confirm_read_timeout; char key[80]; uint8_t early_ok; uint8_t ring_ready; @@ -169,7 +170,7 @@ struct key_collect { char *key; char *file; char *error_file; - int confirm_timeout; + int confirm_read_timeout; switch_core_session_t *session; }; @@ -244,7 +245,7 @@ static void *SWITCH_THREAD_FUNC collect_thread_run(switch_thread_t *thread, void status = switch_ivr_read(collect->session, (uint32_t)len, (uint32_t)len, - file, NULL, buf, sizeof(buf), collect->confirm_timeout, NULL, 0); + file, NULL, buf, sizeof(buf), collect->confirm_read_timeout, NULL, 0); if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK && status != SWITCH_STATUS_TOO_SMALL) { @@ -450,7 +451,7 @@ static void inherit_codec(switch_channel_t *caller_channel, switch_core_session_ } } -static uint8_t check_channel_status(originate_global_t *oglobals, uint32_t len, switch_call_cause_t *force_reason) +static uint8_t check_channel_status(originate_global_t *oglobals, uint32_t len, switch_call_cause_t *force_reason, time_t start) { uint32_t i; @@ -769,8 +770,26 @@ static uint8_t check_channel_status(originate_global_t *oglobals, uint32_t len, if (!zstr(oglobals->key) || !zstr(group_confirm_key)) { struct key_collect *collect; + const char *group_confirm_timeout = switch_channel_get_variable(oglobals->originate_status[i].peer_channel, "group_confirm_timeout"); + int extend_timeout = 0; + int cancel_timeout = 0; + if (group_confirm_timeout && switch_is_number(group_confirm_timeout)) { + // leg var overrides global group_confirm_timeout + extend_timeout = atoi(group_confirm_timeout); + if (extend_timeout == 0) { + cancel_timeout = 1; + } + } else { + extend_timeout = oglobals->confirm_timeout; + } - if (oglobals->cancel_timeout == SWITCH_TRUE) { + if (extend_timeout > 0) { + /* extend timeout for this leg only */ + time_t elapsed = switch_epoch_time_now(NULL) - start; + oglobals->originate_status[i].per_channel_progress_timelimit_sec = elapsed + extend_timeout; + oglobals->originate_status[i].per_channel_timelimit_sec = elapsed + extend_timeout; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "elapsed %" SWITCH_TIME_T_FMT ", timelimit extended to %u\n", elapsed, oglobals->originate_status[i].per_channel_timelimit_sec); + } else if (oglobals->cancel_timeout || cancel_timeout) { /* cancel timeout for this leg only */ oglobals->originate_status[i].per_channel_progress_timelimit_sec = 0; oglobals->originate_status[i].per_channel_timelimit_sec = 0; @@ -793,10 +812,10 @@ static uint8_t check_channel_status(originate_global_t *oglobals, uint32_t len, collect->error_file = switch_core_session_strdup(oglobals->originate_status[i].peer_session, oglobals->error_file); } - if (oglobals->confirm_timeout) { - collect->confirm_timeout = oglobals->confirm_timeout; + if (oglobals->confirm_read_timeout) { + collect->confirm_read_timeout = oglobals->confirm_read_timeout; } else { - collect->confirm_timeout = 5000; + collect->confirm_read_timeout = 5000; } switch_channel_audio_sync(oglobals->originate_status[i].peer_channel); @@ -2304,6 +2323,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess ok = 1; } else if (!strcasecmp((char *) hi->name, "group_confirm_cancel_timeout")) { ok = 1; + } else if (!strcasecmp((char *) hi->name, "group_confirm_timeout")) { + ok = 1; } else if (!strcasecmp((char *) hi->name, "forked_dial")) { ok = 1; } else if (!strcasecmp((char *) hi->name, "fail_on_single_reject")) { @@ -2381,7 +2402,15 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } #endif - if (switch_true(switch_event_get_header(var_event, "group_confirm_cancel_timeout"))) { + if ((var = switch_event_get_header(var_event, "group_confirm_timeout"))) { + // has precedence over group_confirm_cancel_timeout + if (switch_is_number(var)) { + oglobals.confirm_timeout = atoi(var); + if (oglobals.confirm_timeout == 0) { + oglobals.cancel_timeout = SWITCH_TRUE; + } + } + } else if (switch_true(switch_event_get_header(var_event, "group_confirm_cancel_timeout"))) { oglobals.cancel_timeout = SWITCH_TRUE; } @@ -2401,7 +2430,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess int tmp = atoi(var); if (tmp >= 0) { - oglobals.confirm_timeout = tmp; + oglobals.confirm_read_timeout = tmp; } } @@ -3252,9 +3281,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } while ((!caller_channel || switch_channel_ready(caller_channel) || switch_channel_test_flag(caller_channel, CF_XFER_ZOMBIE)) && - check_channel_status(&oglobals, and_argc, &force_reason)) { + check_channel_status(&oglobals, and_argc, &force_reason, start)) { time_t elapsed = switch_epoch_time_now(NULL) - start; - read_packet = 0; if (cancel_cause && *cancel_cause > 0) { diff --git a/tests/unit/switch_ivr_originate.c b/tests/unit/switch_ivr_originate.c index 983f26d0a7..0b91f3ee16 100644 --- a/tests/unit/switch_ivr_originate.c +++ b/tests/unit/switch_ivr_originate.c @@ -1,6 +1,6 @@ /* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - * Copyright (C) 2005-2018, Anthony Minessale II + * Copyright (C) 2005-2020, Anthony Minessale II * * Version: MPL 1.1 * @@ -461,9 +461,8 @@ FST_CORE_BEGIN("./conf") status = switch_ivr_originate(NULL, &session, &cause, dialstring, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); fst_requires(status == SWITCH_STATUS_SUCCESS); fst_requires(session); - switch_yield(1000000); + switch_yield(2000000); switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_NORMAL_CLEARING); - switch_yield(1000000); switch_core_session_rwunlock(session); switch_event_unbind_callback(loopback_group_confirm_event_handler); fst_check(application_hit == 1); @@ -486,6 +485,150 @@ FST_CORE_BEGIN("./conf") fst_check(application_hit == 1); } FST_SESSION_END() + + FST_TEST_BEGIN(originate_test_group_confirm_leg_timeout_not_finished) + { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + switch_status_t status; + switch_call_cause_t cause; + switch_dial_handle_t *dh; + switch_dial_leg_list_t *ll; + switch_dial_leg_t *leg = NULL; + + switch_dial_handle_create(&dh); + switch_dial_handle_add_leg_list(dh, &ll); + + switch_dial_leg_list_add_leg(ll, &leg, "null/test"); + switch_dial_handle_add_leg_var(leg, "null_auto_answer", "2000"); + switch_dial_handle_add_leg_var(leg, "leg_timeout", "4"); + switch_dial_handle_add_leg_var(leg, "group_confirm_file", "playback silence_stream://6000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_key", "exec"); + + status = switch_ivr_originate(NULL, &session, &cause, NULL, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, dh); + fst_requires(status == SWITCH_STATUS_FALSE); + fst_check(session == NULL); + fst_check_duration(4500, 600); // (>= 3.9 sec, <= 5.1 sec) + switch_dial_handle_destroy(&dh); + } + FST_TEST_END() + + FST_TEST_BEGIN(originate_test_group_confirm_leg_timeout_finished) + { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + switch_status_t status; + switch_call_cause_t cause; + switch_dial_handle_t *dh; + switch_dial_leg_list_t *ll; + switch_dial_leg_t *leg = NULL; + + switch_dial_handle_create(&dh); + switch_dial_handle_add_leg_list(dh, &ll); + + switch_dial_leg_list_add_leg(ll, &leg, "null/test"); + switch_dial_handle_add_leg_var(leg, "null_auto_answer", "2000"); + switch_dial_handle_add_leg_var(leg, "leg_timeout", "6"); + switch_dial_handle_add_leg_var(leg, "group_confirm_file", "playback silence_stream://2000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_key", "exec"); + + status = switch_ivr_originate(NULL, &session, &cause, NULL, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, dh); + fst_requires(status == SWITCH_STATUS_SUCCESS); + fst_requires(session); + fst_xcheck(switch_channel_test_flag(switch_core_session_get_channel(session), CF_WINNER), "Expect session is group confirm winner"); + switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_NORMAL_CLEARING); + switch_core_session_rwunlock(session); + switch_dial_handle_destroy(&dh); + fst_check_duration(4000, 500); + } + FST_TEST_END() + + FST_TEST_BEGIN(originate_test_group_confirm_timeout_leg) + { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + switch_status_t status; + switch_call_cause_t cause; + switch_dial_handle_t *dh; + switch_dial_leg_list_t *ll; + switch_dial_leg_t *leg = NULL; + + switch_dial_handle_create(&dh); + switch_dial_handle_add_leg_list(dh, &ll); + + switch_dial_leg_list_add_leg(ll, &leg, "null/test"); + switch_dial_handle_add_leg_var(leg, "leg_timeout", "15"); + switch_dial_handle_add_leg_var(leg, "null_auto_answer", "2000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_file", "playback silence_stream://10000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_key", "exec"); + switch_dial_handle_add_leg_var(leg, "group_confirm_timeout", "3"); + + status = switch_ivr_originate(NULL, &session, &cause, NULL, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, dh); + fst_requires(status == SWITCH_STATUS_FALSE); + fst_check(session == NULL); + switch_dial_handle_destroy(&dh); + fst_check_duration(5500, 600); // (> 4.9 sec < 6.1 sec) only 1 second resolution with these timeouts + } + FST_TEST_END() + + FST_TEST_BEGIN(originate_test_group_confirm_timeout_global) + { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + switch_status_t status; + switch_call_cause_t cause; + switch_dial_handle_t *dh; + switch_dial_leg_list_t *ll; + switch_dial_leg_t *leg = NULL; + + switch_dial_handle_create(&dh); + switch_dial_handle_add_leg_list(dh, &ll); + + switch_dial_leg_list_add_leg(ll, &leg, "null/test"); + switch_dial_handle_add_leg_var(leg, "leg_timeout", "15"); + switch_dial_handle_add_leg_var(leg, "null_auto_answer", "2000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_file", "playback silence_stream://10000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_key", "exec"); + switch_dial_handle_add_global_var(dh, "group_confirm_timeout", "3"); + + status = switch_ivr_originate(NULL, &session, &cause, NULL, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, dh); + fst_requires(status == SWITCH_STATUS_FALSE); + fst_check(session == NULL); + fst_check_duration(5500, 600); // (>= 4.9 sec, <= 6.1 sec) only 1 second resolution with these timeouts + switch_dial_handle_destroy(&dh); + } + FST_TEST_END() + + FST_TEST_BEGIN(originate_test_group_confirm_cancel_timeout_global) + { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + switch_status_t status; + switch_call_cause_t cause; + switch_dial_handle_t *dh; + switch_dial_leg_list_t *ll; + switch_dial_leg_t *leg = NULL; + + switch_dial_handle_create(&dh); + switch_dial_handle_add_leg_list(dh, &ll); + + switch_dial_leg_list_add_leg(ll, &leg, "null/test"); + switch_dial_handle_add_leg_var(leg, "leg_timeout", "3"); + switch_dial_handle_add_leg_var(leg, "null_auto_answer", "2000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_file", "playback silence_stream://2000"); + switch_dial_handle_add_leg_var(leg, "group_confirm_key", "exec"); + switch_dial_handle_add_global_var(dh, "group_confirm_cancel_timeout", "true"); + + status = switch_ivr_originate(NULL, &session, &cause, NULL, 0, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, dh); + fst_requires(status == SWITCH_STATUS_SUCCESS); + fst_requires(session); + fst_xcheck(switch_channel_test_flag(switch_core_session_get_channel(session), CF_WINNER), "Expect session is group confirm winner"); + switch_channel_hangup(switch_core_session_get_channel(session), SWITCH_CAUSE_NORMAL_CLEARING); + switch_core_session_rwunlock(session); + switch_dial_handle_destroy(&dh); + fst_check_duration(4500, 600); // (>= 3.9 sec, <= 5.1 sec) + } + FST_TEST_END() } FST_SUITE_END() }