diff --git a/.travis.yml b/.travis.yml index dc4a7e41dc..95d952919d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ sudo: true branches: only: - master + - develop cache: - directories: diff --git a/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt b/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt index 0e1f5a1196..6db57e03db 100644 --- a/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt +++ b/CMake/Dependencies/libkvsCommonLws-CMakeLists.txt @@ -6,7 +6,7 @@ include(ExternalProject) ExternalProject_Add(libkvsCommonLws-download GIT_REPOSITORY https://github.com/awslabs/amazon-kinesis-video-streams-producer-c.git - GIT_TAG 99c1a8cd8cec88f99c9c4ce3944b53ae341d1491 + GIT_TAG c7fce9e06021452ff3c42dc70c8360606b22ad53 PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${OPEN_SRC_INSTALL_PREFIX} diff --git a/CMake/Dependencies/libwebsockets-CMakeLists.txt b/CMake/Dependencies/libwebsockets-CMakeLists.txt index b18f7b6293..5700a3e4a2 100644 --- a/CMake/Dependencies/libwebsockets-CMakeLists.txt +++ b/CMake/Dependencies/libwebsockets-CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8) project(libwebsocket-download NONE) -SET(PATCH_COMMAND git apply --ignore-whitespace ${CMAKE_CURRENT_LIST_DIR}/libwebsockets-old-gcc-fix-cast-cmakelists.patch) +SET(PATCH_COMMAND git apply --ignore-whitespace ${CMAKE_CURRENT_LIST_DIR}/libwebsockets-old-gcc-fix-cast-cmakelists.patch ${CMAKE_CURRENT_LIST_DIR}/libwebsockets-leak-pipe-fix.patch) include(ExternalProject) @@ -25,7 +25,7 @@ endif() ExternalProject_Add(project_libwebsockets GIT_REPOSITORY https://github.com/warmcat/libwebsockets.git - GIT_TAG v4.2.1 + GIT_TAG v4.2.2 PATCH_COMMAND ${PATCH_COMMAND} PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build CMAKE_ARGS diff --git a/CMake/Dependencies/libwebsockets-leak-pipe-fix.patch b/CMake/Dependencies/libwebsockets-leak-pipe-fix.patch new file mode 100644 index 0000000000..c5a22f1f56 --- /dev/null +++ b/CMake/Dependencies/libwebsockets-leak-pipe-fix.patch @@ -0,0 +1,36 @@ +Author: Andy Green +Date: Wed Sep 08 12:25:47 2021 +0200 + +cancel pipe: make sure we closed it on destroy with no EVENTFD case + + +diff --git a/lib/core/context.c b/lib/core/context.c +index 6194801..4f3bb45 100644 +--- a/lib/core/context.c ++++ b/lib/core/context.c +@@ -1625,11 +1625,25 @@ lws_pt_destroy(struct lws_context_per_thread *pt) + vpt->foreign_pfd_list = NULL; + + lws_pt_lock(pt, __func__); ++ + if (pt->pipe_wsi) { + lws_destroy_event_pipe(pt->pipe_wsi); + pt->pipe_wsi = NULL; + } + ++ if (pt->dummy_pipe_fds[0] ++#if !defined(WIN32) ++ && (int)pt->dummy_pipe_fds[0] != -1 ++#endif ++ ) { ++ struct lws wsi; ++ ++ memset(&wsi, 0, sizeof(wsi)); ++ wsi.a.context = pt->context; ++ wsi.tsi = (char)pt->tid; ++ lws_plat_pipe_close(&wsi); ++ } ++ + #if defined(LWS_WITH_SECURE_STREAMS) + lws_dll2_foreach_safe(&pt->ss_owner, NULL, lws_ss_destroy_dll); + diff --git a/CMake/Dependencies/libwebsockets-old-gcc-fix-cast-cmakelists.patch b/CMake/Dependencies/libwebsockets-old-gcc-fix-cast-cmakelists.patch index f256c78cd7..61e25e8a35 100644 --- a/CMake/Dependencies/libwebsockets-old-gcc-fix-cast-cmakelists.patch +++ b/CMake/Dependencies/libwebsockets-old-gcc-fix-cast-cmakelists.patch @@ -1,19 +1,3 @@ -diff --git a/lib/misc/base64-decode.c b/lib/misc/base64-decode.c -index f8e8e49a..9d18b33f 100644 ---- a/lib/misc/base64-decode.c -+++ b/lib/misc/base64-decode.c -@@ -72,9 +72,9 @@ _lws_b64_encode_string(const char *encode, const char *in, int in_len, - *out++ = encode[triple[0] >> 2]; - *out++ = encode[(((triple[0] & 0x03) << 4) & 0x30) | - (((triple[1] & 0xf0) >> 4) & 0x0f)]; -- *out++ = (len > 1 ? encode[(((triple[1] & 0x0f) << 2) & 0x3c) | -+ *out++ = (char)(len > 1 ? encode[(((triple[1] & 0x0f) << 2) & 0x3c) | - (((triple[2] & 0xc0) >> 6) & 3)] : '='); -- *out++ = (len > 2 ? encode[triple[2] & 0x3f] : '='); -+ *out++ = (char)(len > 2 ? encode[triple[2] & 0x3f] : '='); - - done += 4; - } diff --git a/lib/roles/h2/hpack.c b/lib/roles/h2/hpack.c index 68629e6f..6ef628b8 100644 --- a/lib/roles/h2/hpack.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 52b6fcecb9..f3dc73f9e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,20 @@ option(THREAD_SANITIZER "Build with ThreadSanitizer." OFF) option(UNDEFINED_BEHAVIOR_SANITIZER "Build with UndefinedBehaviorSanitizer." OFF) option(LINK_PROFILER "Link gperftools profiler" OFF) -set(OPEN_SRC_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/open-source" CACHE PATH "Libraries will be downloaded and built in this directory.") +execute_process( + COMMAND git rev-parse HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + +add_definitions(-DSDK_VERSION=\"${GIT_COMMIT_HASH}\") +add_definitions(-DDETECTED_GIT_HASH) + +if(NOT OPEN_SRC_INSTALL_PREFIX OR OPEN_SRC_INSTALL_PREFIX STREQUAL "") + set(OPEN_SRC_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/open-source" CACHE PATH "Libraries will be downloaded and built in this directory.") +else() + set(OPEN_SRC_INSTALL_PREFIX ${OPEN_SRC_INSTALL_PREFIX} CACHE PATH "Libraries will be downloaded and built in this directory.") +endif() if(NOT WIN32) CHECK_INCLUDE_FILES(ifaddrs.h KVSWEBRTC_HAVE_IFADDRS_H) @@ -342,3 +355,9 @@ endif() if(BUILD_BENCHMARK) add_subdirectory(bench) endif() + +get_directory_property(clean_files ADDITIONAL_CLEAN_FILES) +list(APPEND clean_files "${OPEN_SRC_INSTALL_PREFIX}") +list(APPEND clean_files "${CMAKE_CURRENT_SOURCE_DIR}/build") +set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES "${clean_files}") + diff --git a/README.md b/README.md index 989e36db1e..8dc9449b83 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ You can pass the following options to `cmake ..`. * `-DUNDEFINED_BEHAVIOR_SANITIZER` -- Build with UndefinedBehaviorSanitizer * `-DLINK_PROFILER` -- Link with gperftools (available profiler options are listed [here](https://github.com/gperftools/gperftools)) +To clean up the `open-source` and `build` folders from previous build, use `cmake --build . --target clean` from the `build` folder + For windows builds, you will have to include additional flags for libwebsockets CMake. Add the following flags to your cmake command, or edit the CMake file in ./CMake/Dependencies/libwebsockets-CMakeLists.txt with the following: ``` diff --git a/samples/Common.c b/samples/Common.c index 64007774ce..8270e2cb09 100644 --- a/samples/Common.c +++ b/samples/Common.c @@ -24,7 +24,7 @@ VOID onDataChannelMessage(UINT64 customData, PRtcDataChannel pDataChannel, BOOL // Send a response to the message sent by the viewer STATUS retStatus = STATUS_SUCCESS; retStatus = dataChannelSend(pDataChannel, FALSE, (PBYTE) MASTER_DATA_CHANNEL_MESSAGE, STRLEN(MASTER_DATA_CHANNEL_MESSAGE)); - if(retStatus != STATUS_SUCCESS) { + if (retStatus != STATUS_SUCCESS) { DLOGI("[KVS Master] dataChannelSend(): operation returned status code: 0x%08x \n", retStatus); } } @@ -493,7 +493,7 @@ STATUS createSampleStreamingSession(PSampleConfiguration pSampleConfiguration, P CHK_STATUS(transceiverOnBandwidthEstimation(pSampleStreamingSession->pVideoRtcRtpTransceiver, (UINT64) pSampleStreamingSession, sampleBandwidthEstimationHandler)); - // Add a SendRecv Transceiver of type video + // Add a SendRecv Transceiver of type audio audioTrack.kind = MEDIA_STREAM_TRACK_KIND_AUDIO; audioTrack.codec = RTC_CODEC_OPUS; audioRtpTransceiverInit.direction = RTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV; @@ -551,7 +551,7 @@ STATUS freeSampleStreamingSession(PSampleStreamingSession* ppSampleStreamingSess // the running thread but it's OK as it's re-entrant MUTEX_LOCK(pSampleConfiguration->sampleConfigurationObjLock); if (pSampleConfiguration->iceCandidatePairStatsTimerId != MAX_UINT32 && pSampleConfiguration->streamingSessionCount == 0 && - pSampleConfiguration->iceCandidatePairStatsTimerId != MAX_UINT32) { + pSampleConfiguration->iceCandidatePairStatsTimerId != MAX_UINT32 && IS_VALID_TIMER_QUEUE_HANDLE(pSampleConfiguration->timerQueueHandle)) { CHK_LOG_ERR(timerQueueCancelTimer(pSampleConfiguration->timerQueueHandle, pSampleConfiguration->iceCandidatePairStatsTimerId, (UINT64) pSampleConfiguration)); pSampleConfiguration->iceCandidatePairStatsTimerId = MAX_UINT32; @@ -596,10 +596,10 @@ VOID sampleFrameHandler(UINT64 customData, PFrame pFrame) } } -VOID sampleBandwidthEstimationHandler(UINT64 customData, DOUBLE maxiumBitrate) +VOID sampleBandwidthEstimationHandler(UINT64 customData, DOUBLE maximumBitrate) { UNUSED_PARAM(customData); - DLOGV("received bitrate suggestion: %f", maxiumBitrate); + DLOGV("received bitrate suggestion: %f", maximumBitrate); } VOID sampleSenderBandwidthEstimationHandler(UINT64 customData, UINT32 txBytes, UINT32 rxBytes, UINT32 txPacketsCnt, UINT32 rxPacketsCnt, @@ -782,6 +782,7 @@ STATUS createSampleConfiguration(PCHAR channelName, SIGNALING_CHANNEL_ROLE_TYPE pSampleConfiguration->clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; pSampleConfiguration->clientInfo.loggingLevel = logLevel; pSampleConfiguration->clientInfo.cacheFilePath = NULL; // Use the default path + pSampleConfiguration->clientInfo.signalingClientCreationMaxRetryAttempts = CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE; pSampleConfiguration->iceCandidatePairStatsTimerId = MAX_UINT32; pSampleConfiguration->pregenerateCertTimerId = MAX_UINT32; @@ -838,6 +839,7 @@ STATUS logSignalingClientStats(PSignalingClientMetrics pSignalingClientMetrics) // This gives the EMA of the getIceConfig() call. DLOGD("Data Plane API call latency: %" PRIu64 " ms", (pSignalingClientMetrics->signalingClientStats.dpApiCallLatency / HUNDREDS_OF_NANOS_IN_A_MILLISECOND)); + DLOGD("API call retry count: %d", pSignalingClientMetrics->signalingClientStats.apiCallRetryCount); CleanUp: LEAVES(); return retStatus; @@ -1004,6 +1006,28 @@ STATUS freeSampleConfiguration(PSampleConfiguration* ppSampleConfiguration) CHK(pSampleConfiguration != NULL, retStatus); + if (IS_VALID_TIMER_QUEUE_HANDLE(pSampleConfiguration->timerQueueHandle)) { + if (pSampleConfiguration->iceCandidatePairStatsTimerId != MAX_UINT32) { + retStatus = timerQueueCancelTimer(pSampleConfiguration->timerQueueHandle, pSampleConfiguration->iceCandidatePairStatsTimerId, + (UINT64) pSampleConfiguration); + if (STATUS_FAILED(retStatus)) { + DLOGE("Failed to cancel stats timer with: 0x%08x", retStatus); + } + pSampleConfiguration->iceCandidatePairStatsTimerId = MAX_UINT32; + } + + if (pSampleConfiguration->pregenerateCertTimerId != MAX_UINT32) { + retStatus = timerQueueCancelTimer(pSampleConfiguration->timerQueueHandle, pSampleConfiguration->pregenerateCertTimerId, + (UINT64) pSampleConfiguration); + if (STATUS_FAILED(retStatus)) { + DLOGE("Failed to cancel certificate pre-generation timer with: 0x%08x", retStatus); + } + pSampleConfiguration->pregenerateCertTimerId = MAX_UINT32; + } + + timerQueueFree(&pSampleConfiguration->timerQueueHandle); + } + if (pSampleConfiguration->pPendingSignalingMessageForRemoteClient != NULL) { // Iterate and free all the pending queues stackQueueGetIterator(pSampleConfiguration->pPendingSignalingMessageForRemoteClient, &iterator); @@ -1025,12 +1049,7 @@ STATUS freeSampleConfiguration(PSampleConfiguration* ppSampleConfiguration) MUTEX_LOCK(pSampleConfiguration->sampleConfigurationObjLock); locked = TRUE; } - // Cancel the media thread - if(!(pSampleConfiguration->mediaThreadStarted)) { - DLOGD("Canceling media thread"); - THREAD_CANCEL(pSampleConfiguration->mediaSenderTid); - } - + for (i = 0; i < pSampleConfiguration->streamingSessionCount; ++i) { retStatus = gatherIceServerStats(pSampleConfiguration->sampleStreamingSessionList[i]); if (STATUS_FAILED(retStatus)) { @@ -1074,28 +1093,6 @@ STATUS freeSampleConfiguration(PSampleConfiguration* ppSampleConfiguration) freeStaticCredentialProvider(&pSampleConfiguration->pCredentialProvider); #endif - if (IS_VALID_TIMER_QUEUE_HANDLE(pSampleConfiguration->timerQueueHandle)) { - if (pSampleConfiguration->iceCandidatePairStatsTimerId != MAX_UINT32) { - retStatus = timerQueueCancelTimer(pSampleConfiguration->timerQueueHandle, pSampleConfiguration->iceCandidatePairStatsTimerId, - (UINT64) pSampleConfiguration); - if (STATUS_FAILED(retStatus)) { - DLOGE("Failed to cancel stats timer with: 0x%08x", retStatus); - } - pSampleConfiguration->iceCandidatePairStatsTimerId = MAX_UINT32; - } - - if (pSampleConfiguration->pregenerateCertTimerId != MAX_UINT32) { - retStatus = timerQueueCancelTimer(pSampleConfiguration->timerQueueHandle, pSampleConfiguration->pregenerateCertTimerId, - (UINT64) pSampleConfiguration); - if (STATUS_FAILED(retStatus)) { - DLOGE("Failed to cancel certificate pre-generation timer with: 0x%08x", retStatus); - } - pSampleConfiguration->pregenerateCertTimerId = MAX_UINT32; - } - - timerQueueFree(&pSampleConfiguration->timerQueueHandle); - } - if (pSampleConfiguration->pregeneratedCertificates != NULL) { stackQueueGetIterator(pSampleConfiguration->pregeneratedCertificates, &iterator); while (IS_VALID_ITERATOR(iterator)) { @@ -1109,8 +1106,7 @@ STATUS freeSampleConfiguration(PSampleConfiguration* ppSampleConfiguration) pSampleConfiguration->pregeneratedCertificates = NULL; } - MEMFREE(*ppSampleConfiguration); - *ppSampleConfiguration = NULL; + SAFE_MEMFREE(*ppSampleConfiguration); CleanUp: @@ -1161,10 +1157,7 @@ STATUS sessionCleanupWait(PSampleConfiguration pSampleConfiguration) // Check if we need to re-create the signaling client on-the-fly if (ATOMIC_LOAD_BOOL(&pSampleConfiguration->recreateSignalingClient) && - STATUS_SUCCEEDED(freeSignalingClient(&pSampleConfiguration->signalingClientHandle)) && - STATUS_SUCCEEDED(createSignalingClientSync(&pSampleConfiguration->clientInfo, &pSampleConfiguration->channelInfo, - &pSampleConfiguration->signalingClientCallbacks, pSampleConfiguration->pCredentialProvider, - &pSampleConfiguration->signalingClientHandle))) { + STATUS_SUCCEEDED(signalingClientFetchSync(pSampleConfiguration->signalingClientHandle))) { // Re-set the variable again ATOMIC_STORE_BOOL(&pSampleConfiguration->recreateSignalingClient, FALSE); } @@ -1234,7 +1227,7 @@ STATUS signalingMessageReceived(UINT64 customData, PReceivedSignalingMessage pRe { STATUS retStatus = STATUS_SUCCESS; PSampleConfiguration pSampleConfiguration = (PSampleConfiguration) customData; - BOOL peerConnectionFound = FALSE, locked = TRUE, startStats = FALSE; + BOOL peerConnectionFound = FALSE, locked = FALSE, startStats = FALSE; UINT32 clientIdHash; UINT64 hashValue = 0; PPendingMessageQueue pPendingMessageQueue = NULL; diff --git a/samples/kvsWebRTCClientMaster.c b/samples/kvsWebRTCClientMaster.c index dcaaa7c653..d7e29e1506 100644 --- a/samples/kvsWebRTCClientMaster.c +++ b/samples/kvsWebRTCClientMaster.c @@ -11,7 +11,7 @@ INT32 main(INT32 argc, CHAR* argv[]) PSampleConfiguration pSampleConfiguration = NULL; SignalingClientMetrics signalingClientMetrics; PCHAR pChannelName; - signalingClientMetrics.version = 0; + signalingClientMetrics.version = SIGNALING_CLIENT_METRICS_CURRENT_VERSION; SET_INSTRUMENTED_ALLOCATORS(); @@ -91,6 +91,12 @@ INT32 main(INT32 argc, CHAR* argv[]) printf("[KVS Master] Signaling client created successfully\n"); // Enable the processing of the messages + retStatus = signalingClientFetchSync(pSampleConfiguration->signalingClientHandle); + if (retStatus != STATUS_SUCCESS) { + printf("[KVS Master] signalingClientFetchSync(): operation returned status code: 0x%08x \n", retStatus); + goto CleanUp; + } + retStatus = signalingClientConnectSync(pSampleConfiguration->signalingClientHandle); if (retStatus != STATUS_SUCCESS) { printf("[KVS Master] signalingClientConnectSync(): operation returned status code: 0x%08x \n", retStatus); @@ -114,7 +120,7 @@ INT32 main(INT32 argc, CHAR* argv[]) CleanUp: if (retStatus != STATUS_SUCCESS) { - printf("[KVS Master] Terminated with status code 0x%08x", retStatus); + printf("[KVS Master] Terminated with status code 0x%08x\n", retStatus); } printf("[KVS Master] Cleaning up....\n"); @@ -122,6 +128,20 @@ INT32 main(INT32 argc, CHAR* argv[]) // Kick of the termination sequence ATOMIC_STORE_BOOL(&pSampleConfiguration->appTerminateFlag, TRUE); + if (IS_VALID_MUTEX_VALUE(pSampleConfiguration->sampleConfigurationObjLock)) { + MUTEX_LOCK(pSampleConfiguration->sampleConfigurationObjLock); + } + + // Cancel the media thread + if (pSampleConfiguration->mediaThreadStarted) { + DLOGD("Canceling media thread"); + THREAD_CANCEL(pSampleConfiguration->mediaSenderTid); + } + + if (IS_VALID_MUTEX_VALUE(pSampleConfiguration->sampleConfigurationObjLock)) { + MUTEX_UNLOCK(pSampleConfiguration->sampleConfigurationObjLock); + } + if (pSampleConfiguration->mediaSenderTid != INVALID_TID_VALUE) { THREAD_JOIN(pSampleConfiguration->mediaSenderTid, NULL); } @@ -133,7 +153,7 @@ INT32 main(INT32 argc, CHAR* argv[]) if (retStatus == STATUS_SUCCESS) { logSignalingClientStats(&signalingClientMetrics); } else { - printf("[KVS Master] signalingClientGetMetrics() operation returned status code: 0x%08x", retStatus); + printf("[KVS Master] signalingClientGetMetrics() operation returned status code: 0x%08x\n", retStatus); } retStatus = freeSignalingClient(&pSampleConfiguration->signalingClientHandle); if (retStatus != STATUS_SUCCESS) { @@ -272,7 +292,7 @@ PVOID sendVideoPackets(PVOID args) CHK_LOG_ERR(retStatus); - return (PVOID) (ULONG_PTR) retStatus; + return (PVOID)(ULONG_PTR) retStatus; } PVOID sendAudioPackets(PVOID args) @@ -340,7 +360,7 @@ PVOID sendAudioPackets(PVOID args) CleanUp: - return (PVOID) (ULONG_PTR) retStatus; + return (PVOID)(ULONG_PTR) retStatus; } PVOID sampleReceiveVideoFrame(PVOID args) @@ -360,5 +380,5 @@ PVOID sampleReceiveVideoFrame(PVOID args) CleanUp: - return (PVOID) (ULONG_PTR) retStatus; + return (PVOID)(ULONG_PTR) retStatus; } diff --git a/samples/kvsWebRTCClientMasterGstreamerSample.c b/samples/kvsWebRTCClientMasterGstreamerSample.c index 28ba593b41..185859bd57 100644 --- a/samples/kvsWebRTCClientMasterGstreamerSample.c +++ b/samples/kvsWebRTCClientMasterGstreamerSample.c @@ -430,6 +430,12 @@ INT32 main(INT32 argc, CHAR* argv[]) printf("[KVS GStreamer Master] Signaling client created successfully\n"); // Enable the processing of the messages + retStatus = signalingClientFetchSync(pSampleConfiguration->signalingClientHandle); + if (retStatus != STATUS_SUCCESS) { + printf("[KVS Master] signalingClientFetchSync(): operation returned status code: 0x%08x \n", retStatus); + goto CleanUp; + } + retStatus = signalingClientConnectSync(pSampleConfiguration->signalingClientHandle); if (retStatus != STATUS_SUCCESS) { printf("[KVS GStreamer Master] signalingClientConnectSync(): operation returned status code: 0x%08x \n", retStatus); diff --git a/samples/kvsWebRTCClientViewer.c b/samples/kvsWebRTCClientViewer.c index e425ed8594..189a8706db 100644 --- a/samples/kvsWebRTCClientViewer.c +++ b/samples/kvsWebRTCClientViewer.c @@ -2,6 +2,8 @@ extern PSampleConfiguration gSampleConfiguration; +#ifdef ENABLE_DATA_CHANNEL + // onMessage callback for a message received by the viewer on a data channel VOID dataChannelOnMessageCallback(UINT64 customData, PRtcDataChannel pDataChannel, BOOL isBinary, PBYTE pMessage, UINT32 pMessageLen) { @@ -26,6 +28,7 @@ VOID dataChannelOnOpenCallback(UINT64 customData, PRtcDataChannel pDataChannel) DLOGI("[KVS Viewer] dataChannelSend(): operation returned status code: 0x%08x \n", retStatus); } } +#endif INT32 main(INT32 argc, CHAR* argv[]) { @@ -95,6 +98,12 @@ INT32 main(INT32 argc, CHAR* argv[]) printf("[KVS Viewer] Signaling client created successfully\n"); // Enable the processing of the messages + retStatus = signalingClientFetchSync(pSampleConfiguration->signalingClientHandle); + if (retStatus != STATUS_SUCCESS) { + printf("[KVS Master] signalingClientFetchSync(): operation returned status code: 0x%08x \n", retStatus); + goto CleanUp; + } + retStatus = signalingClientConnectSync(pSampleConfiguration->signalingClientHandle); if (retStatus != STATUS_SUCCESS) { printf("[KVS Viewer] signalingClientConnectSync(): operation returned status code: 0x%08x \n", retStatus); diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h index a63e17b82d..55adc47393 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h @@ -299,6 +299,8 @@ extern "C" { #define STATUS_SIGNALING_DELETE_CALL_FAILED STATUS_SIGNALING_BASE + 0x00000031 #define STATUS_SIGNALING_INVALID_METRICS_VERSION STATUS_SIGNALING_BASE + 0x00000032 #define STATUS_SIGNALING_INVALID_CLIENT_INFO_CACHE_FILE_PATH_LEN STATUS_SIGNALING_BASE + 0x00000033 +#define STATUS_SIGNALING_LWS_CALL_FAILED STATUS_SIGNALING_BASE + 0x00000034 + /*!@} */ @@ -495,7 +497,7 @@ extern "C" { /** * Version of signaling client */ -#define SIGNALING_CLIENT_CURRENT_VERSION 0 +#define SIGNALING_CLIENT_CURRENT_VERSION 1 /** * Version of SignalingChannelDescription structure @@ -573,10 +575,15 @@ extern "C" { */ #define SIGNALING_CONNECT_STATE_TIMEOUT (15 * HUNDREDS_OF_NANOS_IN_A_SECOND) +/** + * Default disconnect sync API timeout + */ +#define SIGNALING_DISCONNECT_STATE_TIMEOUT (15 * HUNDREDS_OF_NANOS_IN_A_SECOND) + /** * Default refresh ICE server config API timeout */ -#define SIGNALING_REFRESH_ICE_CONFIG_STATE_TIMEOUT (15 * HUNDREDS_OF_NANOS_IN_A_SECOND) +#define SIGNALING_REFRESH_ICE_CONFIG_STATE_TIMEOUT (20 * HUNDREDS_OF_NANOS_IN_A_SECOND) /** * Default signaling connection establishment timeout @@ -635,7 +642,7 @@ extern "C" { /** * Signaling states default retry count. This will evaluate to the last call being made 20 seconds in which will hit a timeout first. */ -#define SIGNALING_STATES_DEFAULT_RETRY_COUNT 10 +#define SIGNALING_STATES_DEFAULT_RETRY_COUNT 1 /** * Signaling caching policy default TTL period @@ -647,6 +654,11 @@ extern "C" { */ #define SIGNALING_API_CALL_CACHE_TTL_SENTINEL_VALUE 0 +/** + * Signaling caching policy TTL period sentinel value which will force the default period. + */ +#define CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE -1 + /** * @brief Definition of the signaling client handle */ @@ -1170,16 +1182,19 @@ typedef struct { * @brief Populate Signaling client with client ID and application log level */ typedef struct { - UINT32 version; //!< Version of the structure - CHAR clientId[MAX_SIGNALING_CLIENT_ID_LEN + 1]; //!< Client id to use. Defines if the client is a producer/consumer - UINT32 loggingLevel; //!< Verbosity level for the logging. One of LOG_LEVEL_XXX - //!< values or the default verbosity will be assumed. Currently, - //!< default value is LOG_LEVEL_WARNING - PCHAR cacheFilePath; //!< File cache path override. The default - //!< path is "./.SignalingCache_vN" which might not work for - //!< devices which have read only partition where the code is - //!< located. For default value or when file caching is not - //!< being used this value can be NULL or point to an EMPTY_STRING. + UINT32 version; //!< Version of the structure + CHAR clientId[MAX_SIGNALING_CLIENT_ID_LEN + 1]; //!< Client id to use. Defines if the client is a producer/consumer + UINT32 loggingLevel; //!< Verbosity level for the logging. One of LOG_LEVEL_XXX + //!< values or the default verbosity will be assumed. Currently, + //!< default value is LOG_LEVEL_WARNING + PCHAR cacheFilePath; //!< File cache path override. The default + //!< path is "./.SignalingCache_vN" which might not work for + //!< devices which have read only partition where the code is + //!< located. For default value or when file caching is not + //!< being used this value can be NULL or point to an EMPTY_STRING. + KvsRetryStrategyCallbacks signalingRetryStrategyCallbacks; //!< Retry strategy callbacks used while creating signaling client + INT32 signalingClientCreationMaxRetryAttempts; //!< Max attempts to create signaling client before returning error to the caller + UINT32 stateMachineRetryCountReadOnly; //!< Retry count of state machine. Note that this **MUST NOT** be modified by the user. It is a read only field } SignalingClientInfo, *PSignalingClientInfo; /** @@ -1321,8 +1336,9 @@ typedef struct { UINT32 version; //!< Current version of the structure UINT64 customData; //!< Custom data passed by the caller SignalingClientMessageReceivedFunc messageReceivedFn; //!< Callback registration for received SDP - SignalingClientErrorReportFunc errorReportFn; //!< Error reporting function. This is an optional member + SignalingClientErrorReportFunc errorReportFn; //!< Error reporting function. This is an optional member SignalingClientStateChangedFunc stateChangeFn; //!< Signaling client state change callback + GetCurrentTimeFunc getCurrentTimeFn; //!< callback to override system time, used for testing clock skew } SignalingClientCallbacks, *PSignalingClientCallbacks; /** @@ -1918,6 +1934,16 @@ PUBLIC_API STATUS signalingClientGetIceConfigInfoCount(SIGNALING_CLIENT_HANDLE, */ PUBLIC_API STATUS signalingClientGetIceConfigInfo(SIGNALING_CLIENT_HANDLE, UINT32, PIceConfigInfo*); +/** + * @brief Fetches all assets needed to ready the state machine before attempting to connect. + * Can also be used to reallocate missing / expired assets before reconnecting. + * + * @param[in] SIGNALING_CLIENT_HANDLE Signaling client handle + * + * @return STATUS code of the execution. STATUS_SUCCESS on success + */ +PUBLIC_API STATUS signalingClientFetchSync(SIGNALING_CLIENT_HANDLE); + /** * @brief Connects the signaling client to the web socket in order to send/receive messages. * diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h index 615e47e921..a8294fd699 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Stats.h @@ -172,7 +172,7 @@ typedef enum { typedef struct { UINT64 durationInSeconds; //!< Time (seconds) spent in each state RTC_QUALITY_LIMITATION_REASON qualityLimitationReason; //!< Quality limitation reason -} QualityLimitationDurationsRecord, PQualityLimitationDurationsRecord; +} QualityLimitationDurationsRecord, *PQualityLimitationDurationsRecord; /** * @brief Record of total number of packets sent per DSCP. Used by RTCOutboundRtpStreamStats @@ -577,6 +577,7 @@ typedef struct { //!< * When refreshing ICE server configuration fails after pre-configured retries //!< In all of these cases the error callback (if specified) will be called. UINT32 numberOfReconnects; //!< Number of reconnects in the session + UINT32 apiCallRetryCount; //!< Number of retries due to API call failures in the state machine } SignalingClientStats, PSignalingClientStats; /** diff --git a/src/source/Ice/ConnectionListener.c b/src/source/Ice/ConnectionListener.c index 92adbc5fd9..39ebaab8aa 100644 --- a/src/source/Ice/ConnectionListener.c +++ b/src/source/Ice/ConnectionListener.c @@ -374,7 +374,10 @@ PVOID connectionListenerReceiveDataRoutine(PVOID arg) CleanUp: - if (pConnectionListener != NULL) { + // The check for valid mutex is necessary because when we're in freeConnectionListener + // we may free the mutex in another thread so by the time we get here accessing the lock + // will result in accessing a resource after it has been freed + if (pConnectionListener != NULL && IS_VALID_MUTEX_VALUE(pConnectionListener->lock)) { // As TID is 64 bit we can't atomically update it and need to do it under the lock MUTEX_LOCK(pConnectionListener->lock); pConnectionListener->receiveDataRoutine = INVALID_TID_VALUE; diff --git a/src/source/Ice/IceAgent.c b/src/source/Ice/IceAgent.c index 89edca8daf..18f0a1a80c 100644 --- a/src/source/Ice/IceAgent.c +++ b/src/source/Ice/IceAgent.c @@ -99,10 +99,10 @@ STATUS createIceAgent(PCHAR username, PCHAR password, PIceAgentCallbacks pIceAge pIceAgent->rtcIceServerDiagnostics[i].port = (INT32) getInt16(pIceAgent->iceServers[i].ipAddress.port); switch (pIceAgent->iceServers[pIceAgent->iceServersCount].transport) { case KVS_SOCKET_PROTOCOL_UDP: - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_URL_TRANSPORT_UDP); + STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_TRANSPORT_TYPE_UDP); break; case KVS_SOCKET_PROTOCOL_TCP: - STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_URL_TRANSPORT_TCP); + STRCPY(pIceAgent->rtcIceServerDiagnostics[i].protocol, ICE_TRANSPORT_TYPE_TCP); break; default: MEMSET(pIceAgent->rtcIceServerDiagnostics[i].protocol, 0, SIZEOF(pIceAgent->rtcIceServerDiagnostics[i].protocol)); @@ -327,6 +327,7 @@ STATUS iceAgentAddRemoteCandidate(PIceAgent pIceAgent, PCHAR pIceCandidateString PDoubleListNode pCurNode = NULL; SDP_ICE_CANDIDATE_PARSER_STATE state; ICE_CANDIDATE_TYPE iceCandidateType = ICE_CANDIDATE_TYPE_HOST; + KVS_SOCKET_PROTOCOL remoteProtocol = KVS_SOCKET_PROTOCOL_NONE; CHK(pIceAgent != NULL && pIceCandidateString != NULL, STATUS_NULL_ARG); CHK(!IS_EMPTY_STRING(pIceCandidateString), STATUS_INVALID_ARG); @@ -354,7 +355,12 @@ STATUS iceAgentAddRemoteCandidate(PIceAgent pIceAgent, PCHAR pIceCandidateString STRTOUI32(curr, next, 10, &priority); break; case SDP_ICE_CANDIDATE_PARSER_STATE_PROTOCOL: - CHK(STRNCMPI("tcp", curr, tokenLen) != 0, STATUS_ICE_CANDIDATE_STRING_IS_TCP); + if (STRNCMPI(ICE_TRANSPORT_TYPE_TCP, curr, tokenLen) == 0) { + remoteProtocol = KVS_SOCKET_PROTOCOL_TCP; + } else if (STRNCMPI(ICE_TRANSPORT_TYPE_UDP, curr, tokenLen) == 0) { + remoteProtocol = KVS_SOCKET_PROTOCOL_UDP; + } + CHK(STRNCMPI(ICE_TRANSPORT_TYPE_TCP, curr, tokenLen) != 0, STATUS_ICE_CANDIDATE_STRING_IS_TCP); break; case SDP_ICE_CANDIDATE_PARSER_STATE_IP: len = MIN(next - curr, KVS_IP_ADDRESS_STRING_BUFFER_LEN - 1); @@ -411,11 +417,12 @@ STATUS iceAgentAddRemoteCandidate(PIceAgent pIceAgent, PCHAR pIceCandidateString pIceCandidate->state = ICE_CANDIDATE_STATE_VALID; pIceCandidate->priority = priority; pIceCandidate->iceCandidateType = iceCandidateType; + pIceCandidate->remoteProtocol = remoteProtocol; + CHK_STATUS(doubleListInsertItemHead(pIceAgent->remoteCandidates, (UINT64) pIceCandidate)); freeIceCandidateIfFail = FALSE; CHK_STATUS(createIceCandidatePairs(pIceAgent, pIceCandidate, TRUE)); - iceAgentLogNewCandidate(pIceCandidate); /* pass remote candidate to each turnConnection */ @@ -430,7 +437,6 @@ STATUS iceAgentAddRemoteCandidate(PIceAgent pIceAgent, PCHAR pIceCandidateString } CleanUp: - if (locked) { MUTEX_UNLOCK(pIceAgent->lock); } @@ -674,6 +680,7 @@ STATUS iceAgentSendPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen) pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.bytesSent += bytesSent; pIceAgent->pDataSendingIceCandidatePair->rtcIceCandidatePairDiagnostics.packetsSent += packetsSent; } + if (locked) { MUTEX_UNLOCK(pIceAgent->lock); } @@ -689,6 +696,7 @@ STATUS iceAgentPopulateSdpMediaDescriptionCandidates(PIceAgent pIceAgent, PSdpMe PDoubleListNode pCurNode = NULL; BOOL locked = FALSE; UINT32 attrIndex; + PIceCandidate pCandidate = NULL; CHK(pIceAgent != NULL && pSdpMediaDescription != NULL && pIndex != NULL, STATUS_NULL_ARG); @@ -701,10 +709,12 @@ STATUS iceAgentPopulateSdpMediaDescriptionCandidates(PIceAgent pIceAgent, PSdpMe while (pCurNode != NULL) { CHK_STATUS(doubleListGetNodeData(pCurNode, &data)); pCurNode = pCurNode->pNext; - - STRCPY(pSdpMediaDescription->sdpAttributes[attrIndex].attributeName, "candidate"); - CHK_STATUS(iceCandidateSerialize((PIceCandidate) data, pSdpMediaDescription->sdpAttributes[attrIndex].attributeValue, &attrBufferLen)); - attrIndex++; + pCandidate = (PIceCandidate) data; + if (pCandidate->state == ICE_CANDIDATE_STATE_VALID) { + STRCPY(pSdpMediaDescription->sdpAttributes[attrIndex].attributeName, "candidate"); + CHK_STATUS(iceCandidateSerialize((PIceCandidate) data, pSdpMediaDescription->sdpAttributes[attrIndex].attributeValue, &attrBufferLen)); + attrIndex++; + } } *pIndex = attrIndex; @@ -843,7 +853,6 @@ STATUS iceAgentRestart(PIceAgent pIceAgent, PCHAR localIceUfrag, PCHAR localIceP ATOMIC_STORE_BOOL(&pIceAgent->processStun, FALSE); pIceAgent->iceAgentStatus = STATUS_SUCCESS; pIceAgent->lastDataReceivedTime = INVALID_TIMESTAMP_VALUE; - pIceAgent->relayCandidateCount = 0; CHK_STATUS(doubleListGetHeadNode(pIceAgent->localCandidates, &pCurNode)); @@ -1025,7 +1034,7 @@ STATUS createIceCandidatePairs(PIceAgent pIceAgent, PIceCandidate pIceCandidate, CHK(pIceAgent != NULL && pIceCandidate != NULL, STATUS_NULL_ARG); CHK_WARN(pIceCandidate->state == ICE_CANDIDATE_STATE_VALID, retStatus, "New ice candidate need to be valid to form pairs"); - // if pIceCandidate is a remote candidate, then form pairs with every single valid local candidate. Otherwize, + // if pIceCandidate is a remote candidate, then form pairs with every single valid local candidate. Otherwise, // form pairs with every single valid remote candidate pDoubleList = isRemoteCandidate ? pIceAgent->localCandidates : pIceAgent->remoteCandidates; @@ -1905,11 +1914,11 @@ STATUS updateCandidateStats(PIceAgent pIceAgent, BOOL isRemote) if (pIceCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED && pIceCandidate->pTurnConnection != NULL) { switch (pIceCandidate->pTurnConnection->protocol) { case KVS_SOCKET_PROTOCOL_UDP: - STRNCPY(pRtcIceCandidateDiagnostics->relayProtocol, ICE_URL_TRANSPORT_UDP, + STRNCPY(pRtcIceCandidateDiagnostics->relayProtocol, ICE_TRANSPORT_TYPE_UDP, ARRAY_SIZE(pRtcIceCandidateDiagnostics->relayProtocol)); break; case KVS_SOCKET_PROTOCOL_TCP: - STRNCPY(pRtcIceCandidateDiagnostics->relayProtocol, ICE_URL_TRANSPORT_TCP, + STRNCPY(pRtcIceCandidateDiagnostics->relayProtocol, ICE_TRANSPORT_TYPE_TCP, ARRAY_SIZE(pIceAgent->rtcSelectedLocalIceCandidateDiagnostics.relayProtocol)); break; default: @@ -1923,7 +1932,7 @@ STATUS updateCandidateStats(PIceAgent pIceAgent, BOOL isRemote) STRNCPY(pRtcIceCandidateDiagnostics->candidateType, iceAgentGetCandidateTypeStr(pIceCandidate->iceCandidateType), ARRAY_SIZE(pRtcIceCandidateDiagnostics->candidateType)); - STRNCPY(pRtcIceCandidateDiagnostics->protocol, ICE_URL_TRANSPORT_UDP, ARRAY_SIZE(pRtcIceCandidateDiagnostics->protocol)); + STRNCPY(pRtcIceCandidateDiagnostics->protocol, ICE_TRANSPORT_TYPE_UDP, ARRAY_SIZE(pRtcIceCandidateDiagnostics->protocol)); if (pIceCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED) { STRNCPY(pRtcIceCandidateDiagnostics->protocol, pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].protocol, ARRAY_SIZE(pRtcIceCandidateDiagnostics->protocol)); @@ -2631,32 +2640,27 @@ VOID iceAgentLogNewCandidate(PIceCandidate pIceCandidate) { CHAR ipAddr[KVS_IP_ADDRESS_STRING_BUFFER_LEN]; PCHAR protocol = "UNKNOWN"; - + KVS_SOCKET_PROTOCOL candidateProtocol = KVS_SOCKET_PROTOCOL_NONE; if (pIceCandidate != NULL) { getIpAddrStr(&pIceCandidate->ipAddress, ipAddr, ARRAY_SIZE(ipAddr)); - if (pIceCandidate->iceCandidateType == ICE_CANDIDATE_TYPE_RELAYED) { - if (pIceCandidate->pTurnConnection == NULL) { - protocol = "NA"; - } else { - switch (pIceCandidate->pTurnConnection->protocol) { - case KVS_SOCKET_PROTOCOL_TCP: - protocol = "TCP"; - break; - case KVS_SOCKET_PROTOCOL_UDP: - protocol = "UDP"; - break; - case KVS_SOCKET_PROTOCOL_NONE: - protocol = "NONE"; - break; - default: - break; - } - } + if (!pIceCandidate->isRemote && pIceCandidate->pSocketConnection != NULL) { + candidateProtocol = pIceCandidate->pSocketConnection->protocol; + } else { + candidateProtocol = pIceCandidate->remoteProtocol; + } + switch (candidateProtocol) { + case KVS_SOCKET_PROTOCOL_TCP: + protocol = ICE_TRANSPORT_TYPE_TCP; + break; + case KVS_SOCKET_PROTOCOL_UDP: + protocol = ICE_TRANSPORT_TYPE_UDP; + break; + default: + break; } - DLOGD("New %s ice candidate discovered. Id: %s. Ip: %s:%u. Type: %s. Protocol: %s.", pIceCandidate->isRemote ? "remote" : "local", - pIceCandidate->id, ipAddr, (UINT16) getInt16(pIceCandidate->ipAddress.port), - iceAgentGetCandidateTypeStr(pIceCandidate->iceCandidateType), protocol); + pIceCandidate->id, ipAddr, (UINT16) getInt16(pIceCandidate->ipAddress.port), iceAgentGetCandidateTypeStr(pIceCandidate->iceCandidateType), + protocol); } } diff --git a/src/source/Ice/IceAgent.h b/src/source/Ice/IceAgent.h index 39a4e12dca..0b02b87848 100644 --- a/src/source/Ice/IceAgent.h +++ b/src/source/Ice/IceAgent.h @@ -160,6 +160,7 @@ typedef struct { * has been reported through IceNewLocalCandidateFunc */ BOOL reported; CHAR id[ICE_CANDIDATE_ID_LEN + 1]; + KVS_SOCKET_PROTOCOL remoteProtocol; } IceCandidate, *PIceCandidate; typedef struct { diff --git a/src/source/Ice/IceAgentStateMachine.c b/src/source/Ice/IceAgentStateMachine.c index e66e98716b..3f35e6716a 100644 --- a/src/source/Ice/IceAgentStateMachine.c +++ b/src/source/Ice/IceAgentStateMachine.c @@ -8,23 +8,70 @@ * Static definitions of the states */ StateMachineState ICE_AGENT_STATE_MACHINE_STATES[] = { - {ICE_AGENT_STATE_NEW, ICE_AGENT_STATE_NONE | ICE_AGENT_STATE_NEW, fromNewIceAgentState, executeNewIceAgentState, INFINITE_RETRY_COUNT_SENTINEL, - STATUS_ICE_INVALID_STATE}, - {ICE_AGENT_STATE_CHECK_CONNECTION, ICE_AGENT_STATE_NEW | ICE_AGENT_STATE_CHECK_CONNECTION, fromCheckConnectionIceAgentState, - executeCheckConnectionIceAgentState, INFINITE_RETRY_COUNT_SENTINEL, STATUS_ICE_INVALID_STATE}, - {ICE_AGENT_STATE_CONNECTED, ICE_AGENT_STATE_CHECK_CONNECTION | ICE_AGENT_STATE_CONNECTED, fromConnectedIceAgentState, - executeConnectedIceAgentState, INFINITE_RETRY_COUNT_SENTINEL, STATUS_ICE_INVALID_STATE}, - {ICE_AGENT_STATE_NOMINATING, ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING, fromNominatingIceAgentState, executeNominatingIceAgentState, - INFINITE_RETRY_COUNT_SENTINEL, STATUS_ICE_INVALID_STATE}, - {ICE_AGENT_STATE_READY, ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING | ICE_AGENT_STATE_READY | ICE_AGENT_STATE_DISCONNECTED, - fromReadyIceAgentState, executeReadyIceAgentState, INFINITE_RETRY_COUNT_SENTINEL, STATUS_ICE_INVALID_STATE}, - {ICE_AGENT_STATE_DISCONNECTED, - ICE_AGENT_STATE_CHECK_CONNECTION | ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING | ICE_AGENT_STATE_READY | ICE_AGENT_STATE_DISCONNECTED, - fromDisconnectedIceAgentState, executeDisconnectedIceAgentState, INFINITE_RETRY_COUNT_SENTINEL, STATUS_ICE_INVALID_STATE}, - {ICE_AGENT_STATE_FAILED, - ICE_AGENT_STATE_CHECK_CONNECTION | ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING | ICE_AGENT_STATE_READY | - ICE_AGENT_STATE_DISCONNECTED | ICE_AGENT_STATE_FAILED, - fromFailedIceAgentState, executeFailedIceAgentState, INFINITE_RETRY_COUNT_SENTINEL, STATUS_ICE_INVALID_STATE}, + { + ICE_AGENT_STATE_NEW, + ICE_AGENT_STATE_NONE | ICE_AGENT_STATE_NEW, + fromNewIceAgentState, + executeNewIceAgentState, + NULL, + INFINITE_RETRY_COUNT_SENTINEL, + STATUS_ICE_INVALID_STATE + }, + { + ICE_AGENT_STATE_CHECK_CONNECTION, + ICE_AGENT_STATE_NEW | ICE_AGENT_STATE_CHECK_CONNECTION, + fromCheckConnectionIceAgentState, + executeCheckConnectionIceAgentState, + NULL, + INFINITE_RETRY_COUNT_SENTINEL, + STATUS_ICE_INVALID_STATE + }, + { + ICE_AGENT_STATE_CONNECTED, + ICE_AGENT_STATE_CHECK_CONNECTION | ICE_AGENT_STATE_CONNECTED, + fromConnectedIceAgentState, + executeConnectedIceAgentState, + NULL, + INFINITE_RETRY_COUNT_SENTINEL, + STATUS_ICE_INVALID_STATE + }, + { + ICE_AGENT_STATE_NOMINATING, + ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING, + fromNominatingIceAgentState, + executeNominatingIceAgentState, + NULL, + INFINITE_RETRY_COUNT_SENTINEL, + STATUS_ICE_INVALID_STATE + }, + { + ICE_AGENT_STATE_READY, + ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING | ICE_AGENT_STATE_READY | ICE_AGENT_STATE_DISCONNECTED, + fromReadyIceAgentState, + executeReadyIceAgentState, + NULL, + INFINITE_RETRY_COUNT_SENTINEL, + STATUS_ICE_INVALID_STATE + }, + { + ICE_AGENT_STATE_DISCONNECTED, + ICE_AGENT_STATE_CHECK_CONNECTION | ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING | ICE_AGENT_STATE_READY | ICE_AGENT_STATE_DISCONNECTED, + fromDisconnectedIceAgentState, + executeDisconnectedIceAgentState, + NULL, + INFINITE_RETRY_COUNT_SENTINEL, + STATUS_ICE_INVALID_STATE + }, + { + ICE_AGENT_STATE_FAILED, + ICE_AGENT_STATE_CHECK_CONNECTION | ICE_AGENT_STATE_CONNECTED | ICE_AGENT_STATE_NOMINATING | ICE_AGENT_STATE_READY | + ICE_AGENT_STATE_DISCONNECTED | ICE_AGENT_STATE_FAILED, + fromFailedIceAgentState, + executeFailedIceAgentState, + NULL, + INFINITE_RETRY_COUNT_SENTINEL, + STATUS_ICE_INVALID_STATE + }, }; UINT32 ICE_AGENT_STATE_MACHINE_STATE_COUNT = ARRAY_SIZE(ICE_AGENT_STATE_MACHINE_STATES); diff --git a/src/source/Ice/IceUtils.h b/src/source/Ice/IceUtils.h index 47b75a6b50..4b680bdfcc 100644 --- a/src/source/Ice/IceUtils.h +++ b/src/source/Ice/IceUtils.h @@ -21,6 +21,10 @@ extern "C" { #define ICE_URL_TRANSPORT_UDP "transport=udp" #define ICE_URL_TRANSPORT_TCP "transport=tcp" +#define ICE_TRANSPORT_TYPE_UDP "udp" +#define ICE_TRANSPORT_TYPE_TCP "tcp" +#define ICE_TRANSPORT_TYPE_TLS "tls" + /** * Ring buffer storing transactionIds */ diff --git a/src/source/PeerConnection/PeerConnection.c b/src/source/PeerConnection/PeerConnection.c index ff5cc524a1..d6af3e44d3 100644 --- a/src/source/PeerConnection/PeerConnection.c +++ b/src/source/PeerConnection/PeerConnection.c @@ -818,9 +818,7 @@ STATUS freePeerConnection(PRtcPeerConnection* ppPeerConnection) SAFE_MEMFREE(pKvsPeerConnection->pTwccManager); } - SAFE_MEMFREE(pKvsPeerConnection); - - *ppPeerConnection = NULL; + SAFE_MEMFREE(*ppPeerConnection); CleanUp: @@ -1198,11 +1196,21 @@ STATUS addTransceiver(PRtcPeerConnection pPeerConnection, PRtcMediaStreamTrack p UINT32 clockRate = 0; UINT32 ssrc = (UINT32) RAND(), rtxSsrc = (UINT32) RAND(); RTC_RTP_TRANSCEIVER_DIRECTION direction = RTC_RTP_TRANSCEIVER_DIRECTION_SENDRECV; + RtcMediaStreamTrack videoTrack; if (pRtcRtpTransceiverInit != NULL) { direction = pRtcRtpTransceiverInit->direction; } CHK(pKvsPeerConnection != NULL, STATUS_NULL_ARG); + + if (direction == RTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY && pRtcMediaStreamTrack == NULL) { + MEMSET(&videoTrack, 0x00, SIZEOF(RtcMediaStreamTrack)); + videoTrack.kind = MEDIA_STREAM_TRACK_KIND_VIDEO; + videoTrack.codec = RTC_CODEC_H264_PROFILE_42E01F_LEVEL_ASYMMETRY_ALLOWED_PACKETIZATION_MODE; + STRCPY(videoTrack.streamId, "myKvsVideoStream"); + STRCPY(videoTrack.trackId, "myVideoTrack"); + pRtcMediaStreamTrack = &videoTrack; + } switch (pRtcMediaStreamTrack->codec) { case RTC_CODEC_OPUS: @@ -1366,6 +1374,7 @@ STATUS initKvsWebRtc(VOID) initializeEndianness(); KVS_CRYPTO_INIT(); + LOG_GIT_HASH(); #ifdef ENABLE_DATA_CHANNEL CHK_STATUS(initSctpSession()); diff --git a/src/source/PeerConnection/Rtcp.c b/src/source/PeerConnection/Rtcp.c index 6120cea2f1..0aa4a0166e 100644 --- a/src/source/PeerConnection/Rtcp.c +++ b/src/source/PeerConnection/Rtcp.c @@ -41,7 +41,7 @@ static STATUS onRtcpSLIPacket(PRtcpPacket pRtcpPacket, PKvsPeerConnection pKvsPe pTransceiver->outboundStats.sliCount++; MUTEX_UNLOCK(pTransceiver->statsLock); } else { - DLOGW("Received FIR for non existing ssrc: %u", mediaSSRC); + DLOGW("Received SLI for non existing ssrc: %u", mediaSSRC); } CleanUp: diff --git a/src/source/Rtcp/RtpRollingBuffer.c b/src/source/Rtcp/RtpRollingBuffer.c index 0f0ae8799b..123f670a2c 100644 --- a/src/source/Rtcp/RtpRollingBuffer.c +++ b/src/source/Rtcp/RtpRollingBuffer.c @@ -119,7 +119,7 @@ STATUS rtpRollingBufferGetValidSeqIndexList(PRtpRollingBuffer pRollingBuffer, PU pCurSeqIndexListPtr++; // Return if filled up given valid sequence number array CHK(++returnPacketCount < *pValidIndexListLen, retStatus); - *pCurSeqIndexListPtr = (UINT32) NULL; + *pCurSeqIndexListPtr = (UINT64) NULL; } } diff --git a/src/source/Signaling/Client.c b/src/source/Signaling/Client.c index 9a77514a1a..4bc8b0bb9a 100644 --- a/src/source/Signaling/Client.c +++ b/src/source/Signaling/Client.c @@ -1,6 +1,57 @@ #define LOG_CLASS "SignalingClient" #include "../Include_i.h" +STATUS createRetryStrategyForCreatingSignalingClient(PSignalingClientInfo pClientInfo, PKvsRetryStrategy pKvsRetryStrategy) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + + CHK(pKvsRetryStrategy != NULL && pClientInfo != NULL, STATUS_NULL_ARG); + + if (pClientInfo->signalingRetryStrategyCallbacks.createRetryStrategyFn == NULL || + pClientInfo->signalingRetryStrategyCallbacks.freeRetryStrategyFn == NULL || + pClientInfo->signalingRetryStrategyCallbacks.executeRetryStrategyFn == NULL) { + DLOGV("Using exponential backoff retry strategy for creating signaling client"); + pClientInfo->signalingRetryStrategyCallbacks.createRetryStrategyFn = exponentialBackoffRetryStrategyCreate; + pClientInfo->signalingRetryStrategyCallbacks.freeRetryStrategyFn = exponentialBackoffRetryStrategyFree; + pClientInfo->signalingRetryStrategyCallbacks.executeRetryStrategyFn = getExponentialBackoffRetryStrategyWaitTime; + } + + // Create retry strategy will use default config 'DEFAULT_EXPONENTIAL_BACKOFF_CONFIGURATION' defined in - + // https://github.com/awslabs/amazon-kinesis-video-streams-pic/blob/develop/src/utils/include/com/amazonaws/kinesis/video/utils/Include.h + CHK_STATUS(pClientInfo->signalingRetryStrategyCallbacks.createRetryStrategyFn(pKvsRetryStrategy)); + + CHK(pKvsRetryStrategy->retryStrategyType == KVS_RETRY_STRATEGY_EXPONENTIAL_BACKOFF_WAIT, STATUS_INTERNAL_ERROR); + CHK(pKvsRetryStrategy->pRetryStrategy != NULL, STATUS_INTERNAL_ERROR); + +CleanUp: + + if (STATUS_FAILED(retStatus)) { + DLOGE("Some internal error occurred while setting up retry strategy for creating signaling client [0x%08x]", retStatus); + pClientInfo->signalingRetryStrategyCallbacks.freeRetryStrategyFn(pKvsRetryStrategy); + } + + LEAVES(); + return retStatus; +} + +STATUS freeRetryStrategyForCreatingSignalingClient(PSignalingClientInfo pClientInfo, PKvsRetryStrategy pKvsRetryStrategy) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + + CHK(pClientInfo != NULL && pKvsRetryStrategy != NULL, STATUS_NULL_ARG); + + if (pKvsRetryStrategy->pRetryStrategy != NULL) { + pClientInfo->signalingRetryStrategyCallbacks.freeRetryStrategyFn(pKvsRetryStrategy); + } + +CleanUp: + + LEAVES(); + return retStatus; +} + STATUS createSignalingClientSync(PSignalingClientInfo pClientInfo, PChannelInfo pChannelInfo, PSignalingClientCallbacks pCallbacks, PAwsCredentialProvider pCredentialProvider, PSIGNALING_CLIENT_HANDLE pSignalingHandle) { @@ -8,24 +59,63 @@ STATUS createSignalingClientSync(PSignalingClientInfo pClientInfo, PChannelInfo STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient = NULL; SignalingClientInfoInternal signalingClientInfoInternal; + KvsRetryStrategy createSignalingClientRetryStrategy = {NULL, NULL, KVS_RETRY_STRATEGY_DISABLED}; + INT32 signalingClientCreationMaxRetryCount; + UINT64 signalingClientCreationWaitTime; - DLOGI("Creating Signaling Client Sync"); + DLOGV("Creating Signaling Client Sync"); CHK(pSignalingHandle != NULL && pClientInfo != NULL, STATUS_NULL_ARG); // Convert the client info to the internal structure with empty values MEMSET(&signalingClientInfoInternal, 0x00, SIZEOF(signalingClientInfoInternal)); signalingClientInfoInternal.signalingClientInfo = *pClientInfo; - CHK_STATUS(createSignalingSync(&signalingClientInfoInternal, pChannelInfo, pCallbacks, pCredentialProvider, &pSignalingClient)); + CHK_STATUS(createRetryStrategyForCreatingSignalingClient(pClientInfo, &createSignalingClientRetryStrategy)); + + + if(pClientInfo->signalingClientCreationMaxRetryAttempts == CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE) { + signalingClientCreationMaxRetryCount = DEFAULT_CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS; + } else { + signalingClientCreationMaxRetryCount = pClientInfo->signalingClientCreationMaxRetryAttempts; + } + while (TRUE) { + retStatus = createSignalingSync(&signalingClientInfoInternal, pChannelInfo, pCallbacks, pCredentialProvider, &pSignalingClient); + // NOTE: This will retry on all status codes except SUCCESS. + // This includes status codes for bad arguments, internal non-recoverable errors etc. + // Retrying on non-recoverable errors is useless, but it is quite complex to segregate recoverable + // and non-recoverable errors at this layer. So to simplify, we would retry on all non-success status codes. + // It is the application's responsibility to fix any validation/null-arg/bad configuration type errors. + CHK(retStatus != STATUS_SUCCESS, retStatus); + + DLOGE("Create Signaling Sync API returned [0x%08x] %u\n", retStatus, signalingClientCreationMaxRetryCount); + if (signalingClientCreationMaxRetryCount <= 0) { + break; + } + + pClientInfo->stateMachineRetryCountReadOnly = signalingClientInfoInternal.signalingClientInfo.stateMachineRetryCountReadOnly; - *pSignalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + // Wait before attempting to create signaling client + CHK_STATUS(pClientInfo->signalingRetryStrategyCallbacks.executeRetryStrategyFn(&createSignalingClientRetryStrategy, + &signalingClientCreationWaitTime)); + + DLOGV("Attempting to back off for [%lf] milliseconds before creating signaling client again. " + "Signaling client creation retry count [%u]", + retStatus, signalingClientCreationWaitTime / 1000.0f, signalingClientCreationMaxRetryCount); + THREAD_SLEEP(signalingClientCreationWaitTime); + signalingClientCreationMaxRetryCount--; + } CleanUp: if (STATUS_FAILED(retStatus)) { + DLOGE("Create signaling client API failed with return code [0x%08x]", retStatus); freeSignaling(&pSignalingClient); + } else { + *pSignalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); } + freeRetryStrategyForCreatingSignalingClient(pClientInfo, &createSignalingClientRetryStrategy); + LEAVES(); return retStatus; } @@ -36,7 +126,7 @@ STATUS freeSignalingClient(PSIGNALING_CLIENT_HANDLE pSignalingHandle) STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient; - DLOGI("Freeing Signaling Client"); + DLOGV("Freeing Signaling Client"); CHK(pSignalingHandle != NULL, STATUS_NULL_ARG); // Get the client handle @@ -59,7 +149,7 @@ STATUS signalingClientSendMessageSync(SIGNALING_CLIENT_HANDLE signalingClientHan STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingClientHandle); - DLOGI("Signaling Client Sending Message Sync"); + DLOGV("Signaling Client Sending Message Sync"); CHK_STATUS(signalingSendMessageSync(pSignalingClient, pSignalingMessage)); @@ -76,7 +166,7 @@ STATUS signalingClientConnectSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingClientHandle); - DLOGI("Signaling Client Connect Sync"); + DLOGV("Signaling Client Connect Sync"); CHK_STATUS(signalingConnectSync(pSignalingClient)); @@ -87,13 +177,72 @@ STATUS signalingClientConnectSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) return retStatus; } +STATUS signalingClientFetchSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingClientHandle); + SignalingClientInfoInternal signalingClientInfoInternal; + KvsRetryStrategy createSignalingClientRetryStrategy = {NULL, NULL, KVS_RETRY_STRATEGY_DISABLED}; + INT32 signalingClientCreationMaxRetryCount; + UINT64 signalingClientCreationWaitTime; + + DLOGV("Signaling Client Fetch Sync"); + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + // Convert the client info to the internal structure with empty values + MEMSET(&signalingClientInfoInternal, 0x00, SIZEOF(signalingClientInfoInternal)); + signalingClientInfoInternal.signalingClientInfo = pSignalingClient->clientInfo.signalingClientInfo; + + CHK_STATUS(createRetryStrategyForCreatingSignalingClient(&pSignalingClient->clientInfo.signalingClientInfo, &createSignalingClientRetryStrategy)); + + signalingClientCreationMaxRetryCount = pSignalingClient->clientInfo.signalingClientInfo.signalingClientCreationMaxRetryAttempts; + if(signalingClientCreationMaxRetryCount == CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE) { + signalingClientCreationMaxRetryCount = DEFAULT_CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS; + } + + while (TRUE) { + retStatus = signalingFetchSync(pSignalingClient); + // NOTE: This will retry on all status codes except SUCCESS. + // This includes status codes for bad arguments, internal non-recoverable errors etc. + // Retrying on non-recoverable errors is useless, but it is quite complex to segregate recoverable + // and non-recoverable errors at this layer. So to simplify, we would retry on all non-success status codes. + // It is the application's responsibility to fix any validation/null-arg/bad configuration type errors. + CHK(retStatus != STATUS_SUCCESS, retStatus); + + DLOGE("Create Signaling Sync API returned [0x%08x] %u\n", retStatus, signalingClientCreationMaxRetryCount); + if (signalingClientCreationMaxRetryCount <= 0) { + break; + } + + pSignalingClient->clientInfo.signalingClientInfo.stateMachineRetryCountReadOnly = signalingClientInfoInternal.signalingClientInfo.stateMachineRetryCountReadOnly; + + // Wait before attempting to create signaling client + CHK_STATUS(pSignalingClient->clientInfo.signalingStateMachineRetryStrategyCallbacks.executeRetryStrategyFn(&createSignalingClientRetryStrategy, + &signalingClientCreationWaitTime)); + + DLOGV("Attempting to back off for [%lf] milliseconds before creating signaling client again. " + "Signaling client creation retry count [%u]", + retStatus, signalingClientCreationWaitTime / 1000.0f, signalingClientCreationMaxRetryCount); + THREAD_SLEEP(signalingClientCreationWaitTime); + signalingClientCreationMaxRetryCount--; + } + +CleanUp: + + SIGNALING_UPDATE_ERROR_COUNT(pSignalingClient, retStatus); + freeRetryStrategyForCreatingSignalingClient(&pSignalingClient->clientInfo.signalingClientInfo, &createSignalingClientRetryStrategy); + LEAVES(); + return retStatus; +} + STATUS signalingClientDisconnectSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingClientHandle); - DLOGI("Signaling Client Disconnect Sync"); + DLOGV("Signaling Client Disconnect Sync"); CHK_STATUS(signalingDisconnectSync(pSignalingClient)); @@ -110,7 +259,7 @@ STATUS signalingClientDeleteSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingClientHandle); - DLOGI("Signaling Client Delete Sync"); + DLOGV("Signaling Client Delete Sync"); CHK_STATUS(signalingDeleteSync(pSignalingClient)); @@ -127,9 +276,9 @@ STATUS signalingClientGetIceConfigInfoCount(SIGNALING_CLIENT_HANDLE signalingCli STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingClientHandle); - DLOGI("Signaling Client Get ICE Config Info Count"); + DLOGV("Signaling Client Get ICE Config Info Count"); - CHK_STATUS(signalingGetIceConfigInfoCout(pSignalingClient, pIceConfigCount)); + CHK_STATUS(signalingGetIceConfigInfoCount(pSignalingClient, pIceConfigCount)); CleanUp: @@ -144,7 +293,7 @@ STATUS signalingClientGetIceConfigInfo(SIGNALING_CLIENT_HANDLE signalingClientHa STATUS retStatus = STATUS_SUCCESS; PSignalingClient pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingClientHandle); - DLOGI("Signaling Client Get ICE Config Info"); + DLOGV("Signaling Client Get ICE Config Info"); CHK_STATUS(signalingGetIceConfigInfo(pSignalingClient, index, ppIceConfigInfo)); @@ -261,7 +410,6 @@ STATUS signalingClientGetMetrics(SIGNALING_CLIENT_HANDLE signalingClientHandle, CHK_STATUS(signalingGetMetrics(pSignalingClient, pSignalingClientMetrics)); CleanUp: - SIGNALING_UPDATE_ERROR_COUNT(pSignalingClient, retStatus); LEAVES(); return retStatus; diff --git a/src/source/Signaling/LwsApiCalls.c b/src/source/Signaling/LwsApiCalls.c index de556cf87e..1c02bcc172 100644 --- a/src/source/Signaling/LwsApiCalls.c +++ b/src/source/Signaling/LwsApiCalls.c @@ -18,17 +18,23 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, PVOID customData; INT32 status, retValue = 0, size; PCHAR pCurPtr, pBuffer; + CHAR dateHdrBuffer[MAX_DATE_HEADER_BUFFER_LENGTH+1]; PBYTE pEndPtr; PBYTE* ppStartPtr; PLwsCallInfo pLwsCallInfo; PRequestInfo pRequestInfo = NULL; PSingleListNode pCurNode; - UINT64 item; + UINT64 item, serverTime; UINT32 headerCount; UINT32 logLevel; PRequestHeader pRequestHeader; PSignalingClient pSignalingClient = NULL; BOOL locked = FALSE; + time_t td; + SIZE_T len; + UINT64 nowTime, clockSkew = 0; + PStateMachineState pStateMachineState; + BOOL skewMapContains = FALSE; DLOGV("HTTPS callback with reason %d", reason); @@ -64,6 +70,8 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, } pSignalingClient = pLwsCallInfo->pSignalingClient; + nowTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); + pRequestInfo = pLwsCallInfo->callInfo.pRequestInfo; pBuffer = pLwsCallInfo->buffer + LWS_PRE; @@ -93,10 +101,47 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: status = lws_http_client_http_response(wsi); - DLOGD("Connected with server response: %d", status); + getStateMachineCurrentState(pSignalingClient->pStateMachine, &pStateMachineState); + DLOGD("Connected with server response: %d", status); pLwsCallInfo->callInfo.callResult = getServiceCallResultFromHttpStatus((UINT32) status); + len = (SIZE_T)lws_hdr_copy(wsi, &dateHdrBuffer[0], MAX_DATE_HEADER_BUFFER_LENGTH, WSI_TOKEN_HTTP_DATE); + + time(&td); + + if (len) { + // on failure to parse lws_http_date_unix returns non zero value + if (0 == lws_http_date_parse_unix(&dateHdrBuffer[0], len, &td)) { + DLOGV("Date Header Returned By Server: %s", dateHdrBuffer); + + serverTime = ((UINT64) td) * HUNDREDS_OF_NANOS_IN_A_SECOND; + + if (serverTime > nowTime + MIN_CLOCK_SKEW_TIME_TO_CORRECT) { + // Server time is ahead + clockSkew = (serverTime - nowTime); + DLOGD("Detected Clock Skew! Server time is AHEAD of Device time: Server time: %" PRIu64 ", now time: %" PRIu64, serverTime, + nowTime); + } else if (nowTime > serverTime + MIN_CLOCK_SKEW_TIME_TO_CORRECT) { + clockSkew = (nowTime - serverTime); + clockSkew |= ((UINT64)(1ULL << 63)); + DLOGD("Detected Clock Skew! Device time is AHEAD of Server time: Server time: %" PRIu64 ", now time: %" PRIu64, serverTime, + nowTime); + // PIC hashTable implementation only stores UINT64 so I will flip the sign of the msb + // This limits the range of the max clock skew we can represent to just under 2925 years. + } + + hashTableContains(pSignalingClient->diagnostics.pEndpointToClockSkewHashMap, pStateMachineState->state, &skewMapContains); + if (clockSkew > 0) { + hashTablePut(pSignalingClient->diagnostics.pEndpointToClockSkewHashMap, pStateMachineState->state, clockSkew); + } else if (clockSkew == 0 && skewMapContains) { + // This means the item is in the map so at one point there was a clock skew offset but it has been corrected + // So we should no longer be correcting for a clock skew, remove this item from the map + hashTableRemove(pSignalingClient->diagnostics.pEndpointToClockSkewHashMap, pStateMachineState->state); + } + } + } + // Store the Request ID header if ((size = lws_hdr_custom_copy(wsi, pBuffer, LWS_SCRATCH_BUFFER_SIZE, SIGNALING_REQUEST_ID_HEADER_NAME, (SIZEOF(SIGNALING_REQUEST_ID_HEADER_NAME) - 1) * SIZEOF(CHAR))) > 0) { @@ -113,10 +158,21 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, if (dataSize != 0) { CHK(NULL != (pLwsCallInfo->callInfo.responseData = (PCHAR) MEMALLOC(dataSize+1)), STATUS_NOT_ENOUGH_MEMORY); MEMCPY(pLwsCallInfo->callInfo.responseData, pDataIn, dataSize); - pLwsCallInfo->callInfo.responseDataLen = (UINT32) dataSize; pLwsCallInfo->callInfo.responseData[dataSize] = '\0'; + pLwsCallInfo->callInfo.responseDataLen = (UINT32) dataSize; + if (pLwsCallInfo->callInfo.callResult != SERVICE_CALL_RESULT_OK) { DLOGW("Received client http read response: %s", pLwsCallInfo->callInfo.responseData); + if (pLwsCallInfo->callInfo.callResult == SERVICE_CALL_FORBIDDEN) { + if (isCallResultSignatureExpired(&pLwsCallInfo->callInfo)) { + // Set more specific result, this is so in the state machine + // We don't call GetToken again rather RETRY the existing API (now with clock skew correction) + pLwsCallInfo->callInfo.callResult = SERVICE_CALL_SIGNATURE_EXPIRED; + } else if (isCallResultSignatureNotYetCurrent(&pLwsCallInfo->callInfo)) { + // server time is ahead + pLwsCallInfo->callInfo.callResult = SERVICE_CALL_SIGNATURE_NOT_YET_CURRENT; + } + } } else { DLOGV("Received client http read response: %s", pLwsCallInfo->callInfo.responseData); } @@ -320,7 +376,7 @@ INT32 lwsWssCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, P // Store the time when we connect for diagnostics MUTEX_LOCK(pSignalingClient->diagnosticsLock); - pSignalingClient->diagnostics.connectTime = GETTIME(); + pSignalingClient->diagnostics.connectTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); MUTEX_UNLOCK(pSignalingClient->diagnosticsLock); // Notify the listener thread @@ -638,6 +694,46 @@ STATUS lwsCompleteSync(PLwsCallInfo pCallInfo) return retStatus; } +BOOL isCallResultSignatureExpired(PCallInfo pCallInfo) { + return (STRNSTR(pCallInfo->responseData, "Signature expired", pCallInfo->responseDataLen) != NULL); +} + +BOOL isCallResultSignatureNotYetCurrent(PCallInfo pCallInfo) { + return (STRNSTR(pCallInfo->responseData, "Signature not yet current", pCallInfo->responseDataLen) != NULL); +} + +STATUS checkAndCorrectForClockSkew(PSignalingClient pSignalingClient, PRequestInfo pRequestInfo) { + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + + PStateMachineState pStateMachineState; + PHashTable pClockSkewMap; + UINT64 clockSkewOffset; + CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pStateMachineState)); + + pClockSkewMap = pSignalingClient->diagnostics.pEndpointToClockSkewHashMap; + + CHK_STATUS(hashTableGet(pClockSkewMap, pStateMachineState->state, &clockSkewOffset)); + + // if we made it here that means there is clock skew + if (clockSkewOffset & ((UINT64)(1ULL << 63))) { + clockSkewOffset ^= ((UINT64)(1ULL << 63)); + DLOGV("Detected device time is AHEAD of server time!"); + pRequestInfo->currentTime -= clockSkewOffset; + } else { + DLOGV("Detected server time is AHEAD of device time!"); + pRequestInfo->currentTime += clockSkewOffset; + } + + DLOGW("Clockskew corrected!"); + +CleanUp: + + + LEAVES(); + return retStatus; +} + ////////////////////////////////////////////////////////////////////////// // API calls ////////////////////////////////////////////////////////////////////////// @@ -667,13 +763,19 @@ STATUS describeChannelLws(PSignalingClient pSignalingClient, UINT64 time) // Prepare the json params for the call SNPRINTF(paramsJson, ARRAY_SIZE(paramsJson), DESCRIBE_CHANNEL_PARAM_JSON_TEMPLATE, pSignalingClient->pChannelInfo->pChannelName); - // Create the request info with the body CHK_STATUS(createRequestInfo(url, paramsJson, pSignalingClient->pChannelInfo->pRegion, pSignalingClient->pChannelInfo->pCertPath, NULL, NULL, SSL_CERTIFICATE_TYPE_NOT_SPECIFIED, pSignalingClient->pChannelInfo->pUserAgent, SIGNALING_SERVICE_API_CALL_CONNECTION_TIMEOUT, SIGNALING_SERVICE_API_CALL_COMPLETION_TIMEOUT, DEFAULT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_TIME_LIMIT, pSignalingClient->pAwsCredentials, &pRequestInfo)); + // createRequestInfo does not have access to the getCurrentTime callback, this hook is used for tests. + if (pSignalingClient->signalingClientCallbacks.getCurrentTimeFn != NULL) { + pRequestInfo->currentTime = pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); + } + + checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); + CHK_STATUS(createLwsCallInfo(pSignalingClient, pRequestInfo, PROTOCOL_INDEX_HTTPS, &pLwsCallInfo)); // Make a blocking call @@ -685,7 +787,8 @@ STATUS describeChannelLws(PSignalingClient pSignalingClient, UINT64 time) resultLen = pLwsCallInfo->callInfo.responseDataLen; // Early return if we have a non-success result - CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, retStatus); + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, + STATUS_SIGNALING_LWS_CALL_FAILED); // Parse the response jsmn_init(&parser); @@ -759,6 +862,10 @@ STATUS describeChannelLws(PSignalingClient pSignalingClient, UINT64 time) CleanUp: + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + freeLwsCallInfo(&pLwsCallInfo); LEAVES(); @@ -819,6 +926,12 @@ STATUS createChannelLws(PSignalingClient pSignalingClient, UINT64 time) SIGNALING_SERVICE_API_CALL_CONNECTION_TIMEOUT, SIGNALING_SERVICE_API_CALL_COMPLETION_TIMEOUT, DEFAULT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_TIME_LIMIT, pSignalingClient->pAwsCredentials, &pRequestInfo)); + if (pSignalingClient->signalingClientCallbacks.getCurrentTimeFn != NULL) { + pRequestInfo->currentTime = pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); + } + + checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); + CHK_STATUS(createLwsCallInfo(pSignalingClient, pRequestInfo, PROTOCOL_INDEX_HTTPS, &pLwsCallInfo)); // Make a blocking call @@ -830,7 +943,8 @@ STATUS createChannelLws(PSignalingClient pSignalingClient, UINT64 time) resultLen = pLwsCallInfo->callInfo.responseDataLen; // Early return if we have a non-success result - CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, retStatus); + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, + STATUS_SIGNALING_LWS_CALL_FAILED); // Parse out the ARN jsmn_init(&parser); @@ -854,6 +968,10 @@ STATUS createChannelLws(PSignalingClient pSignalingClient, UINT64 time) CleanUp: + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + freeLwsCallInfo(&pLwsCallInfo); LEAVES(); @@ -893,6 +1011,12 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) SIGNALING_SERVICE_API_CALL_CONNECTION_TIMEOUT, SIGNALING_SERVICE_API_CALL_COMPLETION_TIMEOUT, DEFAULT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_TIME_LIMIT, pSignalingClient->pAwsCredentials, &pRequestInfo)); + if (pSignalingClient->signalingClientCallbacks.getCurrentTimeFn != NULL) { + pRequestInfo->currentTime = pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); + } + + checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); + CHK_STATUS(createLwsCallInfo(pSignalingClient, pRequestInfo, PROTOCOL_INDEX_HTTPS, &pLwsCallInfo)); // Make a blocking call @@ -904,7 +1028,8 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) resultLen = pLwsCallInfo->callInfo.responseDataLen; // Early return if we have a non-success result - CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, retStatus); + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, + STATUS_SIGNALING_LWS_CALL_FAILED); // Parse and extract the endpoints jsmn_init(&parser); @@ -979,6 +1104,10 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) CleanUp: + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + freeLwsCallInfo(&pLwsCallInfo); LEAVES(); @@ -1024,6 +1153,12 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time) SIGNALING_SERVICE_API_CALL_CONNECTION_TIMEOUT, SIGNALING_SERVICE_API_CALL_COMPLETION_TIMEOUT, DEFAULT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_TIME_LIMIT, pSignalingClient->pAwsCredentials, &pRequestInfo)); + if (pSignalingClient->signalingClientCallbacks.getCurrentTimeFn != NULL) { + pRequestInfo->currentTime = pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); + } + + checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); + CHK_STATUS(createLwsCallInfo(pSignalingClient, pRequestInfo, PROTOCOL_INDEX_HTTPS, &pLwsCallInfo)); // Make a blocking call @@ -1035,7 +1170,8 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time) resultLen = pLwsCallInfo->callInfo.responseDataLen; // Early return if we have a non-success result - CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, retStatus); + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, + STATUS_SIGNALING_LWS_CALL_FAILED); // Parse the response jsmn_init(&parser); @@ -1100,6 +1236,10 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time) CleanUp: + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + freeLwsCallInfo(&pLwsCallInfo); LEAVES(); @@ -1141,6 +1281,12 @@ STATUS deleteChannelLws(PSignalingClient pSignalingClient, UINT64 time) SIGNALING_SERVICE_API_CALL_CONNECTION_TIMEOUT, SIGNALING_SERVICE_API_CALL_COMPLETION_TIMEOUT, DEFAULT_LOW_SPEED_LIMIT, DEFAULT_LOW_SPEED_TIME_LIMIT, pSignalingClient->pAwsCredentials, &pRequestInfo)); + if (pSignalingClient->signalingClientCallbacks.getCurrentTimeFn != NULL) { + pRequestInfo->currentTime = pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); + } + + checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); + CHK_STATUS(createLwsCallInfo(pSignalingClient, pRequestInfo, PROTOCOL_INDEX_HTTPS, &pLwsCallInfo)); // Make a blocking call @@ -1151,13 +1297,18 @@ STATUS deleteChannelLws(PSignalingClient pSignalingClient, UINT64 time) // Early return if we have a non-success result and it's not a resource not found result = ATOMIC_LOAD(&pSignalingClient->result); - CHK((SERVICE_CALL_RESULT) result == SERVICE_CALL_RESULT_OK || (SERVICE_CALL_RESULT) result == SERVICE_CALL_RESOURCE_NOT_FOUND, retStatus); + CHK((SERVICE_CALL_RESULT) result == SERVICE_CALL_RESULT_OK || (SERVICE_CALL_RESULT) result == SERVICE_CALL_RESOURCE_NOT_FOUND, + STATUS_SIGNALING_LWS_CALL_FAILED); // Mark the channel as deleted ATOMIC_STORE_BOOL(&pSignalingClient->deleted, TRUE); CleanUp: + if (STATUS_FAILED(retStatus)) { + DLOGE("Call Failed with Status: 0x%08x", retStatus); + } + freeLwsCallInfo(&pLwsCallInfo); LEAVES(); @@ -1382,14 +1533,12 @@ PVOID reconnectHandler(PVOID args) // thread but there is a slight chance of a race condition. CHK(!ATOMIC_LOAD_BOOL(&pSignalingClient->shutdown), retStatus); - // Set the time out before execution - pSignalingClient->stepUntil = GETTIME() + SIGNALING_CONNECT_STATE_TIMEOUT; - // Update the diagnostics info ATOMIC_INCREMENT(&pSignalingClient->diagnostics.numberOfReconnects); // Attempt to reconnect by driving the state machine to connected state - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, + SIGNALING_STATE_CONNECTED)); CleanUp: @@ -1475,7 +1624,7 @@ STATUS sendLwsMessage(PSignalingClient pSignalingClient, SIGNALING_MESSAGE_TYPE // In case of an Offer, package the ICE candidates only if we have a set of non-expired ICE configs if (messageType == SIGNALING_MESSAGE_TYPE_OFFER && pSignalingClient->iceConfigCount != 0 && - (curTime = GETTIME()) <= pSignalingClient->iceConfigExpiration && STATUS_SUCCEEDED(validateIceConfiguration(pSignalingClient))) { + (curTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient)) <= pSignalingClient->iceConfigExpiration && STATUS_SUCCEEDED(validateIceConfiguration(pSignalingClient))) { // Start the ice infos by copying the preamble, then the main body and then the ending STRCPY(encodedIceConfig, SIGNALING_ICE_SERVER_LIST_TEMPLATE_START); iceConfigLen = ARRAY_SIZE(SIGNALING_ICE_SERVER_LIST_TEMPLATE_START) - 1; // remove the null terminator @@ -1810,7 +1959,7 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT SAFE_MEMFREE(pSignalingMessageWrapper); // Iterate the state machinery - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, SIGNALING_STATE_CONNECTED)); CHK(FALSE, retStatus); break; @@ -1823,7 +1972,7 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT SAFE_MEMFREE(pSignalingMessageWrapper); // Iterate the state machinery - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, SIGNALING_STATE_CONNECTED)); CHK(FALSE, retStatus); break; diff --git a/src/source/Signaling/LwsApiCalls.h b/src/source/Signaling/LwsApiCalls.h index ce222e48ac..8d80748d0d 100644 --- a/src/source/Signaling/LwsApiCalls.h +++ b/src/source/Signaling/LwsApiCalls.h @@ -127,6 +127,11 @@ extern "C" { "\t\t\t\"Username\": \"%s\"\n" \ "\t\t}," +// max length for http date header, must follow RFC 7231, should be less than 32 characters +#define MAX_DATE_HEADER_BUFFER_LENGTH 64 + +#define MIN_CLOCK_SKEW_TIME_TO_CORRECT (5 * HUNDREDS_OF_NANOS_IN_A_MINUTE) + // Defining max bloat size per item in the JSON template #define ICE_SERVER_INFO_TEMPLATE_BLOAT_SIZE 128 @@ -163,7 +168,7 @@ extern "C" { // Check for the stale credentials #define CHECK_SIGNALING_CREDENTIALS_EXPIRATION(p) \ do { \ - if (GETTIME() >= (p)->pAwsCredentials->expiration) { \ + if (SIGNALING_GET_CURRENT_TIME((p)) >= (p)->pAwsCredentials->expiration) { \ ATOMIC_STORE(&(p)->result, (SIZE_T) SERVICE_CALL_NOT_AUTHORIZED); \ CHK(FALSE, retStatus); \ } \ @@ -233,6 +238,9 @@ PVOID reconnectHandler(PVOID); INT32 lwsHttpCallbackRoutine(struct lws*, enum lws_callback_reasons, PVOID, PVOID, size_t); INT32 lwsWssCallbackRoutine(struct lws*, enum lws_callback_reasons, PVOID, PVOID, size_t); +BOOL isCallResultSignatureExpired(PCallInfo); +BOOL isCallResultSignatureNotYetCurrent(PCallInfo); + STATUS describeChannelLws(PSignalingClient, UINT64); STATUS createChannelLws(PSignalingClient, UINT64); STATUS getChannelEndpointLws(PSignalingClient, UINT64); diff --git a/src/source/Signaling/Signaling.c b/src/source/Signaling/Signaling.c index f906df16d0..b1d5ee02e4 100644 --- a/src/source/Signaling/Signaling.c +++ b/src/source/Signaling/Signaling.c @@ -28,7 +28,7 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf // Allocate enough storage CHK(NULL != (pSignalingClient = (PSignalingClient) MEMCALLOC(1, SIZEOF(SignalingClient))), STATUS_NOT_ENOUGH_MEMORY); - // Initialize the listener and restarter thread trackers + // Initialize the listener and restart thread trackers CHK_STATUS(initializeThreadTracker(&pSignalingClient->listenerTracker)); CHK_STATUS(initializeThreadTracker(&pSignalingClient->reconnecterTracker)); @@ -37,6 +37,8 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf CHK_STATUS(validateSignalingCallbacks(pSignalingClient, pCallbacks)); CHK_STATUS(validateSignalingClientInfo(pSignalingClient, pClientInfo)); + pSignalingClient->version = SIGNALING_CLIENT_CURRENT_VERSION; + // Set invalid call times pSignalingClient->describeTime = INVALID_TIMESTAMP_VALUE; pSignalingClient->createTime = INVALID_TIMESTAMP_VALUE; @@ -72,6 +74,8 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf // Store the credential provider pSignalingClient->pCredentialProvider = pCredentialProvider; + CHK_STATUS(configureRetryStrategyForSignalingStateMachine(pSignalingClient)); + // Create the state machine CHK_STATUS(createStateMachine(SIGNALING_STATE_MACHINE_STATES, SIGNALING_STATE_MACHINE_STATE_COUNT, CUSTOM_DATA_FROM_SIGNALING_CLIENT(pSignalingClient), signalingGetCurrentTime, @@ -146,14 +150,13 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf CHK(pSignalingClient->pLwsContext != NULL, STATUS_SIGNALING_LWS_CREATE_CONTEXT_FAILED); // Initializing the diagnostics mostly is taken care of by zero-mem in MEMCALLOC - pSignalingClient->diagnostics.createTime = GETTIME(); + pSignalingClient->diagnostics.createTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); + CHK_STATUS(hashTableCreateWithParams(SIGNALING_CLOCKSKEW_HASH_TABLE_BUCKET_COUNT,SIGNALING_CLOCKSKEW_HASH_TABLE_BUCKET_LENGTH, + &pSignalingClient->diagnostics.pEndpointToClockSkewHashMap)); // At this point we have constructed the main object and we can assign to the returned pointer *ppSignalingClient = pSignalingClient; - // Set the time out before execution - pSignalingClient->stepUntil = pSignalingClient->diagnostics.createTime + SIGNALING_CREATE_TIMEOUT; - // Notify of the state change initially as the state machinery is already in the NEW state if (pSignalingClient->signalingClientCallbacks.stateChangeFn != NULL) { CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pStateMachineState)); @@ -164,11 +167,13 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf // Do not force ice config state ATOMIC_STORE_BOOL(&pSignalingClient->refreshIceConfig, FALSE); - // Prime the state machine - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, STATUS_SUCCESS)); + // We do not cache token in file system, so we will always have to retrieve one after creating the client. + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, pSignalingClient->diagnostics.createTime + SIGNALING_CONNECT_STATE_TIMEOUT, SIGNALING_STATE_GET_TOKEN)); CleanUp: - + if (pClientInfo != NULL && pSignalingClient != NULL) { + pClientInfo->signalingClientInfo.stateMachineRetryCountReadOnly = pSignalingClient->diagnostics.stateMachineRetryCount; + } CHK_LOG_ERR(retStatus); if (STATUS_FAILED(retStatus)) { @@ -207,10 +212,14 @@ STATUS freeSignaling(PSignalingClient* ppSignalingClient) freeStateMachine(pSignalingClient->pStateMachine); + freeClientRetryStrategy(pSignalingClient); + freeChannelInfo(&pSignalingClient->pChannelInfo); stackQueueFree(pSignalingClient->pMessageQueue); + hashTableFree(pSignalingClient->diagnostics.pEndpointToClockSkewHashMap); + if (IS_VALID_MUTEX_VALUE(pSignalingClient->connectedLock)) { MUTEX_FREE(pSignalingClient->connectedLock); } @@ -268,6 +277,69 @@ STATUS freeSignaling(PSignalingClient* ppSignalingClient) return retStatus; } +STATUS setupDefaultRetryStrategyForSignalingStateMachine(PSignalingClient pSignalingClient) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PKvsRetryStrategyCallbacks pKvsRetryStrategyCallbacks = &(pSignalingClient->clientInfo.signalingStateMachineRetryStrategyCallbacks); + + // Use default as exponential backoff wait + pKvsRetryStrategyCallbacks->createRetryStrategyFn = exponentialBackoffRetryStrategyCreate; + pKvsRetryStrategyCallbacks->freeRetryStrategyFn = exponentialBackoffRetryStrategyFree; + pKvsRetryStrategyCallbacks->executeRetryStrategyFn = getExponentialBackoffRetryStrategyWaitTime; + pKvsRetryStrategyCallbacks->getCurrentRetryAttemptNumberFn = getExponentialBackoffRetryCount; + + // Use a default exponential backoff config for state machine level retries + pSignalingClient->clientInfo.signalingStateMachineRetryStrategy.pRetryStrategyConfig = + (PRetryStrategyConfig) &DEFAULT_SIGNALING_STATE_MACHINE_EXPONENTIAL_BACKOFF_RETRY_CONFIGURATION; + + LEAVES(); + return retStatus; +} + +STATUS configureRetryStrategyForSignalingStateMachine(PSignalingClient pSignalingClient) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PKvsRetryStrategyCallbacks pKvsRetryStrategyCallbacks = NULL; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + pKvsRetryStrategyCallbacks = &(pSignalingClient->clientInfo.signalingStateMachineRetryStrategyCallbacks); + + // If the callbacks for retry strategy are already set, then use that otherwise + // build the client with a default retry strategy. + if (pKvsRetryStrategyCallbacks->createRetryStrategyFn == NULL || pKvsRetryStrategyCallbacks->freeRetryStrategyFn == NULL || + pKvsRetryStrategyCallbacks->executeRetryStrategyFn == NULL || pKvsRetryStrategyCallbacks->getCurrentRetryAttemptNumberFn == NULL) { + CHK_STATUS(setupDefaultRetryStrategyForSignalingStateMachine(pSignalingClient)); + } + + CHK_STATUS(pKvsRetryStrategyCallbacks->createRetryStrategyFn(&(pSignalingClient->clientInfo.signalingStateMachineRetryStrategy))); + +CleanUp: + + LEAVES(); + return retStatus; +} + +STATUS freeClientRetryStrategy(PSignalingClient pSignalingClient) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PKvsRetryStrategyCallbacks pKvsRetryStrategyCallbacks = NULL; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + pKvsRetryStrategyCallbacks = &(pSignalingClient->clientInfo.signalingStateMachineRetryStrategyCallbacks); + CHK(pKvsRetryStrategyCallbacks->freeRetryStrategyFn != NULL, STATUS_SUCCESS); + + CHK_STATUS(pKvsRetryStrategyCallbacks->freeRetryStrategyFn(&(pSignalingClient->clientInfo.signalingStateMachineRetryStrategy))); + +CleanUp: + + LEAVES(); + return retStatus; +} + STATUS terminateOngoingOperations(PSignalingClient pSignalingClient) { ENTERS(); @@ -323,7 +395,7 @@ STATUS signalingSendMessageSync(PSignalingClient pSignalingClient, PSignalingMes return retStatus; } -STATUS signalingGetIceConfigInfoCout(PSignalingClient pSignalingClient, PUINT32 pIceConfigCount) +STATUS signalingGetIceConfigInfoCount(PSignalingClient pSignalingClient, PUINT32 pIceConfigCount) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; @@ -364,6 +436,42 @@ STATUS signalingGetIceConfigInfo(PSignalingClient pSignalingClient, UINT32 index return retStatus; } +STATUS signalingFetchSync(PSignalingClient pSignalingClient) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + SIZE_T result; + + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + // Check if we are already not connected + if (ATOMIC_LOAD_BOOL(&pSignalingClient->connected)) { + CHK_STATUS(terminateOngoingOperations(pSignalingClient)); + } + + // move to the fromGetToken() so we can move to the necessary step + // We start from get token to keep the design consistent with how it was when the constructor (create) + // would bring you to the READY state, but this is a two-way door and can be redone later. + setStateMachineCurrentState(pSignalingClient->pStateMachine, SIGNALING_STATE_GET_TOKEN); + + //if we're not failing from a bad token, set the result to OK to that fromGetToken will move + //to getEndpoint, describe, or create. If it is bad, keep reiterating on token. + result = ATOMIC_LOAD(&pSignalingClient->result); + if (result != SERVICE_CALL_NOT_AUTHORIZED) { + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_OK); + } + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, SIGNALING_STATE_READY)); + +CleanUp: + + if (STATUS_FAILED(retStatus)) { + resetStateMachineRetryCount(pSignalingClient->pStateMachine); + } + CHK_LOG_ERR(retStatus); + LEAVES(); + return retStatus; +} + STATUS signalingConnectSync(PSignalingClient pSignalingClient) { ENTERS(); @@ -373,22 +481,17 @@ STATUS signalingConnectSync(PSignalingClient pSignalingClient) CHK(pSignalingClient != NULL, STATUS_NULL_ARG); // Validate the state - CHK_STATUS(acceptStateMachineState(pSignalingClient->pStateMachine, - SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_CONNECTED)); + CHK_STATUS(acceptSignalingStateMachineState( + pSignalingClient, SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_CONNECTED)); // Check if we are already connected CHK(!ATOMIC_LOAD_BOOL(&pSignalingClient->connected), retStatus); - // Self-prime through the ready state - pSignalingClient->continueOnReady = TRUE; - // Store the signaling state in case we error/timeout so we can re-set it on exit CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pState)); - // Set the time out before execution - pSignalingClient->stepUntil = GETTIME() + SIGNALING_CONNECT_STATE_TIMEOUT; - - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, + SIGNALING_STATE_CONNECTED)); CleanUp: @@ -411,9 +514,6 @@ STATUS signalingDisconnectSync(PSignalingClient pSignalingClient) CHK(pSignalingClient != NULL, STATUS_NULL_ARG); - // Do not self-prime through the ready state - pSignalingClient->continueOnReady = FALSE; - // Check if we are already not connected CHK(ATOMIC_LOAD_BOOL(&pSignalingClient->connected), retStatus); @@ -421,7 +521,7 @@ STATUS signalingDisconnectSync(PSignalingClient pSignalingClient) ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_OK); - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_DISCONNECT_STATE_TIMEOUT, SIGNALING_STATE_READY)); CleanUp: @@ -449,10 +549,7 @@ STATUS signalingDeleteSync(PSignalingClient pSignalingClient) // Set the state directly setStateMachineCurrentState(pSignalingClient->pStateMachine, SIGNALING_STATE_DELETE); - // Set the time out before execution - pSignalingClient->stepUntil = GETTIME() + SIGNALING_DELETE_TIMEOUT; - - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_DELETE_TIMEOUT, SIGNALING_STATE_DELETED)); CleanUp: @@ -548,7 +645,7 @@ STATUS validateIceConfiguration(PSignalingClient pSignalingClient) CHK(minTtl > ICE_CONFIGURATION_REFRESH_GRACE_PERIOD, STATUS_SIGNALING_ICE_TTL_LESS_THAN_GRACE_PERIOD); - pSignalingClient->iceConfigTime = GETTIME(); + pSignalingClient->iceConfigTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); pSignalingClient->iceConfigExpiration = pSignalingClient->iceConfigTime + (minTtl - ICE_CONFIGURATION_REFRESH_GRACE_PERIOD); CleanUp: @@ -567,19 +664,22 @@ STATUS refreshIceConfiguration(PSignalingClient pSignalingClient) CHAR iceRefreshErrMsg[SIGNALING_MAX_ERROR_MESSAGE_LEN + 1]; UINT32 iceRefreshErrLen; UINT64 curTime; + BOOL locked = FALSE; CHK(pSignalingClient != NULL, STATUS_NULL_ARG); DLOGD("Refreshing the ICE Server Configuration"); // Check whether we have a valid not-yet-expired ICE configuration and if so early exit - curTime = GETTIME(); + curTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); CHK(pSignalingClient->iceConfigCount == 0 || curTime > pSignalingClient->iceConfigExpiration, retStatus); // ICE config can be retrieved in specific states only - CHK_STATUS(acceptStateMachineState(pSignalingClient->pStateMachine, - SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DISCONNECTED)); + CHK_STATUS(acceptSignalingStateMachineState( + pSignalingClient, SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DISCONNECTED)); + MUTEX_LOCK(pSignalingClient->stateLock); + locked = TRUE; // Get and store the current state to re-set to if we fail CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pStateMachineState)); @@ -589,14 +689,15 @@ STATUS refreshIceConfiguration(PSignalingClient pSignalingClient) // Iterate the state machinery in steady states only - ready or connected if (pStateMachineState->state == SIGNALING_STATE_READY || pStateMachineState->state == SIGNALING_STATE_CONNECTED) { - // Set the time out before execution - pSignalingClient->stepUntil = curTime + SIGNALING_REFRESH_ICE_CONFIG_STATE_TIMEOUT; - - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, curTime + SIGNALING_REFRESH_ICE_CONFIG_STATE_TIMEOUT, pStateMachineState->state)); } CleanUp: + if (locked) { + MUTEX_UNLOCK(pSignalingClient->stateLock); + } + CHK_LOG_ERR(retStatus); // Notify the client in case of an error @@ -632,6 +733,7 @@ STATUS signalingStoreOngoingMessage(PSignalingClient pSignalingClient, PSignalin CHK(pSignalingClient != NULL && pSignalingMessage != NULL, STATUS_NULL_ARG); MUTEX_LOCK(pSignalingClient->messageQueueLock); locked = TRUE; + CHK_STATUS(signalingGetOngoingMessage(pSignalingClient, pSignalingMessage->correlationId, pSignalingMessage->peerClientId, &pExistingMessage)); CHK(pExistingMessage == NULL, STATUS_SIGNALING_DUPLICATE_MESSAGE_BEING_SENT); CHK_STATUS(stackQueueEnqueue(pSignalingClient->pMessageQueue, (UINT64) pSignalingMessage)); @@ -811,7 +913,6 @@ STATUS describeChannel(PSignalingClient pSignalingClient, UINT64 time) CHK(pSignalingClient != NULL, STATUS_NULL_ARG); THREAD_SLEEP_UNTIL(time); - // Check for the stale credentials CHECK_SIGNALING_CREDENTIALS_EXPIRATION(pSignalingClient); @@ -841,8 +942,8 @@ STATUS describeChannel(PSignalingClient pSignalingClient, UINT64 time) } if (STATUS_SUCCEEDED(retStatus)) { - retStatus = describeChannelLws(pSignalingClient, time); + retStatus = describeChannelLws(pSignalingClient, time); // Store the last call time on success if (STATUS_SUCCEEDED(retStatus)) { pSignalingClient->describeTime = time; @@ -863,10 +964,6 @@ STATUS describeChannel(PSignalingClient pSignalingClient, UINT64 time) CleanUp: - if (STATUS_FAILED(retStatus) && pSignalingClient != NULL) { - ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_UNKNOWN); - } - LEAVES(); return retStatus; } @@ -909,10 +1006,6 @@ STATUS createChannel(PSignalingClient pSignalingClient, UINT64 time) CleanUp: - if (STATUS_FAILED(retStatus) && pSignalingClient != NULL) { - ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_UNKNOWN); - } - LEAVES(); return retStatus; } @@ -992,10 +1085,6 @@ STATUS getChannelEndpoint(PSignalingClient pSignalingClient, UINT64 time) CleanUp: - if (STATUS_FAILED(retStatus) && pSignalingClient != NULL) { - ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_UNKNOWN); - } - LEAVES(); return retStatus; } @@ -1037,10 +1126,6 @@ STATUS getIceConfig(PSignalingClient pSignalingClient, UINT64 time) CleanUp: - if (STATUS_FAILED(retStatus) && pSignalingClient != NULL) { - ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_UNKNOWN); - } - LEAVES(); return retStatus; } @@ -1083,10 +1168,6 @@ STATUS deleteChannel(PSignalingClient pSignalingClient, UINT64 time) CleanUp: - if (STATUS_FAILED(retStatus) && pSignalingClient != NULL) { - ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_UNKNOWN); - } - LEAVES(); return retStatus; } @@ -1146,7 +1227,9 @@ STATUS signalingGetMetrics(PSignalingClient pSignalingClient, PSignalingClientMe { ENTERS(); STATUS retStatus = STATUS_SUCCESS; - UINT64 curTime = GETTIME(); + UINT64 curTime; + + curTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); CHK(pSignalingClient != NULL && pSignalingClientMetrics != NULL, STATUS_NULL_ARG); CHK(pSignalingClientMetrics->version <= SIGNALING_CLIENT_METRICS_CURRENT_VERSION, STATUS_SIGNALING_INVALID_METRICS_VERSION); @@ -1167,6 +1250,7 @@ STATUS signalingGetMetrics(PSignalingClient pSignalingClient, PSignalingClientMe pSignalingClientMetrics->signalingClientStats.connectionDuration = ATOMIC_LOAD_BOOL(&pSignalingClient->connected) ? curTime - pSignalingClient->diagnostics.connectTime : 0; + pSignalingClientMetrics->signalingClientStats.apiCallRetryCount = pSignalingClient->diagnostics.stateMachineRetryCount; MUTEX_UNLOCK(pSignalingClient->diagnosticsLock); diff --git a/src/source/Signaling/Signaling.h b/src/source/Signaling/Signaling.h index 61a038b404..dd00e1d980 100644 --- a/src/source/Signaling/Signaling.h +++ b/src/source/Signaling/Signaling.h @@ -14,8 +14,8 @@ extern "C" { #define SIGNALING_REQUEST_ID_HEADER_NAME KVS_REQUEST_ID_HEADER_NAME ":" // Signaling client from custom data conversion -#define SIGNALING_CLIENT_FROM_CUSTOM_DATA(h) ((PSignalingClient) (h)) -#define CUSTOM_DATA_FROM_SIGNALING_CLIENT(p) ((UINT64) (p)) +#define SIGNALING_CLIENT_FROM_CUSTOM_DATA(h) ((PSignalingClient)(h)) +#define CUSTOM_DATA_FROM_SIGNALING_CLIENT(p) ((UINT64)(p)) // Grace period for refreshing the ICE configuration #define ICE_CONFIGURATION_REFRESH_GRACE_PERIOD (30 * HUNDREDS_OF_NANOS_IN_A_SECOND) @@ -53,13 +53,21 @@ extern "C" { // Max libWebSockets protocol count. IMPORTANT: Ensure it's 1 + PROTOCOL_INDEX_WSS #define LWS_PROTOCOL_COUNT 2 +/** + * Default signaling clockskew (endpoint --> clockskew) hash table bucket count/length + */ +#define SIGNALING_CLOCKSKEW_HASH_TABLE_BUCKET_LENGTH 2 +#define SIGNALING_CLOCKSKEW_HASH_TABLE_BUCKET_COUNT MIN_HASH_BUCKET_COUNT // 16 + // API call latency calculation #define SIGNALING_API_LATENCY_CALCULATION(pClient, time, isCpApi) \ MUTEX_LOCK((pClient)->diagnosticsLock); \ if (isCpApi) { \ - (pClient)->diagnostics.cpApiLatency = EMA_ACCUMULATOR_GET_NEXT((pClient)->diagnostics.cpApiLatency, GETTIME() - (time)); \ + (pClient)->diagnostics.cpApiLatency = EMA_ACCUMULATOR_GET_NEXT((pClient)->diagnostics.cpApiLatency, \ + SIGNALING_GET_CURRENT_TIME((pClient)) - (time)); \ } else { \ - (pClient)->diagnostics.dpApiLatency = EMA_ACCUMULATOR_GET_NEXT((pClient)->diagnostics.dpApiLatency, GETTIME() - (time)); \ + (pClient)->diagnostics.dpApiLatency = EMA_ACCUMULATOR_GET_NEXT((pClient)->diagnostics.dpApiLatency, \ + SIGNALING_GET_CURRENT_TIME((pClient)) - (time)); \ } \ MUTEX_UNLOCK((pClient)->diagnosticsLock); @@ -68,6 +76,40 @@ extern "C" { ATOMIC_INCREMENT(&(pClient)->diagnostics.numberOfErrors); \ } +#define IS_CURRENT_TIME_CALLBACK_SET(pClient) ((pClient) != NULL && ((pClient)->signalingClientCallbacks.getCurrentTimeFn != NULL)) + +#define SIGNALING_GET_CURRENT_TIME(pClient) (IS_CURRENT_TIME_CALLBACK_SET((pClient)) ? \ + ((pClient)->signalingClientCallbacks.getCurrentTimeFn((pClient)->signalingClientCallbacks.customData)) : \ + GETTIME()) + +#define DEFAULT_CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS 7 + +static const ExponentialBackoffRetryStrategyConfig DEFAULT_SIGNALING_STATE_MACHINE_EXPONENTIAL_BACKOFF_RETRY_CONFIGURATION = { + /* Exponential wait times with this config will look like following - + ************************************ + * Retry Count * Wait time * + * ********************************** + * 1 * 100ms + jitter * + * 2 * 200ms + jitter * + * 3 * 400ms + jitter * + * 4 * 800ms + jitter * + * 5 * 1600ms + jitter * + * 6 * 3200ms + jitter * + * 7 * 6400ms + jitter * + * 8 * 10000ms + jitter * + * 9 * 10000ms + jitter * + * 10 * 10000ms + jitter * + ************************************ + jitter = random number between [0, wait time) + */ + KVS_INFINITE_EXPONENTIAL_RETRIES, /* max retry count */ + 10000, /* max retry wait time in milliseconds */ + 100, /* factor determining exponential curve in milliseconds */ + DEFAULT_KVS_MIN_TIME_TO_RESET_RETRY_STATE_MILLISECONDS, /* minimum time in milliseconds to reset retry state */ + FULL_JITTER, /* use full jitter variant */ + 0 /* jitter value unused for full jitter variant */ +}; + // Forward declaration typedef struct __LwsCallInfo* PLwsCallInfo; @@ -107,6 +149,10 @@ typedef struct { SignalingApiCallHookFunc connectPostHookFn; SignalingApiCallHookFunc deletePreHookFn; SignalingApiCallHookFunc deletePostHookFn; + + // Retry strategy used for signaling state machine + KvsRetryStrategy signalingStateMachineRetryStrategy; + KvsRetryStrategyCallbacks signalingStateMachineRetryStrategyCallbacks; } SignalingClientInfoInternal, *PSignalingClientInfoInternal; /** @@ -133,12 +179,17 @@ typedef struct { UINT64 connectTime; UINT64 cpApiLatency; UINT64 dpApiLatency; + PHashTable pEndpointToClockSkewHashMap; + UINT32 stateMachineRetryCount; } SignalingDiagnostics, PSignalingDiagnostics; /** * Internal representation of the Signaling client. */ typedef struct { + // Current version of the structure + UINT32 version; + // Current service call result volatile SIZE_T result; @@ -170,9 +221,6 @@ typedef struct { // Indicates that there is another thread attempting to grab the service lock volatile ATOMIC_BOOL serviceLockContention; - // Current version of the structure - UINT32 version; - // Stored Client info SignalingClientInfoInternal clientInfo; @@ -209,9 +257,6 @@ typedef struct { // Service call context ServiceCallContext serviceCallContext; - // Indicates whether to self-prime on Ready or not - BOOL continueOnReady; - // Interlocking the state transitions MUTEX stateLock; @@ -233,9 +278,6 @@ typedef struct { // Conditional variable for receiving response to the sent message CVAR receiveCvar; - // Execute the state machine until this time - UINT64 stepUntil; - // Indicates when the ICE configuration has been retrieved UINT64 iceConfigTime; @@ -288,15 +330,16 @@ typedef struct { } SignalingClient, *PSignalingClient; // Public handle to and from object converters -#define TO_SIGNALING_CLIENT_HANDLE(p) ((SIGNALING_CLIENT_HANDLE) (p)) -#define FROM_SIGNALING_CLIENT_HANDLE(h) (IS_VALID_SIGNALING_CLIENT_HANDLE(h) ? (PSignalingClient) (h) : NULL) +#define TO_SIGNALING_CLIENT_HANDLE(p) ((SIGNALING_CLIENT_HANDLE)(p)) +#define FROM_SIGNALING_CLIENT_HANDLE(h) (IS_VALID_SIGNALING_CLIENT_HANDLE(h) ? (PSignalingClient)(h) : NULL) STATUS createSignalingSync(PSignalingClientInfoInternal, PChannelInfo, PSignalingClientCallbacks, PAwsCredentialProvider, PSignalingClient*); STATUS freeSignaling(PSignalingClient*); STATUS signalingSendMessageSync(PSignalingClient, PSignalingMessage); -STATUS signalingGetIceConfigInfoCout(PSignalingClient, PUINT32); +STATUS signalingGetIceConfigInfoCount(PSignalingClient, PUINT32); STATUS signalingGetIceConfigInfo(PSignalingClient, UINT32, PIceConfigInfo*); +STATUS signalingFetchSync(PSignalingClient); STATUS signalingConnectSync(PSignalingClient); STATUS signalingDisconnectSync(PSignalingClient); STATUS signalingDeleteSync(PSignalingClient); @@ -327,6 +370,10 @@ STATUS connectSignalingChannel(PSignalingClient, UINT64); STATUS deleteChannel(PSignalingClient, UINT64); STATUS signalingGetMetrics(PSignalingClient, PSignalingClientMetrics); +STATUS configureRetryStrategyForSignalingStateMachine(PSignalingClient); +STATUS setupDefaultRetryStrategyForSignalingStateMachine(PSignalingClient); +STATUS freeClientRetryStrategy(PSignalingClient); + #ifdef __cplusplus } #endif diff --git a/src/source/Signaling/StateMachine.c b/src/source/Signaling/StateMachine.c index 0868e9be4a..45c1bea1f2 100644 --- a/src/source/Signaling/StateMachine.c +++ b/src/source/Signaling/StateMachine.c @@ -8,90 +8,135 @@ * Static definitions of the states */ StateMachineState SIGNALING_STATE_MACHINE_STATES[] = { - {SIGNALING_STATE_NEW, SIGNALING_STATE_NONE | SIGNALING_STATE_NEW, fromNewSignalingState, executeNewSignalingState, INFINITE_RETRY_COUNT_SENTINEL, - STATUS_SIGNALING_INVALID_READY_STATE}, + {SIGNALING_STATE_NEW, SIGNALING_STATE_NONE | SIGNALING_STATE_NEW, fromNewSignalingState, executeNewSignalingState, + defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_INVALID_READY_STATE}, {SIGNALING_STATE_GET_TOKEN, SIGNALING_STATE_NEW | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DELETE | SIGNALING_STATE_GET_TOKEN, - fromGetTokenSignalingState, executeGetTokenSignalingState, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_GET_TOKEN_CALL_FAILED}, + fromGetTokenSignalingState, executeGetTokenSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + STATUS_SIGNALING_GET_TOKEN_CALL_FAILED}, {SIGNALING_STATE_DESCRIBE, SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_CONNECT | - SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DELETE | SIGNALING_STATE_DESCRIBE, - fromDescribeSignalingState, executeDescribeSignalingState, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_DESCRIBE_CALL_FAILED}, + SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DELETE | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_READY | SIGNALING_STATE_DISCONNECTED, + fromDescribeSignalingState, executeDescribeSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + STATUS_SIGNALING_DESCRIBE_CALL_FAILED}, {SIGNALING_STATE_CREATE, SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE, fromCreateSignalingState, executeCreateSignalingState, - SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_CREATE_CALL_FAILED}, + defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_CREATE_CALL_FAILED}, {SIGNALING_STATE_GET_ENDPOINT, SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_GET_ENDPOINT, - fromGetEndpointSignalingState, executeGetEndpointSignalingState, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + fromGetEndpointSignalingState, executeGetEndpointSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_GET_ENDPOINT_CALL_FAILED}, {SIGNALING_STATE_GET_ICE_CONFIG, SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_READY | SIGNALING_STATE_GET_ICE_CONFIG, - fromGetIceConfigSignalingState, executeGetIceConfigSignalingState, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + fromGetIceConfigSignalingState, executeGetIceConfigSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_GET_ICE_CONFIG_CALL_FAILED}, {SIGNALING_STATE_READY, SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_READY, fromReadySignalingState, - executeReadySignalingState, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_READY_CALLBACK_FAILED}, + executeReadySignalingState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_READY_CALLBACK_FAILED}, {SIGNALING_STATE_CONNECT, SIGNALING_STATE_READY | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_CONNECT, - fromConnectSignalingState, executeConnectSignalingState, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_CONNECT_CALL_FAILED}, + fromConnectSignalingState, executeConnectSignalingState, defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, + STATUS_SIGNALING_CONNECT_CALL_FAILED}, {SIGNALING_STATE_CONNECTED, SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED, fromConnectedSignalingState, executeConnectedSignalingState, - INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_CONNECTED_CALLBACK_FAILED}, + defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_CONNECTED_CALLBACK_FAILED}, {SIGNALING_STATE_DISCONNECTED, SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED, fromDisconnectedSignalingState, - executeDisconnectedSignalingState, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_DISCONNECTED_CALLBACK_FAILED}, + executeDisconnectedSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + STATUS_SIGNALING_DISCONNECTED_CALLBACK_FAILED}, {SIGNALING_STATE_DELETE, SIGNALING_STATE_GET_TOKEN | SIGNALING_STATE_DESCRIBE | SIGNALING_STATE_CREATE | SIGNALING_STATE_GET_ENDPOINT | SIGNALING_STATE_GET_ICE_CONFIG | SIGNALING_STATE_READY | SIGNALING_STATE_CONNECT | SIGNALING_STATE_CONNECTED | SIGNALING_STATE_DISCONNECTED | SIGNALING_STATE_DELETE, - fromDeleteSignalingState, executeDeleteSignalingState, SIGNALING_STATES_DEFAULT_RETRY_COUNT, STATUS_SIGNALING_DELETE_CALL_FAILED}, + fromDeleteSignalingState, executeDeleteSignalingState, defaultSignalingStateTransitionHook, SIGNALING_STATES_DEFAULT_RETRY_COUNT, + STATUS_SIGNALING_DELETE_CALL_FAILED}, {SIGNALING_STATE_DELETED, SIGNALING_STATE_DELETE | SIGNALING_STATE_DELETED, fromDeletedSignalingState, executeDeletedSignalingState, - INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_DELETE_CALL_FAILED}, + defaultSignalingStateTransitionHook, INFINITE_RETRY_COUNT_SENTINEL, STATUS_SIGNALING_DELETE_CALL_FAILED}, }; UINT32 SIGNALING_STATE_MACHINE_STATE_COUNT = ARRAY_SIZE(SIGNALING_STATE_MACHINE_STATES); -STATUS stepSignalingStateMachine(PSignalingClient pSignalingClient, STATUS status) +STATUS defaultSignalingStateTransitionHook(UINT64 customData /* customData should be PSignalingClient */, PUINT64 stateTransitionWaitTime) { ENTERS(); STATUS retStatus = STATUS_SUCCESS; - UINT32 i; - BOOL locked = FALSE; - UINT64 currentTime; + STATUS countStatus = STATUS_SUCCESS; + PSignalingClient pSignalingClient = NULL; + PKvsRetryStrategy pSignalingStateMachineRetryStrategy = NULL; + PKvsRetryStrategyCallbacks pSignalingStateMachineRetryStrategyCallbacks = NULL; + UINT64 retryWaitTime = 0; + + pSignalingClient = SIGNALING_CLIENT_FROM_CUSTOM_DATA(customData); + CHK(pSignalingClient != NULL && stateTransitionWaitTime != NULL, STATUS_NULL_ARG); + + pSignalingStateMachineRetryStrategy = &(pSignalingClient->clientInfo.signalingStateMachineRetryStrategy); + pSignalingStateMachineRetryStrategyCallbacks = &(pSignalingClient->clientInfo.signalingStateMachineRetryStrategyCallbacks); + + // result > SERVICE_CALL_RESULT_OK covers case for - + // result != SERVICE_CALL_RESULT_NOT_SET and != SERVICE_CALL_RESULT_OK + // If we support any other 2xx service call results, the condition + // should change to (pSignalingClient->result > 299 && ..) + CHK(pSignalingClient->result > SERVICE_CALL_RESULT_OK && pSignalingStateMachineRetryStrategyCallbacks->executeRetryStrategyFn != NULL, + STATUS_SUCCESS); + + // A retry is considered only after executeRetry is executed. This will avoid publishing count + 1 + if(pSignalingStateMachineRetryStrategyCallbacks->getCurrentRetryAttemptNumberFn != NULL) { + if((countStatus = pSignalingStateMachineRetryStrategyCallbacks->getCurrentRetryAttemptNumberFn(pSignalingStateMachineRetryStrategy, &pSignalingClient->diagnostics.stateMachineRetryCount)) != STATUS_SUCCESS) { + DLOGW("Failed to get retry count. Error code: %08x", countStatus); + } else { + DLOGD("Retry count: %llu", pSignalingClient->diagnostics.stateMachineRetryCount); + } + } + DLOGV("Signaling Client base result is [%u]. Executing KVS retry handler of retry strategy type [%u]", + pSignalingClient->result, pSignalingStateMachineRetryStrategy->retryStrategyType); + pSignalingStateMachineRetryStrategyCallbacks->executeRetryStrategyFn(pSignalingStateMachineRetryStrategy, &retryWaitTime); + *stateTransitionWaitTime = retryWaitTime; - CHK(pSignalingClient != NULL, STATUS_NULL_ARG); +CleanUp: - // Check for a shutdown - CHK(!ATOMIC_LOAD_BOOL(&pSignalingClient->shutdown), retStatus); + LEAVES(); + return retStatus; +} + +STATUS signalingStateMachineIterator(PSignalingClient pSignalingClient, UINT64 expiration, UINT64 finalState) +{ + ENTERS(); + UINT64 currentTime; + UINT32 i; + STATUS retStatus = STATUS_SUCCESS; + PStateMachineState pState = NULL; + BOOL locked = FALSE; MUTEX_LOCK(pSignalingClient->stateLock); locked = TRUE; - // Check if an error and the retry is OK - if (!pSignalingClient->pChannelInfo->retry && STATUS_FAILED(status)) { - CHK(FALSE, status); - } + while (TRUE) { + CHK(pSignalingClient != NULL, STATUS_NULL_ARG); + + CHK(!ATOMIC_LOAD_BOOL(&pSignalingClient->shutdown), retStatus); + + if (STATUS_FAILED(retStatus)) { + CHK(pSignalingClient->pChannelInfo->retry, retStatus); + for (i = 0; i < SIGNALING_STATE_MACHINE_STATE_COUNT; i++) { + CHK(retStatus != SIGNALING_STATE_MACHINE_STATES[i].status, SIGNALING_STATE_MACHINE_STATES[i].status); + } + } - currentTime = GETTIME(); + currentTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); - CHK(pSignalingClient->stepUntil == 0 || currentTime <= pSignalingClient->stepUntil, STATUS_OPERATION_TIMED_OUT); + CHK(expiration == 0 || currentTime <= expiration, STATUS_OPERATION_TIMED_OUT); - // Check if the status is any of the retry/failed statuses - if (STATUS_FAILED(status)) { - for (i = 0; i < SIGNALING_STATE_MACHINE_STATE_COUNT; i++) { - CHK(status != SIGNALING_STATE_MACHINE_STATES[i].status, SIGNALING_STATE_MACHINE_STATES[i].status); + // Fix-up the expired credentials transition + // NOTE: Api Gateway might not return an error that can be interpreted as unauthorized to + // make the correct transition to auth integration state. + if (retStatus == STATUS_SERVICE_CALL_NOT_AUTHORIZED_ERROR || + (pSignalingClient->pAwsCredentials != NULL && pSignalingClient->pAwsCredentials->expiration < currentTime)) { + // Set the call status as auth error + ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_NOT_AUTHORIZED); } - } - // Fix-up the expired credentials transition - // NOTE: Api Gateway might not return an error that can be interpreted as unauthorized to - // make the correct transition to auth integration state. - if (status == STATUS_SERVICE_CALL_NOT_AUTHORIZED_ERROR || - (SERVICE_CALL_UNKNOWN == (SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) && - pSignalingClient->pAwsCredentials->expiration < currentTime)) { - // Set the call status as auth error - ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_NOT_AUTHORIZED); - } + retStatus = stepStateMachine(pSignalingClient->pStateMachine); - // Step the state machine - CHK_STATUS(stepStateMachine(pSignalingClient->pStateMachine)); + CHK_STATUS(getStateMachineCurrentState(pSignalingClient->pStateMachine, &pState)); + CHK(!(pState->state == finalState), STATUS_SUCCESS); + } CleanUp: @@ -275,11 +320,13 @@ STATUS executeGetTokenSignalingState(UINT64 customData, UINT64 time) SIGNALING_CLIENT_STATE_GET_CREDENTIALS)); } + THREAD_SLEEP_UNTIL(time); + // Use the credential provider to get the token retStatus = pSignalingClient->pCredentialProvider->getCredentialsFn(pSignalingClient->pCredentialProvider, &pSignalingClient->pAwsCredentials); // Check the expiration - if (NULL == pSignalingClient->pAwsCredentials || GETTIME() >= pSignalingClient->pAwsCredentials->expiration) { + if (NULL == pSignalingClient->pAwsCredentials || SIGNALING_GET_CURRENT_TIME(pSignalingClient) >= pSignalingClient->pAwsCredentials->expiration) { serviceCallResult = SERVICE_CALL_NOT_AUTHORIZED; } else { serviceCallResult = SERVICE_CALL_RESULT_OK; @@ -287,12 +334,6 @@ STATUS executeGetTokenSignalingState(UINT64 customData, UINT64 time) ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) serviceCallResult); - // Self-prime the next state - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - - // Reset the ret status - retStatus = STATUS_SUCCESS; - CleanUp: LEAVES(); @@ -361,11 +402,6 @@ STATUS executeDescribeSignalingState(UINT64 customData, UINT64 time) // Call the aggregate function retStatus = describeChannel(pSignalingClient, time); - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - - // Reset the ret status - retStatus = STATUS_SUCCESS; - CleanUp: LEAVES(); @@ -424,11 +460,6 @@ STATUS executeCreateSignalingState(UINT64 customData, UINT64 time) // Call the aggregate function retStatus = createChannel(pSignalingClient, time); - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - - // Reset the ret status - retStatus = STATUS_SUCCESS; - CleanUp: LEAVES(); @@ -459,7 +490,6 @@ STATUS fromGetEndpointSignalingState(UINT64 customData, PUINT64 pState) default: break; } - *pState = state; CleanUp: @@ -487,11 +517,6 @@ STATUS executeGetEndpointSignalingState(UINT64 customData, UINT64 time) // Call the aggregate function retStatus = getChannelEndpoint(pSignalingClient, time); - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - - // Reset the ret status - retStatus = STATUS_SUCCESS; - CleanUp: LEAVES(); @@ -550,11 +575,6 @@ STATUS executeGetIceConfigSignalingState(UINT64 customData, UINT64 time) // Call the aggregate function retStatus = getIceConfig(pSignalingClient, time); - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - - // Reset the ret status - retStatus = STATUS_SUCCESS; - CleanUp: LEAVES(); @@ -619,16 +639,6 @@ STATUS executeReadySignalingState(UINT64 customData, UINT64 time) SIGNALING_CLIENT_STATE_READY)); } - if (pSignalingClient->continueOnReady) { - // Self-prime the connect - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - } else { - // Reset the timeout for the state machine - pSignalingClient->stepUntil = 0; - } - - // Reset the ret status - retStatus = STATUS_SUCCESS; CleanUp: LEAVES(); @@ -719,11 +729,6 @@ STATUS executeConnectSignalingState(UINT64 customData, UINT64 time) retStatus = connectSignalingChannel(pSignalingClient, time); - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - - // Reset the ret status - retStatus = STATUS_SUCCESS; - CleanUp: LEAVES(); @@ -759,10 +764,11 @@ STATUS fromConnectedSignalingState(UINT64 customData, PUINT64 pState) break; case SERVICE_CALL_INTERNAL_ERROR: - state = SIGNALING_STATE_GET_ENDPOINT; - break; - case SERVICE_CALL_BAD_REQUEST: + case SERVICE_CALL_NETWORK_CONNECTION_TIMEOUT: + case SERVICE_CALL_NETWORK_READ_TIMEOUT: + case SERVICE_CALL_REQUEST_TIMEOUT: + case SERVICE_CALL_GATEWAY_TIMEOUT: state = SIGNALING_STATE_GET_ENDPOINT; break; @@ -805,11 +811,6 @@ STATUS executeConnectedSignalingState(UINT64 customData, UINT64 time) SIGNALING_CLIENT_STATE_CONNECTED)); } - // Reset the timeout for the state machine - MUTEX_LOCK(pSignalingClient->stateLock); - pSignalingClient->stepUntil = 0; - MUTEX_UNLOCK(pSignalingClient->stateLock); - CleanUp: LEAVES(); @@ -826,9 +827,6 @@ STATUS fromDisconnectedSignalingState(UINT64 customData, PUINT64 pState) CHK(pSignalingClient != NULL && pState != NULL, STATUS_NULL_ARG); - // See if we need to retry first of all - CHK(pSignalingClient->pChannelInfo->reconnect, STATUS_SUCCESS); - result = ATOMIC_LOAD(&pSignalingClient->result); switch (result) { case SERVICE_CALL_FORBIDDEN: @@ -870,9 +868,6 @@ STATUS executeDisconnectedSignalingState(UINT64 customData, UINT64 time) SIGNALING_CLIENT_STATE_DISCONNECTED)); } - // Self-prime the next state - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - CleanUp: LEAVES(); @@ -940,11 +935,6 @@ STATUS executeDeleteSignalingState(UINT64 customData, UINT64 time) // Call the aggregate function retStatus = deleteChannel(pSignalingClient, time); - CHK_STATUS(stepSignalingStateMachine(pSignalingClient, retStatus)); - - // Reset the ret status - retStatus = STATUS_SUCCESS; - CleanUp: LEAVES(); diff --git a/src/source/Signaling/StateMachine.h b/src/source/Signaling/StateMachine.h index ef48eb31cd..545596cd89 100644 --- a/src/source/Signaling/StateMachine.h +++ b/src/source/Signaling/StateMachine.h @@ -31,7 +31,7 @@ extern "C" { #define INFINITE_RETRY_COUNT_SENTINEL 0 // Whether to step the state machine -STATUS stepSignalingStateMachine(PSignalingClient, STATUS); +STATUS signalingStateMachineIterator(PSignalingClient, UINT64, UINT64); STATUS acceptSignalingStateMachineState(PSignalingClient, UINT64); SIGNALING_CLIENT_STATE getSignalingStateFromStateMachineState(UINT64); @@ -64,6 +64,8 @@ STATUS executeDeleteSignalingState(UINT64, UINT64); STATUS fromDeletedSignalingState(UINT64, PUINT64); STATUS executeDeletedSignalingState(UINT64, UINT64); +STATUS defaultSignalingStateTransitionHook(UINT64, PUINT64); + #ifdef __cplusplus } #endif diff --git a/tst/MetricsApiTest.cpp b/tst/MetricsApiTest.cpp index 926e9dcb8c..0e014421e3 100644 --- a/tst/MetricsApiTest.cpp +++ b/tst/MetricsApiTest.cpp @@ -72,7 +72,7 @@ TEST_F(MetricsApiTest, webRtcIceServerGetMetrics) EXPECT_EQ(STATUS_SUCCESS, rtcPeerConnectionGetMetrics(pRtcPeerConnection, NULL, &rtcIceMetrics)); EXPECT_EQ(443, rtcIceMetrics.rtcStatsObject.iceServerStats.port); EXPECT_PRED_FORMAT2(testing::IsSubstring, configuration.iceServers[1].urls, rtcIceMetrics.rtcStatsObject.iceServerStats.url); - EXPECT_PRED_FORMAT2(testing::IsSubstring, "transport=tcp", rtcIceMetrics.rtcStatsObject.iceServerStats.protocol); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "tcp", rtcIceMetrics.rtcStatsObject.iceServerStats.protocol); EXPECT_EQ(STATUS_SUCCESS, closePeerConnection(pRtcPeerConnection)); EXPECT_EQ(STATUS_SUCCESS, freePeerConnection(&pRtcPeerConnection)); diff --git a/tst/SdpApiTest.cpp b/tst/SdpApiTest.cpp index b9b470ea2a..cff77583c9 100644 --- a/tst/SdpApiTest.cpp +++ b/tst/SdpApiTest.cpp @@ -428,6 +428,29 @@ TEST_F(SdpApiTest, populateSingleMediaSection_TestTxRecvOnly) freePeerConnection(&offerPc); } +TEST_F(SdpApiTest, populateSingleMediaSection_TestTxRecvOnlyStreamNull) +{ + PRtcPeerConnection offerPc = NULL; + RtcConfiguration configuration; + RtcSessionDescriptionInit sessionDescriptionInit; + + MEMSET(&configuration, 0x00, SIZEOF(RtcConfiguration)); + + // Create peer connection + EXPECT_EQ(createPeerConnection(&configuration, &offerPc), STATUS_SUCCESS); + + PRtcRtpTransceiver pTransceiver; + RtcRtpTransceiverInit rtcRtpTransceiverInit; + rtcRtpTransceiverInit.direction = RTC_RTP_TRANSCEIVER_DIRECTION_RECVONLY; + + EXPECT_EQ(STATUS_SUCCESS, addTransceiver(offerPc, NULL, &rtcRtpTransceiverInit, &pTransceiver)); + EXPECT_EQ(STATUS_SUCCESS, createOffer(offerPc, &sessionDescriptionInit)); + EXPECT_PRED_FORMAT2(testing::IsSubstring, "recvonly", sessionDescriptionInit.sdp); + + closePeerConnection(offerPc); + freePeerConnection(&offerPc); +} + TEST_F(SdpApiTest, populateSingleMediaSection_TestPayloadNoFmtp) { CHAR remoteSessionDescription[] = R"(v=0 diff --git a/tst/SignalingApiFunctionalityTest.cpp b/tst/SignalingApiFunctionalityTest.cpp index fba2c19c05..c88e812e55 100644 --- a/tst/SignalingApiFunctionalityTest.cpp +++ b/tst/SignalingApiFunctionalityTest.cpp @@ -144,6 +144,16 @@ STATUS getIceConfigPreHook(UINT64 hookCustomData) return retStatus; } +UINT64 getCurrentTimeFastClock(UINT64 customData) { + UNUSED_PARAM(customData); + return GETTIME() + (30 * HUNDREDS_OF_NANOS_IN_A_MINUTE); +} + +UINT64 getCurrentTimeSlowClock(UINT64 customData) { + UNUSED_PARAM(customData); + return GETTIME() - (30 * HUNDREDS_OF_NANOS_IN_A_MINUTE); +} + STATUS connectPreHook(UINT64 hookCustomData) { STATUS retStatus = STATUS_SUCCESS; @@ -189,6 +199,22 @@ STATUS getEndpointPreHook(UINT64 hookCustomData) return retStatus; }; +VOID setupSignalingStateMachineRetryStrategyCallbacks(PSignalingClientInfoInternal pSignalingClientInfoInternal) +{ + pSignalingClientInfoInternal->signalingStateMachineRetryStrategyCallbacks.createRetryStrategyFn = SignalingApiFunctionalityTest::createRetryStrategyFn; + pSignalingClientInfoInternal->signalingStateMachineRetryStrategyCallbacks.getCurrentRetryAttemptNumberFn = SignalingApiFunctionalityTest::getCurrentRetryAttemptNumberFn; + pSignalingClientInfoInternal->signalingStateMachineRetryStrategyCallbacks.freeRetryStrategyFn = SignalingApiFunctionalityTest::freeRetryStrategyFn; + pSignalingClientInfoInternal->signalingStateMachineRetryStrategyCallbacks.executeRetryStrategyFn = SignalingApiFunctionalityTest::executeRetryStrategyFn; +} + +VOID setupSignalingStateMachineRetryStrategyCallbacks(PSignalingClientInfo pSignalingClientInfo) +{ + pSignalingClientInfo->signalingRetryStrategyCallbacks.createRetryStrategyFn = SignalingApiFunctionalityTest::createRetryStrategyFn; + pSignalingClientInfo->signalingRetryStrategyCallbacks.getCurrentRetryAttemptNumberFn = SignalingApiFunctionalityTest::getCurrentRetryAttemptNumberFn; + pSignalingClientInfo->signalingRetryStrategyCallbacks.freeRetryStrategyFn = SignalingApiFunctionalityTest::freeRetryStrategyFn; + pSignalingClientInfo->signalingRetryStrategyCallbacks.executeRetryStrategyFn = SignalingApiFunctionalityTest::executeRetryStrategyFn; +} + //////////////////////////////////////////////////////////////////// // Functionality Tests //////////////////////////////////////////////////////////////////// @@ -208,11 +234,14 @@ TEST_F(SignalingApiFunctionalityTest, basicCreateConnectFree) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; clientInfo.cacheFilePath = NULL; + clientInfo.signalingClientCreationMaxRetryAttempts = 0; STRCPY(clientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -230,6 +259,7 @@ TEST_F(SignalingApiFunctionalityTest, basicCreateConnectFree) EXPECT_EQ(STATUS_SUCCESS, createSignalingClientSync(&clientInfo, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); // Connect twice - the second time will be no-op EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); @@ -240,6 +270,35 @@ TEST_F(SignalingApiFunctionalityTest, basicCreateConnectFree) EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); } +TEST_F(SignalingApiFunctionalityTest, basicCreateWithRetries) +{ + if (!mAccessKeyIdSet) { + return; + } + + SignalingClientInfo clientInfo; + SignalingClientCallbacks signalingClientCallbacks; + SIGNALING_CLIENT_HANDLE signalingHandle = INVALID_SIGNALING_CLIENT_HANDLE_VALUE; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + + clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; + clientInfo.cacheFilePath = NULL; + clientInfo.signalingClientCreationMaxRetryAttempts = 3; + STRCPY(clientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); + + // We would retry 'signalingClientCreationMaxRetryAttempts' times and fail all three times + EXPECT_EQ(STATUS_NULL_ARG, + createSignalingClientSync(&clientInfo, NULL, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + &signalingHandle)); +} + TEST_F(SignalingApiFunctionalityTest, mockMaster) { ChannelInfo channelInfo; @@ -265,11 +324,14 @@ TEST_F(SignalingApiFunctionalityTest, mockMaster) signalingClientCallbacks.messageReceivedFn = masterMessageReceived; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; clientInfo.cacheFilePath = NULL; + clientInfo.signalingClientCreationMaxRetryAttempts = 0; STRCPY(clientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -310,6 +372,7 @@ TEST_F(SignalingApiFunctionalityTest, mockMaster) EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); } + EXPECT_EQ(expectedStatus,signalingClientFetchSync(signalingHandle)); pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingHandle); pActiveClient = pSignalingClient; @@ -400,11 +463,15 @@ TEST_F(SignalingApiFunctionalityTest, mockViewer) signalingClientCallbacks.messageReceivedFn = viewerMessageReceived; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; clientInfo.cacheFilePath = NULL; + clientInfo.signalingClientCreationMaxRetryAttempts = 0; STRCPY(clientInfo.clientId, TEST_SIGNALING_VIEWER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -443,9 +510,11 @@ TEST_F(SignalingApiFunctionalityTest, mockViewer) EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); } + EXPECT_EQ(expectedStatus,signalingClientFetchSync(signalingHandle)); pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingHandle); pActiveClient = pSignalingClient; + // Connect to the signaling client EXPECT_EQ(expectedStatus, signalingClientConnectSync(signalingHandle)); @@ -499,11 +568,15 @@ TEST_F(SignalingApiFunctionalityTest, invalidChannelInfoInput) signalingClientCallbacks.customData = (UINT64) this; signalingClientCallbacks.messageReceivedFn = (SignalingClientMessageReceivedFunc) 1; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; STRCPY(clientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; clientInfo.cacheFilePath = NULL; + clientInfo.signalingClientCreationMaxRetryAttempts = 0; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -536,21 +609,6 @@ TEST_F(SignalingApiFunctionalityTest, invalidChannelInfoInput) MEMSET(tempBuf, 'X', SIZEOF(tempBuf)); tempBuf[ARRAY_SIZE(tempBuf) - 1] = '\0'; - EXPECT_NE(STATUS_SUCCESS, - createSignalingClientSync(&clientInfo, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, - &signalingHandle)); - EXPECT_FALSE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - - pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingHandle); - pActiveClient = pSignalingClient; - - EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); - EXPECT_FALSE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - - // Free again - EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); - EXPECT_FALSE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); - // Invalid version channelInfo.version++; retStatus = createSignalingClientSync(&clientInfo, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, @@ -705,10 +763,13 @@ TEST_F(SignalingApiFunctionalityTest, invalidChannelInfoInput) signalingClientCallbacks.messageReceivedFn = viewerMessageReceived; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; + clientInfo.signalingClientCreationMaxRetryAttempts = 0; STRCPY(clientInfo.clientId, TEST_SIGNALING_VIEWER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -725,10 +786,11 @@ TEST_F(SignalingApiFunctionalityTest, invalidChannelInfoInput) // channel name validation error - name with spaces channelInfo.pChannelName = (PCHAR) "Name With Spaces"; - retStatus = createSignalingClientSync(&clientInfo, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, + createSignalingClientSync(&clientInfo, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &signalingHandle); + retStatus = signalingClientFetchSync(signalingHandle); if (mAccessKeyIdSet) { - EXPECT_TRUE(retStatus == STATUS_OPERATION_TIMED_OUT || retStatus == STATUS_SIGNALING_DESCRIBE_CALL_FAILED); + EXPECT_TRUE(retStatus == STATUS_OPERATION_TIMED_OUT || retStatus == STATUS_SIGNALING_DESCRIBE_CALL_FAILED || retStatus == STATUS_SIGNALING_GET_TOKEN_CALL_FAILED); } else { EXPECT_EQ(STATUS_NULL_ARG, retStatus); } @@ -774,11 +836,14 @@ TEST_F(SignalingApiFunctionalityTest, iceReconnectEmulation) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; clientInfo.cacheFilePath = NULL; + clientInfo.signalingClientCreationMaxRetryAttempts = 0; STRCPY(clientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -802,6 +867,7 @@ TEST_F(SignalingApiFunctionalityTest, iceReconnectEmulation) pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingHandle); pActiveClient = pSignalingClient; + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); // Connect to the signaling client EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); @@ -859,12 +925,14 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedVariatio signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); // Set the ICE hook clientInfoInternal.hookCustomData = (UINT64) this; @@ -892,6 +960,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedVariatio EXPECT_EQ(STATUS_SUCCESS,createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -923,7 +992,6 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedVariatio for (i = 0; i < iceCount; i++) { EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); } - // Make sure no APIs have been called EXPECT_EQ(1, getIceConfigCount); @@ -1122,12 +1190,15 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedVariations) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); // Set the ICE hook clientInfoInternal.hookCustomData = (UINT64) this; @@ -1155,6 +1226,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedVariations) EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); @@ -1386,12 +1458,14 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedAuthExpi signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -1413,6 +1487,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedAuthExpi EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -1449,7 +1524,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedAuthExpi // Check the states - we should have failed on get credentials EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(23, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_EQ(5, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); @@ -1466,7 +1541,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedAuthExpi EXPECT_NE((UINT64) NULL, (UINT64) pIceConfigInfo); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(23, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_EQ(5, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); @@ -1506,12 +1581,14 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedAuthExpirat signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -1533,6 +1610,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedAuthExpirat EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); // Connect the client EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); @@ -1572,7 +1650,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedAuthExpirat // Check the states - we should have failed on get credentials EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(23, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_EQ(5, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); @@ -1589,7 +1667,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedAuthExpirat EXPECT_NE((UINT64) NULL, (UINT64) pIceConfigInfo); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(23, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_EQ(5, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); @@ -1629,6 +1707,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -1637,11 +1716,12 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.hookCustomData = (UINT64) this; clientInfoInternal.getIceConfigPreHookFn = getIceConfigPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); // Make it fail after the first call and recover after two failures on the 3rd call getIceConfigResult = STATUS_INVALID_OPERATION; getIceConfigFail = 1; - getIceConfigRecover = 3; + getIceConfigRecover = 1; MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -1660,6 +1740,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -1688,7 +1769,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); - EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); @@ -1701,7 +1782,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); - EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); @@ -1743,6 +1824,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithFaultIn signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -1751,11 +1833,12 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithFaultIn STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.hookCustomData = (UINT64) this; clientInfoInternal.getIceConfigPreHookFn = getIceConfigPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); // Make it fail after the first call and recover after two failures on the 3rd call getIceConfigResult = STATUS_INVALID_OPERATION; getIceConfigFail = 1; - getIceConfigRecover = 3; + getIceConfigRecover = 1; MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -1774,6 +1857,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithFaultIn EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -1817,7 +1901,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithFaultIn EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); - EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); @@ -1859,6 +1943,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -1867,6 +1952,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.hookCustomData = (UINT64) this; clientInfoInternal.getIceConfigPreHookFn = getIceConfigPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); // Make it fail after the first call and recover after two failures on the 3rd call getIceConfigResult = STATUS_INVALID_OPERATION; @@ -1890,6 +1976,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithFaul EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -1971,6 +2058,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithFaultIn signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -1979,6 +2067,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithFaultIn STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.hookCustomData = (UINT64) this; clientInfoInternal.getIceConfigPreHookFn = getIceConfigPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); // Make it fail after the first call and recover after two failures on the 3rd call getIceConfigResult = STATUS_INVALID_OPERATION; @@ -2002,6 +2091,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithFaultIn EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2086,12 +2176,14 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithBadA signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2110,6 +2202,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithBadA EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2127,7 +2220,11 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshNotConnectedWithBadA // Set bad auth info BYTE firstByte = pSignalingClient->pAwsCredentials->secretKey[0]; - pSignalingClient->pAwsCredentials->secretKey[0] = 'A'; + if(firstByte == 'A') { + pSignalingClient->pAwsCredentials->secretKey[0] = 'B'; + } else { + pSignalingClient->pAwsCredentials->secretKey[0] = 'A'; + } // Trigger the ICE refresh on the next call pSignalingClient->iceConfigCount = 0; @@ -2200,12 +2297,15 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithBadAuth signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2224,6 +2324,7 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithBadAuth EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); // Connect to the channel EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); @@ -2244,7 +2345,11 @@ TEST_F(SignalingApiFunctionalityTest, iceServerConfigRefreshConnectedWithBadAuth // Set bad auth info BYTE firstByte = pSignalingClient->pAwsCredentials->secretKey[0]; - pSignalingClient->pAwsCredentials->secretKey[0] = 'A'; + if(firstByte == 'A') { + pSignalingClient->pAwsCredentials->secretKey[0] = 'B'; + } else { + pSignalingClient->pAwsCredentials->secretKey[0] = 'A'; + } // Trigger the ICE refresh on the next call pSignalingClient->iceConfigCount = 0; @@ -2315,11 +2420,13 @@ TEST_F(SignalingApiFunctionalityTest, goAwayEmulation) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; clientInfo.cacheFilePath = NULL; STRCPY(clientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2339,6 +2446,7 @@ TEST_F(SignalingApiFunctionalityTest, goAwayEmulation) createSignalingClientSync(&clientInfo, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &signalingHandle)); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingHandle); pActiveClient = pSignalingClient; @@ -2398,11 +2506,14 @@ TEST_F(SignalingApiFunctionalityTest, unknownMessageTypeEmulation) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; clientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfo.loggingLevel = LOG_LEVEL_VERBOSE; clientInfo.cacheFilePath = NULL; + clientInfo.signalingClientCreationMaxRetryAttempts = 0; STRCPY(clientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfo); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2422,6 +2533,7 @@ TEST_F(SignalingApiFunctionalityTest, unknownMessageTypeEmulation) createSignalingClientSync(&clientInfo, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &signalingHandle)); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pSignalingClient = FROM_SIGNALING_CLIENT_HANDLE(signalingHandle); pActiveClient = pSignalingClient; @@ -2478,12 +2590,16 @@ TEST_F(SignalingApiFunctionalityTest, connectTimeoutEmulation) SignalingClientInfoInternal clientInfoInternal; PSignalingClient pSignalingClient; SIGNALING_CLIENT_HANDLE signalingHandle; + PKvsRetryStrategy pKvsRetryStrategy = NULL; + PKvsRetryStrategyCallbacks pKvsRetryStrategyCallbacks = NULL; + UINT32 retryCount = 0, previousRetryCount = 0, readyCount = 0, retryCountDiff = 0; signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; signalingClientCallbacks.customData = (UINT64) this; signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -2491,6 +2607,8 @@ TEST_F(SignalingApiFunctionalityTest, connectTimeoutEmulation) clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.connectTimeout = 100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); + pKvsRetryStrategyCallbacks = &(clientInfoInternal.signalingStateMachineRetryStrategyCallbacks); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2511,6 +2629,7 @@ TEST_F(SignalingApiFunctionalityTest, connectTimeoutEmulation) &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2526,6 +2645,13 @@ TEST_F(SignalingApiFunctionalityTest, connectTimeoutEmulation) EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + pKvsRetryStrategy = &(pSignalingClient->clientInfo.signalingStateMachineRetryStrategy); + + retryCount = 0; + pKvsRetryStrategyCallbacks->getCurrentRetryAttemptNumberFn(pKvsRetryStrategy, &retryCount); + // retry count is 1 because we moved from describe to create state since describe failed + EXPECT_EQ(1, retryCount); + // Connect to the signaling client - should time out EXPECT_EQ(STATUS_OPERATION_TIMED_OUT, signalingClientConnectSync(signalingHandle)); @@ -2541,11 +2667,23 @@ TEST_F(SignalingApiFunctionalityTest, connectTimeoutEmulation) EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + retryCount = 0; + pKvsRetryStrategyCallbacks->getCurrentRetryAttemptNumberFn(pKvsRetryStrategy, &retryCount); + EXPECT_LE(2, retryCount); + + readyCount = signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]; // Connect to the signaling client - should connect OK pSignalingClient->clientInfo.connectTimeout = 0; EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + retryCountDiff = (signalingStatesCounts[SIGNALING_CLIENT_STATE_READY] - readyCount) ? 0 : 1; + + previousRetryCount = retryCount + retryCountDiff; + retryCount = 0; + pKvsRetryStrategyCallbacks->getCurrentRetryAttemptNumberFn(pKvsRetryStrategy, &retryCount); + // retry count should not increase since there were no timeouts + EXPECT_EQ(previousRetryCount, retryCount); // Check that we are connected and can send a message SignalingMessage signalingMessage; @@ -2582,6 +2720,7 @@ TEST_F(SignalingApiFunctionalityTest, channelInfoArnSkipDescribe) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -2589,6 +2728,7 @@ TEST_F(SignalingApiFunctionalityTest, channelInfoArnSkipDescribe) clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.connectTimeout = 0; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2609,6 +2749,7 @@ TEST_F(SignalingApiFunctionalityTest, channelInfoArnSkipDescribe) &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2661,6 +2802,7 @@ TEST_F(SignalingApiFunctionalityTest, channelInfoArnSkipDescribe) &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2712,6 +2854,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedWithArn) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -2719,6 +2862,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedWithArn) clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.connectTimeout = 0; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2739,6 +2883,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedWithArn) &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2791,6 +2936,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedWithArn) &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2841,6 +2987,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedAuthExpiration) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -2848,6 +2995,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedAuthExpiration) clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); clientInfoInternal.connectTimeout = 0; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -2871,6 +3019,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedAuthExpiration) &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -2894,7 +3043,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedAuthExpiration) // Check the states - we should have failed on get credentials EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(12, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_LT(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); @@ -2903,7 +3052,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedAuthExpiration) EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); - EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DELETE]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DELETE]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DELETED]); // Reset it back right after the GetIce is called already @@ -2914,7 +3063,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedAuthExpiration) // Check the states - we should have got the credentials now and directly moved to delete EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); - EXPECT_EQ(13, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + EXPECT_LT(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); @@ -2923,7 +3072,7 @@ TEST_F(SignalingApiFunctionalityTest, deleteChannelCreatedAuthExpiration) EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); - EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_DELETE]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DELETE]); EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_DELETED]); // Shouldn't be able to connect as it's not in ready state @@ -2997,6 +3146,7 @@ TEST_F(SignalingApiFunctionalityTest, cachingWithFaultInjection) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -3007,6 +3157,7 @@ TEST_F(SignalingApiFunctionalityTest, cachingWithFaultInjection) clientInfoInternal.connectPreHookFn = connectPreHook; clientInfoInternal.describePreHookFn = describePreHook; clientInfoInternal.getEndpointPreHookFn = getEndpointPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); // Make describe and getendpoint fail once so we can check the no-caching behavior // in case when there is a failure. @@ -3039,6 +3190,7 @@ TEST_F(SignalingApiFunctionalityTest, cachingWithFaultInjection) &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); pActiveClient = pSignalingClient; @@ -3059,7 +3211,7 @@ TEST_F(SignalingApiFunctionalityTest, cachingWithFaultInjection) EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); // Validate the hook count - EXPECT_EQ(2, describeCount); + EXPECT_EQ(3, describeCount); EXPECT_EQ(2, getEndpointCount); // Connect to the signaling client and make it fail @@ -3081,7 +3233,7 @@ TEST_F(SignalingApiFunctionalityTest, cachingWithFaultInjection) EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); // Validate the hook count - EXPECT_EQ(2, describeCount); + EXPECT_EQ(3, describeCount); EXPECT_EQ(2, getEndpointCount); // Wait for the cache TTL to expire and retry @@ -3103,7 +3255,7 @@ TEST_F(SignalingApiFunctionalityTest, cachingWithFaultInjection) EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); // Validate the hook count is incremented due to cache miss - EXPECT_EQ(3, describeCount); + EXPECT_EQ(4, describeCount); EXPECT_EQ(3, getEndpointCount); EXPECT_EQ(STATUS_SUCCESS, signalingClientDisconnectSync(signalingHandle)); @@ -3134,6 +3286,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); @@ -3144,6 +3297,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) clientInfoInternal.connectPreHookFn = connectPreHook; clientInfoInternal.describePreHookFn = describePreHook; clientInfoInternal.getEndpointPreHookFn = getEndpointPreHook; + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -3168,6 +3322,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); } @@ -3184,11 +3339,13 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) &pSignalingClient)) << "Failed on channel name: " << channelInfo.pChannelName; + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + // Store the channel ARN to be used later STRCPY(channelArn, pSignalingClient->channelDescription.channelArn); - signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); - EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); // Repeat the same with the ARN only @@ -3201,6 +3358,7 @@ TEST_F(SignalingApiFunctionalityTest, fileCachingTest) signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); } @@ -3258,12 +3416,14 @@ TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer) signalingClientCallbacks.messageReceivedFn = NULL; signalingClientCallbacks.errorReportFn = signalingClientError; signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = NULL; MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + setupSignalingStateMachineRetryStrategyCallbacks(&clientInfoInternal); MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; @@ -3283,6 +3443,9 @@ TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer) signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + // Fetch credentials, describe, get endpoint, get ice config + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + // Connect to the channel EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); @@ -3422,6 +3585,607 @@ TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer) EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); } +TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer_SlowClockSkew) +{ + if (!mAccessKeyIdSet) { + return; + } + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + PSignalingClient pSignalingClient; + SIGNALING_CLIENT_HANDLE signalingHandle; + UINT32 i, iceCount; + PIceConfigInfo pIceConfigInfo; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = getCurrentTimeSlowClock; + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = mChannelName; + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + + EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + + // Connect to the channel + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + pActiveClient = pSignalingClient; + + // Connect to the signaling client + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + // Should have an exiting ICE configuration + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + // Describe is 3 because the first one requests in expired signature, second + // one results in 404 (not found because it doesn't exist) + // Then we call create which also fails once due to expired signature then succeeds + // the second time, then we call describe a 3rd time which succeeds + // At this point we have both fixed the clock skew offset in the code + // as well as created the channel we are calling describe on. + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Ensure the ICE is not refreshed as we already have a current non-expired set + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + EXPECT_NE(0, iceCount); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Trigger the ICE refresh immediately on any of the ICE accessor calls + pSignalingClient->iceConfigCount = 0; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + EXPECT_NE(0, iceCount); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Set to invalid again and trigger an update via offer message + pSignalingClient->iceConfigCount = 0; + + // Inject a reconnect ice server message + CHAR message[] = "{\n" + " \"messageType\": \"SDP_OFFER\",\n" + " \"senderClientId\": \"ProducerMaster\",\n" + " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" + " \"IceServerList\": [{\n" + " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " },\n" + " {\n" + " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " }\n" + " ],\n" + " \"statusResponse\": {\n" + " \"correlationId\": \"CorrelationID\",\n" + " \"errorType\": \"Unknown message\",\n" + " \"statusCode\": \"200\",\n" + " \"description\": \"Test attempt to send an unknown message\"\n" + " }\n" + "}"; + + EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); + + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // ICE should not have been called again as we updated it via a message + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Validate the retrieved info + EXPECT_EQ(2, iceCount); + + for (i = 0; i < iceCount; i++) { + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); + EXPECT_NE(0, pIceConfigInfo->uriCount); + EXPECT_EQ(298 * HUNDREDS_OF_NANOS_IN_A_SECOND, pIceConfigInfo->ttl); + EXPECT_EQ(SIGNALING_ICE_CONFIG_INFO_CURRENT_VERSION, pIceConfigInfo->version); + EXPECT_EQ(0, STRCMP("1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==", pIceConfigInfo->userName)); + } + + // + // Set to invalid again to trigger an update. + // The message will not update as the type is not an offer + // + pSignalingClient->iceConfigCount = 0; + + // Inject a reconnect ice server message + CHAR message2[] = "{\n" + " \"messageType\": \"SDP_ANSWER\",\n" + " \"senderClientId\": \"ProducerMaster\",\n" + " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" + " \"IceServerList\": [{\n" + " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " },\n" + " {\n" + " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " }\n" + " ],\n" + " \"statusResponse\": {\n" + " \"correlationId\": \"CorrelationID\",\n" + " \"errorType\": \"Unknown message\",\n" + " \"statusCode\": \"200\",\n" + " \"description\": \"Test attempt to send an unknown message\"\n" + " }\n" + "}"; + + EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message2, ARRAY_SIZE(message2))); + + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // ICE should have been called again as we couldn't have updated via the message + EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Check that we are connected and can send a message + SignalingMessage signalingMessage; + signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; + signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; + STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); + MEMSET(signalingMessage.payload, 'A', 100); + signalingMessage.payload[100] = '\0'; + signalingMessage.payloadLen = 0; + signalingMessage.correlationId[0] = '\0'; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + + deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + + +TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer_FastClockSkew) +{ + if (!mAccessKeyIdSet) { + return; + } + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + PSignalingClient pSignalingClient; + SIGNALING_CLIENT_HANDLE signalingHandle; + UINT32 i, iceCount; + PIceConfigInfo pIceConfigInfo; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = getCurrentTimeFastClock; + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = mChannelName; + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + + EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + + // Connect to the channel + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + pActiveClient = pSignalingClient; + + // Connect to the signaling client + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + // Should have an exiting ICE configuration + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + // Describe is 3 because the first one requests in expired signature, second + // one results in 404 (not found because it doesn't exist) + // Then we call create which also fails once due to expired signature then succeeds + // the second time, then we call describe a 3rd time which succeeds + // At this point we have both fixed the clock skew offset in the code + // as well as created the channel we are calling describe on. + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // Ensure the ICE is not refreshed as we already have a current non-expired set + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + EXPECT_NE(0, iceCount); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Trigger the ICE refresh immediately on any of the ICE accessor calls + pSignalingClient->iceConfigCount = 0; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + EXPECT_NE(0, iceCount); + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Set to invalid again and trigger an update via offer message + pSignalingClient->iceConfigCount = 0; + + // Inject a reconnect ice server message + CHAR message[] = "{\n" + " \"messageType\": \"SDP_OFFER\",\n" + " \"senderClientId\": \"ProducerMaster\",\n" + " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" + " \"IceServerList\": [{\n" + " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " },\n" + " {\n" + " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " }\n" + " ],\n" + " \"statusResponse\": {\n" + " \"correlationId\": \"CorrelationID\",\n" + " \"errorType\": \"Unknown message\",\n" + " \"statusCode\": \"200\",\n" + " \"description\": \"Test attempt to send an unknown message\"\n" + " }\n" + "}"; + + EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); + + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // ICE should not have been called again as we updated it via a message + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Validate the retrieved info + EXPECT_EQ(2, iceCount); + + for (i = 0; i < iceCount; i++) { + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); + EXPECT_NE(0, pIceConfigInfo->uriCount); + EXPECT_EQ(298 * HUNDREDS_OF_NANOS_IN_A_SECOND, pIceConfigInfo->ttl); + EXPECT_EQ(SIGNALING_ICE_CONFIG_INFO_CURRENT_VERSION, pIceConfigInfo->version); + EXPECT_EQ(0, STRCMP("1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==", pIceConfigInfo->userName)); + } + + // + // Set to invalid again to trigger an update. + // The message will not update as the type is not an offer + // + pSignalingClient->iceConfigCount = 0; + + // Inject a reconnect ice server message + CHAR message2[] = "{\n" + " \"messageType\": \"SDP_ANSWER\",\n" + " \"senderClientId\": \"ProducerMaster\",\n" + " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" + " \"IceServerList\": [{\n" + " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " },\n" + " {\n" + " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " }\n" + " ],\n" + " \"statusResponse\": {\n" + " \"correlationId\": \"CorrelationID\",\n" + " \"errorType\": \"Unknown message\",\n" + " \"statusCode\": \"200\",\n" + " \"description\": \"Test attempt to send an unknown message\"\n" + " }\n" + "}"; + + EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message2, ARRAY_SIZE(message2))); + + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // ICE should have been called again as we couldn't have updated via the message + EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Check that we are connected and can send a message + SignalingMessage signalingMessage; + signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; + signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; + STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); + MEMSET(signalingMessage.payload, 'A', 100); + signalingMessage.payload[100] = '\0'; + signalingMessage.payloadLen = 0; + signalingMessage.correlationId[0] = '\0'; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + + deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + +TEST_F(SignalingApiFunctionalityTest, receivingIceConfigOffer_FastClockSkew_VerifyOffsetRemovedWhenClockFixed) +{ + if (!mAccessKeyIdSet) { + return; + } + + ChannelInfo channelInfo; + SignalingClientCallbacks signalingClientCallbacks; + SignalingClientInfoInternal clientInfoInternal; + PSignalingClient pSignalingClient; + SIGNALING_CLIENT_HANDLE signalingHandle; + UINT32 i, iceCount; + PIceConfigInfo pIceConfigInfo; + STATUS retStatus = STATUS_SUCCESS; + + signalingClientCallbacks.version = SIGNALING_CLIENT_CALLBACKS_CURRENT_VERSION; + signalingClientCallbacks.customData = (UINT64) this; + signalingClientCallbacks.messageReceivedFn = NULL; + signalingClientCallbacks.errorReportFn = signalingClientError; + signalingClientCallbacks.stateChangeFn = signalingClientStateChanged; + signalingClientCallbacks.getCurrentTimeFn = getCurrentTimeFastClock; + + MEMSET(&clientInfoInternal, 0x00, SIZEOF(SignalingClientInfoInternal)); + + clientInfoInternal.signalingClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; + clientInfoInternal.signalingClientInfo.loggingLevel = mLogLevel; + STRCPY(clientInfoInternal.signalingClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + + MEMSET(&channelInfo, 0x00, SIZEOF(ChannelInfo)); + channelInfo.version = CHANNEL_INFO_CURRENT_VERSION; + channelInfo.pChannelName = mChannelName; + channelInfo.pKmsKeyId = NULL; + channelInfo.tagCount = 0; + channelInfo.pTags = NULL; + channelInfo.channelType = SIGNALING_CHANNEL_TYPE_SINGLE_MASTER; + channelInfo.channelRoleType = SIGNALING_CHANNEL_ROLE_TYPE_MASTER; + channelInfo.cachingPolicy = SIGNALING_API_CALL_CACHE_TYPE_NONE; + channelInfo.retry = TRUE; + channelInfo.reconnect = TRUE; + channelInfo.pCertPath = mCaCertPath; + channelInfo.messageTtl = TEST_SIGNALING_MESSAGE_TTL; + + EXPECT_EQ(STATUS_SUCCESS, createSignalingSync(&clientInfoInternal, &channelInfo, &signalingClientCallbacks, (PAwsCredentialProvider) mTestCredentialProvider, &pSignalingClient)); + signalingHandle = TO_SIGNALING_CLIENT_HANDLE(pSignalingClient); + EXPECT_TRUE(IS_VALID_SIGNALING_CLIENT_HANDLE(signalingHandle)); + EXPECT_EQ(STATUS_SUCCESS,signalingClientFetchSync(signalingHandle)); + + // Connect to the channel + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + pActiveClient = pSignalingClient; + + // Connect to the signaling client + EXPECT_EQ(STATUS_SUCCESS, signalingClientConnectSync(signalingHandle)); + + // Should have an exiting ICE configuration + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_NEW]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_CREDENTIALS]); + // Describe is 3 because the first one requests in expired signature, second + // one results in 404 (not found because it doesn't exist) + // Then we call create which also fails once due to expired signature then succeeds + // the second time, then we call describe a 3rd time which succeeds + // At this point we have both fixed the clock skew offset in the code + // as well as created the channel we are calling describe on. + EXPECT_EQ(3, signalingStatesCounts[SIGNALING_CLIENT_STATE_DESCRIBE]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_CREATE]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ENDPOINT]); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_READY]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTING]); + EXPECT_EQ(1, signalingStatesCounts[SIGNALING_CLIENT_STATE_CONNECTED]); + EXPECT_EQ(0, signalingStatesCounts[SIGNALING_CLIENT_STATE_DISCONNECTED]); + + // allow for timeouts, as the forced 403 from clock skew can result in a 1 time timeout. + // however it must succeed after that 1 failure. + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + EXPECT_NE(0, iceCount); + EXPECT_EQ(2, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // reset the current time callback to "fix" the clock + MUTEX_LOCK(pSignalingClient->diagnosticsLock); + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn = NULL; + MUTEX_UNLOCK(pSignalingClient->diagnosticsLock); + + // Trigger the ICE refresh immediately on any of the ICE accessor calls + pSignalingClient->iceConfigCount = 0; + + for(int i = 0; i < 2; i++) + { + retStatus = signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount); + if(retStatus == STATUS_SUCCESS) + { + break; + } + } + EXPECT_EQ(STATUS_SUCCESS, retStatus); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + EXPECT_NE(0, iceCount); + // Called an extra time because first time will fail with 403 + // Due to application of clock skew offset but after failure + // We will remove the offset correction from the map and retry + EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Set to invalid again and trigger an update via offer message + pSignalingClient->iceConfigCount = 0; + + // Inject a reconnect ice server message + CHAR message[] = "{\n" + " \"messageType\": \"SDP_OFFER\",\n" + " \"senderClientId\": \"ProducerMaster\",\n" + " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" + " \"IceServerList\": [{\n" + " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " },\n" + " {\n" + " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " }\n" + " ],\n" + " \"statusResponse\": {\n" + " \"correlationId\": \"CorrelationID\",\n" + " \"errorType\": \"Unknown message\",\n" + " \"statusCode\": \"200\",\n" + " \"description\": \"Test attempt to send an unknown message\"\n" + " }\n" + "}"; + + EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message, ARRAY_SIZE(message))); + + for(int i = 0; i < 2; i++) + { + retStatus = signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount); + if(retStatus == STATUS_SUCCESS) + { + break; + } + } + EXPECT_EQ(STATUS_SUCCESS, retStatus); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // ICE should not have been called again as we updated it via a message + EXPECT_EQ(4, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Validate the retrieved info + EXPECT_EQ(2, iceCount); + + for (i = 0; i < iceCount; i++) { + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, i, &pIceConfigInfo)); + EXPECT_NE(0, pIceConfigInfo->uriCount); + EXPECT_EQ(298 * HUNDREDS_OF_NANOS_IN_A_SECOND, pIceConfigInfo->ttl); + EXPECT_EQ(SIGNALING_ICE_CONFIG_INFO_CURRENT_VERSION, pIceConfigInfo->version); + EXPECT_EQ(0, STRCMP("1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==", pIceConfigInfo->userName)); + } + + // + // Set to invalid again to trigger an update. + // The message will not update as the type is not an offer + // + pSignalingClient->iceConfigCount = 0; + + // Inject a reconnect ice server message + CHAR message2[] = "{\n" + " \"messageType\": \"SDP_ANSWER\",\n" + " \"senderClientId\": \"ProducerMaster\",\n" + " \"messagePayload\": \"eyJ0eXBlIjogIm9mZmVyIiwgInNkcCI6ICJ2PTBcclxubz0tIDIwNTE4OTcyNDggMiBJTiBJUDQgMTI3LjAuMC4xXHJcbnM9LVxyXG50PTAgMFxyXG5hPWdyb3VwOkJVTkRMRSAwIDEgMlxyXG5hPW1zaWQtc2VtYW50aWM6IFdNUyBteUt2c1ZpZGVvU3RyZWFtXHJcbm09YXVkaW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMTFcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteUF1ZGlvVHJhY2tcclxuYT1zc3JjOjE4OTEzODY4OTYgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MTg5MTM4Njg5NiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlBdWRpb1RyYWNrXHJcbmE9c3NyYzoxODkxMzg2ODk2IG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MTg5MTM4Njg5NiBsYWJlbDpteUF1ZGlvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjBcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMTEgb3B1cy80ODAwMC8yXHJcbmE9Zm10cDoxMTEgbWlucHRpbWU9MTA7dXNlaW5iYW5kZmVjPTFcclxuYT1ydGNwLWZiOjExMSBuYWNrXHJcbm09dmlkZW8gOSBVRFAvVExTL1JUUC9TQVZQRiAxMjVcclxuYz1JTiBJUDQgMTI3LjAuMC4xXHJcbmE9Y2FuZGlkYXRlOjUgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTo0IDEgdWRwIDE2Nzc3MjE1IDAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMCAwIHR5cCByZWxheSByYWRkciA6Oi8wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MyAxIHVkcCAxNjk0NDk4ODE1IDE5Mi4xNjguMC4yMyA1MTIwNSB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjIgMSB1ZHAgMTY3NzcyMTg1NSAxMC45NS4yMDQuNjEgNTI2NDYgdHlwIHNyZmx4IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZToxIDEgdWRwIDIxMTM5Mjk0NzEgMTAuOTUuMjA0LjYxIDUzNDI4IHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTowIDEgdWRwIDIxMzA3MDY0MzEgMTkyLjE2OC4wLjIzIDUwMTIzIHR5cCBob3N0IHJhZGRyIDAuMC4wLjAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPW1zaWQ6bXlLdnNWaWRlb1N0cmVhbSBteVZpZGVvVHJhY2tcclxuYT1zc3JjOjIxNDEwMjk1OTIgY25hbWU6QlA0bEVqdTBHK0VBQk0yS1xyXG5hPXNzcmM6MjE0MTAyOTU5MiBtc2lkOm15S3ZzVmlkZW9TdHJlYW0gbXlWaWRlb1RyYWNrXHJcbmE9c3NyYzoyMTQxMDI5NTkyIG1zbGFiZWw6bXlLdnNWaWRlb1N0cmVhbVxyXG5hPXNzcmM6MjE0MTAyOTU5MiBsYWJlbDpteVZpZGVvVHJhY2tcclxuYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcclxuYT1pY2UtdWZyYWc6VVhwM1xyXG5hPWljZS1wd2Q6NGZZbTlEa1FQazl1YmRRQ2RyaFBhVFpnXHJcbmE9aWNlLW9wdGlvbnM6dHJpY2tsZVxyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjFcclxuYT1zZW5kcmVjdlxyXG5hPXJ0Y3AtbXV4XHJcbmE9cnRjcC1yc2l6ZVxyXG5hPXJ0cG1hcDoxMjUgSDI2NC85MDAwMFxyXG5hPWZtdHA6MTI1IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcclxuYT1ydGNwLWZiOjEyNSBuYWNrXHJcbm09YXBwbGljYXRpb24gOSBVRFAvRFRMUy9TQ1RQIHdlYnJ0Yy1kYXRhY2hhbm5lbFxyXG5jPUlOIElQNCAxMjcuMC4wLjFcclxuYT1jYW5kaWRhdGU6NSAxIHVkcCAxNjc3NzIxNSAwMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDAgMCB0eXAgcmVsYXkgcmFkZHIgOjovMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjQgMSB1ZHAgMTY3NzcyMTUgMDAwMDowMDAwOjAwMDA6MDAwMDowMDAwOjAwMDA6MDAwMDowMDAwIDAgdHlwIHJlbGF5IHJhZGRyIDo6LzAgcnBvcnQgMCBnZW5lcmF0aW9uIDAgbmV0d29yay1jb3N0IDk5OVxyXG5hPWNhbmRpZGF0ZTozIDEgdWRwIDE2OTQ0OTg4MTUgMTkyLjE2OC4wLjIzIDUxMjA1IHR5cCBzcmZseCByYWRkciAwLjAuMC4wIHJwb3J0IDAgZ2VuZXJhdGlvbiAwIG5ldHdvcmstY29zdCA5OTlcclxuYT1jYW5kaWRhdGU6MiAxIHVkcCAxNjc3NzIxODU1IDEwLjk1LjIwNC42MSA1MjY0NiB0eXAgc3JmbHggcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjEgMSB1ZHAgMjExMzkyOTQ3MSAxMC45NS4yMDQuNjEgNTM0MjggdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9Y2FuZGlkYXRlOjAgMSB1ZHAgMjEzMDcwNjQzMSAxOTIuMTY4LjAuMjMgNTAxMjMgdHlwIGhvc3QgcmFkZHIgMC4wLjAuMCBycG9ydCAwIGdlbmVyYXRpb24gMCBuZXR3b3JrLWNvc3QgOTk5XHJcbmE9cnRjcDo5IElOIElQNCAwLjAuMC4wXHJcbmE9aWNlLXVmcmFnOlVYcDNcclxuYT1pY2UtcHdkOjRmWW05RGtRUGs5dWJkUUNkcmhQYVRaZ1xyXG5hPWZpbmdlcnByaW50OnNoYS0yNTYgQkQ6RTk6QkI6RTE6ODE6NzQ6MDU6RkQ6Mzc6QUI6MzU6MTU6OTE6NTQ6ODc6RDU6NDI6QkU6RjQ6RjE6MUQ6NjA6OEI6REQ6NEQ6RUM6QzM6NDQ6RkU6OTc6ODg6MjBcclxuYT1zZXR1cDphY3RwYXNzXHJcbmE9bWlkOjJcclxuYT1zY3RwLXBvcnQ6NTAwMFxyXG4ifQ==\",\n" + " \"IceServerList\": [{\n" + " \"Password\": \"ZEXx/a0G7reNO4SrDoK0zYoXZCamD+k/mIn6PEiuDTk=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:18-236-143-60.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " },\n" + " {\n" + " \"Password\": \"k5PFpnyKu+oLa3Y3QUIhi+NA3BONdSUevw7NAAy/Nms=\",\n" + " \"Ttl\": 298,\n" + " \"Uris\": [\"turn:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=udp\", \"turns:52-25-38-73.t-4f692171.kinesisvideo.us-west-2.amazonaws.com:443?transport=tcp\"],\n" + " \"Username\": \"1607424954:djE6YXJuOmF3czpraW5lc2lzdmlkZW86dXMtd2VzdC0yOjgzNjIwMzExNzk3MTpjaGFubmVsL1NjYXJ5VGVzdENoYW5uZWwvMTU5OTg1NjczODM5OA==\"\n" + " }\n" + " ],\n" + " \"statusResponse\": {\n" + " \"correlationId\": \"CorrelationID\",\n" + " \"errorType\": \"Unknown message\",\n" + " \"statusCode\": \"200\",\n" + " \"description\": \"Test attempt to send an unknown message\"\n" + " }\n" + "}"; + + EXPECT_EQ(STATUS_SUCCESS, receiveLwsMessage(pSignalingClient, message2, ARRAY_SIZE(message2))); + + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(signalingHandle, &iceCount)); + EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(signalingHandle, 0, &pIceConfigInfo)); + + // ICE should have been called again as we couldn't have updated via the message + EXPECT_EQ(5, signalingStatesCounts[SIGNALING_CLIENT_STATE_GET_ICE_CONFIG]); + + // Check that we are connected and can send a message + SignalingMessage signalingMessage; + signalingMessage.version = SIGNALING_MESSAGE_CURRENT_VERSION; + signalingMessage.messageType = SIGNALING_MESSAGE_TYPE_OFFER; + STRCPY(signalingMessage.peerClientId, TEST_SIGNALING_MASTER_CLIENT_ID); + MEMSET(signalingMessage.payload, 'A', 100); + signalingMessage.payload[100] = '\0'; + signalingMessage.payloadLen = 0; + signalingMessage.correlationId[0] = '\0'; + + EXPECT_EQ(STATUS_SUCCESS, signalingClientSendMessageSync(signalingHandle, &signalingMessage)); + + deleteChannelLws(FROM_SIGNALING_CLIENT_HANDLE(signalingHandle), 0); + + EXPECT_EQ(STATUS_SUCCESS, freeSignalingClient(&signalingHandle)); +} + + } // namespace webrtcclient } // namespace video } // namespace kinesis diff --git a/tst/WebRTCClientTestFixture.h b/tst/WebRTCClientTestFixture.h index b92bb11208..c4b5691251 100644 --- a/tst/WebRTCClientTestFixture.h +++ b/tst/WebRTCClientTestFixture.h @@ -80,12 +80,19 @@ class WebRtcClientTestBase : public ::testing::Test { mSignalingClientCallbacks.messageReceivedFn = NULL; mSignalingClientCallbacks.errorReportFn = NULL; mSignalingClientCallbacks.stateChangeFn = NULL; + mSignalingClientCallbacks.getCurrentTimeFn = NULL; mClientInfo.version = SIGNALING_CLIENT_INFO_CURRENT_VERSION; mClientInfo.loggingLevel = LOG_LEVEL_VERBOSE; mClientInfo.cacheFilePath = NULL; // Use the default path STRCPY(mClientInfo.clientId, TEST_SIGNALING_MASTER_CLIENT_ID); + mClientInfo.signalingRetryStrategyCallbacks.createRetryStrategyFn = createRetryStrategyFn; + mClientInfo.signalingRetryStrategyCallbacks.getCurrentRetryAttemptNumberFn = getCurrentRetryAttemptNumberFn; + mClientInfo.signalingRetryStrategyCallbacks.freeRetryStrategyFn = freeRetryStrategyFn; + mClientInfo.signalingRetryStrategyCallbacks.executeRetryStrategyFn = executeRetryStrategyFn; + mClientInfo.signalingClientCreationMaxRetryAttempts = 0; + MEMSET(&mChannelInfo, 0x00, SIZEOF(mChannelInfo)); mChannelInfo.version = CHANNEL_INFO_CURRENT_VERSION; mChannelInfo.pChannelName = mChannelName; @@ -121,6 +128,9 @@ class WebRtcClientTestBase : public ::testing::Test { EXPECT_NE(STATUS_SUCCESS, retStatus); } + retStatus = signalingClientFetchSync(mSignalingClientHandle); + + return retStatus; } @@ -201,6 +211,35 @@ class WebRtcClientTestBase : public ::testing::Test { return retStatus; } + static STATUS createRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy) { + STATUS retStatus = STATUS_SUCCESS; + PExponentialBackoffRetryStrategyState pExponentialBackoffRetryStrategyState = NULL; + + CHK_STATUS(exponentialBackoffRetryStrategyCreate(pKvsRetryStrategy)); + CHK(pKvsRetryStrategy->retryStrategyType == KVS_RETRY_STRATEGY_EXPONENTIAL_BACKOFF_WAIT, STATUS_INTERNAL_ERROR); + + pExponentialBackoffRetryStrategyState = TO_EXPONENTIAL_BACKOFF_STATE(pKvsRetryStrategy->pRetryStrategy); + + // Overwrite retry config to avoid slow long running tests + pExponentialBackoffRetryStrategyState->exponentialBackoffRetryStrategyConfig.retryFactorTime = HUNDREDS_OF_NANOS_IN_A_MILLISECOND * 5; + pExponentialBackoffRetryStrategyState->exponentialBackoffRetryStrategyConfig.maxRetryWaitTime = HUNDREDS_OF_NANOS_IN_A_MILLISECOND * 75; + + CleanUp: + return retStatus; + } + + static STATUS getCurrentRetryAttemptNumberFn(PKvsRetryStrategy pKvsRetryStrategy, PUINT32 pRetryCount) { + return getExponentialBackoffRetryCount(pKvsRetryStrategy, pRetryCount); + } + + static STATUS freeRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy) { + return exponentialBackoffRetryStrategyFree(pKvsRetryStrategy); + } + + static STATUS executeRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy, PUINT64 retryWaitTime) { + return getExponentialBackoffRetryStrategyWaitTime(pKvsRetryStrategy, retryWaitTime); + } + STATUS readFrameData(PBYTE pFrame, PUINT32 pSize, UINT32 index, PCHAR frameFilePath) { STATUS retStatus = STATUS_SUCCESS;