/* * AWS IoT Device Embedded C SDK for ZephyrRTOS * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of * the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Standard includes. */ #include #include /* Zephyr random */ #include /* Demo utils header. */ #include "http_demo_utils.h" /*Include backoff algorithm header for retry logic.*/ #include "backoff_algorithm.h" /*Include clock header for millisecond sleep function. */ #include "clock.h" /* Third party parser utilities. */ #include "http_parser.h" /*-----------------------------------------------------------*/ /** * @brief The maximum number of retries for connecting to server. */ #define CONNECTION_RETRY_MAX_ATTEMPTS ( 5U ) /** * @brief The maximum back-off delay (in milliseconds) for retrying connection to server. */ #define CONNECTION_RETRY_MAX_BACKOFF_DELAY_MS ( 5000U ) /** * @brief The base back-off delay (in milliseconds) to use for connection retry attempts. */ #define CONNECTION_RETRY_BACKOFF_BASE_MS ( 500U ) /*-----------------------------------------------------------*/ /* Each compilation unit must define the NetworkContext struct. * Because these utilities are shared by both plaintext and TLS demos, * we must define pParams as void *. */ struct NetworkContext { void * pParams; }; /*-----------------------------------------------------------*/ /** * @brief The random number generator to use for exponential backoff with * jitter retry logic. * * @return The generated random number. */ static uint32_t generateRandomNumber(); /*-----------------------------------------------------------*/ static uint32_t generateRandomNumber() { return sys_rand32_get(); } /*-----------------------------------------------------------*/ int32_t connectToServerWithBackoffRetries( TransportConnect_t connectFunction, NetworkContext_t * pNetworkContext ) { int32_t returnStatus = EXIT_FAILURE; /* Status returned by the retry utilities. */ BackoffAlgorithmStatus_t backoffAlgStatus = BackoffAlgorithmSuccess; /* Struct containing the next backoff time. */ BackoffAlgorithmContext_t reconnectParams; uint16_t nextRetryBackOff = 0U; assert( connectFunction != NULL ); /* Initialize reconnect attempts and interval */ BackoffAlgorithm_InitializeParams( &reconnectParams, CONNECTION_RETRY_BACKOFF_BASE_MS, CONNECTION_RETRY_MAX_BACKOFF_DELAY_MS, CONNECTION_RETRY_MAX_ATTEMPTS ); /* Attempt to connect to HTTP server. If connection fails, retry after * a timeout. Timeout value will exponentially increase until maximum * attempts are reached. */ do { returnStatus = connectFunction( pNetworkContext ); if( returnStatus != EXIT_SUCCESS ) { /* Generate a random number and get back-off value (in milliseconds) for the next connection retry. */ backoffAlgStatus = BackoffAlgorithm_GetNextBackoff( &reconnectParams, generateRandomNumber(), &nextRetryBackOff ); if( backoffAlgStatus == BackoffAlgorithmSuccess ) { LogWarn( ( "Connection to the HTTP server failed. Retrying " "connection after %hu ms backoff.", ( unsigned short ) nextRetryBackOff ) ); Clock_SleepMs( nextRetryBackOff ); } else { LogError( ( "Connection to the HTTP server failed, all attempts exhausted." ) ); } } } while( ( returnStatus == EXIT_FAILURE ) && ( backoffAlgStatus == BackoffAlgorithmSuccess ) ); if( returnStatus == EXIT_FAILURE ) { LogError( ( "Connection to the server failed, all attempts exhausted." ) ); } return returnStatus; } /*-----------------------------------------------------------*/ HTTPStatus_t getUrlPath( const char * pUrl, size_t urlLen, const char ** pPath, size_t * pPathLen ) { /* http-parser status. Initialized to 1 to signify failure. */ int parserStatus = 1; struct http_parser_url urlParser; HTTPStatus_t httpStatus = HTTPSuccess; /* Sets all members in urlParser to 0. */ http_parser_url_init( &urlParser ); if( ( pUrl == NULL ) || ( pPath == NULL ) || ( pPathLen == NULL ) ) { LogError( ( "NULL parameter passed to getUrlPath()." ) ); httpStatus = HTTPInvalidParameter; } if( httpStatus == HTTPSuccess ) { parserStatus = http_parser_parse_url( pUrl, urlLen, 0, &urlParser ); if( parserStatus != 0 ) { LogError( ( "Error parsing the input URL %.*s. Error code: %d.", ( int32_t ) urlLen, pUrl, parserStatus ) ); httpStatus = HTTPParserInternalError; } } if( httpStatus == HTTPSuccess ) { *pPathLen = ( size_t ) ( urlParser.field_data[ UF_PATH ].len ); if( *pPathLen == 0 ) { httpStatus = HTTPNoResponse; *pPath = NULL; } else { *pPath = &pUrl[ urlParser.field_data[ UF_PATH ].off ]; } } if( httpStatus != HTTPSuccess ) { LogError( ( "Error parsing the path from URL %s. Error code: %d", pUrl, httpStatus ) ); } return httpStatus; } /*-----------------------------------------------------------*/ HTTPStatus_t getUrlAddress( const char * pUrl, size_t urlLen, const char ** pAddress, size_t * pAddressLen ) { /* http-parser status. Initialized to 1 to signify failure. */ int parserStatus = 1; struct http_parser_url urlParser; HTTPStatus_t httpStatus = HTTPSuccess; /* Sets all members in urlParser to 0. */ http_parser_url_init( &urlParser ); if( ( pUrl == NULL ) || ( pAddress == NULL ) || ( pAddressLen == NULL ) ) { LogError( ( "NULL parameter passed to getUrlAddress()." ) ); httpStatus = HTTPInvalidParameter; } if( httpStatus == HTTPSuccess ) { parserStatus = http_parser_parse_url( pUrl, urlLen, 0, &urlParser ); if( parserStatus != 0 ) { LogError( ( "Error parsing the input URL %.*s. Error code: %d.", ( int32_t ) urlLen, pUrl, parserStatus ) ); httpStatus = HTTPParserInternalError; } } if( httpStatus == HTTPSuccess ) { *pAddressLen = ( size_t ) ( urlParser.field_data[ UF_HOST ].len ); if( *pAddressLen == 0 ) { httpStatus = HTTPNoResponse; *pAddress = NULL; } else { *pAddress = &pUrl[ urlParser.field_data[ UF_HOST ].off ]; } } if( httpStatus != HTTPSuccess ) { LogError( ( "Error parsing the address from URL %s. Error code %d", pUrl, httpStatus ) ); } return httpStatus; }