]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
[core] Add global/leg variable to originate: group_confirm_timeout
authorSeven Du <dujinfang@gmail.com>
Mon, 6 Jan 2020 12:25:44 +0000 (20:25 +0800)
committerAndrey Volk <andywolk@gmail.com>
Thu, 7 May 2020 21:17:22 +0000 (01:17 +0400)
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
tests/unit/switch_ivr_originate.c

index 6adaba174edf8d7fe59781345acd13acaa36bd2a..170a5ed1b81c898cdf1f2c19c34bc64730aff9f9 100644 (file)
@@ -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) {
index 983f26d0a70f37ff467ecae4c93341051bcc0592..0b91f3ee16ea01f03e71690473df3ff79b89d6a7 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
- * Copyright (C) 2005-2018, Anthony Minessale II <anthm@freeswitch.org>
+ * Copyright (C) 2005-2020, Anthony Minessale II <anthm@freeswitch.org>
  *
  * 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()
 }