From 0be097904010bb53cca44fd6453c2da953ead4d9 Mon Sep 17 00:00:00 2001 From: Niyati Maheshwari Date: Mon, 16 Aug 2021 10:01:08 -0700 Subject: [PATCH 01/19] create data channel sample (#1203) * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * removed unused variables, moved variable declarations to the top of a block * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive --- samples/Common.c | 6 ++++ samples/Samples.h | 3 ++ samples/kvsWebRTCClientViewer.c | 49 +++++++++++++++++++++++++++++++-- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/samples/Common.c b/samples/Common.c index 0d78ec9957..f1c71621f2 100644 --- a/samples/Common.c +++ b/samples/Common.c @@ -21,6 +21,12 @@ VOID onDataChannelMessage(UINT64 customData, PRtcDataChannel pDataChannel, BOOL } else { DLOGI("DataChannel String Message: %.*s\n", pMessageLen, pMessage); } + // 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) { + DLOGI("[KVS Master] dataChannelSend(): operation returned status code: 0x%08x \n", retStatus); + } } VOID onDataChannel(UINT64 customData, PRtcDataChannel pRtcDataChannel) diff --git a/samples/Samples.h b/samples/Samples.h index 34a8baec85..6b721a8d6f 100644 --- a/samples/Samples.h +++ b/samples/Samples.h @@ -46,6 +46,9 @@ extern "C" { #define IOT_CORE_ROLE_ALIAS ((PCHAR) "AWS_IOT_CORE_ROLE_ALIAS") #define IOT_CORE_THING_NAME ((PCHAR) "AWS_IOT_CORE_THING_NAME") +#define MASTER_DATA_CHANNEL_MESSAGE "This message is from the KVS Master" +#define VIEWER_DATA_CHANNEL_MESSAGE "This message is from the KVS Viewer" + /* Uncomment the following line in order to enable IoT credentials checks in the provided samples */ //#define IOT_CORE_ENABLE_CREDENTIALS 1 diff --git a/samples/kvsWebRTCClientViewer.c b/samples/kvsWebRTCClientViewer.c index 92d5affaf5..e425ed8594 100644 --- a/samples/kvsWebRTCClientViewer.c +++ b/samples/kvsWebRTCClientViewer.c @@ -2,6 +2,31 @@ extern PSampleConfiguration gSampleConfiguration; +// 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) +{ + UNUSED_PARAM(customData); + UNUSED_PARAM(pDataChannel); + if (isBinary) { + DLOGI("DataChannel Binary Message"); + } else { + DLOGI("DataChannel String Message: %.*s\n", pMessageLen, pMessage); + } +} + +// onOpen callback for the onOpen event of a viewer created data channel +VOID dataChannelOnOpenCallback(UINT64 customData, PRtcDataChannel pDataChannel) { + STATUS retStatus = STATUS_SUCCESS; + DLOGI("New DataChannel has been opened %s \n", pDataChannel->name); + dataChannelOnMessage(pDataChannel, customData, dataChannelOnMessageCallback); + ATOMIC_INCREMENT((PSIZE_T) customData); + // Sending first message to the master over the data channel + retStatus = dataChannelSend(pDataChannel, FALSE, (PBYTE) VIEWER_DATA_CHANNEL_MESSAGE, STRLEN(VIEWER_DATA_CHANNEL_MESSAGE)); + if(retStatus != STATUS_SUCCESS){ + DLOGI("[KVS Viewer] dataChannelSend(): operation returned status code: 0x%08x \n", retStatus); + } +} + INT32 main(INT32 argc, CHAR* argv[]) { STATUS retStatus = STATUS_SUCCESS; @@ -81,13 +106,11 @@ INT32 main(INT32 argc, CHAR* argv[]) // Initialize streaming session MUTEX_LOCK(pSampleConfiguration->sampleConfigurationObjLock); locked = TRUE; - retStatus = createSampleStreamingSession(pSampleConfiguration, NULL, FALSE, &pSampleStreamingSession); if (retStatus != STATUS_SUCCESS) { printf("[KVS Viewer] createSampleStreamingSession(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } - printf("[KVS Viewer] Creating streaming session...completed\n"); pSampleConfiguration->sampleStreamingSessionList[pSampleConfiguration->streamingSessionCount++] = pSampleStreamingSession; @@ -165,6 +188,28 @@ INT32 main(INT32 argc, CHAR* argv[]) goto CleanUp; } +#ifdef ENABLE_DATA_CHANNEL + PRtcDataChannel pDataChannel = NULL; + PRtcPeerConnection pPeerConnection = pSampleStreamingSession->pPeerConnection; + SIZE_T datachannelLocalOpenCount = 0; + + // Creating a new datachannel on the peer connection of the existing sample streaming session + retStatus = createDataChannel(pPeerConnection, pChannelName, NULL, &pDataChannel); + if(retStatus != STATUS_SUCCESS) { + printf("[KVS Viewer] createDataChannel(): operation returned status code: 0x%08x \n", retStatus); + goto CleanUp; + } + printf("[KVS Viewer] Creating data channel...completed\n"); + + // Setting a callback for when the data channel is open + retStatus = dataChannelOnOpen(pDataChannel, (UINT64) &datachannelLocalOpenCount, dataChannelOnOpenCallback); + if(retStatus != STATUS_SUCCESS) { + printf("[KVS Viewer] dataChannelOnOpen(): operation returned status code: 0x%08x \n", retStatus); + goto CleanUp; + } + printf("[KVS Viewer] Data Channel open now...\n"); +#endif + // Block until interrupted while (!ATOMIC_LOAD_BOOL(&pSampleConfiguration->interrupted) && !ATOMIC_LOAD_BOOL(&pSampleStreamingSession->terminateFlag)) { THREAD_SLEEP(HUNDREDS_OF_NANOS_IN_A_SECOND); From 76dbe286cef2575401f68f2d701eb5e9742209d9 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Mon, 16 Aug 2021 19:39:38 -0700 Subject: [PATCH 02/19] Cancel the thread once mediaThreadStarted flag is set to false (#1227) --- samples/Common.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/samples/Common.c b/samples/Common.c index f1c71621f2..64007774ce 100644 --- a/samples/Common.c +++ b/samples/Common.c @@ -1025,6 +1025,12 @@ 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)) { From dd4598304ac35be2e7411d3eb512be9369fc5613 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Tue, 17 Aug 2021 07:59:14 -0700 Subject: [PATCH 03/19] Initial stale PR template (#1226) --- .github/workflows/close-stale-issues.yml | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/close-stale-issues.yml diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml new file mode 100644 index 0000000000..59e13434d0 --- /dev/null +++ b/.github/workflows/close-stale-issues.yml @@ -0,0 +1,32 @@ +name: Close Stale Issues + +# Controls when the action will be run +on: + schedule: + - cron: "0 0 * * *" + +jobs: + cleanup: + runs-on: ubuntu-latest + name: Close stale issues + steps: + - uses: actions/stale@v4.0.0 + with: + stale-issue-message: It looks like this issue has not been active for a long time. If the issue is not resolved, please add an update to the ticket, else it will be automatically resolved in a few days. + close-issue-message: The issue has been stale for a while and hence it has been auto-closed. If the issue persists, feel free to open a new issue with details. + + # labels to be added + stale-issue-label: closing-soon + only-issue-labels: [question, bug] + + # SLAs + days-before-issue-stale: 7 + days-before-issue-close: 3 + + + repo-token: ${{ secrets.GITHUB_TOKEN }} + loglevel: DEBUG + # Set dry-run to true to not perform label or close actions. + dry-run: false + + From 845f3c4ba827db076a04894cfd3963706e1d281e Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Wed, 18 Aug 2021 09:47:23 -0700 Subject: [PATCH 04/19] Update stale issue template --- .github/workflows/close-stale-issues.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index 59e13434d0..4fd7a738e1 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -17,7 +17,9 @@ jobs: # labels to be added stale-issue-label: closing-soon - only-issue-labels: [question, bug] + only-issue-labels: + - question + - bug # SLAs days-before-issue-stale: 7 From e8438274a2f2d0d363e2707ee7a64749bcfb7e1e Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Thu, 19 Aug 2021 11:06:53 -0700 Subject: [PATCH 05/19] Testing every minute --- .github/workflows/close-stale-issues.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index 4fd7a738e1..7fa8fbd302 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -3,7 +3,7 @@ name: Close Stale Issues # Controls when the action will be run on: schedule: - - cron: "0 0 * * *" + - cron: "* * * * *" jobs: cleanup: @@ -17,9 +17,7 @@ jobs: # labels to be added stale-issue-label: closing-soon - only-issue-labels: - - question - - bug + any-of-labels: [question, bug] # SLAs days-before-issue-stale: 7 From 65c1810e6265983c478031cc9c9a672c9fc0c867 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Thu, 19 Aug 2021 13:03:09 -0700 Subject: [PATCH 06/19] Fix stale issue PR template (#1234) No code changed. Just a template change. So merging --- .github/workflows/close-stale-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index 7fa8fbd302..a81a8487eb 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -17,7 +17,7 @@ jobs: # labels to be added stale-issue-label: closing-soon - any-of-labels: [question, bug] + any-of-labels: question # SLAs days-before-issue-stale: 7 From bedd1a8225e97b0953ab79023ad6f27ecc3c16fd Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Thu, 19 Aug 2021 13:58:56 -0700 Subject: [PATCH 07/19] Add bug and question label --- .github/workflows/close-stale-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index a81a8487eb..fca51f181f 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -17,7 +17,7 @@ jobs: # labels to be added stale-issue-label: closing-soon - any-of-labels: question + any-of-labels: question,bug # SLAs days-before-issue-stale: 7 From bbc5344022deb891e4fe925c70fd1ae9b0e3689f Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Thu, 19 Aug 2021 14:14:39 -0700 Subject: [PATCH 08/19] Switch stale PR action to daily cadence --- .github/workflows/close-stale-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index fca51f181f..a1745b1b92 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -3,7 +3,7 @@ name: Close Stale Issues # Controls when the action will be run on: schedule: - - cron: "* * * * *" + - cron: "0 0 * * *" jobs: cleanup: From f87c0c6f459696d3793b22d9f4a8f22bc83309bd Mon Sep 17 00:00:00 2001 From: ela34 <8700736+ela34@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:41:25 +0200 Subject: [PATCH 09/19] [FIX] When protocol in DCEP header of data channel open command is not empty, there is a check preventing the data channel to be opened. Now protocol length is correctly handled to avoid check failing. (#1228) --- src/source/Sctp/Sctp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/source/Sctp/Sctp.c b/src/source/Sctp/Sctp.c index e1916dd0cf..a0d0742d7e 100644 --- a/src/source/Sctp/Sctp.c +++ b/src/source/Sctp/Sctp.c @@ -305,14 +305,17 @@ STATUS handleDcepPacket(PSctpSession pSctpSession, UINT32 streamId, PBYTE data, ENTERS(); STATUS retStatus = STATUS_SUCCESS; UINT16 labelLength = 0; + UINT16 protocolLength = 0; // Assert that is DCEP of type DataChannelOpen CHK(length > SCTP_DCEP_HEADER_LENGTH && data[0] == DCEP_DATA_CHANNEL_OPEN, STATUS_SUCCESS); - MEMCPY(&labelLength, data + 8, SIZEOF(UINT16)); - putInt16((PINT16) &labelLength, labelLength); + MEMCPY(&labelLength, data + 8, SIZEOF(UINT16)); + MEMCPY(&protocolLength, data + 10, SIZEOF(UINT16)); + putInt16((PINT16) &labelLength, labelLength); + putInt16((PINT16) &protocolLength, protocolLength); - CHK((labelLength + SCTP_DCEP_HEADER_LENGTH) >= length, STATUS_SCTP_INVALID_DCEP_PACKET); + CHK((labelLength + protocolLength + SCTP_DCEP_HEADER_LENGTH) >= length, STATUS_SCTP_INVALID_DCEP_PACKET); pSctpSession->sctpSessionCallbacks.dataChannelOpenFunc(pSctpSession->sctpSessionCallbacks.customData, streamId, data + SCTP_DCEP_HEADER_LENGTH, labelLength); From 38b033540d80c974c4bf9122e2ee0eedb5fd0587 Mon Sep 17 00:00:00 2001 From: jdelapla Date: Fri, 10 Sep 2021 13:28:57 -0700 Subject: [PATCH 10/19] Update README to include instructions for pull requests. (#1232) * Update README to include instructions for pull requests. * Put develop branch mentions in README in a code block --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index de73b31d6f..989e36db1e 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,10 @@ All Public APIs are documented in our [Include.h](https://github.com/awslabs/ama Refer to [related](#related) for more about WebRTC and KVS. +## Development + +If you would like to contribute to the development of this project, please base your pull requests off of the `origin/develop` branch, and to the `origin/develop` branch. Commits from `develop` will be merged into master periodically as a part of each release cycle. + ## Outbound hostname and port requirements * KVS endpoint : TCP 443 (ex: kinesisvideo.us-west-2.amazonaws.com) * HTTPS channel endpoint : TCP 443 (ex: r-2c136a55.kinesisvideo.us-west-2.amazonaws.com) From d08634bda8b32b0e9993a83835c4f0481c060abd Mon Sep 17 00:00:00 2001 From: Kevin Allen Date: Tue, 14 Sep 2021 09:49:43 -0400 Subject: [PATCH 11/19] Fix scripts/pare_status.py for non-status #defines (#1268) --- scripts/parse_status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/parse_status.py b/scripts/parse_status.py index bcab2e9c73..28a32e2d13 100755 --- a/scripts/parse_status.py +++ b/scripts/parse_status.py @@ -20,7 +20,7 @@ print(example_sums) # {'STATUS_CHILD': "0x5", 'STATUS_PARENT': "0x4"} ''' -pattern = re.compile("#define *([A-Z_]*) *(([A-Z_]*) *\+ *)?0x([0-9]*)") +pattern = re.compile("#define *(STATUS\_[A-Z_]*) *(([A-Z_]*) *\+ *)?0x([0-9]*)") def operands_by_name(paragraph): matches = filter(None, [pattern.match(line) for line in paragraph.splitlines()]) From 4127014aff98bef86962be5f0c4acf4ba9024de1 Mon Sep 17 00:00:00 2001 From: Hassan Sahibzada Date: Fri, 29 Oct 2021 17:14:41 -0400 Subject: [PATCH 12/19] log response, particularly in case of failure (#1314) * add the call response, it's verbose but maybe we should make it debug * WARN level log when result is not 200 and VERBOSE for 200s * missing null terminator * alloc right amount to include null char --- src/source/Signaling/LwsApiCalls.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/source/Signaling/LwsApiCalls.c b/src/source/Signaling/LwsApiCalls.c index 8398508161..de556cf87e 100644 --- a/src/source/Signaling/LwsApiCalls.c +++ b/src/source/Signaling/LwsApiCalls.c @@ -111,9 +111,15 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, lwsl_hexdump_debug(pDataIn, dataSize); if (dataSize != 0) { - CHK(NULL != (pLwsCallInfo->callInfo.responseData = (PCHAR) MEMALLOC(dataSize)), STATUS_NOT_ENOUGH_MEMORY); + 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'; + if (pLwsCallInfo->callInfo.callResult != SERVICE_CALL_RESULT_OK) { + DLOGW("Received client http read response: %s", pLwsCallInfo->callInfo.responseData); + } else { + DLOGV("Received client http read response: %s", pLwsCallInfo->callInfo.responseData); + } } break; From 22c9e3d7932365f0d04181b339f17e4870036f5c Mon Sep 17 00:00:00 2001 From: jdelapla Date: Thu, 23 Dec 2021 11:02:52 -0800 Subject: [PATCH 13/19] Release 1 7 0 (#1354) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Log ice candidate protocol appropriately (#1230) * Log ice candidate protocol appropriately * Add develop to travis CI * Address comments * Realign branches during transition (#1257) * create data channel sample (#1203) * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * removed unused variables, moved variable declarations to the top of a block * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * Cancel the thread once mediaThreadStarted flag is set to false (#1227) * Initial stale PR template (#1226) * Update stale issue template * Testing every minute * Fix stale issue PR template (#1234) No code changed. Just a template change. So merging * Add bug and question label * Switch stale PR action to daily cadence * [FIX] When protocol in DCEP header of data channel open command is not empty, there is a check preventing the data channel to be opened. Now protocol length is correctly handled to avoid check failing. (#1228) Co-authored-by: Niyati Maheshwari Co-authored-by: Divya Sampath Kumar Co-authored-by: ela34 <8700736+ela34@users.noreply.github.com> * set protocol value following the spec (#1259) Co-authored-by: liyufang * filter out invalid candidate in sdp (#1260) Co-authored-by: liyufang * Added targets for cmake clean (#1253) * added targets for cmake clean * added new line, updated readme * added missing new line * updated readme to include folder details * updated readme to include folder details * viewer sample: wrap datachannel callback with `ENABLE_DATA_CHANNEL` (#1261) * create data channel sample (#1203) * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * removed unused variables, moved variable declarations to the top of a block * create data channel sample * moved variables to Samples.h, encapsulated with ENABLE_DATA_CHANNEL directive * Cancel the thread once mediaThreadStarted flag is set to false (#1227) * Initial stale PR template (#1226) * Update stale issue template * Testing every minute * Fix stale issue PR template (#1234) No code changed. Just a template change. So merging * Add bug and question label * Switch stale PR action to daily cadence * [FIX] When protocol in DCEP header of data channel open command is not empty, there is a check preventing the data channel to be opened. Now protocol length is correctly handled to avoid check failing. (#1228) * viewer sample: wrap datachannel callback with `ENABLE_DATA_CHANNEL` Signed-off-by: zhiqinli@amazon.com Co-authored-by: Niyati Maheshwari Co-authored-by: Divya Sampath Kumar Co-authored-by: ela34 <8700736+ela34@users.noreply.github.com> * libwebsockets: add patch to fix pipe fd leak issue (#1264) Signed-off-by: Alex.Li * Fix scripts/pare_status.py for non-status #defines (#1268) (#1274) Co-authored-by: Kevin Allen * fix a few bugs * Revert "fix a few bugs" This reverts commit 093902432fb24f42ade2857c23eca950e0b41a2f. * fix some bugs (#1277) * Fix bugs (#1279) * fix edge case try to unlock a mutex which is not locked in teh acse the sample config was NULL (#1286) * cmake: allow user to specify OPEN_SRC_INSTALL_PREFIX (#1293) Signed-off-by: Alex.Li * Fix inconsistent log priority (#1309) Patch reduces priority of a few info level log messages to verbose level to be consistent with other functions in file and rest of code base. * Modify protocol loggin design in develop (#1311) * typo: typo fix (#1317) Signed-off-by: Alex.Li * Signaling state machine rework (#1323) * replaced recursive calls to stepSignalingStateMachine with loops * removed recursive calls to stepSignalingStateMachine * replaced stepSignalingstatemachine with signalingStateMachineIterator * removed stepUntil, continueOnReady; removed status from iterator signature; set signalingclient version; added a lock in refreshIceConfiguration * changed the declaration for i from int to UINT32 * added signaling version; removed unnecessary comment * removed redeclaration; changed value in an existing macro * Fix issue with API call failures being treated as success (#1328) * return proper error, do not reset call result value * for non retriable failures, set the terminal exit status for state in… (#1320) * for non retriable failures, set the terminal exit status for state in state machine * address comments * adjust tests set retry max to 1 * Update LwsApiCalls.c trigger travis ci * Incorporating PIC state machine level retry changes into webrtc signaling state machine (#1326) * Incorporating PIC state machine level retry changes into webrtc signaling state machine * Signaling state machine rework (#1323) * replaced recursive calls to stepSignalingStateMachine with loops * removed recursive calls to stepSignalingStateMachine * replaced stepSignalingstatemachine with signalingStateMachineIterator * removed stepUntil, continueOnReady; removed status from iterator signature; set signalingclient version; added a lock in refreshIceConfiguration * changed the declaration for i from int to UINT32 * added signaling version; removed unnecessary comment * removed redeclaration; changed value in an existing macro * Fix issue with API call failures being treated as success (#1328) * return proper error, do not reset call result value * for non retriable failures, set the terminal exit status for state in… (#1320) * for non retriable failures, set the terminal exit status for state in state machine * address comments * adjust tests set retry max to 1 * Update LwsApiCalls.c trigger travis ci * Add retry strategy to client info to avoiud changing create signaling channel API signature * Incorporating PIC state machine level retry changes into webrtc signaling state machine * Add retry strategy to client info to avoiud changing create signaling channel API signature * Adding more debug logs in the code * fix merge conflicts * PR feedback Co-authored-by: Niyati Maheshwari Co-authored-by: Hassan Sahibzada * Revert " Incorporating PIC state machine level retry changes into webrtc signaling state machine (#1326)" (#1339) This reverts commit 06bffcecd7f60571e29e51eb15fa8e4549638213. * Incorporating PIC state machine level retry changes into webrtc signaling state machine (#1341) * Incorporating PIC state machine level retry changes into webrtc signaling state machine * Add a n optional check for free retry strategy * Remove unused goto label * add missing sleep in get token state machine execute API * update log line * Update Producer hash * Adding a high level retry strategy while creating signaling client * Add Retry count retrieval in hook (#1335) * Add Retry count retrieval in hook * Fix compile issue on travis * Pull in latest changes in retry structures * Rebasing off develop with successful travis run for retry * Log git hash (#1345) * Log git hash * Update CMakeLists.txt white space change to trigger travis Co-authored-by: Hassan Sahibzada * Fix retry count to indicate count only after a retry has occured (#1348) Fix retry count to indicate count only after a retry has occurred * add support for automatic clock skew detection and correction (#1344) * add support for automatic clock skew detection and correction * auto fix clock skew and add tests * enable test to check clock recovery * remove stuff left over from debugging, reset logging default to WARN * remove unused variable * get rid of verbose logging in unit tests causes static build to fail due to excessive log output * simplify code get rid of extra call to length because the lws_hdr_copy returns the same value * handle null stream track in case of recvonly (#1346) * handle null stream track in case of recvonly * a test with null stream track for revconly * Fix a comment * whitespace change * State machine preparation is decoupled from the constructor (#1343) * Merge master into develop (#1352) * Missing the public API switch in the other samples (#1356) * Fix Fetch error retry cases (#1359) * Fix Fetch error retry cases * update comment Co-authored-by: Divya Sampath Kumar Co-authored-by: Niyati Maheshwari Co-authored-by: ela34 <8700736+ela34@users.noreply.github.com> Co-authored-by: northteam Co-authored-by: liyufang Co-authored-by: Alex.D.Scofield Co-authored-by: Kevin Allen Co-authored-by: Hassan Sahibzada Co-authored-by: Chris Hiszpanski Co-authored-by: Anurag Katey --- .travis.yml | 1 + .../libkvsCommonLws-CMakeLists.txt | 2 +- .../Dependencies/libwebsockets-CMakeLists.txt | 4 +- .../libwebsockets-leak-pipe-fix.patch | 36 + ...bsockets-old-gcc-fix-cast-cmakelists.patch | 16 - CMakeLists.txt | 21 +- README.md | 2 + samples/Common.c | 73 +- samples/kvsWebRTCClientMaster.c | 32 +- .../kvsWebRTCClientMasterGstreamerSample.c | 6 + samples/kvsWebRTCClientViewer.c | 9 + .../kinesis/video/webrtcclient/Include.h | 54 +- .../kinesis/video/webrtcclient/Stats.h | 3 +- src/source/Ice/ConnectionListener.c | 5 +- src/source/Ice/IceAgent.c | 76 +- src/source/Ice/IceAgent.h | 1 + src/source/Ice/IceAgentStateMachine.c | 81 +- src/source/Ice/IceUtils.h | 4 + src/source/PeerConnection/PeerConnection.c | 15 +- src/source/PeerConnection/Rtcp.c | 2 +- src/source/Rtcp/RtpRollingBuffer.c | 2 +- src/source/Signaling/Client.c | 172 +++- src/source/Signaling/LwsApiCalls.c | 183 +++- src/source/Signaling/LwsApiCalls.h | 10 +- src/source/Signaling/Signaling.c | 198 +++-- src/source/Signaling/Signaling.h | 79 +- src/source/Signaling/StateMachine.c | 200 ++--- src/source/Signaling/StateMachine.h | 4 +- tst/MetricsApiTest.cpp | 2 +- tst/SdpApiTest.cpp | 23 + tst/SignalingApiFunctionalityTest.cpp | 840 +++++++++++++++++- tst/WebRTCClientTestFixture.h | 39 + 32 files changed, 1807 insertions(+), 388 deletions(-) create mode 100644 CMake/Dependencies/libwebsockets-leak-pipe-fix.patch 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; From 111c253a0ebea9f4f774ee5896ab5f15c6a0ea12 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Tue, 8 Feb 2022 13:54:27 -0800 Subject: [PATCH 14/19] =?UTF-8?q?new=20Chrome=20v98=20produces=20extra=20s?= =?UTF-8?q?dp=20attributes,=20up=20the=20limit=20so=20we=20do=20n=E2=80=A6?= =?UTF-8?q?=20(#1391)=20(#1393)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new Chrome v98 produces extra sdp attributes, up the limit so we do not reject * Fix test and change count to unit16 to avoid overflow issues * fix issue reported by codeql Co-authored-by: Divya Sampath Kumar Co-authored-by: Hassan Sahibzada --- src/source/PeerConnection/SessionDescription.c | 3 ++- src/source/Sdp/Deserialize.c | 3 +-- src/source/Sdp/Sdp.h | 6 +++--- tst/SdpApiTest.cpp | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/source/PeerConnection/SessionDescription.c b/src/source/PeerConnection/SessionDescription.c index 74bd84d5b4..cab1eb0662 100644 --- a/src/source/PeerConnection/SessionDescription.c +++ b/src/source/PeerConnection/SessionDescription.c @@ -151,7 +151,8 @@ STATUS setPayloadTypesFromOffer(PHashTable codecTable, PHashTable rtxTable, PSes ENTERS(); STATUS retStatus = STATUS_SUCCESS; PSdpMediaDescription pMediaDescription = NULL; - UINT8 currentMedia, currentAttribute; + UINT8 currentAttribute; + UINT16 currentMedia; PCHAR attributeValue, end; UINT64 parsedPayloadType, hashmapPayloadType, fmtpVal, aptVal; UINT16 aptFmtpVals[MAX_SDP_FMTP_VALUES]; diff --git a/src/source/Sdp/Deserialize.c b/src/source/Sdp/Deserialize.c index 9a2f12d2e4..c67d684140 100644 --- a/src/source/Sdp/Deserialize.c +++ b/src/source/Sdp/Deserialize.c @@ -5,7 +5,6 @@ STATUS parseMediaName(PSessionDescription pSessionDescription, PCHAR pch, UINT32 { ENTERS(); STATUS retStatus = STATUS_SUCCESS; - CHK(pSessionDescription->mediaCount < MAX_SDP_SESSION_MEDIA_COUNT, STATUS_BUFFER_TOO_SMALL); STRNCPY(pSessionDescription->mediaDescriptions[pSessionDescription->mediaCount].mediaName, (pch + SDP_ATTRIBUTE_LENGTH), @@ -48,7 +47,7 @@ STATUS parseMediaAttributes(PSessionDescription pSessionDescription, PCHAR pch, ENTERS(); STATUS retStatus = STATUS_SUCCESS; PCHAR search; - UINT8 currentMediaAttributesCount; + UINT16 currentMediaAttributesCount; currentMediaAttributesCount = pSessionDescription->mediaDescriptions[pSessionDescription->mediaCount - 1].mediaAttributesCount; diff --git a/src/source/Sdp/Sdp.h b/src/source/Sdp/Sdp.h index af1cae5e14..c156e80357 100644 --- a/src/source/Sdp/Sdp.h +++ b/src/source/Sdp/Sdp.h @@ -75,7 +75,7 @@ extern "C" { #define MAX_SDP_SESSION_MEDIA_COUNT 5 #define MAX_SDP_MEDIA_BANDWIDTH_COUNT 2 -#define MAX_SDP_ATTRIBUTES_COUNT 128 +#define MAX_SDP_ATTRIBUTES_COUNT 256 /* * c= @@ -198,9 +198,9 @@ typedef struct { SdpMediaDescription mediaDescriptions[MAX_SDP_SESSION_MEDIA_COUNT]; - UINT8 sessionAttributesCount; + UINT16 sessionAttributesCount; - UINT8 mediaCount; + UINT16 mediaCount; UINT8 timezoneCount; diff --git a/tst/SdpApiTest.cpp b/tst/SdpApiTest.cpp index cff77583c9..1b05d288bb 100644 --- a/tst/SdpApiTest.cpp +++ b/tst/SdpApiTest.cpp @@ -232,7 +232,7 @@ s=- t=0 0 )"; - for (auto i = 0; i < 250; i++) { + for (auto i = 0; i <= MAX_SDP_ATTRIBUTES_COUNT + 1; i++) { sessionDescriptionNoMedia += "a=b\n"; } From 2dff44fea83a79336fda676ba2801d0d77417cd1 Mon Sep 17 00:00:00 2001 From: jdelapla Date: Mon, 21 Feb 2022 13:16:49 -0800 Subject: [PATCH 15/19] Hangup value included in LWS retry strategy (#1403) (#1404) --- src/source/Signaling/LwsApiCalls.h | 1 + src/source/Signaling/Signaling.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/source/Signaling/LwsApiCalls.h b/src/source/Signaling/LwsApiCalls.h index 8d80748d0d..a08c54c937 100644 --- a/src/source/Signaling/LwsApiCalls.h +++ b/src/source/Signaling/LwsApiCalls.h @@ -19,6 +19,7 @@ extern "C" { #define SIGNALING_SERVICE_TCP_KEEPALIVE_PROBE_COUNT 3 #define SIGNALING_SERVICE_TCP_KEEPALIVE_PROBE_INTERVAL_IN_SECONDS 1 #define SIGNALING_SERVICE_WSS_PING_PONG_INTERVAL_IN_SECONDS 10 +#define SIGNALING_SERVICE_WSS_HANGUP_IN_SECONDS 7200 // Protocol indexes #define PROTOCOL_INDEX_HTTPS 0 diff --git a/src/source/Signaling/Signaling.c b/src/source/Signaling/Signaling.c index b1d5ee02e4..ccdd76e77a 100644 --- a/src/source/Signaling/Signaling.c +++ b/src/source/Signaling/Signaling.c @@ -15,6 +15,7 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf struct lws_context_creation_info creationInfo; const lws_retry_bo_t retryPolicy = { .secs_since_valid_ping = SIGNALING_SERVICE_WSS_PING_PONG_INTERVAL_IN_SECONDS, + .secs_since_valid_hangup = SIGNALING_SERVICE_WSS_HANGUP_IN_SECONDS, }; PStateMachineState pStateMachineState; BOOL cacheFound = FALSE; From ced19d02ed6fbc1e59dd63bc19d847c5ec5ebf47 Mon Sep 17 00:00:00 2001 From: jdelapla Date: Wed, 18 May 2022 17:15:13 -0700 Subject: [PATCH 16/19] Release 1 7 3 (#1473) * recreate signaling client & lws_context whenever a significant error has occurred * Clang format * More verbose and debug logging for ice & turn * Clang formatting * travis be gone * add github-actions * more clang formatting * more clang formatting * address sanitizer * fix clang-format * fix client.c clang-format Co-authored-by: Niyati Maheshwari --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/ISSUE_TEMPLATE/questions-help.md | 2 +- .github/build_windows.bat | 2 +- .github/github_deploy_key.enc | Bin 3392 -> 0 bytes .github/workflows/ci.yml | 380 ++++++++++++++++++ .github/workflows/close-stale-issues.yml | 16 +- .github/workflows/codecov.yml | 29 ++ .github/workflows/codeql-analysis.yml | 4 +- .github/workflows/doxygen-gh-pages.yml | 29 ++ .travis.yml | 270 ------------- .github/Doxyfile => Doxyfile | 0 .../DoxygenLayout.xml => DoxygenLayout.xml | 0 .github/Introduction.md => Introduction.md | 0 README.md | 3 +- samples/Common.c | 47 ++- samples/kvsWebRTCClientMaster.c | 6 +- samples/kvsWebRTCClientViewer.c | 9 +- .../kinesis/video/webrtcclient/Include.h | 28 +- src/source/Ice/IceAgent.c | 51 ++- src/source/Ice/IceAgentStateMachine.c | 82 +--- src/source/Ice/TurnConnection.c | 15 +- src/source/PeerConnection/PeerConnection.c | 2 +- src/source/Sctp/Sctp.c | 10 +- src/source/Signaling/Client.c | 16 +- src/source/Signaling/LwsApiCalls.c | 56 +-- src/source/Signaling/LwsApiCalls.h | 2 +- src/source/Signaling/Signaling.c | 19 +- src/source/Signaling/Signaling.h | 33 +- src/source/Signaling/StateMachine.c | 9 +- tst/WebRTCClientTestFixture.h | 13 +- 30 files changed, 668 insertions(+), 467 deletions(-) delete mode 100644 .github/github_deploy_key.enc create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/codecov.yml create mode 100644 .github/workflows/doxygen-gh-pages.yml delete mode 100644 .travis.yml rename .github/Doxyfile => Doxyfile (100%) rename .github/DoxygenLayout.xml => DoxygenLayout.xml (100%) rename .github/Introduction.md => Introduction.md (100%) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 641e20b5f3..1ed5fb63b4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: "[BUG]" -labels: bug +labels: bug,needs-triage assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/questions-help.md b/.github/ISSUE_TEMPLATE/questions-help.md index 1cc48d57c9..83d1501847 100644 --- a/.github/ISSUE_TEMPLATE/questions-help.md +++ b/.github/ISSUE_TEMPLATE/questions-help.md @@ -2,7 +2,7 @@ name: Questions/Help about: Describe this issue template's purpose here. title: "[QUESTION]" -labels: question +labels: question,needs-triage assignees: '' --- diff --git a/.github/build_windows.bat b/.github/build_windows.bat index 17c70858c6..66fc5f2093 100644 --- a/.github/build_windows.bat +++ b/.github/build_windows.bat @@ -1,4 +1,4 @@ -call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat" amd64 mkdir build cd build cmd.exe /c cmake -G "NMake Makefiles" .. diff --git a/.github/github_deploy_key.enc b/.github/github_deploy_key.enc deleted file mode 100644 index 9be25b50328bdf2d92eb5bbb4519878ecf3381b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3392 zcmV-G4ZreI*WR;ZoK+!`&a5Mq(Q)~31Ltiu>A2k``!C(_^|-0(pstWmft8Mfl`ns+ z_Er>vW}{j4LS+BFW`eq;CQ1!sB?Kw3j7lD7p%mC9o`e+V5}2zLzu)pCsM?K;(CsXL zyZA>g1e`L;B5xgu@~x`+g3Uz>XFLcFNx5CXx8Xd4-gtK-2X&ijRKNnJzH(mVQZA+q z**?nsle3iTK(Li-;1&)=sGj_tP_e}Ew+876b#F}!^t-c)Vrl%;AUYR{38!g{p}9!@ z`8Os&yEb;>{hh+0Ga&F?Q_(Z%(cr$N`$IRQrP1BKEnD=Qwmg2laBENxra(PsZL2OV zPO3`a@$fO!0*CPf(l64jyS91FWmATX4Ee|cBBk2Qyj5U@jeCk75w^4>ml?Em9*z4+3R{S&FozhPu^bow#^v}g!-tnI#TEhTwnma}HCBsc z=a=-n!4dG0E}M;%JfccyV@@M15h~ds8^P$Uin>g9z?pdx@BN?upzL)QTA0}*p{MX- zniZcK?=D5QVJXzAh)wGQG|IWkzS89}E%1dN0(l+pOml$+y@O1$`Cxzbq)W@Jm4lk^kD}e7aeg!cxjZ#PL*PCa zdy-J9uwWe7i_M_@Oc@&BBznX@1QdIA6>94hWH_rKyLGFTn-Rl;4gU{Ef-MzD zSmhbJBA-WGBQLHJS@hJroHYen0TlS$((g5M2e9LJ=U`k#|=nZYwH>zb~CO6 z9B1TQ!yD9=hF(q?kz3i&eFR(i589Zl8;Z@LNg}l$#**D($SW(B-=JZAST}l$^9B?^n0hK3xhcBY2_&P_#$R!nM*%lG_KZ#L5KNAmeT}!}>n(Z5v zU9UvZ2*V+sZJib%6TJA0B!T&X`SBOVV5P2d zU^Rp2IE3m@IBIr56Qqku&2lAJQIo7ybue`Skj7B2ScpR*-=%}F?^Pgpteew@0xzo` z_V+o4sa8or6Ne+G=XD8#^Dh!wI6=n>(j)nT3O~N~hM>q_Z--yyv%KiDq*8^QG@J*x zeby5iZa2@CUbhtledUd$weaLHcJBb+D8qfcB#mHecTz%To+ct2C{m2L@5Q&bp{EQ- zoF2^w_z5m(b9X~YAipCz(gyBoOL^z7RYJBX>8cs8`a+4If{NO&SjIaBv@^4tQb$2M zRMV9u5P|LQXshSnoxHd?FTcsi3-S0do5Zy4OgGWJ_h9wonh&>S=e2q4ej4(``2}A_ z)-aTic{68{avQc*4Vk3}1XL{9B8^~D<4nzuk5;>LG#wuIIDq!AU-=bUggoM4u2 zb7zebEIW}L!)^uoSKv#lWU{VV=6mNNNuZfCM&p;Yl7TvTQ9ejeBMM~irvRhBF~+Rg5)~W#;YgR|QgvgAB*xnyLn|7?=1 z%m*3!F#pN%F}Z1-?G6bTb^`dQMNni6i4U|LdY(PXVM=BUcnAN(!#(~&nhvw511FxY z+?K6yap%wLZ0@O!ZsK63FjZ4{Op4apWxyD%u>S9*ThHhwApjqUWHe5 zUG&x#7wlf;m?M|E%bSWfZ5+~AE{l))Hwk;WQH|w+Tzm5Lb`og_uuHBa^`y3KGM97IF{QfeS#Q? zLAWT|kJ{ZbwM-FpHY>i~NHT^Aw*?B@1hw0H{h}zq=xR62ZX|2N zt$VYL){kS4f>$&5A`>1eFax?Y_zc&=~77R0?HO`Ji^*81FBs( zBOh;!4tPSQDHV<1T7x1jQf|h+G64%mB!HnPbQ=EpU`}(z5bexOl(VERG7}<@jCDUwuD?FMBWiN*7<=J>S&9%sb=8%}`g(hMo{sAy_v-B?Tzp(=*nTsu zcg7TvC--H%6JMiH^KLPapAD1-OV*Ux)_&Wd8?Zt+g;lyTg48tckhfmC9XhrgrEvCy zi$cynBvMQn9{iYJiigRk@z=_{aTwTou23gz@5w1Igr!6mizQW(c-LY997=8^{8{5a zdc87a48q?wpdu{H|2FEu3r6lzGrc)w6;X5zYvkh~UEtemXdnS|myV>jGF#(cjF)XLnzY#l&t_O-d^rB&+P!~z>= z@OMh2sMp7_(T)e52=hqHF$T*t173Tk*qVy_OemTU8;Je%IzWn$4)u /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE + make + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + - name: Code coverage + run: | + for test_file in $(find CMakeFiles/kvsWebrtcClient.dir CMakeFiles/kvsWebrtcSignalingClient.dir -name '*.gcno'); do gcov $test_file; done + bash <(curl -s https://codecov.io/bash) + address-sanitizer: + runs-on: ubuntu-18.04 + env: + ASAN_OPTIONS: detect_odr_violation=0:detect_leaks=1 + LSAN_OPTIONS: suppressions=../tst/suppressions/LSAN.supp + CC: clang-7 + CXX: clang++-7 + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install clang-7 + - name: Build repository + run: | + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DADDRESS_SANITIZER=TRUE + make + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + undefined-behavior-sanitizer: + runs-on: ubuntu-18.04 + env: + UBSAN_OPTIONS: halt_on_error=1 + CC: clang-7 + CXX: clang++-7 + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install clang-7 + - name: Build repository + run: | + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DUNDEFINED_BEHAVIOR_SANITIZER=TRUE + make + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + # memory-sanitizer: + # runs-on: ubuntu-18.04 + # env: + # CC: clang-7 + # CXX: clang++-7 + # AWS_KVS_LOG_LEVEL: 2 + # steps: + # - name: Clone repository + # uses: actions/checkout@v2 + # - name: Install dependencies + # run: | + # sudo apt-get update + # sudo apt-get -y install clang-7 + # - name: Build repository + # run: | + # sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + # mkdir build && cd build + # cmake .. -DMEMORY_SANITIZER=TRUE -DBUILD_TEST=TRUE + # make + # ulimit -c unlimited -S + # timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + thread-sanitizer: + runs-on: ubuntu-latest + env: + TSAN_OPTIONS: halt_on_error=1:suppressions=../tst/suppressions/TSAN.supp + CC: clang-7 + CXX: clang++-7 + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install clang-7 + - name: Build repository + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE + make + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + linux-gcc-4_4: + runs-on: ubuntu-18.04 + env: + AWS_KVS_LOG_LEVEL: 2 + CC: gcc-4.4 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install deps + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ trusty main' + sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ trusty universe' + sudo apt-get -q update + sudo apt-get -y install gcc-4.4 + sudo apt-get -y install gdb + - name: Build repository + run: | + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE + make + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + static-build-linux: + runs-on: ubuntu-18.04 + container: + image: alpine:latest + env: + CC: gcc + CXX: g++ + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install dependencies + run: | + apk update + apk upgrade + apk add alpine-sdk cmake clang linux-headers perl bash openssl-dev + - name: Build Repository + run: | + mkdir build && cd build + cmake .. -DBUILD_STATIC_LIBS=TRUE -DBUILD_TEST=TRUE + make + mbedtls-ubuntu-gcc: + runs-on: ubuntu-latest + env: + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install deps + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ trusty main' + sudo add-apt-repository 'deb http://archive.ubuntu.com/ubuntu/ trusty universe' + sudo apt-get -q update + sudo apt-get -y install gcc-4.4 + sudo apt-get -y install gdb + - name: Build repository + run: | + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON + make + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + mbedtls-ubuntu-clang: + runs-on: ubuntu-18.04 + env: + CC: clang-7 + CXX: clang++-7 + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install clang-7 + - name: Build repository + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON + make + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + sample-check: + if: github.repository == 'awslabs/amazon-kinesis-video-streams-webrtc-sdk-c' + runs-on: ubuntu-latest + env: + AWS_KVS_LOG_LEVEL: 2 + permissions: + id-token: write + contents: read + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + role-session-name: ${{ secrets.AWS_ROLE_SESSION_NAME }} + aws-region: ${{ secrets.AWS_REGION }} + - name: Build repository + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. + make + cd .. + ./scripts/check-sample.sh + ubuntu-os-build: + runs-on: ubuntu-18.04 + env: + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Build repository + run: | + # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE + make + ./tst/webrtc_client_test + # windows-msvc: + # runs-on: windows-2019 + # permissions: + # id-token: write + # contents: read + # steps: + # - name: Setup MSVC + # uses: ilammy/msvc-dev-cmd@v1 + # - name: Clone repository + # uses: actions/checkout@v2 + # - name: Build and run + # run: | + # choco install nasm strawberryperl + # $env:Path += ';C:\Strawberry\perl\site\bin;C:\Strawberry\perl\bin;C:\Strawberry\c\bin;C:\Program Files\NASM;D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\open-source\lib;D:\a\amazon-kinesis-video-streams-producer-c\amazon-kinesis-video-streams-producer-c\open-source\bin' + # git config --system core.longpaths true + # .github/build_windows.bat + # cd tst && .\webrtc_client_test.exe --gtest_filter="-DataChannelFunctionalityTest.*:IceApiTest.*:IceFunctionalityTest.*:PeerConnectionFunctionalityTest.*:SignalingApiFunctionalityTest.*:TurnConnectionFunctionalityTest.*:RtpFunctionalityTest.marshallUnmarshallH264Data:RtpFunctionalityTest.packingUnpackingVerifySameH264Frame:RtcpFunctionalityTest.onRtcpPacketCompound:RtcpFunctionalityTest.twcc3" + arm64-cross-compilation: + runs-on: ubuntu-latest + env: + CC: aarch64-linux-gnu-gcc + CXX: aarch64-linux-gnu-g++ + steps: + - name: Install dependencies + run: | + sudo apt update + sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu + - name: Clone repository + uses: actions/checkout@v2 + - name: Build Repository + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_OPENSSL=TRUE -DBUILD_OPENSSL_PLATFORM=linux-generic64 -DBUILD_LIBSRTP_HOST_PLATFORM=x86_64-unknown-linux-gnu -DBUILD_LIBSRTP_DESTINATION_PLATFORM=arm-unknown-linux-uclibcgnueabi + make + linux-aarch64-cross-compilation: + runs-on: ubuntu-latest + env: + CC: aarch64-linux-gnu-gcc + CXX: aarch64-linux-gnu-g++ + steps: + - name: Install dependencies + run: | + sudo apt update + sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu binutils-aarch64-linux-gnu + - name: Clone repository + uses: actions/checkout@v2 + - name: Build Repository + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_OPENSSL=TRUE -DBUILD_OPENSSL_PLATFORM=linux-aarch64 -DBUILD_LIBSRTP_HOST_PLATFORM=x86_64-unknown-linux-gnu -DBUILD_LIBSRTP_DESTINATION_PLATFORM=arm-unknown-linux-uclibcgnueabi + make + arm32-cross-compilation: + runs-on: ubuntu-latest + env: + CC: arm-linux-gnueabi-gcc + CXX: arm-linux-gnueabi-g++ + steps: + - name: Install dependencies + run: | + sudo apt update + sudo apt-get -y install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi binutils-arm-linux-gnueabi + - name: Clone repository + uses: actions/checkout@v2 + - name: Build Repository + run: | + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' + mkdir build && cd build + cmake .. -DBUILD_OPENSSL=TRUE -DBUILD_OPENSSL_PLATFORM=linux-generic32 -DBUILD_LIBSRTP_HOST_PLATFORM=x86_64-unknown-linux-gnu -DBUILD_LIBSRTP_DESTINATION_PLATFORM=arm-unknown-linux-uclibcgnueabi + make diff --git a/.github/workflows/close-stale-issues.yml b/.github/workflows/close-stale-issues.yml index a1745b1b92..4f721c936b 100644 --- a/.github/workflows/close-stale-issues.yml +++ b/.github/workflows/close-stale-issues.yml @@ -10,18 +10,22 @@ jobs: runs-on: ubuntu-latest name: Close stale issues steps: - - uses: actions/stale@v4.0.0 + - uses: aws-actions/stale-issue-cleanup@v3 with: - stale-issue-message: It looks like this issue has not been active for a long time. If the issue is not resolved, please add an update to the ticket, else it will be automatically resolved in a few days. - close-issue-message: The issue has been stale for a while and hence it has been auto-closed. If the issue persists, feel free to open a new issue with details. + ancient-issue-message: This is a very old issue. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to open a new one. + stale-issue-message: It looks like this issue has not been active for 10 days. If the issue is not resolved, please add an update to the ticket, else it will be automatically resolved in a few days. # labels to be added stale-issue-label: closing-soon - any-of-labels: question,bug + exempt-issue-labels: enhancement,pending-research,pending-action,needs-triage + response-requested-label: pending-response + + closed-for-staleness-label: closed-for-staleness # SLAs - days-before-issue-stale: 7 - days-before-issue-close: 3 + days-before-stale: 7 + days-before-close: 3 + days-before-ancient: 180 repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000000..dd7093eacb --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,29 @@ +name: Codecov for WebRTC C SDK +on: + push: + branches: + - develop + - master + pull_request: + branches: + - develop + - master +jobs: + linux-gcc-codecov: + runs-on: ubuntu-latest + steps: + - name: Fetch + uses: actions/checkout@v2 + with: + fetch-depth: 2 + - name: Run code coverage + run: | + mkdir build + cd build + cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE + make + export AWS_KVS_LOG_LEVEL=3 + ulimit -c unlimited -S + timeout --signal=SIGABRT 60m ./tst/webrtc_client_test + for test_file in $(find CMakeFiles/kvsWebrtcClient.dir CMakeFiles/kvsWebrtcSignalingClient.dir -name '*.gcno'); do gcov $test_file; done + bash <(curl -s https://codecov.io/bash) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 48df0f11f6..ec9d11cfb0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,10 +2,10 @@ name: "CodeQL" on: push: - branches: [ master ] + branches: [ develop, master ] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [ develop, master ] jobs: analyze: diff --git a/.github/workflows/doxygen-gh-pages.yml b/.github/workflows/doxygen-gh-pages.yml new file mode 100644 index 0000000000..488b4fb1fd --- /dev/null +++ b/.github/workflows/doxygen-gh-pages.yml @@ -0,0 +1,29 @@ +name: Doxygen GitHub Pages Deploy Action WebRTC C SDK + +on: + push: + branches: + - master + - develop + +jobs: + generate-and-deploy-doxygen: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install requirements + run: sudo apt-get install doxygen graphviz -y + shell: bash + + - name: Generate Doxygen Documentation + run: doxygen Doxyfile + shell: bash + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4.2.5 + with: + folder: doc/html + branch: gh-pages + \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 95d952919d..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,270 +0,0 @@ -language: cpp -sudo: true - -branches: - only: - - master - - develop - -cache: -- directories: - - $HOME/.cache - - -services: - - docker - -addons: - apt: - packages: - - gdb - - docker-ce - -script: - - export AWS_KVS_LOG_LEVEL=3 - - make - - ulimit -c unlimited -S - - timeout --signal=SIGABRT 60m ./tst/webrtc_client_test - -after_failure: - - for i in $(find ./ -maxdepth 1 -name 'core*' -print); do gdb $(pwd)/tst/webrtc_client_test core* -ex "thread apply all bt" -ex "set pagination 0" -batch; done; - -matrix: - # MemorySanitizer and UndefinedBehaviorSanitizer are still WIP - allow_failures: - - env: allowTestFail=true - - include: - #clang check - - name: "clang-format Check" - os: linux - compiler: clang - before_script: - - sudo apt-get -q update - - sudo apt-get -y install clang-format - - mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TEST=TRUE - script: - - cd .. - - bash scripts/check-clang.sh - - # MacOS Builds - - name: "OSX GCC" - os: osx - compiler: gcc - before_script: - - mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE - script: - - make - - export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:`pwd`/../open-source/lib" - - ./tst/webrtc_client_test - after_failure: skip # timeout not available on MacOS - - - name: "OSX Clang" - os: osx - compiler: clang - before_script: - - mkdir build && cd build && cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE - script: - - make - - export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:`pwd`/../open-source/lib" - - ./tst/webrtc_client_test || travis_terminate 1; - # Execute selected tests without auth integration - - unset AWS_ACCESS_KEY_ID - - unset AWS_SECRET_ACCESS_KEY - - ./tst/webrtc_client_test --gtest_break_on_failure --gtest_filter="SignalingApiFunctionalityTest.*:SignalingApiTest.*:TurnConnectionFunctionalityTest.*" - after_failure: skip # timeout not available on MacOS - - # Code Coverage - - name: "Linux GCC Code Coverage" - os: linux - compiler: gcc - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - before_script: - - mkdir build && cd build && cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE - after_success: - - for test_file in $(find CMakeFiles/kvsWebrtcClient.dir CMakeFiles/kvsWebrtcSignalingClient.dir -name '*.gcno'); do gcov $test_file; done - - bash <(curl -s https://codecov.io/bash) - - # AddressSanitizer - - name: "Linux Clang AddressSanitizer" - os: linux - compiler: clang - env: - - ASAN_OPTIONS=detect_odr_violation=0:detect_leaks=1 - - LSAN_OPTIONS=suppressions=../tst/suppressions/LSAN.supp - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - before_script: mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TEST=TRUE -DADDRESS_SANITIZER=TRUE - - # UndefinedBehaviorSanitizer - - name: "Linux Clang UndefinedBehaviorSanitizer" - os: linux - compiler: clang - env: UBSAN_OPTIONS=halt_on_error=1 - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - before_script: mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TEST=TRUE -DUNDEFINED_BEHAVIOR_SANITIZER=TRUE - - # MemorySanitizer - - name: "Linux Clang MemorySanitizer" - env: allowTestFail=true - before_install: - # TODO: Remove the following 2 lines. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - echo '{"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' | sudo tee /etc/docker/daemon.json - - sudo service docker restart - - mkdir build - - docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -w /src/build -dit --name msan-tester -v $(pwd):/src seaduboi/kvs-msan-tester - - msan-tester() { docker exec -it msan-tester "$@"; } - script: - - msan-tester cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_DEPENDENCIES=FALSE -DBUILD_TEST=TRUE -DMEMORY_SANITIZER=TRUE -DCMAKE_CXX_FLAGS="-stdlib=libc++ -L/usr/src/libcxx_msan/lib -lc++abi -I/usr/src/libcxx_msan/include -I/usr/src/libcxx_msan/include/c++/v1 -fsanitize=memory -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize-memory-track-origins" - - msan-tester make - - msan-tester ./tst/webrtc_client_test - after_failure: skip # no coredumps in container - - # ThreadSanitizer - - name: "Linux Clang ThreadSanitizer" - os: linux - compiler: clang - env: TSAN_OPTIONS=halt_on_error=1:suppressions=../tst/suppressions/TSAN.supp - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - before_script: mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE - - # Old Version GCC 4.4 - - name: "Linux GCC 4.4 Build" - os: linux - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-get -q update - - sudo apt-get -y install gcc-4.4 - - sudo apt-get -y install gdb - compiler: gcc - before_script: export CC=gcc-4.4 && mkdir build && cd build && cmake .. -DBUILD_TEST=TRUE - - # Static Build - - name: "Static Build" - before_install: - # TODO: Remove the following 2 lines. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - echo '{"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' | sudo tee /etc/docker/daemon.json - - sudo service docker restart - - mkdir build - - docker run -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -w /src/build -dit --security-opt=seccomp=.github/default.json --name alpine -v $(pwd):/src alpine:latest - - alpine() { docker exec -it alpine "$@"; } - install: - - alpine apk update - - alpine apk upgrade - - alpine apk add alpine-sdk cmake clang linux-headers perl bash openssl-dev - script: - - alpine cmake .. -DBUILD_STATIC_LIBS=TRUE -DBUILD_TEST=TRUE - - alpine make - # ldd will return non-zero when there's no dynamic link. So, the positive value for static builds is non-zero - - alpine ../scripts/check-static-build.sh || travis_terminate 1 - - alpine ./tst/webrtc_client_test - after_failure: skip # no coredumps in container - - # Cross-compilation to ARM, no tests are run - - name: "ARM Cross-compilation" - os: linux - addons: - apt: - packages: - - gcc-arm-linux-gnueabi - - g++-arm-linux-gnueabi - - binutils-arm-linux-gnueabi - compiler: gcc - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - before_script: - - export CC=arm-linux-gnueabi-gcc CXX=arm-linux-gnueabi-g++ - - mkdir build && cd build - - cmake .. -DBUILD_OPENSSL=TRUE -DBUILD_OPENSSL_PLATFORM=linux-generic32 -DBUILD_LIBSRTP_HOST_PLATFORM=x86_64-unknown-linux-gnu -DBUILD_LIBSRTP_DESTINATION_PLATFORM=arm-unknown-linux-uclibcgnueabi - script: make - - - name: "mbedTLS - Linux GCC 4.4 Build" - os: linux - compiler: gcc - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-get -q update - - sudo apt-get -y install gcc-4.4 - - sudo apt-get -y install gdb - before_script: mkdir build && cd build && cmake .. -DBUILD_TEST=TRUE -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON - - - name: "mbedTLS - Linux Clang" - os: linux - compiler: clang - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - before_script: mkdir build && cd build && cmake .. -DBUILD_TEST=TRUE -DUSE_OPENSSL=OFF -DUSE_MBEDTLS=ON - - - name: "Windows MSVC" - env: allowTestFail=true - os: windows - script: - - choco install nasm strawberryperl - - unset CC CC_FOR_BUILD CXX CXX_FOR_BUILD # We want to use MSVC - - export "PATH=/c/Strawberry/perl/site/bin:/c/Strawberry/perl/bin:/c/Strawberry/c/bin:/c/Program Files/NASM:`pwd`/open-source/lib:`pwd`/open-source/bin:$PATH" - - .github/build_windows.bat - - cd build/tst && ./webrtc_client_test.exe --gtest_filter="-DataChannelFunctionalityTest.*:IceApiTest.*:IceFunctionalityTest.*:PeerConnectionFunctionalityTest.*:SignalingApiFunctionalityTest.*:TurnConnectionFunctionalityTest.*:RtpFunctionalityTest.marshallUnmarshallH264Data:RtpFunctionalityTest.packingUnpackingVerifySameH264Frame:RtcpFunctionalityTest.onRtcpPacketCompound:RtcpFunctionalityTest.twcc3" - - - name: "Sample check" - os: linux - compiler: gcc - before_install: - # TODO: Remove the following line. This is only a workaround for enabling IPv6, https://github.com/travis-ci/travis-ci/issues/8891. - - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6' - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-get -q update - - sudo apt-get -y install gcc-4.4 - before_script: mkdir build && cd build && cmake .. - script: - - make - - cd .. - - ./scripts/check-sample.sh - - # Generate Doxygen - - name: "Generate Doxygen" - if: type = push - before_install: - - docker run -w /src/.github -dit --name alpine -v $(pwd):/src alpine - - alpine() { docker exec -it alpine "$@"; } - - alpine apk update - - alpine apk add doxygen graphviz - - alpine apk add --no-cache ttf-freefont - script: - # Add SSH key to agent - - | - eval "$(ssh-agent -s)" - openssl aes-256-cbc -K $encrypted_d627db542948_key -iv $encrypted_d627db542948_iv -in .github/github_deploy_key.enc -out .github/github_deploy_key -d - chmod 600 .github/github_deploy_key - ssh-add .github/github_deploy_key - rm .github/github_deploy_key - # Generate doxygen in container, need latest version - - alpine doxygen Doxyfile - - alpine chmod -R 777 doc - - mv .github/doc/html /tmp - # Unshallow repo - - | - git remote rm origin - git remote add origin git@github.com:awslabs/amazon-kinesis-video-streams-webrtc-sdk-c.git - git fetch - # Move to gh-pages and create new commit - - | - git checkout gh-pages - rm -rf * .github - mv /tmp/html/* . - # Commit and push - - | - git add . - git commit -m "Auto-generated from travis" - git push diff --git a/.github/Doxyfile b/Doxyfile similarity index 100% rename from .github/Doxyfile rename to Doxyfile diff --git a/.github/DoxygenLayout.xml b/DoxygenLayout.xml similarity index 100% rename from .github/DoxygenLayout.xml rename to DoxygenLayout.xml diff --git a/.github/Introduction.md b/Introduction.md similarity index 100% rename from .github/Introduction.md rename to Introduction.md diff --git a/README.md b/README.md index 8dc9449b83..a824f36eb3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@

Pure C WebRTC Client for Amazon Kinesis Video Streams

- Build Status Coverage Status

@@ -73,7 +72,7 @@ If you do wish to link to existing libraries you can use the following flags to #### Cross-Compilation -If you wish to cross-compile `CC` and `CXX` are respected when building the library and all its dependencies. You will also need to set `BUILD_OPENSSL_PLATFORM`, `BUILD_LIBSRTP_HOST_PLATFORM` and `BUILD_LIBSRTP_DESTINATION_PLATFORM`. See our [.travis.yml](.travis.yml) for an example of this. Every commit is cross compiled to ensure that it continues to work. +If you wish to cross-compile `CC` and `CXX` are respected when building the library and all its dependencies. You will also need to set `BUILD_OPENSSL_PLATFORM`, `BUILD_LIBSRTP_HOST_PLATFORM` and `BUILD_LIBSRTP_DESTINATION_PLATFORM`. See our codecov.io for an example of this. Every commit is cross compiled to ensure that it continues to work. #### Static Builds diff --git a/samples/Common.c b/samples/Common.c index 8270e2cb09..d03f5c64b0 100644 --- a/samples/Common.c +++ b/samples/Common.c @@ -12,6 +12,13 @@ VOID sigintHandler(INT32 sigNum) } } +STATUS signalingCallFailed(STATUS status) +{ + return (STATUS_SIGNALING_GET_TOKEN_CALL_FAILED == status || STATUS_SIGNALING_DESCRIBE_CALL_FAILED == status || + STATUS_SIGNALING_CREATE_CALL_FAILED == status || STATUS_SIGNALING_GET_ENDPOINT_CALL_FAILED == status || + STATUS_SIGNALING_GET_ICE_CONFIG_CALL_FAILED == status || STATUS_SIGNALING_CONNECT_CALL_FAILED == status); +} + VOID onDataChannelMessage(UINT64 customData, PRtcDataChannel pDataChannel, BOOL isBinary, PBYTE pMessage, UINT32 pMessageLen) { UNUSED_PARAM(customData); @@ -886,32 +893,32 @@ STATUS getIceCandidatePairStatsCallback(UINT32 timerId, UINT64 currentTime, UINT pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.nominated ? "nominated" : "not nominated"); averageNumberOfPacketsSentPerSecond = - (DOUBLE)(pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.packetsSent - - pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfPacketsSent) / + (DOUBLE) (pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.packetsSent - + pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfPacketsSent) / (DOUBLE) currentMeasureDuration; DLOGD("Packet send rate: %lf pkts/sec", averageNumberOfPacketsSentPerSecond); averageNumberOfPacketsReceivedPerSecond = - (DOUBLE)(pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.packetsReceived - - pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfPacketsReceived) / + (DOUBLE) (pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.packetsReceived - + pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfPacketsReceived) / (DOUBLE) currentMeasureDuration; DLOGD("Packet receive rate: %lf pkts/sec", averageNumberOfPacketsReceivedPerSecond); - outgoingBitrate = (DOUBLE)((pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.bytesSent - - pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfBytesSent) * - 8.0) / + outgoingBitrate = (DOUBLE) ((pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.bytesSent - + pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfBytesSent) * + 8.0) / currentMeasureDuration; DLOGD("Outgoing bit rate: %lf bps", outgoingBitrate); - incomingBitrate = (DOUBLE)((pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.bytesReceived - - pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfBytesReceived) * - 8.0) / + incomingBitrate = (DOUBLE) ((pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.bytesReceived - + pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevNumberOfBytesReceived) * + 8.0) / currentMeasureDuration; DLOGD("Incoming bit rate: %lf bps", incomingBitrate); averagePacketsDiscardedOnSend = - (DOUBLE)(pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.packetsDiscardedOnSend - - pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevPacketsDiscardedOnSend) / + (DOUBLE) (pSampleConfiguration->rtcIceCandidatePairMetrics.rtcStatsObject.iceCandidatePairStats.packetsDiscardedOnSend - + pSampleConfiguration->sampleStreamingSessionList[i]->rtcMetricsHistory.prevPacketsDiscardedOnSend) / (DOUBLE) currentMeasureDuration; DLOGD("Packet discard rate: %lf pkts/sec", averagePacketsDiscardedOnSend); @@ -1156,10 +1163,18 @@ 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(signalingClientFetchSync(pSampleConfiguration->signalingClientHandle))) { - // Re-set the variable again - ATOMIC_STORE_BOOL(&pSampleConfiguration->recreateSignalingClient, FALSE); + if (ATOMIC_LOAD_BOOL(&pSampleConfiguration->recreateSignalingClient)) { + retStatus = signalingClientFetchSync(pSampleConfiguration->signalingClientHandle); + if (STATUS_SUCCEEDED(retStatus)) { + // Re-set the variable again + ATOMIC_STORE_BOOL(&pSampleConfiguration->recreateSignalingClient, FALSE); + } else if (signalingCallFailed(retStatus)) { + printf("[KVS Common] recreating Signaling Client\n"); + freeSignalingClient(&pSampleConfiguration->signalingClientHandle); + createSignalingClientSync(&pSampleConfiguration->clientInfo, &pSampleConfiguration->channelInfo, + &pSampleConfiguration->signalingClientCallbacks, pSampleConfiguration->pCredentialProvider, + &pSampleConfiguration->signalingClientHandle); + } } // Check the signaling client state and connect if needed diff --git a/samples/kvsWebRTCClientMaster.c b/samples/kvsWebRTCClientMaster.c index d7e29e1506..a8a4348ec2 100644 --- a/samples/kvsWebRTCClientMaster.c +++ b/samples/kvsWebRTCClientMaster.c @@ -292,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) @@ -360,7 +360,7 @@ PVOID sendAudioPackets(PVOID args) CleanUp: - return (PVOID)(ULONG_PTR) retStatus; + return (PVOID) (ULONG_PTR) retStatus; } PVOID sampleReceiveVideoFrame(PVOID args) @@ -380,5 +380,5 @@ PVOID sampleReceiveVideoFrame(PVOID args) CleanUp: - return (PVOID)(ULONG_PTR) retStatus; + return (PVOID) (ULONG_PTR) retStatus; } diff --git a/samples/kvsWebRTCClientViewer.c b/samples/kvsWebRTCClientViewer.c index 189a8706db..218f920426 100644 --- a/samples/kvsWebRTCClientViewer.c +++ b/samples/kvsWebRTCClientViewer.c @@ -17,14 +17,15 @@ VOID dataChannelOnMessageCallback(UINT64 customData, PRtcDataChannel pDataChanne } // onOpen callback for the onOpen event of a viewer created data channel -VOID dataChannelOnOpenCallback(UINT64 customData, PRtcDataChannel pDataChannel) { +VOID dataChannelOnOpenCallback(UINT64 customData, PRtcDataChannel pDataChannel) +{ STATUS retStatus = STATUS_SUCCESS; DLOGI("New DataChannel has been opened %s \n", pDataChannel->name); dataChannelOnMessage(pDataChannel, customData, dataChannelOnMessageCallback); ATOMIC_INCREMENT((PSIZE_T) customData); // Sending first message to the master over the data channel retStatus = dataChannelSend(pDataChannel, FALSE, (PBYTE) VIEWER_DATA_CHANNEL_MESSAGE, STRLEN(VIEWER_DATA_CHANNEL_MESSAGE)); - if(retStatus != STATUS_SUCCESS){ + if (retStatus != STATUS_SUCCESS) { DLOGI("[KVS Viewer] dataChannelSend(): operation returned status code: 0x%08x \n", retStatus); } } @@ -204,7 +205,7 @@ INT32 main(INT32 argc, CHAR* argv[]) // Creating a new datachannel on the peer connection of the existing sample streaming session retStatus = createDataChannel(pPeerConnection, pChannelName, NULL, &pDataChannel); - if(retStatus != STATUS_SUCCESS) { + if (retStatus != STATUS_SUCCESS) { printf("[KVS Viewer] createDataChannel(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } @@ -212,7 +213,7 @@ INT32 main(INT32 argc, CHAR* argv[]) // Setting a callback for when the data channel is open retStatus = dataChannelOnOpen(pDataChannel, (UINT64) &datachannelLocalOpenCount, dataChannelOnOpenCallback); - if(retStatus != STATUS_SUCCESS) { + if (retStatus != STATUS_SUCCESS) { printf("[KVS Viewer] dataChannelOnOpen(): operation returned status code: 0x%08x \n", retStatus); goto CleanUp; } diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h index 55adc47393..bf91f41c48 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h @@ -301,7 +301,6 @@ extern "C" { #define STATUS_SIGNALING_INVALID_CLIENT_INFO_CACHE_FILE_PATH_LEN STATUS_SIGNALING_BASE + 0x00000033 #define STATUS_SIGNALING_LWS_CALL_FAILED STATUS_SIGNALING_BASE + 0x00000034 - /*!@} */ ///////////////////////////////////////////////////// @@ -1182,19 +1181,20 @@ 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. - 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 + 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; /** diff --git a/src/source/Ice/IceAgent.c b/src/source/Ice/IceAgent.c index 18f0a1a80c..3747b956cc 100644 --- a/src/source/Ice/IceAgent.c +++ b/src/source/Ice/IceAgent.c @@ -1233,6 +1233,15 @@ STATUS iceCandidatePairCheckConnection(PStunPacket pStunBindingRequest, PIceAgen CHK(pStunAttributePriority != NULL, STATUS_INVALID_ARG); + if (pIceCandidatePair->local->ipAddress.family == KVS_IP_FAMILY_TYPE_IPV4) { + DLOGD("remote ip:%u.%u.%u.%u, port:%u, local ip:%u.%u.%u.%u, port:%u", pIceCandidatePair->remote->ipAddress.address[0], + pIceCandidatePair->remote->ipAddress.address[1], pIceCandidatePair->remote->ipAddress.address[2], + pIceCandidatePair->remote->ipAddress.address[3], pIceCandidatePair->remote->ipAddress.address[0], + pIceCandidatePair->remote->ipAddress.port, pIceCandidatePair->local->ipAddress.address[1], + pIceCandidatePair->local->ipAddress.address[2], pIceCandidatePair->local->ipAddress.address[3], + pIceCandidatePair->local->ipAddress.address[0], pIceCandidatePair->local->ipAddress.port); + } + // update priority and transaction id pStunAttributePriority->priority = pIceCandidatePair->local->priority; CHK_STATUS(iceUtilsGenerateTransactionId(pStunBindingRequest->header.transactionId, ARRAY_SIZE(pStunBindingRequest->header.transactionId))); @@ -2247,11 +2256,12 @@ STATUS incomingRelayedDataHandler(UINT64 customData, PSocketConnection pSocketCo STATUS retStatus = STATUS_SUCCESS; PIceCandidate pRelayedCandidate = (PIceCandidate) customData; // this should be more than enough. Usually the number of channel data in each tcp message is around 4 - TurnChannelData turnChannelData[DEFAULT_TURN_CHANNEL_DATA_BUFFER_SIZE]; + TurnChannelData turnChannelData[DEFAULT_TURN_CHANNEL_DATA_BUFFER_SIZE] = {0}; UINT32 turnChannelDataCount = ARRAY_SIZE(turnChannelData), i = 0; CHK(pRelayedCandidate != NULL && pSocketConnection != NULL, STATUS_NULL_ARG); + DLOGV("Candidate id: %s", pRelayedCandidate->id); CHK_STATUS(turnConnectionIncomingDataHandler(pRelayedCandidate->pTurnConnection, pBuffer, bufferLen, pSrc, pDest, turnChannelData, &turnChannelDataCount)); for (i = 0; i < turnChannelDataCount; ++i) { @@ -2282,6 +2292,7 @@ STATUS incomingDataHandler(UINT64 customData, PSocketConnection pSocketConnectio // for stun packets, first 8 bytes are 4 byte type and length, then 4 byte magic byte if ((bufferLen < 8 || !IS_STUN_PACKET(pBuffer)) && pIceAgent->iceAgentCallbacks.inboundPacketFn != NULL) { // release lock early + MUTEX_UNLOCK(pIceAgent->lock); locked = FALSE; pIceAgent->iceAgentCallbacks.inboundPacketFn(pIceAgent->iceAgentCallbacks.customData, pBuffer, bufferLen); @@ -2441,7 +2452,9 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].totalResponsesReceived++; retStatus = hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime); if (retStatus != STATUS_SUCCESS) { - DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x)", retStatus); + DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x), " + "stunBindingRequest", + retStatus); } else { pIceAgent->rtcIceServerDiagnostics[pIceCandidate->iceServerIndex].totalRoundTripTime += GETTIME() - requestSentTime; CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum)); @@ -2469,6 +2482,7 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS "Cannot find candidate pair with local candidate %s and remote candidate %s. Dropping STUN binding success response", ipAddrStr2, ipAddrStr); } + DLOGV("Pair binding response! %s %s", pIceCandidatePair->local->id, pIceCandidatePair->remote->id); retStatus = hashTableGet(pIceCandidatePair->requestSentTime, checkSum, &requestSentTime); if (retStatus != STATUS_SUCCESS) { DLOGW("Unable to fetch request Timestamp from the hash table. No update to RTT for the pair (error code: 0x%08x)", retStatus); @@ -2485,7 +2499,8 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS pIceAgent->rtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex].totalResponsesReceived++; retStatus = hashTableGet(pIceAgent->requestTimestampDiagnostics, checkSum, &requestSentTime); if (retStatus != STATUS_SUCCESS) { - DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x)", retStatus); + DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x), typeRelayed", + retStatus); } else { pIceAgent->rtcIceServerDiagnostics[pIceCandidatePair->local->iceServerIndex].totalRoundTripTime += GETTIME() - requestSentTime; CHK_STATUS(hashTableRemove(pIceAgent->requestTimestampDiagnostics, checkSum)); @@ -2513,10 +2528,13 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS } if (pIceCandidatePair->state != ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { + DLOGV("Pair succeeded! %s %s", pIceCandidatePair->local->id, pIceCandidatePair->remote->id); pIceCandidatePair->state = ICE_CANDIDATE_PAIR_STATE_SUCCEEDED; retStatus = hashTableGet(pIceCandidatePair->requestSentTime, checkSum, &requestSentTime); if (retStatus != STATUS_SUCCESS) { - DLOGW("Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x)", retStatus); + DLOGW( + "Unable to fetch request Timestamp from the hash table. No update to totalRoundTripTime (error code: 0x%08x), stateSucceeded", + retStatus); } else { pIceCandidatePair->roundTripTime = GETTIME() - requestSentTime; DLOGD("Ice candidate pair %s_%s is connected. Round trip time: %" PRIu64 "ms", pIceCandidatePair->local->id, @@ -2537,12 +2555,21 @@ STATUS handleStunPacket(PIceAgent pIceAgent, PBYTE pBuffer, UINT32 bufferLen, PS break; default: - CHK_STATUS(hexEncode(pBuffer, bufferLen, NULL, &hexStrLen)); - hexStr = MEMCALLOC(1, hexStrLen * SIZEOF(CHAR)); - CHK(hexStr != NULL, STATUS_NOT_ENOUGH_MEMORY); - CHK_STATUS(hexEncode(pBuffer, bufferLen, hexStr, &hexStrLen)); - DLOGW("Dropping unrecognized STUN packet. Packet type: 0x%02x. Packet content: \n\t%s", stunPacketType, hexStr); - SAFE_MEMFREE(hexStr); + if (!IS_STUN_PACKET(pBuffer)) { + CHK_STATUS(hexEncode(pBuffer, bufferLen, NULL, &hexStrLen)); + hexStr = MEMCALLOC(1, hexStrLen * SIZEOF(CHAR)); + CHK(hexStr != NULL, STATUS_NOT_ENOUGH_MEMORY); + CHK_STATUS(hexEncode(pBuffer, bufferLen, hexStr, &hexStrLen)); + DLOGW("Dropping unrecognized STUN packet. Packet type: 0x%02x. Packet content: \n\t%s", stunPacketType, hexStr); + SAFE_MEMFREE(hexStr); + } else if (STUN_PACKET_IS_TYPE_ERROR(pBuffer)) { + CHK_STATUS(hexEncode(pBuffer, bufferLen, NULL, &hexStrLen)); + hexStr = MEMCALLOC(1, hexStrLen * SIZEOF(CHAR)); + CHK(hexStr != NULL, STATUS_NOT_ENOUGH_MEMORY); + CHK_STATUS(hexEncode(pBuffer, bufferLen, hexStr, &hexStrLen)); + DLOGW("Error STUN packet. Packet type: 0x%02x. Packet content: \n\t%s", stunPacketType, hexStr); + SAFE_MEMFREE(hexStr); + } break; } @@ -2659,8 +2686,8 @@ VOID iceAgentLogNewCandidate(PIceCandidate pIceCandidate) 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/IceAgentStateMachine.c b/src/source/Ice/IceAgentStateMachine.c index 3f35e6716a..b9c2c293fa 100644 --- a/src/source/Ice/IceAgentStateMachine.c +++ b/src/source/Ice/IceAgentStateMachine.c @@ -8,70 +8,23 @@ * 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, - 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 - }, + {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); @@ -273,6 +226,7 @@ STATUS fromCheckConnectionIceAgentState(UINT64 customData, PUINT64 pState) CHK_STATUS(doubleListGetHeadNode(pIceAgent->iceCandidatePairs, &pCurNode)); while (pCurNode != NULL && !connectedCandidatePairFound) { pIceCandidatePair = (PIceCandidatePair) pCurNode->data; + DLOGD("Checking pair: %s %s, state: %d", pIceCandidatePair->local->id, pIceCandidatePair->remote->id, pIceCandidatePair->state); pCurNode = pCurNode->pNext; if (pIceCandidatePair->state == ICE_CANDIDATE_PAIR_STATE_SUCCEEDED) { diff --git a/src/source/Ice/TurnConnection.c b/src/source/Ice/TurnConnection.c index cbd615dd54..e1a833be35 100644 --- a/src/source/Ice/TurnConnection.c +++ b/src/source/Ice/TurnConnection.c @@ -352,6 +352,7 @@ STATUS turnConnectionHandleStunError(PTurnConnection pTurnConnection, PBYTE pBuf MUTEX_LOCK(pTurnConnection->lock); locked = TRUE; + stunPacketType = (UINT16) getInt16(*((PUINT16) pBuffer)); /* we could get errors like expired nonce after sending the deallocation packet. The allocate would have been * deallocated even if the response is error response, and if we try to deallocate again we would get invalid * allocation error. Therefore if we get an error after we've sent the deallocation packet, consider the @@ -450,9 +451,10 @@ STATUS turnConnectionHandleChannelData(PTurnConnection pTurnConnection, PBYTE pB STATUS retStatus = STATUS_SUCCESS; BOOL locked = FALSE; - UINT32 turnChannelDataCount = 0; + UINT32 turnChannelDataCount = 0, hexStrLen = 0; UINT16 channelNumber = 0; PTurnPeer pTurnPeer = NULL; + PCHAR hexStr = NULL; CHK(pTurnConnection != NULL && pChannelData != NULL && pChannelDataCount != NULL && pProcessedDataLen != NULL, STATUS_NULL_ARG); CHK(pBuffer != NULL && bufferLen > 0, STATUS_INVALID_ARG); @@ -478,7 +480,14 @@ STATUS turnConnectionHandleChannelData(PTurnConnection pTurnConnection, PBYTE pB } } else { + CHK_STATUS(hexEncode(pBuffer, bufferLen, NULL, &hexStrLen)); + hexStr = MEMCALLOC(1, hexStrLen * SIZEOF(CHAR)); + CHK(hexStr != NULL, STATUS_NOT_ENOUGH_MEMORY); + CHK_STATUS(hexEncode(pBuffer, bufferLen, hexStr, &hexStrLen)); + DLOGE("Turn connection does not have channel number, dumping payload: %s", hexStr); turnChannelDataCount = 0; + + SAFE_MEMFREE(hexStr); } *pProcessedDataLen = bufferLen; @@ -493,6 +502,9 @@ STATUS turnConnectionHandleChannelData(PTurnConnection pTurnConnection, PBYTE pB CHK_LOG_ERR(retStatus); + if (hexStr != NULL) { + SAFE_MEMFREE(hexStr); + } if (locked) { MUTEX_UNLOCK(pTurnConnection->lock); } @@ -524,6 +536,7 @@ STATUS turnConnectionHandleChannelDataTcpMode(PTurnConnection pTurnConnection, P /* process only one channel data and return. Because channel data can be intermixed with STUN packet. * need to check remainingBufLen too because channel data could be incomplete. */ while (remainingBufLen != 0 && channelDataCount == 0) { + DLOGV("currRecvDataLen: %d", pTurnConnection->currRecvDataLen); if (pTurnConnection->currRecvDataLen != 0) { if (pTurnConnection->currRecvDataLen >= TURN_DATA_CHANNEL_SEND_OVERHEAD) { /* pTurnConnection->recvDataBuffer always has channel data start */ diff --git a/src/source/PeerConnection/PeerConnection.c b/src/source/PeerConnection/PeerConnection.c index d6af3e44d3..3e4de6823a 100644 --- a/src/source/PeerConnection/PeerConnection.c +++ b/src/source/PeerConnection/PeerConnection.c @@ -1202,7 +1202,7 @@ STATUS addTransceiver(PRtcPeerConnection pPeerConnection, PRtcMediaStreamTrack p } 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; diff --git a/src/source/Sctp/Sctp.c b/src/source/Sctp/Sctp.c index a0d0742d7e..401fce5f0f 100644 --- a/src/source/Sctp/Sctp.c +++ b/src/source/Sctp/Sctp.c @@ -310,12 +310,12 @@ STATUS handleDcepPacket(PSctpSession pSctpSession, UINT32 streamId, PBYTE data, // Assert that is DCEP of type DataChannelOpen CHK(length > SCTP_DCEP_HEADER_LENGTH && data[0] == DCEP_DATA_CHANNEL_OPEN, STATUS_SUCCESS); - MEMCPY(&labelLength, data + 8, SIZEOF(UINT16)); - MEMCPY(&protocolLength, data + 10, SIZEOF(UINT16)); - putInt16((PINT16) &labelLength, labelLength); - putInt16((PINT16) &protocolLength, protocolLength); + MEMCPY(&labelLength, data + 8, SIZEOF(UINT16)); + MEMCPY(&protocolLength, data + 10, SIZEOF(UINT16)); + putInt16((PINT16) &labelLength, labelLength); + putInt16((PINT16) &protocolLength, protocolLength); - CHK((labelLength + protocolLength + SCTP_DCEP_HEADER_LENGTH) >= length, STATUS_SCTP_INVALID_DCEP_PACKET); + CHK((labelLength + protocolLength + SCTP_DCEP_HEADER_LENGTH) >= length, STATUS_SCTP_INVALID_DCEP_PACKET); pSctpSession->sctpSessionCallbacks.dataChannelOpenFunc(pSctpSession->sctpSessionCallbacks.customData, streamId, data + SCTP_DCEP_HEADER_LENGTH, labelLength); diff --git a/src/source/Signaling/Client.c b/src/source/Signaling/Client.c index 4bc8b0bb9a..7e54682918 100644 --- a/src/source/Signaling/Client.c +++ b/src/source/Signaling/Client.c @@ -72,8 +72,7 @@ STATUS createSignalingClientSync(PSignalingClientInfo pClientInfo, PChannelInfo CHK_STATUS(createRetryStrategyForCreatingSignalingClient(pClientInfo, &createSignalingClientRetryStrategy)); - - if(pClientInfo->signalingClientCreationMaxRetryAttempts == CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE) { + if (pClientInfo->signalingClientCreationMaxRetryAttempts == CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE) { signalingClientCreationMaxRetryCount = DEFAULT_CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS; } else { signalingClientCreationMaxRetryCount = pClientInfo->signalingClientCreationMaxRetryAttempts; @@ -197,7 +196,7 @@ STATUS signalingClientFetchSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) CHK_STATUS(createRetryStrategyForCreatingSignalingClient(&pSignalingClient->clientInfo.signalingClientInfo, &createSignalingClientRetryStrategy)); signalingClientCreationMaxRetryCount = pSignalingClient->clientInfo.signalingClientInfo.signalingClientCreationMaxRetryAttempts; - if(signalingClientCreationMaxRetryCount == CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE) { + if (signalingClientCreationMaxRetryCount == CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS_SENTINEL_VALUE) { signalingClientCreationMaxRetryCount = DEFAULT_CREATE_SIGNALING_CLIENT_RETRY_ATTEMPTS; } @@ -215,11 +214,12 @@ STATUS signalingClientFetchSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) break; } - pSignalingClient->clientInfo.signalingClientInfo.stateMachineRetryCountReadOnly = signalingClientInfoInternal.signalingClientInfo.stateMachineRetryCountReadOnly; + pSignalingClient->clientInfo.signalingClientInfo.stateMachineRetryCountReadOnly = + signalingClientInfoInternal.signalingClientInfo.stateMachineRetryCountReadOnly; // Wait before attempting to create signaling client - CHK_STATUS(pSignalingClient->clientInfo.signalingStateMachineRetryStrategyCallbacks.executeRetryStrategyFn(&createSignalingClientRetryStrategy, - &signalingClientCreationWaitTime)); + 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]", @@ -231,7 +231,9 @@ STATUS signalingClientFetchSync(SIGNALING_CLIENT_HANDLE signalingClientHandle) CleanUp: SIGNALING_UPDATE_ERROR_COUNT(pSignalingClient, retStatus); - freeRetryStrategyForCreatingSignalingClient(&pSignalingClient->clientInfo.signalingClientInfo, &createSignalingClientRetryStrategy); + if (pSignalingClient != NULL) { + freeRetryStrategyForCreatingSignalingClient(&pSignalingClient->clientInfo.signalingClientInfo, &createSignalingClientRetryStrategy); + } LEAVES(); return retStatus; } diff --git a/src/source/Signaling/LwsApiCalls.c b/src/source/Signaling/LwsApiCalls.c index 1c02bcc172..b2429399f9 100644 --- a/src/source/Signaling/LwsApiCalls.c +++ b/src/source/Signaling/LwsApiCalls.c @@ -18,7 +18,7 @@ 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]; + CHAR dateHdrBuffer[MAX_DATE_HEADER_BUFFER_LENGTH + 1]; PBYTE pEndPtr; PBYTE* ppStartPtr; PLwsCallInfo pLwsCallInfo; @@ -106,7 +106,7 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, 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); + len = (SIZE_T) lws_hdr_copy(wsi, &dateHdrBuffer[0], MAX_DATE_HEADER_BUFFER_LENGTH, WSI_TOKEN_HTTP_DATE); time(&td); @@ -124,7 +124,7 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, nowTime); } else if (nowTime > serverTime + MIN_CLOCK_SKEW_TIME_TO_CORRECT) { clockSkew = (nowTime - serverTime); - clockSkew |= ((UINT64)(1ULL << 63)); + 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 @@ -156,7 +156,7 @@ INT32 lwsHttpCallbackRoutine(struct lws* wsi, enum lws_callback_reasons reason, lwsl_hexdump_debug(pDataIn, dataSize); if (dataSize != 0) { - CHK(NULL != (pLwsCallInfo->callInfo.responseData = (PCHAR) MEMALLOC(dataSize+1)), STATUS_NOT_ENOUGH_MEMORY); + CHK(NULL != (pLwsCallInfo->callInfo.responseData = (PCHAR) MEMALLOC(dataSize + 1)), STATUS_NOT_ENOUGH_MEMORY); MEMCPY(pLwsCallInfo->callInfo.responseData, pDataIn, dataSize); pLwsCallInfo->callInfo.responseData[dataSize] = '\0'; pLwsCallInfo->callInfo.responseDataLen = (UINT32) dataSize; @@ -694,15 +694,18 @@ STATUS lwsCompleteSync(PLwsCallInfo pCallInfo) return retStatus; } -BOOL isCallResultSignatureExpired(PCallInfo pCallInfo) { +BOOL isCallResultSignatureExpired(PCallInfo pCallInfo) +{ return (STRNSTR(pCallInfo->responseData, "Signature expired", pCallInfo->responseDataLen) != NULL); } -BOOL isCallResultSignatureNotYetCurrent(PCallInfo pCallInfo) { +BOOL isCallResultSignatureNotYetCurrent(PCallInfo pCallInfo) +{ return (STRNSTR(pCallInfo->responseData, "Signature not yet current", pCallInfo->responseDataLen) != NULL); } -STATUS checkAndCorrectForClockSkew(PSignalingClient pSignalingClient, PRequestInfo pRequestInfo) { +STATUS checkAndCorrectForClockSkew(PSignalingClient pSignalingClient, PRequestInfo pRequestInfo) +{ ENTERS(); STATUS retStatus = STATUS_SUCCESS; @@ -716,8 +719,8 @@ STATUS checkAndCorrectForClockSkew(PSignalingClient pSignalingClient, PRequestIn 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)); + if (clockSkewOffset & ((UINT64) (1ULL << 63))) { + clockSkewOffset ^= ((UINT64) (1ULL << 63)); DLOGV("Detected device time is AHEAD of server time!"); pRequestInfo->currentTime -= clockSkewOffset; } else { @@ -729,7 +732,6 @@ STATUS checkAndCorrectForClockSkew(PSignalingClient pSignalingClient, PRequestIn CleanUp: - LEAVES(); return retStatus; } @@ -771,7 +773,8 @@ STATUS describeChannelLws(PSignalingClient pSignalingClient, UINT64 time) // 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); + pRequestInfo->currentTime = + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); } checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); @@ -787,7 +790,7 @@ 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, + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, STATUS_SIGNALING_LWS_CALL_FAILED); // Parse the response @@ -927,7 +930,8 @@ STATUS createChannelLws(PSignalingClient pSignalingClient, UINT64 time) 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); + pRequestInfo->currentTime = + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); } checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); @@ -943,7 +947,7 @@ 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, + 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 @@ -1012,7 +1016,8 @@ STATUS getChannelEndpointLws(PSignalingClient pSignalingClient, UINT64 time) 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); + pRequestInfo->currentTime = + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); } checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); @@ -1028,7 +1033,7 @@ 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, + 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 @@ -1154,7 +1159,8 @@ STATUS getIceConfigLws(PSignalingClient pSignalingClient, UINT64 time) 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); + pRequestInfo->currentTime = + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); } checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); @@ -1170,7 +1176,7 @@ 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, + CHK((SERVICE_CALL_RESULT) ATOMIC_LOAD(&pSignalingClient->result) == SERVICE_CALL_RESULT_OK && resultLen != 0 && pResponseStr != NULL, STATUS_SIGNALING_LWS_CALL_FAILED); // Parse the response @@ -1282,7 +1288,8 @@ STATUS deleteChannelLws(PSignalingClient pSignalingClient, UINT64 time) 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); + pRequestInfo->currentTime = + pSignalingClient->signalingClientCallbacks.getCurrentTimeFn(pSignalingClient->signalingClientCallbacks.customData); } checkAndCorrectForClockSkew(pSignalingClient, pRequestInfo); @@ -1297,7 +1304,7 @@ 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, + 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 @@ -1624,7 +1631,8 @@ 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 = SIGNALING_GET_CURRENT_TIME(pSignalingClient)) <= 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 @@ -1959,7 +1967,8 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT SAFE_MEMFREE(pSignalingMessageWrapper); // Iterate the state machinery - CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, SIGNALING_STATE_CONNECTED)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, + SIGNALING_STATE_CONNECTED)); CHK(FALSE, retStatus); break; @@ -1972,7 +1981,8 @@ STATUS receiveLwsMessage(PSignalingClient pSignalingClient, PCHAR pMessage, UINT SAFE_MEMFREE(pSignalingMessageWrapper); // Iterate the state machinery - CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, SIGNALING_STATE_CONNECTED)); + 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 a08c54c937..83d76c032b 100644 --- a/src/source/Signaling/LwsApiCalls.h +++ b/src/source/Signaling/LwsApiCalls.h @@ -169,7 +169,7 @@ extern "C" { // Check for the stale credentials #define CHECK_SIGNALING_CREDENTIALS_EXPIRATION(p) \ do { \ - if (SIGNALING_GET_CURRENT_TIME((p)) >= (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); \ } \ diff --git a/src/source/Signaling/Signaling.c b/src/source/Signaling/Signaling.c index ccdd76e77a..c3d02b9e8a 100644 --- a/src/source/Signaling/Signaling.c +++ b/src/source/Signaling/Signaling.c @@ -152,7 +152,7 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf // Initializing the diagnostics mostly is taken care of by zero-mem in MEMCALLOC pSignalingClient->diagnostics.createTime = SIGNALING_GET_CURRENT_TIME(pSignalingClient); - CHK_STATUS(hashTableCreateWithParams(SIGNALING_CLOCKSKEW_HASH_TABLE_BUCKET_COUNT,SIGNALING_CLOCKSKEW_HASH_TABLE_BUCKET_LENGTH, + 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 @@ -169,7 +169,8 @@ STATUS createSignalingSync(PSignalingClientInfoInternal pClientInfo, PChannelInf ATOMIC_STORE_BOOL(&pSignalingClient->refreshIceConfig, FALSE); // 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)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, pSignalingClient->diagnostics.createTime + SIGNALING_CONNECT_STATE_TIMEOUT, + SIGNALING_STATE_GET_TOKEN)); CleanUp: if (pClientInfo != NULL && pSignalingClient != NULL) { @@ -455,13 +456,14 @@ STATUS signalingFetchSync(PSignalingClient pSignalingClient) // 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. + // 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)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_CONNECT_STATE_TIMEOUT, + SIGNALING_STATE_READY)); CleanUp: @@ -522,7 +524,8 @@ STATUS signalingDisconnectSync(PSignalingClient pSignalingClient) ATOMIC_STORE(&pSignalingClient->result, (SIZE_T) SERVICE_CALL_RESULT_OK); - CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_DISCONNECT_STATE_TIMEOUT, SIGNALING_STATE_READY)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_DISCONNECT_STATE_TIMEOUT, + SIGNALING_STATE_READY)); CleanUp: @@ -550,7 +553,8 @@ STATUS signalingDeleteSync(PSignalingClient pSignalingClient) // Set the state directly setStateMachineCurrentState(pSignalingClient->pStateMachine, SIGNALING_STATE_DELETE); - CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_DELETE_TIMEOUT, SIGNALING_STATE_DELETED)); + CHK_STATUS(signalingStateMachineIterator(pSignalingClient, SIGNALING_GET_CURRENT_TIME(pSignalingClient) + SIGNALING_DELETE_TIMEOUT, + SIGNALING_STATE_DELETED)); CleanUp: @@ -943,7 +947,6 @@ STATUS describeChannel(PSignalingClient pSignalingClient, UINT64 time) } if (STATUS_SUCCEEDED(retStatus)) { - retStatus = describeChannelLws(pSignalingClient, time); // Store the last call time on success if (STATUS_SUCCEEDED(retStatus)) { diff --git a/src/source/Signaling/Signaling.h b/src/source/Signaling/Signaling.h index dd00e1d980..738e980fb0 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) @@ -63,11 +63,11 @@ extern "C" { #define SIGNALING_API_LATENCY_CALCULATION(pClient, time, isCpApi) \ MUTEX_LOCK((pClient)->diagnosticsLock); \ if (isCpApi) { \ - (pClient)->diagnostics.cpApiLatency = EMA_ACCUMULATOR_GET_NEXT((pClient)->diagnostics.cpApiLatency, \ - SIGNALING_GET_CURRENT_TIME((pClient)) - (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, \ - SIGNALING_GET_CURRENT_TIME((pClient)) - (time)); \ + (pClient)->diagnostics.dpApiLatency = \ + EMA_ACCUMULATOR_GET_NEXT((pClient)->diagnostics.dpApiLatency, SIGNALING_GET_CURRENT_TIME((pClient)) - (time)); \ } \ MUTEX_UNLOCK((pClient)->diagnosticsLock); @@ -78,9 +78,10 @@ extern "C" { #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 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 @@ -102,12 +103,12 @@ static const ExponentialBackoffRetryStrategyConfig DEFAULT_SIGNALING_STATE_MACHI ************************************ 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 */ + 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 */ + FULL_JITTER, /* use full jitter variant */ + 0 /* jitter value unused for full jitter variant */ }; // Forward declaration @@ -330,8 +331,8 @@ 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*); diff --git a/src/source/Signaling/StateMachine.c b/src/source/Signaling/StateMachine.c index 45c1bea1f2..47a49e993a 100644 --- a/src/source/Signaling/StateMachine.c +++ b/src/source/Signaling/StateMachine.c @@ -77,15 +77,16 @@ STATUS defaultSignalingStateTransitionHook(UINT64 customData /* customData shoul 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) { + 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); + 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; diff --git a/tst/WebRTCClientTestFixture.h b/tst/WebRTCClientTestFixture.h index c4b5691251..c1d6f52d1f 100644 --- a/tst/WebRTCClientTestFixture.h +++ b/tst/WebRTCClientTestFixture.h @@ -130,7 +130,6 @@ class WebRtcClientTestBase : public ::testing::Test { retStatus = signalingClientFetchSync(mSignalingClientHandle); - return retStatus; } @@ -211,7 +210,8 @@ class WebRtcClientTestBase : public ::testing::Test { return retStatus; } - static STATUS createRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy) { + static STATUS createRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy) + { STATUS retStatus = STATUS_SUCCESS; PExponentialBackoffRetryStrategyState pExponentialBackoffRetryStrategyState = NULL; @@ -228,15 +228,18 @@ class WebRtcClientTestBase : public ::testing::Test { return retStatus; } - static STATUS getCurrentRetryAttemptNumberFn(PKvsRetryStrategy pKvsRetryStrategy, PUINT32 pRetryCount) { + static STATUS getCurrentRetryAttemptNumberFn(PKvsRetryStrategy pKvsRetryStrategy, PUINT32 pRetryCount) + { return getExponentialBackoffRetryCount(pKvsRetryStrategy, pRetryCount); } - static STATUS freeRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy) { + static STATUS freeRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy) + { return exponentialBackoffRetryStrategyFree(pKvsRetryStrategy); } - static STATUS executeRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy, PUINT64 retryWaitTime) { + static STATUS executeRetryStrategyFn(PKvsRetryStrategy pKvsRetryStrategy, PUINT64 retryWaitTime) + { return getExponentialBackoffRetryStrategyWaitTime(pKvsRetryStrategy, retryWaitTime); } From b820e2ea3d28c670da2fbc789face742cb949ef1 Mon Sep 17 00:00:00 2001 From: "Alex.D.Scofield" Date: Fri, 20 Jan 2023 19:10:23 +0000 Subject: [PATCH 17/19] add support for CN region (#1612) Signed-off-by: Alex Li Signed-off-by: Alex Li --- samples/Common.c | 8 +++++++- .../com/amazonaws/kinesis/video/webrtcclient/Include.h | 4 +++- src/source/Signaling/ChannelInfo.c | 4 ++++ tst/PeerConnectionFunctionalityTest.cpp | 4 ++-- tst/SdpApiTest.cpp | 8 ++++---- tst/WebRTCClientTestFixture.cpp | 2 +- tst/WebRTCClientTestFixture.h | 1 + 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/samples/Common.c b/samples/Common.c index d03f5c64b0..b1588693c2 100644 --- a/samples/Common.c +++ b/samples/Common.c @@ -357,7 +357,13 @@ STATUS initializePeerConnection(PSampleConfiguration pSampleConfiguration, PRtcP configuration.iceTransportPolicy = ICE_TRANSPORT_POLICY_ALL; // Set the STUN server - SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, pSampleConfiguration->channelInfo.pRegion); + PCHAR pKinesisVideoStunUrlPostFix = KINESIS_VIDEO_STUN_URL_POSTFIX; + // If region is in CN, add CN region uri postfix + if (STRSTR(pSampleConfiguration->channelInfo.pRegion, "cn-")) { + pKinesisVideoStunUrlPostFix = KINESIS_VIDEO_STUN_URL_POSTFIX_CN; + } + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, pSampleConfiguration->channelInfo.pRegion, + pKinesisVideoStunUrlPostFix); if (pSampleConfiguration->useTurn) { // Set the URIs from the configuration diff --git a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h index bf91f41c48..6baa03a278 100644 --- a/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h +++ b/src/include/com/amazonaws/kinesis/video/webrtcclient/Include.h @@ -623,7 +623,9 @@ extern "C" { /** * Parameterized string for KVS STUN Server */ -#define KINESIS_VIDEO_STUN_URL "stun:stun.kinesisvideo.%s.amazonaws.com:443" +#define KINESIS_VIDEO_STUN_URL_POSTFIX "amazonaws.com" +#define KINESIS_VIDEO_STUN_URL_POSTFIX_CN "amazonaws.com.cn" +#define KINESIS_VIDEO_STUN_URL "stun:stun.kinesisvideo.%s.%s:443" /** * Default signaling SSL port diff --git a/src/source/Signaling/ChannelInfo.c b/src/source/Signaling/ChannelInfo.c index 18e0df7dce..cd794cb2a5 100644 --- a/src/source/Signaling/ChannelInfo.c +++ b/src/source/Signaling/ChannelInfo.c @@ -127,6 +127,10 @@ STATUS createValidateChannelInfo(PChannelInfo pOrigChannelInfo, PChannelInfo* pp // Create a fully qualified URI SNPRINTF(pCurPtr, MAX_CONTROL_PLANE_URI_CHAR_LEN, "%s%s.%s%s", CONTROL_PLANE_URI_PREFIX, KINESIS_VIDEO_SERVICE_NAME, pChannelInfo->pRegion, CONTROL_PLANE_URI_POSTFIX); + // If region is in CN, add CN region uri postfix + if (STRSTR(pChannelInfo->pRegion, "cn-")) { + STRCAT(pCurPtr, ".cn"); + } } pChannelInfo->pControlPlaneUrl = pCurPtr; diff --git a/tst/PeerConnectionFunctionalityTest.cpp b/tst/PeerConnectionFunctionalityTest.cpp index 955d0a25af..5a5e3ed3d5 100644 --- a/tst/PeerConnectionFunctionalityTest.cpp +++ b/tst/PeerConnectionFunctionalityTest.cpp @@ -232,7 +232,7 @@ TEST_F(PeerConnectionFunctionalityTest, sendDataWithClosedSocketConnectionWithHo PSocketConnection pSocketConnection; MEMSET(&configuration, 0x00, SIZEOF(RtcConfiguration)); - SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION); + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); EXPECT_EQ(createPeerConnection(&configuration, &offerPc), STATUS_SUCCESS); EXPECT_EQ(createPeerConnection(&configuration, &answerPc), STATUS_SUCCESS); @@ -524,7 +524,7 @@ TEST_F(PeerConnectionFunctionalityTest, connectTwoPeersWithHostAndStun) MEMSET(&configuration, 0x00, SIZEOF(RtcConfiguration)); // Set the STUN server - SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION); + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); EXPECT_EQ(createPeerConnection(&configuration, &offerPc), STATUS_SUCCESS); EXPECT_EQ(createPeerConnection(&configuration, &answerPc), STATUS_SUCCESS); diff --git a/tst/SdpApiTest.cpp b/tst/SdpApiTest.cpp index 1b05d288bb..0edf661361 100644 --- a/tst/SdpApiTest.cpp +++ b/tst/SdpApiTest.cpp @@ -614,7 +614,7 @@ a=group:BUNDLE 0 RtcSessionDescriptionInit offerSdp{}; RtcSessionDescriptionInit answerSdp{}; - SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION); + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); track1.kind = MEDIA_STREAM_TRACK_KIND_VIDEO; track1.codec = RTC_CODEC_VP8; @@ -667,7 +667,7 @@ a=group:BUNDLE 0 RtcSessionDescriptionInit offerSdp{}; RtcSessionDescriptionInit answerSdp{}; - SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION); + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); track1.kind = MEDIA_STREAM_TRACK_KIND_VIDEO; track1.codec = RTC_CODEC_VP8; @@ -725,7 +725,7 @@ a=group:BUNDLE 0 RtcSessionDescriptionInit offerSdp{}; RtcSessionDescriptionInit answerSdp{}; - SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION); + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); track1.kind = MEDIA_STREAM_TRACK_KIND_VIDEO; track1.codec = RTC_CODEC_VP8; @@ -788,7 +788,7 @@ a=ice-options:trickle RtcSessionDescriptionInit offerSdp{}; RtcSessionDescriptionInit answerSdp{}; - SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION); + SNPRINTF(configuration.iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); track1.kind = MEDIA_STREAM_TRACK_KIND_VIDEO; track1.codec = RTC_CODEC_VP8; diff --git a/tst/WebRTCClientTestFixture.cpp b/tst/WebRTCClientTestFixture.cpp index 8291bcf6de..d041db728e 100644 --- a/tst/WebRTCClientTestFixture.cpp +++ b/tst/WebRTCClientTestFixture.cpp @@ -256,7 +256,7 @@ void WebRtcClientTestBase::getIceServers(PRtcConfiguration pRtcConfiguration) EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfoCount(mSignalingClientHandle, &iceConfigCount)); // Set the STUN server - SNPRINTF(pRtcConfiguration->iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION); + SNPRINTF(pRtcConfiguration->iceServers[0].urls, MAX_ICE_CONFIG_URI_LEN, KINESIS_VIDEO_STUN_URL, TEST_DEFAULT_REGION, TEST_DEFAULT_STUN_URL_POSTFIX); for (uriCount = 0, i = 0; i < iceConfigCount; i++) { EXPECT_EQ(STATUS_SUCCESS, signalingClientGetIceConfigInfo(mSignalingClientHandle, i, &pIceConfigInfo)); diff --git a/tst/WebRTCClientTestFixture.h b/tst/WebRTCClientTestFixture.h index c1d6f52d1f..845abbc4f5 100644 --- a/tst/WebRTCClientTestFixture.h +++ b/tst/WebRTCClientTestFixture.h @@ -8,6 +8,7 @@ #include #define TEST_DEFAULT_REGION ((PCHAR) "us-west-2") +#define TEST_DEFAULT_STUN_URL_POSTFIX (KINESIS_VIDEO_STUN_URL_POSTFIX) #define TEST_STREAMING_TOKEN_DURATION (40 * HUNDREDS_OF_NANOS_IN_A_SECOND) #define TEST_JITTER_BUFFER_CLOCK_RATE (1000) #define TEST_SIGNALING_MASTER_CLIENT_ID (PCHAR) "Test_Master_ClientId" From 9759590f07d6428bb203c7f323b0f051f8c1f70e Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Tue, 1 Aug 2023 19:43:17 -0700 Subject: [PATCH 18/19] Modify issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 48 --------------- .github/ISSUE_TEMPLATE/bug_report.yml | 68 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 6 ++ .github/ISSUE_TEMPLATE/contribution.md | 22 ------- .github/ISSUE_TEMPLATE/documentation.yml | 23 ++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ------- .github/ISSUE_TEMPLATE/feature_request.yml | 38 ++++++++++++ .github/ISSUE_TEMPLATE/questions-help.md | 18 ------ 8 files changed, 135 insertions(+), 108 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/contribution.md create mode 100644 .github/ISSUE_TEMPLATE/documentation.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml delete mode 100644 .github/ISSUE_TEMPLATE/questions-help.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 1ed5fb63b4..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: "[BUG]" -labels: bug,needs-triage -assignees: '' - ---- - -**Logging** -Add relevent SDK logging. IMPORTANT NOTE: Please make sure to NOT share AWS access credentials under any circumstance! Please make sure they are not in the logs. - -**Describe the bug** -A clear and concise description of what the bug is. - -**SDK version number** -Include the SDK version you are running. If it is a specific commit on master branch, include that - -**Open source building** -If it is a build issue, include 3rd party library version and steps to how you are building it - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. - diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..807d26d40d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,68 @@ +--- +name: Bug Report +description: File a bug report +title: "[Bug]: " +labels: ["bug", "needs-triage"] +assignees: [] +body: + - type: textarea + id: description + attributes: + label: Describe the bug + description: What is the problem? A clear and concise description of the bug. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: | + What is the expected behavior? + validations: + required: true + - type: textarea + id: current + attributes: + label: Current Behavior + description: | + What actually happened? + + Please include full errors, uncaught exceptions, stack traces, and relevant VERBOSE logs. + To get relevant VERBOSE logs from the SDK, you can retrieve by running `export AWS_KVS_LOG_LEVEL = 1` + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: Reproduction Steps + description: | + Provide a self-contained, concise snippet of code that can be used to reproduce the issue. + For more complex issues provide a repo with the smallest sample that reproduces the bug. + Avoid including business logic or unrelated code, it makes diagnosis more difficult. + validations: + required: true + - type: input + id: sdk-version + attributes: + label: WebRTC C SDK version being used + validations: + required: true + - type: input + id: compiler-version + attributes: + label: Compiler and Version used + description: gcc --version / Visual Studio / clang --version + validations: + required: true + - type: input + id: operating-system + attributes: + label: Operating System and version + validations: + required: true + - type: input + id: platform + attributes: + label: Platform being used + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..d31b60f5ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,6 @@ +--- +blank_issues_enabled: false +contact_links: + - name: General Question + url: https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/discussions + about: Please ask and answer questions as a discussion thread \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/contribution.md b/.github/ISSUE_TEMPLATE/contribution.md deleted file mode 100644 index 35e4281634..0000000000 --- a/.github/ISSUE_TEMPLATE/contribution.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: Contribution -about: We always welcome open-source contributions! Please open an issue to tag your pull request -title: "[CONTRIBUTION]" -labels: contribution -assignees: '' - ---- - -PLEASE ADD THE APPROPRIATE TAG TO ALLOW BUCKETIZATION OF THE SOLUTIONS. IF THE TAG IS NOT AVAILABLE, DO NOT WORRY, WE WILL TAKE CARE OF IT! - -** Describe the issue you are trying to solve ** -Add a one line description of the issue you are trying to solve - -** Details of the changes ** -Give a couple of points to describe the changes you have made - -** Test cases ** -Give a detailed description of the tests you are running, if applicable - -** Additional context ** -Any details that can be preserved for the future diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 0000000000..df5e608796 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,23 @@ +--- +name: "Documentation Issue" +description: Report an issue in the API Reference documentation or Developer Guide +title: "[Documentation]: " +labels: ["documentation", "needs-triage"] +assignees: [] +body: + - type: textarea + id: description + attributes: + label: Describe the issue + description: A clear and concise description of the issue. + validations: + required: true + + - type: textarea + id: links + attributes: + label: Links + description: | + Include links to affected documentation page(s). + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 8403b86c9a..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: "[FEATURE] " -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..a5273fca24 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,38 @@ +--- +name: Feature Request +description: Suggest an idea for this project +title: "[Feature request]: " +labels: [feature-request, needs-triage] +assignees: [] +body: + - type: textarea + id: description + attributes: + label: Describe the feature + description: A clear and concise description of the feature you are proposing. + validations: + required: true + - type: textarea + id: use-case + attributes: + label: Use Case + description: | + Why do you need this feature? + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed Solution + description: | + Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. + validations: + required: false + - type: textarea + id: other + attributes: + label: Other Information + description: | + Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/questions-help.md b/.github/ISSUE_TEMPLATE/questions-help.md deleted file mode 100644 index 83d1501847..0000000000 --- a/.github/ISSUE_TEMPLATE/questions-help.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Questions/Help -about: Describe this issue template's purpose here. -title: "[QUESTION]" -labels: question,needs-triage -assignees: '' - ---- - -A one liner description about the use case and what you are trying to achieve - -** Logging ** -Add relevent SDK logging. IMPORTANT NOTE: Please make sure to NOT share AWS access credentials under any circumstance! Please make sure they are not in the logs. - -** Any design considerations/constraints ** -Explain in detail how you would like to integrate our SDK into your solution - -** If you would not like to open an issue to discuss your solution in open-platform, please email your question to kinesis-video-support@amazon.com ** From b2b05c8982beed62e4c589d2890621bf98638153 Mon Sep 17 00:00:00 2001 From: Divya Sampath Kumar Date: Tue, 1 Aug 2023 19:57:39 -0700 Subject: [PATCH 19/19] Revert "Modify issue templates" This reverts commit 9759590f07d6428bb203c7f323b0f051f8c1f70e. --- .github/ISSUE_TEMPLATE/bug_report.md | 48 +++++++++++++++ .github/ISSUE_TEMPLATE/bug_report.yml | 68 ---------------------- .github/ISSUE_TEMPLATE/config.yml | 6 -- .github/ISSUE_TEMPLATE/contribution.md | 22 +++++++ .github/ISSUE_TEMPLATE/documentation.yml | 23 -------- .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++ .github/ISSUE_TEMPLATE/feature_request.yml | 38 ------------ .github/ISSUE_TEMPLATE/questions-help.md | 18 ++++++ 8 files changed, 108 insertions(+), 135 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml delete mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/contribution.md delete mode 100644 .github/ISSUE_TEMPLATE/documentation.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/questions-help.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..1ed5fb63b4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,48 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: bug,needs-triage +assignees: '' + +--- + +**Logging** +Add relevent SDK logging. IMPORTANT NOTE: Please make sure to NOT share AWS access credentials under any circumstance! Please make sure they are not in the logs. + +**Describe the bug** +A clear and concise description of what the bug is. + +**SDK version number** +Include the SDK version you are running. If it is a specific commit on master branch, include that + +**Open source building** +If it is a build issue, include 3rd party library version and steps to how you are building it + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. + diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 807d26d40d..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: Bug Report -description: File a bug report -title: "[Bug]: " -labels: ["bug", "needs-triage"] -assignees: [] -body: - - type: textarea - id: description - attributes: - label: Describe the bug - description: What is the problem? A clear and concise description of the bug. - validations: - required: true - - type: textarea - id: expected - attributes: - label: Expected Behavior - description: | - What is the expected behavior? - validations: - required: true - - type: textarea - id: current - attributes: - label: Current Behavior - description: | - What actually happened? - - Please include full errors, uncaught exceptions, stack traces, and relevant VERBOSE logs. - To get relevant VERBOSE logs from the SDK, you can retrieve by running `export AWS_KVS_LOG_LEVEL = 1` - validations: - required: true - - type: textarea - id: reproduction - attributes: - label: Reproduction Steps - description: | - Provide a self-contained, concise snippet of code that can be used to reproduce the issue. - For more complex issues provide a repo with the smallest sample that reproduces the bug. - Avoid including business logic or unrelated code, it makes diagnosis more difficult. - validations: - required: true - - type: input - id: sdk-version - attributes: - label: WebRTC C SDK version being used - validations: - required: true - - type: input - id: compiler-version - attributes: - label: Compiler and Version used - description: gcc --version / Visual Studio / clang --version - validations: - required: true - - type: input - id: operating-system - attributes: - label: Operating System and version - validations: - required: true - - type: input - id: platform - attributes: - label: Platform being used - validations: - required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index d31b60f5ae..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -blank_issues_enabled: false -contact_links: - - name: General Question - url: https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c/discussions - about: Please ask and answer questions as a discussion thread \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/contribution.md b/.github/ISSUE_TEMPLATE/contribution.md new file mode 100644 index 0000000000..35e4281634 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/contribution.md @@ -0,0 +1,22 @@ +--- +name: Contribution +about: We always welcome open-source contributions! Please open an issue to tag your pull request +title: "[CONTRIBUTION]" +labels: contribution +assignees: '' + +--- + +PLEASE ADD THE APPROPRIATE TAG TO ALLOW BUCKETIZATION OF THE SOLUTIONS. IF THE TAG IS NOT AVAILABLE, DO NOT WORRY, WE WILL TAKE CARE OF IT! + +** Describe the issue you are trying to solve ** +Add a one line description of the issue you are trying to solve + +** Details of the changes ** +Give a couple of points to describe the changes you have made + +** Test cases ** +Give a detailed description of the tests you are running, if applicable + +** Additional context ** +Any details that can be preserved for the future diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml deleted file mode 100644 index df5e608796..0000000000 --- a/.github/ISSUE_TEMPLATE/documentation.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: "Documentation Issue" -description: Report an issue in the API Reference documentation or Developer Guide -title: "[Documentation]: " -labels: ["documentation", "needs-triage"] -assignees: [] -body: - - type: textarea - id: description - attributes: - label: Describe the issue - description: A clear and concise description of the issue. - validations: - required: true - - - type: textarea - id: links - attributes: - label: Links - description: | - Include links to affected documentation page(s). - validations: - required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..8403b86c9a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE] " +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index a5273fca24..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: Feature Request -description: Suggest an idea for this project -title: "[Feature request]: " -labels: [feature-request, needs-triage] -assignees: [] -body: - - type: textarea - id: description - attributes: - label: Describe the feature - description: A clear and concise description of the feature you are proposing. - validations: - required: true - - type: textarea - id: use-case - attributes: - label: Use Case - description: | - Why do you need this feature? - validations: - required: true - - type: textarea - id: solution - attributes: - label: Proposed Solution - description: | - Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. - validations: - required: false - - type: textarea - id: other - attributes: - label: Other Information - description: | - Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. - validations: - required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/questions-help.md b/.github/ISSUE_TEMPLATE/questions-help.md new file mode 100644 index 0000000000..83d1501847 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/questions-help.md @@ -0,0 +1,18 @@ +--- +name: Questions/Help +about: Describe this issue template's purpose here. +title: "[QUESTION]" +labels: question,needs-triage +assignees: '' + +--- + +A one liner description about the use case and what you are trying to achieve + +** Logging ** +Add relevent SDK logging. IMPORTANT NOTE: Please make sure to NOT share AWS access credentials under any circumstance! Please make sure they are not in the logs. + +** Any design considerations/constraints ** +Explain in detail how you would like to integrate our SDK into your solution + +** If you would not like to open an issue to discuss your solution in open-platform, please email your question to kinesis-video-support@amazon.com **