Add the Labs projects provided in the V10.2.1_191129 zip file.

This commit is contained in:
Richard Barry 2019-12-02 23:39:25 +00:00
parent 46e5937529
commit e5708b38e9
801 changed files with 356576 additions and 0 deletions

View file

@ -0,0 +1,547 @@
/*
* FreeRTOS Kernel V10.2.0
* Copyright (C) 2019 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.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/**
* @file atomic.h
* @brief FreeRTOS atomic operation support.
*
* Two implementations of atomic are given in this header file:
* 1. Disabling interrupt globally.
* 2. ISA native atomic support.
* The former is available to all ports (compiler-architecture combination),
* while the latter is only available to ports compiling with GCC (version at
* least 4.7.0), which also have ISA atomic support.
*
* User can select which implementation to use by:
* setting/clearing configUSE_ATOMIC_INSTRUCTION in FreeRTOSConfig.h.
* Define AND set configUSE_ATOMIC_INSTRUCTION to 1 for ISA native atomic support.
* Undefine OR clear configUSE_ATOMIC_INSTRUCTION for disabling global interrupt
* implementation.
*
* @see GCC Built-in Functions for Memory Model Aware Atomic Operations
* https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
*/
#ifndef ATOMIC_H
#define ATOMIC_H
#ifndef INC_FREERTOS_H
#error "include FreeRTOS.h must appear in source files before include atomic.h"
#endif
/* Standard includes. */
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
/* Needed for __atomic_compare_exchange() weak=false. */
#include <stdbool.h>
/* This branch is for GCC compiler and GCC compiler only. */
#ifndef portFORCE_INLINE
#define portFORCE_INLINE inline __attribute__((always_inline))
#endif
#else
/* Port specific definitions -- entering/exiting critical section.
* Refer template -- ./lib/FreeRTOS/portable/Compiler/Arch/portmacro.h
*
* Every call to ATOMIC_EXIT_CRITICAL() must be closely paired with
* ATOMIC_ENTER_CRITICAL().
*/
#if defined( portSET_INTERRUPT_MASK_FROM_ISR )
/* Nested interrupt scheme is supported in this port. */
#define ATOMIC_ENTER_CRITICAL() \
UBaseType_t uxCriticalSectionType = portSET_INTERRUPT_MASK_FROM_ISR()
#define ATOMIC_EXIT_CRITICAL() \
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxCriticalSectionType )
#else
/* Nested interrupt scheme is NOT supported in this port. */
#define ATOMIC_ENTER_CRITICAL() portENTER_CRITICAL()
#define ATOMIC_EXIT_CRITICAL() portEXIT_CRITICAL()
#endif /* portSET_INTERRUPT_MASK_FROM_ISR() */
/* Port specific definition -- "always inline".
* Inline is compiler specific, and may not always get inlined depending on your optimization level.
* Also, inline is considered as performance optimization for atomic.
* Thus, if portFORCE_INLINE is not provided by portmacro.h, instead of resulting error,
* simply define it.
*/
#ifndef portFORCE_INLINE
#define portFORCE_INLINE
#endif
#endif /* configUSE_GCC_BUILTIN_ATOMICS */
#define ATOMIC_COMPARE_AND_SWAP_SUCCESS 0x1U /**< Compare and swap succeeded, swapped. */
#define ATOMIC_COMPARE_AND_SWAP_FAILURE 0x0U /**< Compare and swap failed, did not swap. */
/*----------------------------- Swap && CAS ------------------------------*/
/**
* Atomic compare-and-swap
*
* @brief Performs an atomic compare-and-swap operation on the specified values.
*
* @param[in, out] pDestination Pointer to memory location from where value is
* to be loaded and checked.
* @param[in] ulExchange If condition meets, write this value to memory.
* @param[in] ulComparand Swap condition.
*
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
*
* @note This function only swaps *pDestination with ulExchange, if previous
* *pDestination value equals ulComparand.
*/
static portFORCE_INLINE uint32_t Atomic_CompareAndSwap_u32(
uint32_t volatile * pDestination,
uint32_t ulExchange,
uint32_t ulComparand )
{
uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
if ( __atomic_compare_exchange( pDestination,
&ulComparand,
&ulExchange,
false,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST ) )
{
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
#else
ATOMIC_ENTER_CRITICAL();
if ( *pDestination == ulComparand )
{
*pDestination = ulExchange;
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
ATOMIC_EXIT_CRITICAL();
#endif
return ulReturnValue;
}
/**
* Atomic swap (pointers)
*
* @brief Atomically sets the address pointed to by *ppDestination to the value
* of *pExchange.
*
* @param[in, out] ppDestination Pointer to memory location from where a pointer
* value is to be loaded and written back to.
* @param[in] pExchange Pointer value to be written to *ppDestination.
*
* @return The initial value of *ppDestination.
*/
static portFORCE_INLINE void * Atomic_SwapPointers_p32(
void * volatile * ppDestination,
void * pExchange )
{
void * pReturnValue;
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
__atomic_exchange( ppDestination, &pExchange, &pReturnValue, __ATOMIC_SEQ_CST );
#else
ATOMIC_ENTER_CRITICAL();
pReturnValue = *ppDestination;
*ppDestination = pExchange;
ATOMIC_EXIT_CRITICAL();
#endif
return pReturnValue;
}
/**
* Atomic compare-and-swap (pointers)
*
* @brief Performs an atomic compare-and-swap operation on the specified pointer
* values.
*
* @param[in, out] ppDestination Pointer to memory location from where a pointer
* value is to be loaded and checked.
* @param[in] pExchange If condition meets, write this value to memory.
* @param[in] pComparand Swap condition.
*
* @return Unsigned integer of value 1 or 0. 1 for swapped, 0 for not swapped.
*
* @note This function only swaps *ppDestination with pExchange, if previous
* *ppDestination value equals pComparand.
*/
static portFORCE_INLINE uint32_t Atomic_CompareAndSwapPointers_p32(
void * volatile * ppDestination,
void * pExchange, void * pComparand )
{
uint32_t ulReturnValue = ATOMIC_COMPARE_AND_SWAP_FAILURE;
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
if ( __atomic_compare_exchange( ppDestination,
&pComparand,
&pExchange,
false,
__ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST ) )
{
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
#else
ATOMIC_ENTER_CRITICAL();
if ( *ppDestination == pComparand )
{
*ppDestination = pExchange;
ulReturnValue = ATOMIC_COMPARE_AND_SWAP_SUCCESS;
}
ATOMIC_EXIT_CRITICAL();
#endif
return ulReturnValue;
}
/*----------------------------- Arithmetic ------------------------------*/
/**
* Atomic add
*
* @brief Atomically adds count to the value of the specified pointer points to.
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
* @param[in] ulCount Value to be added to *pAddend.
*
* @return previous *pAddend value.
*/
static portFORCE_INLINE uint32_t Atomic_Add_u32(
uint32_t volatile * pAddend,
uint32_t ulCount )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_add(pAddend, ulCount, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend += ulCount;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic subtract
*
* @brief Atomically subtracts count from the value of the specified pointer
* pointers to.
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
* @param[in] ulCount Value to be subtract from *pAddend.
*
* @return previous *pAddend value.
*/
static portFORCE_INLINE uint32_t Atomic_Subtract_u32(
uint32_t volatile * pAddend,
uint32_t ulCount )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_sub(pAddend, ulCount, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend -= ulCount;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic increment
*
* @brief Atomically increments the value of the specified pointer points to.
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
*
* @return *pAddend value before increment.
*/
static portFORCE_INLINE uint32_t Atomic_Increment_u32( uint32_t volatile * pAddend )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_add(pAddend, 1, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend += 1;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic decrement
*
* @brief Atomically decrements the value of the specified pointer points to
*
* @param[in,out] pAddend Pointer to memory location from where value is to be
* loaded and written back to.
*
* @return *pAddend value before decrement.
*/
static portFORCE_INLINE uint32_t Atomic_Decrement_u32( uint32_t volatile * pAddend )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_sub(pAddend, 1, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pAddend;
*pAddend -= 1;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/*----------------------------- Bitwise Logical ------------------------------*/
/**
* Atomic OR
*
* @brief Performs an atomic OR operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be ORed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_OR_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_or(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination |= ulValue;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic AND
*
* @brief Performs an atomic AND operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be ANDed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_AND_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_and(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination &= ulValue;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic NAND
*
* @brief Performs an atomic NAND operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be NANDed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_NAND_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_nand(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination = ~(ulCurrent & ulValue);
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
/**
* Atomic XOR
*
* @brief Performs an atomic XOR operation on the specified values.
*
* @param [in, out] pDestination Pointer to memory location from where value is
* to be loaded and written back to.
* @param [in] ulValue Value to be XORed with *pDestination.
*
* @return The original value of *pDestination.
*/
static portFORCE_INLINE uint32_t Atomic_XOR_u32(
uint32_t volatile * pDestination,
uint32_t ulValue )
{
#if defined ( configUSE_GCC_BUILTIN_ATOMICS ) && ( configUSE_GCC_BUILTIN_ATOMICS == 1 )
return __atomic_fetch_xor(pDestination, ulValue, __ATOMIC_SEQ_CST);
#else
uint32_t ulCurrent;
ATOMIC_ENTER_CRITICAL();
ulCurrent = *pDestination;
*pDestination ^= ulValue;
ATOMIC_EXIT_CRITICAL();
return ulCurrent;
#endif
}
#ifdef __cplusplus
}
#endif
#endif /* ATOMIC_H */

View file

@ -0,0 +1,39 @@
/*
* Amazon FreeRTOS Common V1.0.0
* Copyright (C) 2018 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.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_atomic.h
* @brief Chooses the appropriate atomic operations header.
*
* On FreeRTOS, this file chooses the atomic header provided with the FreeRTOS
* kernel.
*/
#ifndef IOT_ATOMIC_H_
#define IOT_ATOMIC_H_
#include "atomic.h"
#endif /* ifndef IOT_ATOMIC_H_ */

View file

@ -0,0 +1,114 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2019 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.
*/
/**
* @file iot_error.h
* @brief Provides macros for error checking and function cleanup.
*
* The macros in this file are generic. They may be customized by each library
* by setting the library prefix.
*/
#ifndef IOT_ERROR_H_
#define IOT_ERROR_H_
/* The config header is always included first. */
#include "iot_config.h"
/**
* @brief Declare the status variable and an initial value.
*
* This macro should be at the beginning of any functions that use cleanup sections.
*
* @param[in] statusType The type of the status variable for this function.
* @param[in] initialValue The initial value to assign to the status variable.
*/
#define IOT_FUNCTION_ENTRY( statusType, initialValue ) statusType status = initialValue
/**
* @brief Declares the label that begins a cleanup section.
*
* This macro should be placed at the end of a function and followed by
* #IOT_FUNCTION_CLEANUP_END.
*/
#define IOT_FUNCTION_CLEANUP_BEGIN() iotCleanup:
/**
* @brief Declares the end of a cleanup section.
*
* This macro should be placed at the end of a function and preceded by
* #IOT_FUNCTION_CLEANUP_BEGIN.
*/
#define IOT_FUNCTION_CLEANUP_END() return status
/**
* @brief Declares an empty cleanup section.
*
* This macro should be placed at the end of a function to exit on error if no
* cleanup is required.
*/
#define IOT_FUNCTION_EXIT_NO_CLEANUP() IOT_FUNCTION_CLEANUP_BEGIN(); IOT_FUNCTION_CLEANUP_END()
/**
* @brief Jump to the cleanup section.
*/
#define IOT_GOTO_CLEANUP() goto iotCleanup
/**
* @brief Assign a value to the status variable and jump to the cleanup section.
*
* @param[in] statusValue The value to assign to the status variable.
*/
#define IOT_SET_AND_GOTO_CLEANUP( statusValue ) { status = ( statusValue ); IOT_GOTO_CLEANUP(); }
/**
* @brief Jump to the cleanup section if a condition is `false`.
*
* This macro may be used in place of `assert` to exit a function is a condition
* is `false`.
*
* @param[in] condition The condition to check.
*/
#define IOT_GOTO_CLEANUP_IF_FALSE( condition ) { if( ( condition ) == false ) { IOT_GOTO_CLEANUP(); } }
/**
* @brief Assign a value to the status variable and jump to the cleanup section
* if a condition is `false`.
*
* @param[in] statusValue The value to assign to the status variable.
* @param[in] condition The condition to check.
*/
#define IOT_SET_AND_GOTO_CLEANUP_IF_FALSE( statusValue, condition ) \
if( ( condition ) == false ) \
IOT_SET_AND_GOTO_CLEANUP( statusValue )
/**
* @brief Check a condition; if `false`, assign the "Bad parameter" status value
* and jump to the cleanup section.
*
* @param[in] libraryPrefix The library prefix of the status variable.
* @param[in] condition The condition to check.
*/
#define IOT_VALIDATE_PARAMETER( libraryPrefix, condition ) \
IOT_SET_AND_GOTO_CLEANUP_IF_FALSE( libraryPrefix ## _BAD_PARAMETER, condition )
#endif /* ifndef IOT_ERROR_H_ */

View file

@ -0,0 +1,64 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2019 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.
*/
/**
* @file iot_init.h
* @brief Provides function signatures for common initialization and cleanup of
* this SDK.
*/
#ifndef IOT_INIT_H_
#define IOT_INIT_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
/**
* @brief One-time initialization function for this SDK.
*
* This function initializes common libraries, such as static memory and task
* pool. <b>It must be called once (and only once) before calling any other
* function in this SDK.</b> Calling this function more than once without first
* calling `IotSdk_Cleanup` may result in a crash.
*
* @return `true` if initialization succeeded; `false` otherwise. Logs may be
* printed in case of failure.
*
* @warning No thread-safety guarantees are provided for this function.
*/
bool IotSdk_Init( void );
/**
* @brief One-time deinitialization function for all common libraries.
*
* This function frees resources taken in `IotSdk_Init`. No other function
* in this SDK may be called after calling this function unless `IotSdk_Init`
* is called again.
*
* @warning No thread-safety guarantees are provided for this function.
*/
void IotSdk_Cleanup( void );
#endif /* IOT_INIT_H_ */

View file

@ -0,0 +1,956 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2019 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.
*/
/**
* @file iot_linear_containers.h
* @brief Declares and implements doubly-linked lists and queues.
*/
#ifndef IOT_LINEAR_CONTAINERS_H_
#define IOT_LINEAR_CONTAINERS_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* @defgroup linear_containers_datatypes_listqueue List and queue
* @brief Structures that represent a list or queue.
*/
/**
* @ingroup linear_containers_datatypes_listqueue
* @brief Link member placed in structs of a list or queue.
*
* All elements in a list or queue must contain one of these members. The macro
* #IotLink_Container can be used to calculate the starting address of the
* link's container.
*/
typedef struct IotLink
{
struct IotLink * pPrevious; /**< @brief Pointer to the previous element. */
struct IotLink * pNext; /**< @brief Pointer to the next element. */
} IotLink_t;
/**
* @ingroup linear_containers_datatypes_listqueue
* @brief Represents a doubly-linked list.
*/
typedef IotLink_t IotListDouble_t;
/**
* @ingroup linear_containers_datatypes_listqueue
* @brief Represents a queue.
*/
typedef IotLink_t IotDeQueue_t;
/**
* @constantspage{linear_containers,linear containers library}
*
* @section linear_containers_constants_initializers Linear Containers Initializers
* @brief Provides default values for initializing the linear containers data types.
*
* @snippet this define_linear_containers_initializers
*
* All user-facing data types of the linear containers library should be initialized
* using one of the following.
*
* @warning Failure to initialize a linear containers data type with the appropriate
* initializer may result in a runtime error!
* @note The initializers may change at any time in future versions, but their
* names will remain the same.
*/
/* @[define_linear_containers_initializers] */
#define IOT_LINK_INITIALIZER { 0 } /**< @brief Initializer for an #IotLink_t. */
#define IOT_LIST_DOUBLE_INITIALIZER IOT_LINK_INITIALIZER /**< @brief Initializer for an #IotListDouble_t. */
#define IOT_DEQUEUE_INITIALIZER IOT_LINK_INITIALIZER /**< @brief Initializer for an #IotDeQueue_t. */
/* @[define_linear_containers_initializers] */
/**
* @def IotContainers_Assert( expression )
* @brief Assertion macro for the linear containers library.
*
* Set @ref IOT_CONTAINERS_ENABLE_ASSERTS to `1` to enable assertions in the linear
* containers library.
*
* @param[in] expression Expression to be evaluated.
*/
#if IOT_CONTAINERS_ENABLE_ASSERTS == 1
#ifndef IotContainers_Assert
#ifdef Iot_DefaultAssert
#define IotContainers_Assert( expression ) Iot_DefaultAssert( expression )
#else
#error "Asserts are enabled for containers, but IotContainers_Assert is not defined"
#endif
#endif
#else /* if IOT_CONTAINERS_ENABLE_ASSERTS == 1 */
#define IotContainers_Assert( expression )
#endif /* if IOT_CONTAINERS_ENABLE_ASSERTS == 1 */
/**
* @brief Calculates the starting address of a containing struct.
*
* @param[in] type Type of the containing struct.
* @param[in] pLink Pointer to a link member.
* @param[in] linkName Name of the #IotLink_t in the containing struct.
*/
#define IotLink_Container( type, pLink, linkName ) \
( ( type * ) ( void * ) ( ( ( uint8_t * ) ( pLink ) ) - offsetof( type, linkName ) ) )
/**
* @brief Iterates through all elements of a linear container.
*
* Container elements must not be freed or removed while iterating.
*
* @param[in] pStart The first element to iterate from.
* @param[out] pLink Pointer to a container element.
*/
#define IotContainers_ForEach( pStart, pLink ) \
for( ( pLink ) = ( pStart )->pNext; \
( pLink ) != ( pStart ); \
( pLink ) = ( pLink )->pNext )
/**
* @functionspage{linear_containers,linear containers library}
* - @functionname{linear_containers_function_link_islinked}
* - @functionname{linear_containers_function_list_double_create}
* - @functionname{linear_containers_function_list_double_count}
* - @functionname{linear_containers_function_list_double_isempty}
* - @functionname{linear_containers_function_list_double_peekhead}
* - @functionname{linear_containers_function_list_double_peektail}
* - @functionname{linear_containers_function_list_double_inserthead}
* - @functionname{linear_containers_function_list_double_inserttail}
* - @functionname{linear_containers_function_list_double_insertbefore}
* - @functionname{linear_containers_function_list_double_insertafter}
* - @functionname{linear_containers_function_list_double_insertsorted}
* - @functionname{linear_containers_function_list_double_remove}
* - @functionname{linear_containers_function_list_double_removehead}
* - @functionname{linear_containers_function_list_double_removetail}
* - @functionname{linear_containers_function_list_double_removeall}
* - @functionname{linear_containers_function_list_double_findfirstmatch}
* - @functionname{linear_containers_function_list_double_removefirstmatch}
* - @functionname{linear_containers_function_list_double_removeallmatches}
* - @functionname{linear_containers_function_queue_create}
* - @functionname{linear_containers_function_queue_count}
* - @functionname{linear_containers_function_queue_isempty}
* - @functionname{linear_containers_function_queue_peekhead}
* - @functionname{linear_containers_function_queue_peektail}
* - @functionname{linear_containers_function_queue_enqueuehead}
* - @functionname{linear_containers_function_queue_dequeuehead}
* - @functionname{linear_containers_function_queue_enqueuetail}
* - @functionname{linear_containers_function_queue_dequeuetail}
* - @functionname{linear_containers_function_queue_remove}
* - @functionname{linear_containers_function_queue_removeall}
* - @functionname{linear_containers_function_queue_removeallmatches}
*/
/**
* @functionpage{IotLink_IsLinked,linear_containers,link_islinked}
* @functionpage{IotListDouble_Create,linear_containers,list_double_create}
* @functionpage{IotListDouble_Count,linear_containers,list_double_count}
* @functionpage{IotListDouble_IsEmpty,linear_containers,list_double_isempty}
* @functionpage{IotListDouble_PeekHead,linear_containers,list_double_peekhead}
* @functionpage{IotListDouble_PeekTail,linear_containers,list_double_peektail}
* @functionpage{IotListDouble_InsertHead,linear_containers,list_double_inserthead}
* @functionpage{IotListDouble_InsertTail,linear_containers,list_double_inserttail}
* @functionpage{IotListDouble_InsertBefore,linear_containers,list_double_insertbefore}
* @functionpage{IotListDouble_InsertAfter,linear_containers,list_double_insertafter}
* @functionpage{IotListDouble_InsertSorted,linear_containers,list_double_insertsorted}
* @functionpage{IotListDouble_Remove,linear_containers,list_double_remove}
* @functionpage{IotListDouble_RemoveHead,linear_containers,list_double_removehead}
* @functionpage{IotListDouble_RemoveTail,linear_containers,list_double_removetail}
* @functionpage{IotListDouble_RemoveAll,linear_containers,list_double_removeall}
* @functionpage{IotListDouble_FindFirstMatch,linear_containers,list_double_findfirstmatch}
* @functionpage{IotListDouble_RemoveFirstMatch,linear_containers,list_double_removefirstmatch}
* @functionpage{IotListDouble_RemoveAllMatches,linear_containers,list_double_removeallmatches}
* @functionpage{IotDeQueue_Create,linear_containers,queue_create}
* @functionpage{IotDeQueue_Count,linear_containers,queue_count}
* @functionpage{IotDeQueue_IsEmpty,linear_containers,queue_isempty}
* @functionpage{IotDeQueue_PeekHead,linear_containers,queue_peekhead}
* @functionpage{IotDeQueue_PeekTail,linear_containers,queue_peektail}
* @functionpage{IotDeQueue_EnqueueHead,linear_containers,queue_enqueuehead}
* @functionpage{IotDeQueue_DequeueHead,linear_containers,queue_dequeuehead}
* @functionpage{IotDeQueue_EnqueueTail,linear_containers,queue_enqueuetail}
* @functionpage{IotDeQueue_DequeueTail,linear_containers,queue_dequeuetail}
* @functionpage{IotDeQueue_Remove,linear_containers,queue_remove}
* @functionpage{IotDeQueue_RemoveAll,linear_containers,queue_removeall}
* @functionpage{IotDeQueue_RemoveAllMatches,linear_containers,queue_removeallmatches}
*/
/**
* @brief Check if an #IotLink_t is linked in a list or queue.
*
* @param[in] pLink The link to check.
*
* @return `true` if `pCurrent` is linked in a list or queue; `false` otherwise.
*/
/* @[declare_linear_containers_link_islinked] */
static inline bool IotLink_IsLinked( const IotLink_t * const pLink )
/* @[declare_linear_containers_link_islinked] */
{
bool isLinked = false;
if( pLink != NULL )
{
isLinked = ( pLink->pNext != NULL ) && ( pLink->pPrevious != NULL );
}
return isLinked;
}
/**
* @brief Create a new doubly-linked list.
*
* This function initializes a new doubly-linked list. It must be called on an
* uninitialized #IotListDouble_t before calling any other doubly-linked list
* function. This function must not be called on an already-initialized
* #IotListDouble_t.
*
* This function will not fail. The function @ref linear_containers_function_list_double_removeall
* may be called to destroy a list.
*
* @param[in] pList Pointer to the memory that will hold the new doubly-linked list.
*/
/* @[declare_linear_containers_list_double_create] */
static inline void IotListDouble_Create( IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_create] */
{
/* This function must not be called with a NULL parameter. */
IotContainers_Assert( pList != NULL );
/* An empty list is a link pointing to itself. */
pList->pPrevious = pList;
pList->pNext = pList;
}
/**
* @brief Return the number of elements contained in an #IotListDouble_t.
*
* @param[in] pList The doubly-linked list with the elements to count.
*
* @return The number of elements in the doubly-linked list.
*/
/* @[declare_linear_containers_list_double_count] */
static inline size_t IotListDouble_Count( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_count] */
{
size_t count = 0;
if( pList != NULL )
{
/* Get the list head. */
const IotLink_t * pCurrent = pList->pNext;
/* Iterate through the list to count the elements. */
while( pCurrent != pList )
{
count++;
pCurrent = pCurrent->pNext;
}
}
return count;
}
/**
* @brief Check if a doubly-linked list is empty.
*
* @param[in] pList The doubly-linked list to check.
*
* @return `true` if the list is empty; `false` otherwise.
*/
/* @[declare_linear_containers_list_double_isempty] */
static inline bool IotListDouble_IsEmpty( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_isempty] */
{
/* An empty list is NULL link, or a link pointing to itself. */
return( ( pList == NULL ) || ( pList->pNext == pList ) );
}
/**
* @brief Return an #IotLink_t representing the first element in a doubly-linked list
* without removing it.
*
* @param[in] pList The list to peek.
*
* @return Pointer to an #IotLink_t representing the element at the head of the
* list; `NULL` if the list is empty. The macro #IotLink_Container may be used to
* determine the address of the link's container.
*/
/* @[declare_linear_containers_list_double_peekhead] */
static inline IotLink_t * IotListDouble_PeekHead( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_peekhead] */
{
IotLink_t * pHead = NULL;
if( pList != NULL )
{
if( IotListDouble_IsEmpty( pList ) == false )
{
pHead = pList->pNext;
}
}
return pHead;
}
/**
* @brief Return an #IotLink_t representing the last element in a doubly-linked
* list without removing it.
*
* @param[in] pList The list to peek.
*
* @return Pointer to an #IotLink_t representing the element at the tail of the
* list; `NULL` if the list is empty. The macro #IotLink_Container may be used to
* determine the address of the link's container.
*/
/* @[declare_linear_containers_list_double_peektail] */
static inline IotLink_t * IotListDouble_PeekTail( const IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_peektail] */
{
IotLink_t * pTail = NULL;
if( pList != NULL )
{
if( IotListDouble_IsEmpty( pList ) == false )
{
pTail = pList->pPrevious;
}
}
return pTail;
}
/**
* @brief Insert an element at the head of a doubly-linked list.
*
* @param[in] pList The doubly-linked list that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_inserthead] */
static inline void IotListDouble_InsertHead( IotListDouble_t * const pList,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_inserthead] */
{
/* This function must not be called with NULL parameters. */
IotContainers_Assert( pList != NULL );
IotContainers_Assert( pLink != NULL );
/* Save current list head. */
IotLink_t * pHead = pList->pNext;
/* Place new element before list head. */
pLink->pNext = pHead;
pLink->pPrevious = pList;
/* Assign new list head. */
pHead->pPrevious = pLink;
pList->pNext = pLink;
}
/**
* @brief Insert an element at the tail of a doubly-linked list.
*
* @param[in] pList The double-linked list that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_inserttail] */
static inline void IotListDouble_InsertTail( IotListDouble_t * const pList,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_inserttail] */
{
/* This function must not be called with NULL parameters. */
IotContainers_Assert( pList != NULL );
IotContainers_Assert( pLink != NULL );
/* Save current list tail. */
IotLink_t * pTail = pList->pPrevious;
pLink->pNext = pList;
pLink->pPrevious = pTail;
pList->pPrevious = pLink;
pTail->pNext = pLink;
}
/**
* @brief Insert an element before another element in a doubly-linked list.
*
* @param[in] pElement The new element will be placed before this element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_insertbefore] */
static inline void IotListDouble_InsertBefore( IotLink_t * const pElement,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_insertbefore] */
{
IotListDouble_InsertTail( pElement, pLink );
}
/**
* @brief Insert an element after another element in a doubly-linked list.
*
* @param[in] pElement The new element will be placed after this element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_list_double_insertafter] */
static inline void IotListDouble_InsertAfter( IotLink_t * const pElement,
IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_insertafter] */
{
IotListDouble_InsertHead( pElement, pLink );
}
/**
* @brief Insert an element in a sorted doubly-linked list.
*
* Places an element into a list by sorting it into order. The function
* `compare` is used to determine where to place the new element.
*
* @param[in] pList The list that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
* @param[in] compare Determines the order of the list. Returns a negative
* value if its first argument is less than its second argument; returns
* zero if its first argument is equal to its second argument; returns a
* positive value if its first argument is greater than its second argument.
* The parameters to this function are #IotLink_t, so the macro #IotLink_Container
* may be used to determine the address of the link's container.
*/
/* @[declare_linear_containers_list_double_insertsorted] */
static inline void IotListDouble_InsertSorted( IotListDouble_t * const pList,
IotLink_t * const pLink,
int32_t ( *compare )( const IotLink_t * const, const IotLink_t * const ) )
/* @[declare_linear_containers_list_double_insertsorted] */
{
/* This function must not be called with NULL parameters. */
IotContainers_Assert( pList != NULL );
IotContainers_Assert( pLink != NULL );
IotContainers_Assert( compare != NULL );
/* Insert at head for empty list. */
if( IotListDouble_IsEmpty( pList ) == true )
{
IotListDouble_InsertHead( pList, pLink );
}
else
{
bool inserted = false;
IotLink_t * pCurrent = pList->pNext;
/* Iterate through the list to find the correct position. */
while( pCurrent != pList )
{
/* Comparing for '<' preserves the order of insertion. */
if( compare( pLink, pCurrent ) < 0 )
{
IotListDouble_InsertBefore( pCurrent, pLink );
inserted = true;
break;
}
pCurrent = pCurrent->pNext;
}
/* New element is greater than all elements in list. Insert at tail. */
if( inserted == false )
{
IotListDouble_InsertTail( pList, pLink );
}
}
}
/**
* @brief Remove a single element from a doubly-linked list.
*
* @param[in] pLink The element to remove.
*/
/* @[declare_linear_containers_list_double_remove] */
static inline void IotListDouble_Remove( IotLink_t * const pLink )
/* @[declare_linear_containers_list_double_remove] */
{
/* This function must not be called with a NULL parameter. */
IotContainers_Assert( pLink != NULL );
/* This function must be called on a linked element. */
IotContainers_Assert( IotLink_IsLinked( pLink ) == true );
pLink->pPrevious->pNext = pLink->pNext;
pLink->pNext->pPrevious = pLink->pPrevious;
pLink->pPrevious = NULL;
pLink->pNext = NULL;
}
/**
* @brief Remove the element at the head of a doubly-linked list.
*
* @param[in] pList The doubly-linked list that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed list head; `NULL`
* if the list is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_list_double_removehead] */
static inline IotLink_t * IotListDouble_RemoveHead( IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_removehead] */
{
IotLink_t * pHead = NULL;
if( IotListDouble_IsEmpty( pList ) == false )
{
pHead = pList->pNext;
IotListDouble_Remove( pHead );
}
return pHead;
}
/**
* @brief Remove the element at the tail of a doubly-linked list.
*
* @param[in] pList The doubly-linked list that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed list tail; `NULL`
* if the list is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_list_double_removetail] */
static inline IotLink_t * IotListDouble_RemoveTail( IotListDouble_t * const pList )
/* @[declare_linear_containers_list_double_removetail] */
{
IotLink_t * pTail = NULL;
if( IotListDouble_IsEmpty( pList ) == false )
{
pTail = pList->pPrevious;
IotListDouble_Remove( pTail );
}
return pTail;
}
/**
* @brief Remove all elements in a doubly-linked list.
*
* @param[in] pList The list to empty.
* @param[in] freeElement A function to free memory used by each removed list
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_list_double_removeall] */
static inline void IotListDouble_RemoveAll( IotListDouble_t * const pList,
void ( *freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_list_double_removeall] */
{
/* This function must not be called with a NULL pList parameter. */
IotContainers_Assert( pList != NULL );
/* Get the list head. */
IotLink_t * pCurrent = pList->pNext;
/* Iterate through the list and remove all elements. */
while( pCurrent != pList )
{
/* Save a pointer to the next list element. */
IotLink_t * pNext = pCurrent->pNext;
/* Remove and free the current list element. */
IotListDouble_Remove( pCurrent );
if( freeElement != NULL )
{
freeElement( ( ( uint8_t * ) pCurrent ) - linkOffset );
}
/* Move the iterating pointer to the next list element. */
pCurrent = pNext;
}
}
/**
* @brief Search a doubly-linked list for the first matching element.
*
* If a match is found, the matching element is <b>not</b> removed from the list.
* See @ref linear_containers_function_list_double_removefirstmatch for the function
* that searches and removes.
*
* @param[in] pList The doubly-linked list to search.
* @param[in] pStartPoint An element in `pList`. Only elements between this one and
* the list tail are checked. Pass `NULL` to search from the beginning of the list.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the list is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
*
* @return Pointer to an #IotLink_t representing the first matched element; `NULL`
* if no match is found. The macro #IotLink_Container may be used to determine the
* address of the link's container.
*/
/* @[declare_linear_containers_list_double_findfirstmatch] */
static inline IotLink_t * IotListDouble_FindFirstMatch( const IotListDouble_t * const pList,
const IotLink_t * const pStartPoint,
bool ( *isMatch )( const IotLink_t * const, void * ),
void * pMatch )
/* @[declare_linear_containers_list_double_findfirstmatch] */
{
/* The const must be cast away to match this function's return value. Nevertheless,
* this function will respect the const-ness of pStartPoint. */
IotLink_t * pCurrent = ( IotLink_t * ) pStartPoint;
/* This function must not be called with a NULL pList parameter. */
IotContainers_Assert( pList != NULL );
/* Search starting from list head if no start point is given. */
if( pStartPoint == NULL )
{
pCurrent = pList->pNext;
}
/* Iterate through the list to search for matches. */
while( pCurrent != pList )
{
/* Call isMatch if provided. Otherwise, compare pointers. */
if( isMatch != NULL )
{
if( isMatch( pCurrent, pMatch ) == true )
{
return pCurrent;
}
}
else
{
if( pCurrent == pMatch )
{
return pCurrent;
}
}
pCurrent = pCurrent->pNext;
}
/* No match found, return NULL. */
return NULL;
}
/**
* @brief Search a doubly-linked list for the first matching element and remove
* it.
*
* An #IotLink_t may be passed as `pList` to start searching after the head of a
* doubly-linked list.
*
* @param[in] pList The doubly-linked list to search.
* @param[in] pStartPoint An element in `pList`. Only elements between this one and
* the list tail are checked. Pass `NULL` to search from the beginning of the list.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the list is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
*
* @return Pointer to an #IotLink_t representing the matched and removed element;
* `NULL` if no match is found. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_list_double_removefirstmatch] */
static inline IotLink_t * IotListDouble_RemoveFirstMatch( IotListDouble_t * const pList,
const IotLink_t * const pStartPoint,
bool ( *isMatch )( const IotLink_t *, void * ),
void * pMatch )
/* @[declare_linear_containers_list_double_removefirstmatch] */
{
IotLink_t * pMatchedElement = IotListDouble_FindFirstMatch( pList,
pStartPoint,
isMatch,
pMatch );
if( pMatchedElement != NULL )
{
IotListDouble_Remove( pMatchedElement );
}
return pMatchedElement;
}
/**
* @brief Remove all matching elements from a doubly-linked list.
*
* @param[in] pList The doubly-linked list to search.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the list is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
* @param[in] freeElement A function to free memory used by each removed list
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_list_double_removeallmatches] */
static inline void IotListDouble_RemoveAllMatches( IotListDouble_t * const pList,
bool ( *isMatch )( const IotLink_t *, void * ),
void * pMatch,
void ( *freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_list_double_removeallmatches] */
{
IotLink_t * pMatchedElement = NULL, * pNextElement = NULL;
/* Search the list for all matching elements. */
do
{
pMatchedElement = IotListDouble_FindFirstMatch( pList,
pMatchedElement,
isMatch,
pMatch );
if( pMatchedElement != NULL )
{
/* Save pointer to next element. */
pNextElement = pMatchedElement->pNext;
/* Match found; remove and free. */
IotListDouble_Remove( pMatchedElement );
if( freeElement != NULL )
{
freeElement( ( ( uint8_t * ) pMatchedElement ) - linkOffset );
}
/* Continue search from next element. */
pMatchedElement = pNextElement;
}
} while( pMatchedElement != NULL );
}
/**
* @brief Create a new queue.
*
* This function initializes a new double-ended queue. It must be called on an uninitialized
* #IotDeQueue_t before calling any other queue function. This function must not be
* called on an already-initialized #IotDeQueue_t.
*
* This function will not fail.
*
* @param[in] pQueue Pointer to the memory that will hold the new queue.
*/
/* @[declare_linear_containers_queue_create] */
static inline void IotDeQueue_Create( IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_create] */
{
IotListDouble_Create( pQueue );
}
/**
* @brief Return the number of elements contained in an #IotDeQueue_t.
*
* @param[in] pQueue The queue with the elements to count.
*
* @return The number of items elements in the queue.
*/
/* @[declare_linear_containers_queue_count] */
static inline size_t IotDeQueue_Count( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_count] */
{
return IotListDouble_Count( pQueue );
}
/**
* @brief Check if a queue is empty.
*
* @param[in] pQueue The queue to check.
*
* @return `true` if the queue is empty; `false` otherwise.
*
*/
/* @[declare_linear_containers_queue_isempty] */
static inline bool IotDeQueue_IsEmpty( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_isempty] */
{
return IotListDouble_IsEmpty( pQueue );
}
/**
* @brief Return an #IotLink_t representing the element at the front of the queue
* without removing it.
*
* @param[in] pQueue The queue to peek.
*
* @return Pointer to an #IotLink_t representing the element at the head of the
* queue; `NULL` if the queue is empty. The macro #IotLink_Container may be used
* to determine the address of the link's container.
*/
/* @[declare_linear_containers_queue_peekhead] */
static inline IotLink_t * IotDeQueue_PeekHead( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_peekhead] */
{
return IotListDouble_PeekHead( pQueue );
}
/**
* @brief Return an #IotLink_t representing the element at the back of the queue
* without removing it.
*
* @param[in] pQueue The queue to peek.
*
* @return Pointer to an #IotLink_t representing the element at the head of the
* queue; `NULL` if the queue is empty. The macro #IotLink_Container may be used
* to determine the address of the link's container.
*/
/* @[declare_linear_containers_queue_peektail] */
static inline IotLink_t * IotDeQueue_PeekTail( const IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_peektail] */
{
return IotListDouble_PeekTail( pQueue );
}
/**
* @brief Add an element at the head of the queue.
*
* @param[in] pQueue The queue that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_queue_enqueuehead] */
static inline void IotDeQueue_EnqueueHead( IotDeQueue_t * const pQueue,
IotLink_t * const pLink )
/* @[declare_linear_containers_queue_enqueuehead] */
{
IotListDouble_InsertHead( pQueue, pLink );
}
/**
* @brief Remove an element at the head of the queue.
*
* @param[in] pQueue The queue that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed queue element; `NULL`
* if the queue is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_queue_dequeuehead] */
static inline IotLink_t * IotDeQueue_DequeueHead( IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_dequeuehead] */
{
return IotListDouble_RemoveHead( pQueue );
}
/**
* @brief Add an element at the tail of the queue.
*
* @param[in] pQueue The queue that will hold the new element.
* @param[in] pLink Pointer to the new element's link member.
*/
/* @[declare_linear_containers_queue_enqueuetail] */
static inline void IotDeQueue_EnqueueTail( IotDeQueue_t * const pQueue,
IotLink_t * const pLink )
/* @[declare_linear_containers_queue_enqueuetail] */
{
IotListDouble_InsertTail( pQueue, pLink );
}
/**
* @brief Remove an element at the tail of the queue.
*
* @param[in] pQueue The queue that holds the element to remove.
*
* @return Pointer to an #IotLink_t representing the removed queue element; `NULL`
* if the queue is empty. The macro #IotLink_Container may be used to determine
* the address of the link's container.
*/
/* @[declare_linear_containers_queue_dequeuetail] */
static inline IotLink_t * IotDeQueue_DequeueTail( IotDeQueue_t * const pQueue )
/* @[declare_linear_containers_queue_dequeuetail] */
{
return IotListDouble_RemoveTail( pQueue );
}
/**
* @brief Remove a single element from a queue.
*
* @param[in] pLink The element to remove.
*/
/* @[declare_linear_containers_queue_remove] */
static inline void IotDeQueue_Remove( IotLink_t * const pLink )
/* @[declare_linear_containers_queue_remove] */
{
IotListDouble_Remove( pLink );
}
/**
* @brief Remove all elements in a queue.
*
* @param[in] pQueue The queue to empty.
* @param[in] freeElement A function to free memory used by each removed queue
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_queue_removeall] */
static inline void IotDeQueue_RemoveAll( IotDeQueue_t * const pQueue,
void ( * freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_queue_removeall] */
{
IotListDouble_RemoveAll( pQueue, freeElement, linkOffset );
}
/**
* @brief Remove all matching elements from a queue.
*
* @param[in] pQueue The queue to search.
* @param[in] isMatch Function to determine if an element matches. Pass `NULL` to
* search using the address `pMatch`, i.e. `element == pMatch`.
* @param[in] pMatch If `isMatch` is `NULL`, each element in the queue is compared
* to this address to find a match. Otherwise, it is passed as the second argument
* to `isMatch`.
* @param[in] freeElement A function to free memory used by each removed queue
* element. Optional; pass `NULL` to ignore.
* @param[in] linkOffset Offset in bytes of a link member in its container, used
* to calculate the pointer to pass to `freeElement`. This value should be calculated
* with the C `offsetof` macro. This parameter is ignored if `freeElement` is `NULL`
* or its value is `0`.
*/
/* @[declare_linear_containers_queue_removeallmatches] */
static inline void IotDeQueue_RemoveAllMatches( IotDeQueue_t * const pQueue,
bool ( * isMatch )( const IotLink_t *, void * ),
void * pMatch,
void ( * freeElement )( void * ),
size_t linkOffset )
/* @[declare_linear_containers_queue_removeallmatches] */
{
IotListDouble_RemoveAllMatches( pQueue, isMatch, pMatch, freeElement, linkOffset );
}
#endif /* IOT_LINEAR_CONTAINERS_H_ */

View file

@ -0,0 +1,226 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_logging.h
* @brief Generic logging function header file.
*
* Declares the generic logging function and the log levels. This file never
* needs to be included in source code. The header iot_logging_setup.h should
* be included instead.
*
* @see iot_logging_setup.h
*/
#ifndef IOT_LOGGING_H_
#define IOT_LOGGING_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* @constantspage{logging,logging library}
*
* @section logging_constants_levels Log levels
* @brief Log levels for the libraries in this SDK.
*
* Each library should specify a log level by setting @ref LIBRARY_LOG_LEVEL.
* All log messages with a level at or below the specified level will be printed
* for that library.
*
* Currently, there are 4 log levels. In the order of lowest to highest, they are:
* - #IOT_LOG_NONE <br>
* @copybrief IOT_LOG_NONE
* - #IOT_LOG_ERROR <br>
* @copybrief IOT_LOG_ERROR
* - #IOT_LOG_WARN <br>
* @copybrief IOT_LOG_WARN
* - #IOT_LOG_INFO <br>
* @copybrief IOT_LOG_INFO
* - #IOT_LOG_DEBUG <br>
* @copybrief IOT_LOG_DEBUG
*/
/**
* @brief No log messages.
*
* Log messages with this level will be silently discarded. When @ref
* LIBRARY_LOG_LEVEL is #IOT_LOG_NONE, logging is disabled and no [logging functions]
* (@ref logging_functions) can be called.
*/
#define IOT_LOG_NONE 0
/**
* @brief Only critical, unrecoverable errors.
*
* Log messages with this level will be printed when a library encounters an
* error from which it cannot easily recover.
*/
#define IOT_LOG_ERROR 1
/**
* @brief Message about an abnormal but recoverable event.
*
* Log messages with this level will be printed when a library encounters an
* abnormal event that may be indicative of an error. Libraries should continue
* execution after logging a warning.
*/
#define IOT_LOG_WARN 2
/**
* @brief A helpful, informational message.
*
* Log messages with this level may indicate the normal status of a library
* function. They should be used to track how far a program has executed.
*/
#define IOT_LOG_INFO 3
/**
* @brief Detailed and excessive debug information.
*
* Log messages with this level are intended for developers. They may contain
* excessive information such as internal variables, buffers, or other specific
* information.
*/
#define IOT_LOG_DEBUG 4
/**
* @paramstructs{logging,logging}
*/
/**
* @ingroup logging_datatypes_paramstructs
* @brief Log message configuration struct.
*
* @paramfor @ref logging_function_log, @ref logging_function_generic
*
* By default, log messages print the library name, log level, and a timestring.
* This struct can be passed to @ref logging_function_generic to disable one of
* the above components in the log message.
*
* <b>Example:</b>
*
* @code{c}
* IotLog_Generic( IOT_LOG_DEBUG, "SAMPLE", IOT_LOG_DEBUG, NULL, "Hello world!" );
* @endcode
* The code above prints the following message:
* @code
* [DEBUG][SAMPLE][2018-01-01 12:00:00] Hello world!
* @endcode
*
* The timestring can be disabled as follows:
* @code
* IotLogConfig_t logConfig = { .hideLogLevel = false, .hideLibraryName = false, .hideTimestring = true};
* IotLog_Generic( IOT_LOG_DEBUG, "SAMPLE", IOT_LOG_DEBUG, &logConfig, "Hello world!" );
* @endcode
* The resulting log message will be:
* @code
* [DEBUG][SAMPLE] Hello world!
* @endcode
*/
typedef struct IotLogConfig
{
bool hideLogLevel; /**< @brief Don't print the log level string for this message. */
bool hideLibraryName; /**< @brief Don't print the library name for this message. */
bool hideTimestring; /**< @brief Don't print the timestring for this message. */
} IotLogConfig_t;
/**
* @functionspage{logging,logging library}
*
* - @functionname{logging_function_log}
* - @functionname{logging_function_printbuffer}
* - @functionname{logging_function_generic}
* - @functionname{logging_function_genericprintbuffer}
*/
/**
* @functionpage{IotLog_Generic,logging,generic}
* @functionpage{IotLog_PrintBuffer,logging,genericprintbuffer}
*/
/**
* @brief Generic logging function that prints a single message.
*
* This function is the generic logging function shared across all libraries.
* The library-specific logging function @ref logging_function_log is implemented
* using this function. Like @ref logging_function_log, this function is only
* available when @ref LIBRARY_LOG_LEVEL is #IOT_LOG_NONE.
*
* In most cases, the library-specific logging function @ref logging_function_log
* should be called instead of this function.
*
* @param[in] libraryLogSetting The log level setting of the library, used to
* determine if the log message should be printed. Must be one of the @ref
* logging_constants_levels.
* @param[in] pLibraryName The library name to print. See @ref LIBRARY_LOG_NAME.
* @param[in] messageLevel The log level of the this message. See @ref LIBRARY_LOG_LEVEL.
* @param[in] pLogConfig Pointer to a #IotLogConfig_t. Optional; pass `NULL` to ignore.
* @param[in] pFormat Format string for the log message.
* @param[in] ... Arguments for format specification.
*
* @return No return value. On errors, it prints nothing.
*/
/* @[declare_logging_generic] */
void IotLog_Generic( int libraryLogSetting,
const char * const pLibraryName,
int messageLevel,
const IotLogConfig_t * const pLogConfig,
const char * const pFormat,
... );
/* @[declare_logging_generic] */
/**
* @brief Generic function to log the contents of a buffer as bytes.
*
* This function is the generic buffer logging function shared across all libraries.
* The library-specific buffer logging function @ref logging_function_printbuffer is
* implemented using this function. Like @ref logging_function_printbuffer, this
* function is only available when @ref LIBRARY_LOG_LEVEL is #IOT_LOG_DEBUG.
*
* In most cases, the library-specific buffer logging function @ref
* logging_function_printbuffer should be called instead of this function.
*
* @param[in] pLibraryName The library name to print with the log. See @ref LIBRARY_LOG_NAME.
* @param[in] pHeader A message to print before printing the buffer.
* @param[in] pBuffer The buffer to print.
* @param[in] bufferSize The number of bytes in `pBuffer` to print.
*
* @return No return value. On errors, it prints nothing.
*
* @note To conserve memory, this function only allocates enough memory for a
* single line of output. Therefore, in multithreaded systems, its output may
* appear "fragmented" if other threads are logging simultaneously.
*/
/* @[declare_logging_genericprintbuffer] */
void IotLog_GenericPrintBuffer( const char * const pLibraryName,
const char * const pHeader,
const uint8_t * const pBuffer,
size_t bufferSize );
/* @[declare_logging_genericprintbuffer] */
#endif /* ifndef IOT_LOGGING_H_ */

View file

@ -0,0 +1,220 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_logging_setup.h
* @brief Defines the logging macro #IotLog.
*/
#ifndef IOT_LOGGING_SETUP_H_
#define IOT_LOGGING_SETUP_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Logging include. Because it's included here, iot_logging.h never needs
* to be included in source. */
#include "iot_logging.h"
/**
* @functionpage{IotLog,logging,log}
* @functionpage{IotLog_PrintBuffer,logging,printbuffer}
*/
/**
* @def IotLog( messageLevel, pLogConfig, ... )
* @brief Logging function for a specific library. In most cases, this is the
* logging function to call.
*
* This function prints a single log message. It is available when @ref
* LIBRARY_LOG_LEVEL is not #IOT_LOG_NONE. Log messages automatically
* include the [log level](@ref logging_constants_levels), [library name]
* (@ref LIBRARY_LOG_NAME), and time. An optional @ref IotLogConfig_t may
* be passed to this function to hide information for a single log message.
*
* The logging library must be set up before this function may be called. See
* @ref logging_setup_use for more information.
*
* This logging function also has the following abbreviated forms that can be used
* when an #IotLogConfig_t isn't needed.
*
* Name | Equivalent to
* ---- | -------------
* #IotLogError | @code{c} IotLog( IOT_LOG_ERROR, NULL, ... ) @endcode
* #IotLogWarn | @code{c} IotLog( IOT_LOG_WARN, NULL, ... ) @endcode
* #IotLogInfo | @code{c} IotLog( IOT_LOG_INFO, NULL, ... ) @endcode
* #IotLogDebug | @code{c} IotLog( IOT_LOG_DEBUG, NULL, ... ) @endcode
*
* @param[in] messageLevel Log level of this message. Must be one of the
* @ref logging_constants_levels.
* @param[in] pLogConfig Pointer to an #IotLogConfig_t. Optional; pass `NULL`
* to ignore.
* @param[in] ... Message and format specification.
*
* @return No return value. On errors, it prints nothing.
*
* @note This function may be implemented as a macro.
* @see @ref logging_function_generic for the generic (not library-specific)
* logging function.
*/
/**
* @def IotLog_PrintBuffer( pHeader, pBuffer, bufferSize )
* @brief Log the contents of buffer as bytes. Only available when @ref
* LIBRARY_LOG_LEVEL is #IOT_LOG_DEBUG.
*
* This function prints the bytes located at a given memory address. It is
* intended for debugging only, and is therefore only available when @ref
* LIBRARY_LOG_LEVEL is #IOT_LOG_DEBUG.
*
* Log messages printed by this function <b>always</b> include the [log level]
* (@ref logging_constants_levels), [library name](@ref LIBRARY_LOG_NAME),
* and time. In addition, this function may print an optional header `pHeader`
* before it prints the contents of the buffer. This function does not have an
* #IotLogConfig_t parameter.
*
* The logging library must be set up before this function may be called. See
* @ref logging_setup_use for more information.
*
* @param[in] pHeader A message to log before the buffer. Optional; pass `NULL`
* to ignore.
* @param[in] pBuffer Pointer to start of buffer.
* @param[in] bufferSize Size of `pBuffer`.
*
* @return No return value. On errors, it prints nothing.
*
* @note This function may be implemented as a macro.
* @note To conserve memory, @ref logging_function_genericprintbuffer (the underlying
* implementation) only allocates enough memory for a single line of output. Therefore,
* in multithreaded systems, its output may appear "fragmented" if other threads are
* logging simultaneously.
* @see @ref logging_function_genericprintbuffer for the generic (not library-specific)
* buffer logging function.
*
* <b>Example</b>
* @code{c}
* const uint8_t pBuffer[] = { 0x00, 0x01, 0x02, 0x03 };
*
* IotLog_PrintBuffer( "This buffer contains:",
* pBuffer,
* 4 );
* @endcode
* The code above prints something like the following:
* @code{c}
* [DEBUG][LIB_NAME][2018-01-01 12:00:00] This buffer contains:
* 00 01 02 03
* @endcode
*/
/**
* @def IotLogError( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_ERROR.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_ERROR, NULL, ... )
* @endcode
*/
/**
* @def IotLogWarn( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_WARN.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_WARN, NULL, ... )
* @endcode
*/
/**
* @def IotLogInfo( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_INFO.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_INFO, NULL, ... )
* @endcode
*/
/**
* @def IotLogDebug( ... )
* @brief Abbreviated logging macro for level #IOT_LOG_DEBUG.
*
* Equivalent to:
* @code{c}
* IotLog( IOT_LOG_DEBUG, NULL, ... )
* @endcode
*/
/* Check that LIBRARY_LOG_LEVEL is defined and has a valid value. */
#if !defined( LIBRARY_LOG_LEVEL ) || \
( LIBRARY_LOG_LEVEL != IOT_LOG_NONE && \
LIBRARY_LOG_LEVEL != IOT_LOG_ERROR && \
LIBRARY_LOG_LEVEL != IOT_LOG_WARN && \
LIBRARY_LOG_LEVEL != IOT_LOG_INFO && \
LIBRARY_LOG_LEVEL != IOT_LOG_DEBUG )
#error "Please define LIBRARY_LOG_LEVEL as either IOT_LOG_NONE, IOT_LOG_ERROR, IOT_LOG_WARN, IOT_LOG_INFO, or IOT_LOG_DEBUG."
/* Check that LIBRARY_LOG_NAME is defined and has a valid value. */
#elif !defined( LIBRARY_LOG_NAME )
#error "Please define LIBRARY_LOG_NAME."
#else
/* Define IotLog if the log level is greater than "none". */
#if LIBRARY_LOG_LEVEL > IOT_LOG_NONE
#define IotLog( messageLevel, pLogConfig, ... ) \
IotLog_Generic( LIBRARY_LOG_LEVEL, \
LIBRARY_LOG_NAME, \
messageLevel, \
pLogConfig, \
__VA_ARGS__ )
/* Define the abbreviated logging macros. */
#define IotLogError( ... ) IotLog( IOT_LOG_ERROR, NULL, __VA_ARGS__ )
#define IotLogWarn( ... ) IotLog( IOT_LOG_WARN, NULL, __VA_ARGS__ )
#define IotLogInfo( ... ) IotLog( IOT_LOG_INFO, NULL, __VA_ARGS__ )
#define IotLogDebug( ... ) IotLog( IOT_LOG_DEBUG, NULL, __VA_ARGS__ )
/* If log level is DEBUG, enable the function to print buffers. */
#if LIBRARY_LOG_LEVEL >= IOT_LOG_DEBUG
#define IotLog_PrintBuffer( pHeader, pBuffer, bufferSize ) \
IotLog_GenericPrintBuffer( LIBRARY_LOG_NAME, \
pHeader, \
pBuffer, \
bufferSize )
#else
#define IotLog_PrintBuffer( pHeader, pBuffer, bufferSize )
#endif
/* Remove references to IotLog from the source code if logging is disabled. */
#else
/* @[declare_logging_log] */
#define IotLog( messageLevel, pLogConfig, ... )
/* @[declare_logging_log] */
/* @[declare_logging_printbuffer] */
#define IotLog_PrintBuffer( pHeader, pBuffer, bufferSize )
/* @[declare_logging_printbuffer] */
#define IotLogError( ... )
#define IotLogWarn( ... )
#define IotLogInfo( ... )
#define IotLogDebug( ... )
#endif
#endif
#endif /* ifndef IOT_LOGGING_SETUP_H_ */

View file

@ -0,0 +1,197 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_static_memory.h
* @brief Common functions for managing static buffers. Only used when
* @ref IOT_STATIC_MEMORY_ONLY is `1`.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* The functions in this file should only exist in static memory only mode, hence
* the check for IOT_STATIC_MEMORY_ONLY in the double inclusion guard. */
#if !defined( IOT_STATIC_MEMORY_H_ ) && ( IOT_STATIC_MEMORY_ONLY == 1 )
#define IOT_STATIC_MEMORY_H_
/* Standard includes. */
#include <stddef.h>
#include <stdint.h>
/**
* @functionspage{static_memory,static memory component}
* - @functionname{static_memory_function_findfree}
* - @functionname{static_memory_function_returninuse}
* - @functionname{static_memory_function_messagebuffersize}
* - @functionname{static_memory_function_mallocmessagebuffer}
* - @functionname{static_memory_function_freemessagebuffer}
*/
/*------------------------- Buffer allocation and free ----------------------*/
/**
* @functionpage{IotStaticMemory_FindFree,static_memory,findfree}
* @functionpage{IotStaticMemory_ReturnInUse,static_memory,returninuse}
*/
/**
* @brief Find a free buffer using the "in-use" flags.
*
* If a free buffer is found, this function marks the buffer in-use. This function
* is common to the static memory implementation.
*
* @param[in] pInUse The "in-use" flags to search.
* @param[in] limit How many flags to check, i.e. the size of `pInUse`.
*
* @return The index of a free buffer; `-1` if no free buffers are available.
*
* <b>Example</b>:
* @code{c}
* // To use this function, first declare two arrays. One provides the statically-allocated
* // objects, the other provides flags to determine which objects are in-use.
* #define NUMBER_OF_OBJECTS ...
* #define OBJECT_SIZE ...
* static uint32_t _pInUseObjects[ NUMBER_OF_OBJECTS ] = { 0 };
* static uint8_t _pObjects[ NUMBER_OF_OBJECTS ][ OBJECT_SIZE ] = { { 0 } }; // Placeholder for objects.
*
* // The function to statically allocate objects. Must have the same signature
* // as malloc().
* void * Iot_MallocObject( size_t size )
* {
* int32_t freeIndex = -1;
* void * pNewObject = NULL;
*
* // Check that sizes match.
* if( size != OBJECT_SIZE )
* {
* // Get the index of a free object.
* freeIndex = IotStaticMemory_FindFree( _pInUseMessageBuffers,
* IOT_MESSAGE_BUFFERS );
*
* if( freeIndex != -1 )
* {
* pNewBuffer = &( _pMessageBuffers[ freeIndex ][ 0 ] );
* }
* }
*
* return pNewBuffer;
* }
* @endcode
*/
/* @[declare_static_memory_findfree] */
int32_t IotStaticMemory_FindFree( uint32_t * pInUse,
size_t limit );
/* @[declare_static_memory_findfree] */
/**
* @brief Return an "in-use" buffer.
*
* This function is common to the static memory implementation.
*
* @param[in] ptr Pointer to the buffer to return.
* @param[in] pPool The pool of buffers that the in-use buffer was allocated from.
* @param[in] pInUse The "in-use" flags for pPool.
* @param[in] limit How many buffers (and flags) to check while searching for ptr.
* @param[in] elementSize The size of a single element in pPool.
*
* <b>Example</b>:
* @code{c}
* // To use this function, first declare two arrays. One provides the statically-allocated
* // objects, the other provides flags to determine which objects are in-use.
* #define NUMBER_OF_OBJECTS ...
* #define OBJECT_SIZE ...
* static uint32_t _pInUseObjects[ NUMBER_OF_OBJECTS ] = { 0 };
* static uint8_t _pObjects[ NUMBER_OF_OBJECTS ][ OBJECT_SIZE ] = { { 0 } }; // Placeholder for objects.
*
* // The function to free statically-allocated objects. Must have the same signature
* // as free().
* void Iot_FreeObject( void * ptr )
* {
* IotStaticMemory_ReturnInUse( ptr,
* _pObjects,
* _pInUseObjects,
* NUMBER_OF_OBJECTS,
* OBJECT_SIZE );
* }
* @endcode
*/
/* @[declare_static_memory_returninuse] */
void IotStaticMemory_ReturnInUse( void * ptr,
void * pPool,
uint32_t * pInUse,
size_t limit,
size_t elementSize );
/* @[declare_static_memory_returninuse] */
/*------------------------ Message buffer management ------------------------*/
/**
* @functionpage{Iot_MessageBufferSize,static_memory,messagebuffersize}
* @functionpage{Iot_MallocMessageBuffer,static_memory,mallocmessagebuffer}
* @functionpage{Iot_FreeMessageBuffer,static_memory,freemessagebuffer}
*/
/**
* @brief Get the fixed size of a message buffer.
*
* The size of the message buffers are known at compile time, but it is a [constant]
* (@ref IOT_MESSAGE_BUFFER_SIZE) that may not be visible to all source files.
* This function allows other source files to know the size of a message buffer.
*
* @return The size, in bytes, of a single message buffer.
*/
/* @[declare_static_memory_messagebuffersize] */
size_t Iot_MessageBufferSize( void );
/* @[declare_static_memory_messagebuffersize] */
/**
* @brief Get an empty message buffer.
*
* This function is the analog of [malloc]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html)
* for message buffers.
*
* @param[in] size Requested size for a message buffer.
*
* @return Pointer to the start of a message buffer. If the `size` argument is larger
* than the [fixed size of a message buffer](@ref IOT_MESSAGE_BUFFER_SIZE)
* or no message buffers are available, `NULL` is returned.
*/
/* @[declare_static_memory_mallocmessagebuffer] */
void * Iot_MallocMessageBuffer( size_t size );
/* @[declare_static_memory_mallocmessagebuffer] */
/**
* @brief Free an in-use message buffer.
*
* This function is the analog of [free]
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html)
* for message buffers.
*
* @param[in] ptr Pointer to the message buffer to free.
*/
/* @[declare_static_memory_freemessagebuffer] */
void Iot_FreeMessageBuffer( void * ptr );
/* @[declare_static_memory_freemessagebuffer] */
#endif /* if !defined( IOT_STATIC_MEMORY_H_ ) && ( IOT_STATIC_MEMORY_ONLY == 1 ) */

View file

@ -0,0 +1,451 @@
/*
* IoT Common V1.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_logging.c
* @brief Implementation of logging functions from iot_logging.h
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
/* Platform clock include. */
#include "platform/iot_clock.h"
/* Logging includes. */
#include "iot_logging.h"
/*-----------------------------------------------------------*/
/* This implementation assumes the following values for the log level constants.
* Ensure that the values have not been modified. */
#if IOT_LOG_NONE != 0
#error "IOT_LOG_NONE must be 0."
#endif
#if IOT_LOG_ERROR != 1
#error "IOT_LOG_ERROR must be 1."
#endif
#if IOT_LOG_WARN != 2
#error "IOT_LOG_WARN must be 2."
#endif
#if IOT_LOG_INFO != 3
#error "IOT_LOG_INFO must be 3."
#endif
#if IOT_LOG_DEBUG != 4
#error "IOT_LOG_DEBUG must be 4."
#endif
/**
* @def IotLogging_Puts( message )
* @brief Function the logging library uses to print a line.
*
* This function can be set by using a define. By default, the standard library
* [puts](http://pubs.opengroup.org/onlinepubs/9699919799/functions/puts.html)
* function is used.
*/
#ifndef IotLogging_Puts
#define IotLogging_Puts puts
#endif
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#if IOT_STATIC_MEMORY_ONLY == 1
/* Static memory allocation header. */
#include "iot_static_memory.h"
/**
* @brief Allocate a new logging buffer. This function must have the same
* signature as [malloc](http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html).
*/
#ifndef IotLogging_Malloc
#define IotLogging_Malloc Iot_MallocMessageBuffer
#endif
/**
* @brief Free a logging buffer. This function must have the same signature
* as [free](http://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html).
*/
#ifndef IotLogging_Free
#define IotLogging_Free Iot_FreeMessageBuffer
#endif
/**
* @brief Get the size of a logging buffer. Statically-allocated buffers
* should all have the same size.
*/
#ifndef IotLogging_StaticBufferSize
#define IotLogging_StaticBufferSize Iot_MessageBufferSize
#endif
#else /* if IOT_STATIC_MEMORY_ONLY == 1 */
#ifndef IotLogging_Malloc
#include <stdlib.h>
#define IotLogging_Malloc malloc
#endif
#ifndef IotLogging_Free
#include <stdlib.h>
#define IotLogging_Free free
#endif
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
/**
* @brief A guess of the maximum length of a timestring.
*
* There's no way for this logging library to know the length of a timestring
* before it's generated. Therefore, the logging library will assume a maximum
* length of any timestring it may get. This value should be generous enough
* to accommodate the vast majority of timestrings.
*
* @see @ref platform_clock_function_gettimestring
*/
#define MAX_TIMESTRING_LENGTH ( 64 )
/**
* @brief The longest string in #_pLogLevelStrings (below), plus 3 to accommodate
* `[]` and a null-terminator.
*/
#define MAX_LOG_LEVEL_LENGTH ( 8 )
/**
* @brief How many bytes @ref logging_function_genericprintbuffer should output on
* each line.
*/
#define BYTES_PER_LINE ( 16 )
/*-----------------------------------------------------------*/
/**
* @brief Lookup table for log levels.
*
* Converts one of the @ref logging_constants_levels to a string.
*/
static const char * const _pLogLevelStrings[ 5 ] =
{
"", /* IOT_LOG_NONE */
"ERROR", /* IOT_LOG_ERROR */
"WARN ", /* IOT_LOG_WARN */
"INFO ", /* IOT_LOG_INFO */
"DEBUG" /* IOT_LOG_DEBUG */
};
/*-----------------------------------------------------------*/
#if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 )
static bool _reallocLoggingBuffer( void ** pOldBuffer,
size_t newSize,
size_t oldSize )
{
bool status = false;
/* Allocate a new, larger buffer. */
void * pNewBuffer = IotLogging_Malloc( newSize );
/* Ensure that memory allocation succeeded. */
if( pNewBuffer != NULL )
{
/* Copy the data from the old buffer to the new buffer. */
( void ) memcpy( pNewBuffer, *pOldBuffer, oldSize );
/* Free the old buffer and update the pointer. */
IotLogging_Free( *pOldBuffer );
*pOldBuffer = pNewBuffer;
status = true;
}
return status;
}
#endif /* if !defined( IOT_STATIC_MEMORY_ONLY ) || ( IOT_STATIC_MEMORY_ONLY == 0 ) */
/*-----------------------------------------------------------*/
void IotLog_Generic( int libraryLogSetting,
const char * const pLibraryName,
int messageLevel,
const IotLogConfig_t * const pLogConfig,
const char * const pFormat,
... )
{
int requiredMessageSize = 0;
size_t bufferSize = 0,
bufferPosition = 0, timestringLength = 0;
char * pLoggingBuffer = NULL;
va_list args;
/* If the library's log level setting is lower than the message level,
* return without doing anything. */
if( ( messageLevel == 0 ) || ( messageLevel > libraryLogSetting ) )
{
return;
}
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )
{
/* Add length of log level if requested. */
bufferSize += MAX_LOG_LEVEL_LENGTH;
}
/* Estimate the amount of buffer needed for this log message. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )
{
/* Add size of library name if requested. Add 2 to accommodate "[]". */
bufferSize += strlen( pLibraryName ) + 2;
}
if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )
{
/* Add length of timestring if requested. */
bufferSize += MAX_TIMESTRING_LENGTH;
}
/* Add 64 as an initial (arbitrary) guess for the length of the message. */
bufferSize += 64;
/* In static memory mode, check that the log message will fit in the a
* static buffer. */
#if IOT_STATIC_MEMORY_ONLY == 1
if( bufferSize >= IotLogging_StaticBufferSize() )
{
/* If the static buffers are likely too small to fit the log message,
* return. */
return;
}
/* Otherwise, update the buffer size to the size of a static buffer. */
bufferSize = IotLogging_StaticBufferSize();
#endif
/* Allocate memory for the logging buffer. */
pLoggingBuffer = ( char * ) IotLogging_Malloc( bufferSize );
if( pLoggingBuffer == NULL )
{
return;
}
/* Print the message log level if requested. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLogLevel == false ) )
{
/* Ensure that message level is valid. */
if( ( messageLevel >= IOT_LOG_NONE ) && ( messageLevel <= IOT_LOG_DEBUG ) )
{
/* Add the log level string to the logging buffer. */
requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
"[%s]",
_pLogLevelStrings[ messageLevel ] );
/* Check for encoding errors. */
if( requiredMessageSize <= 0 )
{
IotLogging_Free( pLoggingBuffer );
return;
}
/* Update the buffer position. */
bufferPosition += ( size_t ) requiredMessageSize;
}
}
/* Print the library name if requested. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideLibraryName == false ) )
{
/* Add the library name to the logging buffer. */
requiredMessageSize = snprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
"[%s]",
pLibraryName );
/* Check for encoding errors. */
if( requiredMessageSize <= 0 )
{
IotLogging_Free( pLoggingBuffer );
return;
}
/* Update the buffer position. */
bufferPosition += ( size_t ) requiredMessageSize;
}
/* Print the timestring if requested. */
if( ( pLogConfig == NULL ) || ( pLogConfig->hideTimestring == false ) )
{
/* Add the opening '[' enclosing the timestring. */
pLoggingBuffer[ bufferPosition ] = '[';
bufferPosition++;
/* Generate the timestring and add it to the buffer. */
if( IotClock_GetTimestring( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
&timestringLength ) == true )
{
/* If the timestring was successfully generated, add the closing "]". */
bufferPosition += timestringLength;
pLoggingBuffer[ bufferPosition ] = ']';
bufferPosition++;
}
else
{
/* Sufficient memory for a timestring should have been allocated. A timestring
* probably failed to generate due to a clock read error; remove the opening '['
* from the logging buffer. */
bufferPosition--;
pLoggingBuffer[ bufferPosition ] = '\0';
}
}
/* Add a padding space between the last closing ']' and the message, unless
* the logging buffer is empty. */
if( bufferPosition > 0 )
{
pLoggingBuffer[ bufferPosition ] = ' ';
bufferPosition++;
}
va_start( args, pFormat );
/* Add the log message to the logging buffer. */
requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
pFormat,
args );
va_end( args );
/* If the logging buffer was too small to fit the log message, reallocate
* a larger logging buffer. */
if( ( size_t ) requiredMessageSize >= bufferSize - bufferPosition )
{
#if IOT_STATIC_MEMORY_ONLY == 1
/* There's no point trying to allocate a larger static buffer. Return
* immediately. */
IotLogging_Free( pLoggingBuffer );
return;
#else
if( _reallocLoggingBuffer( ( void ** ) &pLoggingBuffer,
( size_t ) requiredMessageSize + bufferPosition + 1,
bufferSize ) == false )
{
/* If buffer reallocation failed, return. */
IotLogging_Free( pLoggingBuffer );
return;
}
/* Reallocation successful, update buffer size. */
bufferSize = ( size_t ) requiredMessageSize + bufferPosition + 1;
/* Add the log message to the buffer. Now that the buffer has been
* reallocated, this should succeed. */
va_start( args, pFormat );
requiredMessageSize = vsnprintf( pLoggingBuffer + bufferPosition,
bufferSize - bufferPosition,
pFormat,
args );
va_end( args );
#endif /* if IOT_STATIC_MEMORY_ONLY == 1 */
}
/* Check for encoding errors. */
if( requiredMessageSize <= 0 )
{
IotLogging_Free( pLoggingBuffer );
return;
}
/* Print the logging buffer to stdout. */
IotLogging_Puts( pLoggingBuffer );
/* Free the logging buffer. */
IotLogging_Free( pLoggingBuffer );
}
/*-----------------------------------------------------------*/
void IotLog_GenericPrintBuffer( const char * const pLibraryName,
const char * const pHeader,
const uint8_t * const pBuffer,
size_t bufferSize )
{
size_t i = 0, offset = 0;
/* Allocate memory to hold each line of the log message. Since each byte
* of pBuffer is printed in 4 characters (2 digits, a space, and a null-
* terminator), the size of each line is 4 * BYTES_PER_LINE. */
char * pMessageBuffer = IotLogging_Malloc( 4 * BYTES_PER_LINE );
/* Exit if no memory is available. */
if( pMessageBuffer == NULL )
{
return;
}
/* Print pHeader before printing pBuffer. */
if( pHeader != NULL )
{
IotLog_Generic( IOT_LOG_DEBUG,
pLibraryName,
IOT_LOG_DEBUG,
NULL,
pHeader );
}
/* Print each byte in pBuffer. */
for( i = 0; i < bufferSize; i++ )
{
/* Print a line if BYTES_PER_LINE is reached. But don't print a line
* at the beginning (when i=0). */
if( ( i % BYTES_PER_LINE == 0 ) && ( i != 0 ) )
{
IotLogging_Puts( pMessageBuffer );
/* Reset offset so that pMessageBuffer is filled from the beginning. */
offset = 0;
}
/* Print a single byte into pMessageBuffer. */
( void ) snprintf( pMessageBuffer + offset, 4, "%02x ", pBuffer[ i ] );
/* Move the offset where the next character is printed. */
offset += 3;
}
/* Print the final line of bytes. This line isn't printed by the for-loop above. */
IotLogging_Puts( pMessageBuffer );
/* Free memory used by this function. */
IotLogging_Free( pMessageBuffer );
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,10 @@
+ mqtt
Contains the implementation of the MQTT library.
+ https
Contains the implementation of the HTTPS Client library.
+ common
Contains the implementation of utility functions used by other IoT libraries.
Further libraries will be rolled out soon.

View file

@ -0,0 +1,852 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 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.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_client.h
* @brief User-facing functions of the Amazon FreeRTOS HTTPS Client library.
*/
#ifndef IOT_HTTPS_CLIENT_H_
#define IOT_HTTPS_CLIENT_H_
/* The config header is always included first. */
#include "iot_config.h"
/* HTTP types include. */
#include "types/iot_https_types.h"
/**
* @functionspage{https_client,HTTPS Client Library}
* - @functionname{https_client_function_init}
* - @functionname{https_client_function_deinit}
* - @functionname{https_client_function_disconnect}
* - @functionname{https_client_function_connect}
* - @functionname{https_client_function_initializerequest}
* - @functionname{https_client_function_addheader}
* - @functionname{https_client_function_writerequestbody}
* - @functionname{https_client_function_sendsync}
* - @functionname{https_client_function_sendasync}
* - @functionname{https_client_function_cancelrequestasync}
* - @functionname{https_client_function_cancelresponseasync}
* - @functionname{https_client_function_readresponsestatus}
* - @functionname{https_client_function_readcontentlength}
* - @functionname{https_client_function_readheader}
* - @functionname{https_client_function_readresponsebody}
*/
/**
* @functionpage{IotHttpsClient_Init,https_client,init}
* @functionpage{IotHttpsClient_Deinit,https_client,deinit}
* @functionpage{IotHttpsClient_Disconnect,https_client,disconnect}
* @functionpage{IotHttpsClient_Connect,https_client,connect}
* @functionpage{IotHttpsClient_InitializeRequest,https_client,initializerequest}
* @functionpage{IotHttpsClient_AddHeader,https_client,addheader}
* @functionpage{IotHttpsClient_WriteRequestBody,https_client,writerequestbody}
* @functionpage{IotHttpsClient_SendSync,https_client,sendsync}
* @functionpage{IotHttpsClient_SendAsync,https_client,sendasync}
* @functionpage{IotHttpsClient_CancelRequestAsync,https_client,cancelrequestasync}
* @functionpage{IotHttpsClient_CancelResponseAsync,https_client,cancelresponseasync}
* @functionpage{IotHttpsClient_ReadResponseStatus,https_client,readresponsestatus}
* @functionpage{IotHttpsClient_ReadContentLength,https_client,readcontentlength}
* @functionpage{IotHttpsClient_ReadHeader,https_client,readheader}
* @functionpage{IotHttpsClient_ReadResponseBody,https_client,readresponsebody}
*/
/**
* @brief One-time initialization of the IoT HTTPS Client library.
*
* <b>This must be called once before calling any API.</b>
*
* @warning No thread safety guarantees are provided for this function.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the HTTPS library is successfully initialized.
*
* @see @ref https_client_function_cleanup
*/
/* @[declare_https_client_init] */
IotHttpsReturnCode_t IotHttpsClient_Init( void );
/* @[declare_https_client_init] */
/**
* @brief One time clean up of the IoT HTTPS Client library.
*
* This function frees resources taken in in @ref https_client_function_init. It should be called after
* all HTTPS Connections have been close. HTTPS Connections are represented by #IotHttpsConnectionHandle_t and returned
* by @ref https_client_function_connect. After this function returns @ref https_client_function_init
* must be called again to use this library.
*
* @warning No thread safety guarantees are provided for this function.
*/
/* @[declare_https_client_cleanup] */
void IotHttpsClient_Cleanup( void );
/* @[declare_https_client_cleanup] */
/**
* @cond DOXYGEN_IGNORE
*
* Backward compatibility function for one time clean up of the IoT HTTPS Client library.
*/
#define IotHttpsClient_Deinit IotHttpsClient_Cleanup
/** @endcond */
/**
* @brief Explicitly connect to the HTTPS server given the connection configuration pConnConfig.
*
* This routine blocks until the connection is complete.
*
* This function opens a new HTTPS connection with the server specified in #IotHttpsConnectionInfo_t.pAddress. The
* connection is established by default on top of TLS over TCP. If the application wants to connect over TCP only, then
* it must add the @ref IOT_HTTPS_IS_NON_TLS_FLAG to #IotHttpsConnectionInfo_t.flags. This is done at the application's
* own risk.
*
* When the the last HTTP request sent on the connection is specified as persistent and we want to close the connection,
* @ref https_client_function_disconnect must always be called on the valid #IotHttpsConnectionHandle_t. For more
* information about persistent HTTP connections please see #IotHttpsRequestInfo_t.isNonPersistent.
*
* If the application receives a #IOT_HTTPS_NETWORK_ERROR from @ref https_client_function_sendsync or
* @ref https_client_function_sendasync, on a persistent request, then the connection will be closed. The application
* can call this this function again to reestablish the connection.
*
* If pConnHandle passed in is valid and represents a previously opened connection, this function will disconnect,
* then reconnect. Before calling this function make sure that all outstanding requests on the connection have
* completed. Outstanding requests are completed when @ref https_client_function_sendsync has returned or when
* #IotHttpsClientCallbacks_t.responseCompleteCallback has been invoked for requests scheduled with
* @ref https_client_function_sendasync.
*
* Keep in mind that many HTTP servers will close a connection, if it does not receive any requests, after a certain
* amount of time. Many web servers may close the connection after 30-60 seconds. The state of pConnHandle will still be
* in a connected state if this happens. If the server closed the connection, then the next request on the connection
* will fail to send with a network error and the connection will move to a closed state.
*
* Also keep in mind that some HTTP servers do not accept persistent requests. Some HTTP servers will ignore that the
* request contains the "Connection: keep-alive" header and close the connection immediately after sending the response.
* If this happens, then the next request on the connection will fail to send with a network error and the connection
* will close.
*
* To know if the connection was closed by the server, debug logging can be turned on to view the network error code
* received. Debug logging is configured when @ref IOT_LOG_LEVEL_HTTPS is set to @ref IOT_LOG_DEBUG in iot_config.h.
*
* #IotHttpsConnectionInfo_t.userBuffer is used to store the internal context and therefore, multiple threads
* calling this function simultaneously must ensure to use different #IotHttpsConnectionInfo_t objects.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*
* @param[out] pConnHandle - Handle returned representing the open connection. NULL if the function failed.
* @param[in] pConnInfo - Configurations for the HTTPS connection.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the connection was successful.
* - #IOT_HTTPS_CONNECTION_ERROR if the connection failed.
* - #IOT_HTTPS_INVALID_PARAMETER if NULL parameters were passed in.
* - #IOT_HTTPS_INTERNAL_ERROR if there was an error creating resources for the connection context.
*
* <b>Example</b>
* @code{c}
* // An initialized network interface.
* IotNetworkInterface_t* pNetworkInterface;
*
* // Parameters to HTTPS Client connect.
* IotHttpsConnectionInfo_t connInfo = IOT_HTTPS_CONNECTION_INFO_INITIALIZER;
* IotHttpsConnectionHandle_t connHandle = IOT_HTTPS_CONNECTION_HANDLE_INITIALIZER;
* uint8_t* pConnUserBuffer = (uint8_t*)malloc(connectionUserBufferMinimumSize);
*
* // Set the connection configuration information.
* connInfo.pAddress = "www.amazon.com";
* connInfo.addressLen = strlen("www.amazon.com");
* connInfo.port = 443;
* connInfo.flags = 0;
* connInfo.pAlpnProtocols = "alpnproto0,alpnproto1"
* connInfo.pCaCert = HTTPS_TRUSTED_ROOT_CA; // defined elsewhere
* connInfo.caCertLen = sizeof( HTTPS_TRUSTED_ROOT_CA );
* connInfo.userBuffer.pBuffer = pConnUserBuffer;
* connInfo.userBuffer.bufferLen = connectionUserBufferMinimumSize;
* connInfo.pClientCert = TLS_CLIENT_CERT;
* connInfo.clientCertLen = sizeof( TLS_CLIENT_CERT );
* connInfo.pPrivateKey = TLS_CLIENT_PRIV_KEY;
* connInfo.privateKeyLen = sizeof( TLS_CLIENT_PRIV_KEY );
* connInfo.pNetworkInterface = pNetworkInterface;
*
* IotHttpsReturnCode_t returnCode = IotHttpsClient_Connect(&connHandle, &connInfo);
* if( returnCode == IOT_HTTPS_OK )
* {
* // Do something with the HTTPS connection...
*
* // Clean up and close the HTTPS connection once it's no longer needed.
* IotHttpsClient_Disconnect(connHandle);
* }
* @endcode
*/
/* @[declare_https_client_connect] */
IotHttpsReturnCode_t IotHttpsClient_Connect( IotHttpsConnectionHandle_t * pConnHandle,
IotHttpsConnectionInfo_t * pConnInfo );
/* @[declare_https_client_connect] */
/**
* @brief Disconnect from the HTTPS server given the connection handle connHandle.
*
* This routine blocks until the disconnect is complete.
* If the connection handle is not valid, the behavior is undefined.
* If the connection handle is already disconnected then this routine will return IOT_HTTPS_OK.
*
* When the HTTP request is specified as persistent and we want to close the connection, this API must always
* be called on the valid #IotHttpsConnectionHandle_t. For more information about persistent HTTP connections please see
* #IotHttpsRequestInfo_t.isNonPersistent.
*
* When the HTTP request is specified as non-persistent, by setting #IotHttpsRequestInfo_t.isNonPersistent to true, then
* this function will be called automatically on the valid IotHttpsConnectionHandle_t after receiving the response. There
* is no need to call this function in case of a non-persistent request.
*
* This will put the internal connection state in #IotHttpsConnectionHandle_t to disconnected.
*
* If the application receives a #IOT_HTTPS_NETWORK_ERROR from @ref https_client_function_sendsync or
* @ref https_client_function_sendasync, on a persistent request, that does not always mean the connection has been
* disconnected. This function MUST be called to close the connection and clean up connection resources taken by
* #IotHttpsConnectionHandle_t.
*
* This function will cancel all pending requests on the connection. If a request currently being sent on the connection,
* then this function will disconnect the connection, but it will not free network connection resource and will return
* with #IOT_HTTPS_BUSY. The application may call this function again later to try again.
*
* Multiple threads must not call this function for the same #IotHttpsConnectionHandle_t. Multiple threads
* can call this function for different #IotHttpsConnectionHandle_t. Make sure that all request/responses
* have finished on the connection before calling this API. Outstanding requests are completed when
* @ref https_client_function_sendsync has returned or when #IotHttpsClientCallbacks_t.responseCompleteCallback
* has been invoked for requests scheduled with @ref https_client_function_sendasync.
*
* @param[in] connHandle - Valid handle representing an open connection.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the disconnect was successful
* - #IOT_HTTPS_INVALID_PARAMETER if NULL parameters were passed in.
* - #IOT_HTTPS_BUSY if the connection is in use and cannot be destroyed.
*/
/* @[declare_https_client_disconnect] */
IotHttpsReturnCode_t IotHttpsClient_Disconnect( IotHttpsConnectionHandle_t connHandle );
/* @[declare_https_client_disconnect] */
/**
* @brief Initializes the request by adding a formatted Request-Line to the start of HTTPS request header buffer.
*
* This function will initialize the HTTP request context by setting where to write the next headers to the start
* of the configured header buffer in #IotHttpsRequestInfo_t.userBuffer.
*
* The Request-Line will be added to the start of the headers space in #IotHttpsRequestInfo_t.userBuffer.
* The header space follows the request context in the user buffer. See @ref requestUserBufferMinimumSize for more
* information on sizing the #IotHttpsRequestInfo_t.userBuffer so that this function does not fail.
*
* The Request-Line generated is of the following format:
*
* @code
* method path version\r\n
* @endcode
*
* Example:
*
* @code
* GET /path/to/item.file?possible_query HTTP/1.1\r\n
* @endcode
*
* The initial required headers are also added to the #IotHttpsRequestInfo_t.userBuffer. These headers are User-Agent
* and Host. The User-Agent value is configured in iot_config.h using IOT_HTTPS_USER_AGENT. The Host value is the DNS
* resolvable server address.
*
* @param[out] pReqHandle - request handle representing the internal request context is returned. NULL if the function failed.
* @param[in] pReqInfo - HTTPS request information.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the Request-Line was successfully added to the header space in #IotHttpsRequestInfo_t.userBuffer.
* - #IOT_HTTPS_INSUFFICIENT_MEMORY if the Request-Line generated exceeds #IotHttpsUserBuffer_t.bufferLen in #IotHttpsRequestInfo_t.userBuffer.
* - #IOT_HTTPS_INVALID_PARAMETER for NULL parameters.
* - #IOT_HTTPS_INTERNAL_ERROR for library internal errors.
*
* <b>Example</b>
* @code{c}
* // An initialized network interface.
* IotNetworkInterface_t* pNetworkInterface;
*
* // Parameters to HTTPS Client request initialization.
* IotHttpsRequestInfo_t reqInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER;
* IotHttpsRequestHandle_t reqHandle = IOT_HTTPS_REQUEST_HANDLE_INITIALIZER;
* IotHttpsSyncInfo_t syncInfo = IOT_HTTPS_SYNC_INFO_INITIALIZER;
* // Leave some room for extra headers.
* uint32_t userBufferSize = requestUserBufferMinimumSize + 256;
* uint8_t* pRequestUserBuffer = (uint8_t*)malloc(userBufferSize);
*
* // Set the synchronous information.
* syncInfo.pBody = PREDEFINED_BODY_BUFFER;
* syncInfo.bodyLen = PREDEFINED_BODY_BUFFER_LEN;
*
* // Set the request configuration information.
* reqInfo.pPath = "/path_to_item?query_maybe";
* reqInfo.pPathLen = strlen("/path_to_item?query_maybe");
* reqInfo.method = IOT_HTTPS_METHOD_GET;
* reqInfo.pHost = "www.amazon.com";
* reqInfo.hostLen = strlen("www.amazon.com");
* reqInfo.isNonPersistent = false;
* reqInfo.userBuffer.pBuffer = pRequestUserBuffer;
* reqInfo.userBuffer.bufferLen = userBufferSize;
* reqInfo.isAsync = false;
* reqInfo.pSyncInfo = &syncInfo;
*
* IotHttpsReturnCode_t returnCode = IotHttpsClient_InitializeRequest(&reqHandle, &reqInfo);
* if( returnCode == IOT_HTTPS_OK )
* {
* // Handle the error.
* }
* @endcode
*/
/* @[declare_https_client_initializerequest] */
IotHttpsReturnCode_t IotHttpsClient_InitializeRequest( IotHttpsRequestHandle_t * pReqHandle,
IotHttpsRequestInfo_t * pReqInfo );
/* @[declare_https_client_initializerequest] */
/**
* @brief Add a header to the current HTTPS request represented by reqHandle.
*
* The header line is appended to the request header buffer space in #IotHttpsRequestInfo_t.userBuffer.
* Please see #requestUserBufferMinimumSize for information about sizing the #IotHttpsRequestInfo_t.userBuffer so
* that this function does not fail.
*
* Header lines are appended in the following format:
* @code
* header_field_name: header_value\r\n"
* @endcode
* Example:
* @code
* Range: bytes=1024-2047\r\n
* @endcode
* The last header line must be followed by a "\r\n" to separate the last header line from
* the entity body. These 2 characters are accounted for in #requestUserBufferMinimumSize.
*
* The remaining length, after the header is added, is printed to the system configured standard debug output when
* @ref IOT_LOG_LEVEL_HTTPS is set to @ref IOT_LOG_DEBUG in iot_config.h.
*
* For an asynchronous request, this function can be invoked before the request is sent with
* @ref https_client_function_sendasync, or during #IotHttpsClientCallbacks_t.appendHeaderCallback. It is
* recommended to invoke this function in #IotHttpsClientCallbacks_t.appendHeaderCallback.
*
* <b> Asynchronous Example </b>
* @code{c}
* void _applicationDefined_appendHeaderCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* ...
* char date_in_iso8601[17] = { 0 };
* GET_DATE_IN_ISO8601(date_in_iso8601);
* const char amz_date_header[] = "x-amz-date";
* uint32_t amz_date_header_length = strlen(amz_date_header);
* IotHttpsClient_AddHeader(reqHandle, amz_date_header, amz_date_header_length, date_in_iso8601, strlen(date_in_iso8601));
* ...
* }
* @endcode
*
* For a synchronous request, if extra headers are desired to be added, this function must be invoked before
* @ref https_client_function_sendsync.
* <b> Synchronous Example </b>
* @code{c}
* ...
* char date_in_iso8601[17] = { 0 };
* GET_DATE_IN_ISO8601(date_in_iso8601);
* const char amz_date_header[] = "x-amz-date";
* uint32_t amz_date_header_length = strlen(amz_date_header);
* IotHttpsClient_AddHeader(reqHandle, amz_date_header, amz_date_header_length, date_in_iso8601, strlen(date_in_iso8601));
* ...
* IotHttpsClient_SendSync(connHandle, reqHandle, &respHandle, &respInfo, timeout);
* ...
* @endcode
*
* The following header fields are automatically added to the request header buffer and must NOT be added again with
* this routine:
* - Connection: - This header is added to the request when the headers are being sent on the network.
* - User-agent: - This header is added during @ref https_client_function_initializerequest
* - Host: - This header is added during @ref https_client_function_initializerequest
* - Content-Length: - This header is added to the request when the headers are being sent on the network.
*
* The reqHandle is not thread safe. If two threads have the same reqHandle and attempt to add headers at the same
* time, garbage strings may be written to the reqHandle.
*
* @param[in] reqHandle - HTTPS request to write the header line to.
* @param[in] pName - String header field name to write.
* @param[in] nameLen - The length of the header name to write.
* @param[in] pValue - https header value buffer pointer. Do not include token name.
* @param[in] valueLen - length of header value to write.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the header line was successfully added to the header space in #IotHttpsRequestInfo_t.userBuffer.
* - #IOT_HTTPS_INSUFFICIENT_MEMORY if the header line cannot fit into the header buffer.
* - #IOT_HTTPS_INVALID_PARAMETER for NULL parameters or if an attempt to add automatically added headers is made.
*/
/* @[declare_https_client_addheader] */
IotHttpsReturnCode_t IotHttpsClient_AddHeader( IotHttpsRequestHandle_t reqHandle,
char * pName,
uint32_t nameLen,
char * pValue,
uint32_t valueLen );
/* @[declare_https_client_addheader] */
/**
* @brief Writes the request body to the network for the request represented by reqHandle.
*
* This function is intended to be used by an asynchronous request. It must be called within the
* #IotHttpsClientCallbacks_t.writeCallback.
*
* In HTTP/1.1 the headers are sent on the network first before any body can be sent. The auto-generated header
* Content-Length is taken from the len parameter and sent first before the data in parameter pBuf is sent.
* This library does not support Transfer-Encoding: chunked or other requests where the Content-Length is unknown, so
* this function cannot be called more than once in #IotHttpsClientCallbacks_t.writeCallback for an HTTP/1.1 request.
*
* isComplete must always be set to 1 in this current version of the HTTPS client library.
*
* If there are network errors in sending the HTTP headers, then the #IotHttpsClientCallbacks_t.errorCallback will be
* invoked following a return from the #IotHttpsClientCallbacks_t.writeCallback.
*
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_writeCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* ...
* char * writeData[1024];
* IotHttpsClient_WriteRequestBody(reqHandle, writeData, 1024, 1);
* ...
* }
* @endcode
*
* @param[in] reqHandle - identifier of the connection.
* @param[in] pBuf - client write data buffer pointer.
* @param[in] len - length of data to write.
* @param[in] isComplete - This parameter parameter must be set to 1.
*
* @return one of the following:
* - #IOT_HTTPS_OK if write successfully, failure code otherwise.
* - #IOT_HTTPS_MESSAGE_FINISHED if this function is called a second time with the same reqHandle.
* - #IOT_HTTPS_NOT_SUPPORTED if isComplete is set to 0.
* - #IOT_HTTPS_INVALID_PARAMETER if this API is used for a synchronous request.
* - #IOT_HTTPS_NETWORK_ERROR if there was an error sending the headers or body on the network.
* - Please see #IotHttpsReturnCode_t for other failure codes.
*/
/* @[declare_https_client_writerequestbody] */
IotHttpsReturnCode_t IotHttpsClient_WriteRequestBody( IotHttpsRequestHandle_t reqHandle,
uint8_t * pBuf,
uint32_t len,
int isComplete );
/* @[declare_https_client_writerequestbody] */
/**
* @brief Synchronous send of the HTTPS request.
*
* This function blocks waiting for the entirety of sending the request and receiving the response.
*
* If #IotHttpsRequestInfo_t.isNonPersistent is set to true, then the connection will disconnect, close, and clean all
* taken resources automatically after receiving the first response.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*
* To retrieve the response body applications must directly refer #IotHttpsSyncInfo_t.pBody configured in #IotHttpsRequestInfo_t.u.
*
* If the response body does not fit in the configured #IotHttpsSyncInfo_t.pBody, then this function will return with error
* #IOT_HTTPS_MESSAGE_TOO_LARGE. To avoid this issue, the application needs to determine beforehand how large the file
* to download is. This can be done with a HEAD request first, then extracting the "Content-Length" with
* @ref https_client_function_readcontentlength. This could also be done with a GET request with the header
* "Range: bytes=0-0", then extracting the "Content-Range" with @ref https_client_function_readheader. Keep in mind that
* not all HTTP servers support Partial Content responses.
*
* Once a the file size is known, the application can initialize the request with a large
* enough buffer or the application can make a partial content request with the header
* "Range: bytes=N-M", where N is the starting byte requested and M is the ending byte requested.
*
* The response headers as received from the network will be stored in the header buffer space in
* #IotHttpsResponseInfo_t.userBuffer. If the configured #IotHttpsResponseInfo_t.userBuffer is too small
* to fit the headers received, then headers that don't fit will be thrown away. Please see
* #responseUserBufferMinimumSize for information about sizing the #IotHttpsResponseInfo_t.userBuffer.
* To receive feedback on headers discarded, debug logging must be turned on in iot_config.h by setting
* @ref IOT_LOG_LEVEL_HTTPS to @ref IOT_LOG_DEBUG.
*
* Multiple threads must not call this function for the same #IotHttpsRequestHandle_t. Multiple threads can call this
* function for a different #IotHttpsRequestHandle_t, even on the same #IotHttpsConnectionHandle_t. An application must
* wait util a request is fully sent, before scheduling it again. A request is fully sent when this function has returned.
*
* @param[in] connHandle - Handle from an HTTPS connection created with @ref https_client_function_connect.
* @param[in] reqHandle - Handle from a request created with @ref https_client_function_initializerequest.
* @param[out] pRespHandle - HTTPS response handle resulting from a successful send and receive.
* @param[in] pRespInfo - HTTP response configuration information.
* @param[in] timeoutMs - Timeout waiting for the sync request to finish. Set this to 0 to wait forever.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the request was sent and the response was received successfully.
* - #IOT_HTTPS_MESSAGE_TOO_LARGE if the response cannot fit in the configured struct IotHttpsRequestHandle.userBuffer.pBuffer and struct IotHttpsRequestHandle.u.pSyncInfo.pRespData.
* - #IOT_HTTPS_CONNECTION_ERROR if the connection failed.
* - #IOT_HTTPS_INVALID_PARAMETER if there are NULL parameters or the request is asynchronous.
* - #IOT_HTTPS_NETWORK_ERROR if there was an error sending the data on the network.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the HTTP response.
* - #IOT_HTTPS_TIMEOUT_ERROR if the timeoutMs is reached when waiting for a response to the request.
*/
/* @[declare_https_client_sendsync] */
IotHttpsReturnCode_t IotHttpsClient_SendSync( IotHttpsConnectionHandle_t connHandle,
IotHttpsRequestHandle_t reqHandle,
IotHttpsResponseHandle_t * pRespHandle,
IotHttpsResponseInfo_t * pRespInfo,
uint32_t timeoutMs );
/* @[declare_https_client_sendsync] */
/**
* @brief Asynchronous send of the the HTTPS request.
*
* This function will invoke, as needed, each of the non-NULL callbacks configured in #IotHttpsAsyncInfo_t.callbacks
* when the scheduled asynchronous request is progress. Please see #IotHttpsClientCallbacks_t for information on each of
* the callbacks.
*
* After this API is executed, the scheduled async response will store the response headers as received from
* the network, in the header buffer space configured in #IotHttpsResponseInfo_t.userBuffer. If the
* configured #IotHttpsResponseInfo_t.userBuffer is too small to fit the headers received, then headers that don't
* fit will be thrown away. Please see #responseUserBufferMinimumSize for information about sizing the
* #IotHttpsResponseInfo_t.userBuffer.
*
* If #IotHttpsRequestInfo_t.isNonPersistent is set to true, then the connection will disconnect, close, and clean all
* taken resources automatically after receiving the first response.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*
* A #IotHttpsRequestHandle_t cannot be schedule again or reused until the request has finished sending. The request
* has safely finished sending once #IotHttpsClientCallbacks_t.readReadyCallback is invoked. After the
* #IotHttpsClientCallbacks_t.readReadyCallback is invoked the #IotHttpsRequestInfo_t.userBuffer can freed,
* modified, or reused.
*
* @param[in] connHandle - Handle from an HTTPS connection.
* @param[in] reqHandle - Handle from a request created with IotHttpsClient_initialize_request.
* @param[out] pRespHandle - HTTPS response handle.
* @param[in] pRespInfo - HTTP response configuration information.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the request was sent and the response was received successfully.
* - #IOT_HTTPS_MESSAGE_TOO_LARGE if the response cannot fit in the configured
* IotHttpsRequestHandle_t.response_message.headers and IotHttpsRequestHandle_t.response_message.body.
* - #IOT_HTTPS_CONNECTION_ERROR if the connection failed.
* - #IOT_HTTPS_FATAL if there was a grave error with the last async job finishing.
* - #IOT_HTTPS_ASYNC_SCHEDULING_ERROR if there was an error scheduling the asynchronous request.
* - #IOT_HTTPS_INTERNAL_ERROR if there was an internal error with starting an asynchronous request servicing task.
* - #IOT_HTTPS_INVALID_PARAMETER if there were NULL parameters or the request passed in was a synchronous type.
*
*/
/* @[declare_https_client_sendasync] */
IotHttpsReturnCode_t IotHttpsClient_SendAsync( IotHttpsConnectionHandle_t connHandle,
IotHttpsRequestHandle_t reqHandle,
IotHttpsResponseHandle_t * pRespHandle,
IotHttpsResponseInfo_t * pRespInfo );
/* @[declare_https_client_sendasync] */
/**
* @brief Cancel an Asynchronous request.
*
* This will stop an asynchronous request. When an asynchronous request is stopped it will not proceed to do any of
* the following: send headers, send body, receive headers, or receive body. This depends on where in the process
* the request is. For example, if the request is cancelled after sending the headers, then it will not attempt tp
* send the body. A cancelled return code will be returned to the application.
*
* If this is called before the scheduled asynchronous request actually runs, then request will not be sent.
* If this is called during any of the asynchronous callbacks, then the library will stop processing the request when
* the callback returns. This is useful for any error conditions, found during the asynchronous callbacks, where the
* application wants to stop the rest of the request processing.
*
* If the asynchronous request stops processing, the buffers in #IotHttpsRequestInfo_t.userBuffer can be safely freed,
* modified, or reused, only once #IotHttpsClientCallbacks_t.readReadyCallback is invoked.
*
* <b> Example Asynchronous Code </b>
* @code{c}
* void _applicationDefined_appendHeaderCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* char token[MAX_TOKEN_LENGTH] = { 0 }
* int len = MAX_TOKEN_LENGTH;
* int status = gen_auth_token(token, &len);
* if( status == GEN_TOKEN_FAIL)
* {
* IotHttpsClient_CancelRequestAsync(reqHandle);
* }
* ...
* }
*
* void _applicationDefined_writeCallback(void * pPrivData, IotHttpsRequestHandle_t reqHandle)
* {
* if( application_data_get(writeBuffer, writeBufferLen) == GEN_TOKEN_FAIL)
* {
* IotHttpsClient_CancelRequestAsync(reqHandle);
* }
* ...
* }
* @endcode
*
* @param[in] reqHandle - Request handle associated with the request.
*
* @return One of the following:
* - IOT_HTTPS_OK if the request was successfully cancelled.
*/
/* @[declare_https_client_cancelrequestasync] */
IotHttpsReturnCode_t IotHttpsClient_CancelRequestAsync( IotHttpsRequestHandle_t reqHandle );
/* @[declare_https_client_cancelrequestasync] */
/**
* @brief Cancel an Asynchronous response.
*
* This will stop an asynchronous response. When an asynchronous response is stopped it will not proceed to do any of
* the following: send headers, send body, receive headers, or receive body. This depends on where in the process
* the response is. For example, if the response is cancelled after receiving the headers, then it will not attempt tp
* receive the body. A cancelled return code will be returned to the application.
*
* If this is called during ANY of the asynchronous callbacks, then the library will stop processing the response when
* the callback returns. This is useful for any error conditions, found during the asynchronous callbacks, where the
* application wants to stop the rest of the response processing.
*
* If the asynchronous response stops processing, the buffers configured in #IotHttpsResponseInfo_t.userBuffer can
* be freed, modified, or reused only after the #IotHttpsClientCallbacks_t.responseCompleteCallback in invoked.
*
* <b> Example Asynchronous Code </b>
* @code{c}
*
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* if (status != IOT_HTTPS_STATUS_OK)
* {
* IotHttpsClient_CancelResponseAsync(NULL, respHandle);
* }
* ...
* }
* @endcode
*
* @param[in] respHandle - Response handle associated with the response.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response was successfully cancelled.
*/
/* @[declare_https_client_cancelresponseasync] */
IotHttpsReturnCode_t IotHttpsClient_CancelResponseAsync( IotHttpsResponseHandle_t respHandle );
/* @[declare_https_client_cancelresponseasync] */
/**
* @brief Retrieve the HTTPS response status.
*
* The HTTP response status code is contained in the Status-Line of the response header buffer configured in
* #IotHttpsResponseInfo_t.userBuffer. It is the first line of a standard HTTP response message. If the response
* Status-Line could not fit into #IotHttpsResponseInfo_t.userBuffer, then this function will return an error code.
* Please see #responseUserBufferMinimumSize for information about sizing the #IotHttpsResponseInfo_t.userBuffer.
*
* This routine can be used for both a synchronous and asynchronous response.
*
* <b> Example Synchronous Code </b>
* @code{c}
* ...
* IotHttpsClient_SendSync(connHandle, reqHandle, &respHandle, &respInfo, timeout);
* uint16_t status = 0;
* IotHttpsClient_ReadResponseStatus(respHandle, &status);
* if (status != IOT_HTTPS_STATUS_OK)
* {
* // Handle server response status.
* }
* ...
* @endcode
*
* For an asynchronous response the response status is the status parameter in
* #IotHttpsClientCallbacks_t.readReadyCallback and #IotHttpsClientCallbacks_t.responseCompleteCallback. The application
* should refer to that instead of using this function.
* <b> Example Asynchronous Code </b>
* @code
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* if (status != IOT_HTTPS_STATUS_OK)
* {
* // Handle error server response status.
* }
* ...
* }
* @endcode
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[out] pStatus - Integer status returned by the server.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response status was successfully read into *status.
* - #IOT_HTTPS_INVALID_PARAMETER for NULL parameters.
* - #IOT_HTTPS_NOT_FOUND if the HTTP response status was not found in the header buffer.
*/
/* @[declare_https_client_readresponsestatus] */
IotHttpsReturnCode_t IotHttpsClient_ReadResponseStatus( IotHttpsResponseHandle_t respHandle,
uint16_t * pStatus );
/* @[declare_https_client_readresponsestatus] */
/**
* @brief Retrieve the HTTPS response content length.
*
* If the "Content-Length" header is available in #IotHttpsResponseInfo_t.userBuffer, this routine extracts that
* value. In some cases the "Content-Length" header is not available. This could be because the server sent a multi-part
* encoded response (For example, "Transfer-Encoding: chunked") or the "Content-Length" header was far down in the list
* of response headers and could not fit into the header buffer configured in #IotHttpsResponseInfo_t.userBuffer.
* Please see #responseUserBufferMinimumSize for information about sizing the #IotHttpsResponseInfo_t.userBuffer.
*
* In the asynchronous request process, the Content-Length is not available until the
* #IotHttpsClientCallbacks_t.readReadyCallback. Before the #IotHttpsClientCallbacks_t.readReadyCallback is invoked, the
* headers are read into as much as can fit in in the header buffer space of #IotHttpsResponseInfo_t.userBuffer.
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* uint8_t * readBuffer = NULL;
* uint32_t contentLength = 0;
* IotHttpsClient_ReadContentLength(respHandle, &contentLength);
* readBuffer = (uint8_t*)malloc(contentLength);
* ...
* }
* @endcode
*
* In a synchronous request process, the Content-Length is available after @ref https_client_function_sendsync has
* returned successfully.
* <b> Example Synchronous Code </b>
* @code{c}
* ...
* IotHttpsClient_SendSync(connHandle, reqHandle, &respHandle, &respInfo, timeout);
* uint32_t contentLength = 0;
* IotHttpsClient_ReadContentLength(respHandle, &contentLength);
* printf("Content-Length: %u", (unsigned int)contentLength);
* ...
* @endcode
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[out] pContentLength - Integer content length from the Content-Length header from the server. If the content
* length is not found this will be 0.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response body Content-Length was successfully read into contentLength.
* - #IOT_HTTPS_NOT_FOUND if the Content-Length header was not found in the header buffer.
* - #IOT_HTTPS_INVALID_PARAMETER if NULL parameters are passed in.
*/
/* @[declare_https_client_readcontentlength] */
IotHttpsReturnCode_t IotHttpsClient_ReadContentLength( IotHttpsResponseHandle_t respHandle,
uint32_t * pContentLength );
/* @[declare_https_client_readcontentlength] */
/**
* @brief Retrieve the header of interest from the response represented by respHandle.
*
* The response headers as received from the network will be stored in the header buffer space in
* #IotHttpsResponseInfo_t.userBuffer. If the configured #IotHttpsResponseInfo_t.userBuffer is too small to fit
* the headers received, then headers that don't fit will be thrown away. Please see #responseUserBufferMinimumSize for
* information about sizing the #IotHttpsResponseInfo_t.userBuffer.
*
* This routine parses the formatted HTTPS header lines in the header buffer for the header field name specified. If the
* header is not available, then #IOT_HTTPS_NOT_FOUND is returned.
*
* For an asynchronous response, this routine is to be called during the #IotHttpsClientCallbacks_t.readReadyCallback.
* Before the #IotHttpsClientCallbacks_t.readReadyCallback is invoked, the
* headers are read into as much as can fit in in the header buffer space of #IotHttpsResponseInfo_t.userBuffer.
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsResponseHandle_t respHandle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* char valueBuf[64];
* const char contentTypeName[] = "Content-Type";
* uint32_t contentTypeNmeLength = strlen(contentTypeName);
* IotHttpsClient_ReadHeader(respHandle, contentTypeName, contentTypeNameLength, valueBuf, sizeof(valueBuf));
* ...
* }
* @endcode
*
* For a syncrhonous response, this routine is to be called after @ref https_client_function_sendsync has
* returned successfully.
* <b> Example Synchronous Code </b>
* @code{c}
* ...
* IotHttpsClient_SendSync(&connHandle, reqHandle, &respHandle, &respInfo, timeout);
* char valueBuf[10];
* const char contentTypeName[] = "Content-Type";
* uint32_t contentTypeNmeLength = strlen(contentTypeName);
* IotHttpsClient_ReadHeader(respHandle, contentTypeName, contentTypeNmeLength, valueBuf, sizeof(valueBuf));
* uint32_t length = strtoul(valueBuf, NULL, 10);
* ...
* @endcode
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[in] pName - HTTPS Header field name we want the value of. This must be NULL terminated.
* @param[in] nameLen - The length of the name string.
* @param[out] pValue - Buffer to hold the HTTPS field's value. The returned value will be NULL terminated
* and therfore the buffer must be large enough to hold the terminating NULL character.
* @param[in] valueLen - The length of the value buffer.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the header's corresponding value was read into *pValue.
* - #IOT_HTTPS_NOT_FOUND if the header value was not found.
* - #IOT_HTTPS_INVALID_PARAMETER if the respHandle is not valid, there is no response saved or the handle does not exist.
* - #IOT_HTTPS_INSUFFICIENT_MEMORY if the value is too large to fit into *pValue.
*/
/* @[declare_https_client_readheader] */
IotHttpsReturnCode_t IotHttpsClient_ReadHeader( IotHttpsResponseHandle_t respHandle,
char * pName,
uint32_t nameLen,
char * pValue,
uint32_t valueLen );
/* @[declare_https_client_readheader] */
/**
* @brief Read the HTTPS response body from the network.
*
* This is intended to be used with an asynchronous response, this is to be invoked during the
* #IotHttpsClientCallbacks_t.readReadyCallback to read data directly from the network into pBuf.
* <b> Example Asynchronous Code </b>
* @code{c}
* void applicationDefined_readReadyCallback(void * pPrivData, IotHttpsRequestHandle_t handle, IotHttpsReturnCode_t rc, uint16_t status)
* {
* ...
* char * myBuf = STORE_ADDRESS;
* uint32_t len = STORE_READ_SIZE;
* IotHttpsClient_ReadResponseBody(handle, myBuf, &len);
* ...
* }
* @endcode
*
* For a syncrhonous response, to retrieve the response body applications must directly refer to the memory configured
* to receive the response body: #IotHttpsSyncInfo_t.pBody in #IotHttpsResponseInfo_t.pSyncInfo. Otherwise this function
* will return an #IOT_HTTPS_INVALID_PARAMETER error code. This function is intended to read the response entity body
* from the network and the synchronous response process handles all of that in @ref https_client_function_sendsync.
*
* @param[in] respHandle - Unique handle representing the HTTPS response.
* @param[out] pBuf - Pointer to the response body memory location. This is not a char* because the body may have binary data.
* @param[in,out] pLen - The length of the response to read. This should not exceed the size of the buffer that we are reading into. This will be replace with the amount of data read upon return.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the response body was successfully retrieved.
* - #IOT_HTTPS_INVALID_PARAMETER if there are NULL parameters or if the response is a synchronous type.
* - #IOT_HTTPS_NETWORK_ERROR if there was an error sending the data on the network.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the HTTP response.
*/
/* @[declare_https_client_readresponsebody] */
IotHttpsReturnCode_t IotHttpsClient_ReadResponseBody( IotHttpsResponseHandle_t respHandle,
uint8_t * pBuf,
uint32_t * pLen );
/* @[declare_https_client_readresponsebody] */
#endif /* IOT_HTTPS_CLIENT_ */

View file

@ -0,0 +1,95 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 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.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_utils.h
* @brief User facing HTTPS Client library utilities.
*/
#ifndef IOT_HTTPS_UTILS_H_
#define IOT_HTTPS_UTILS_H_
#include "types/iot_https_types.h"
/*-----------------------------------------------------------*/
/**
* @brief Retrieve the path from the input URL.
*
* This function retrieves the location and length of the path from within the
* input the URL. The query is not included in the length returned.
*
* The URL MUST start with "http://" or "https://" to find the path.
*
* For example, if the URL is:
* pUrl = "https://www.somewebsite.com/path/to/item.txt?optionalquery=stuff"
*
* *pPath = "/path/to/item.txt?optionalquery=stuff"
* *pPathLen = 17
*
* @param[in] pUrl - URL string to parse.
* @param[in] urlLen - The length of the URL string input.
* @param[out] pPath - pointer within input url that the path starts at.
* @param[out] pPathLen - Length of the path.
*
* - #IOT_HTTPS_OK if the path was successfully parsed.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the URL.
* - #IOT_HTTPS_NOT_FOUND if the path was not found.
*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlPath( const char * pUrl,
size_t urlLen,
const char ** pPath,
size_t * pPathLen );
/**
* @brief Retrieve the Address from the input URL.
*
* This function retrieves the location and length of the address from within
* the input URL. The path and query are not included in the length returned.
*
* The URL MUST start with "http://" or "https://" to find the address.
*
* For example, if the URL is:
* pUrl = "https://www.somewebsite.com/path/to/item.txt?optionalquery=stuff"
*
* *pAddress = "www.somewebsite.com/path/to/item.txt?optionalquery=stuff"
* *pAddressLen = 19
*
* @param[in] pUrl - URL string to parse.
* @param[in] urlLen - The length of the URL string input.
* @param[out] pAddress - pointer within input url that the address starts at.
* @param[out] pAddressLen - Length of the address.
*
* @return One of the following:
* - #IOT_HTTPS_OK if the path was successfully parsed.
* - #IOT_HTTPS_PARSING_ERROR if there was an error parsing the URL.
* - #IOT_HTTPS_NOT_FOUND if the address was not found.
*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlAddress( const char * pUrl,
size_t urlLen,
const char ** pAddress,
size_t * pAddressLen );
#endif /* IOT_HTTPS_UTILS_H_ */

View file

@ -0,0 +1,907 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 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.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_types.h
* @brief Types of the HTTPS Client library.
*/
#ifndef IOT_HTTPS_TYPES_H_
#define IOT_HTTPS_TYPES_H_
/* The config header is always included first. */
#include "iot_config.h"
/* C standard includes. */
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/* Type includes. */
#include "types/iot_platform_types.h"
/* Platform network include. */
#include "platform/iot_network.h"
/*------------------------- HTTPS defined constants --------------------------*/
/**
* @constantspage{https_client,HTTPS Client library}
*
* @section https_minimum_user_buffer_sizes HTTPS Client Minimum User Buffer Sizes
* @brief Variables calculating the size of #IotHttpsUserBuffer_t.bufferLen needed for the request, response, and
* connection.
*
* @note These user buffer minimum values may change at any time in future versions, but their names will remain the
* same.
* - @ref requestUserBufferMinimumSize <br>
* @copybrief requestUserBufferMinimumSize
* - @ref responseUserBufferMinimumSize <br>
* @copybrief responseUserBufferMinimumSize
* - @ref connectionUserBufferMinimumSize <br>
* @copybrief connectionUserBufferMinimumSize
*
* @section https_connection_flags HTTPS Client Connection Flags
* @brief Flags that modify the behavior of the HTTPS Connection.
*
* Flags should be bitwise-ORed with each other to change the behavior of @ref https_client_function_sendasync and
* @ref https_client_function_sendsync. These flags are set in #IotHttpsConnectionInfo_t.flags.
*
* @note The values of flags may change at any time in future versions, but their names will remain the same.
*
* @section https_initializers HTTP Initializers
* @brief Provide default values for the data types of the HTTP Client Library.
*
* @snippet this define_https_initializers
*
* All user-facing data types of the HTTPS Client library should be initialized using one of the following.
*
* @warning Failing to initialize an HTTPS Client data type with the appropriate initializer may result in undefined
* behavior.
* @note The initializers may change at any time in future versions, but their names will remain the same.
*
* <b>Example</b>
* @code{c}
* IotHttpsConnectionHandle_t connHandle = IOT_HTTPS_CONNECTION_HANDLE_INITIALIZER;
* IotHttpsRequestHandle_t reqHandle = IOT_HTTPS_REQUEST_HANDLE_INITIALIZER;
* IotHttpsResponseHandle_t respHandle = IOT_HTTPS_RESPONSE_HANDLE_INITIALIZER;
* IotHttpsUserBuffer_t userBuffer = IOT_HTTPS_USER_BUFFER_INITIALIZER;
* IotHttpsSyncInfo_t syncInfoReq = IOT_HTTPS_SYNC_INFO_INITIALIZER;
* IotHttpsSyncInfo_t syncInfoResp = IOT_HTTPS_SYNC_INFO_INITIALIZER;
* IotHttpsConnectionInfo_t connInfo = IOT_HTTPS_CONNECTION_INFO_INITIALIZER;
* IotHttpsRequestInfo_t reqInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER
* IotHttpsResponseInfo_t respInfo = IOT_HTTPS_RESPONSE_INFO_INITIALIZER
* @endcode
*
* @section http_constants_connection_flags HTTPS Client Connection Flags
* @brief Flags that modify the behavior the HTTPS connection.
* - #IOT_HTTPS_IS_NON_TLS_FLAG <br>
* @copybrief IOT_HTTPS_IS_NON_TLS_FLAG
* - #IOT_HTTPS_DISABLE_SNI <br>
* @copybrief IOT_HTTPS_DISABLE_SNI
*/
/**
* @brief The minimum user buffer size for the HTTP request context and headers.
*
* This helps to calculate the size of the buffer needed for #IotHttpsRequestInfo_t.userBuffer.
*
* This buffer size is calculated to fit the HTTP Request-Line and the default headers. It does not account for the
* length of the path in the Request-Line nor does it account for the length of the host name. It also does not account
* for extra headers that the application may add. These sizes need to be accounted for by the application when
* assigning a buffer.
*
* A typical value for sizing the request user buffer for the request context is 512 bytes. See the example below.
* @code{c}
* uint8_t requestUserBuffer[512] = { 0 };
* IotHttpsRequestInfo_t requestInfo = IOT_HTTPS_REQUEST_INFO_INITIALIZER;
* requestInfo.userBuffer.pBuffer = requestUserBuffer;
* @endcode
*
* By the application providing the memory for the internal context, no memory is needed to be allocated internally to
* the library for the internal context. The application has control over the memory allocation related to the request,
* response, and connection.
*/
extern const uint32_t requestUserBufferMinimumSize;
/**
* @brief The minimum user buffer size for the HTTP response context and headers.
*
* This helps to calculate the size of the buffer needed for #IotHttpsResponseInfo_t.userBuffer.
*
* The buffer size is calculated to fit the HTTP response context only. It does not account for the HTTP response status
* line. It does not account for any HTTP response headers. If the buffer assigned to
* #IotHttpsResponseInfo_t.userBuffer is of this minimum size, then the response Status-Line and the response headers
* will not be stored. These sizes need to be accounted for by the application when assigning a buffer.
*
* If the response Status-Line and response headers cannot fit into #IotHttpsResponseInfo_t.userBuffer, then after a
* call to @ref https_client_function_sendsync, calls to @ref https_client_function_readresponsestatus,
* @ref https_client_function_readcontentlength, and @ref https_client_function_readheader will return a failure code.
*
* A typical value for sizing the response user buffer for the response context is 512 bytes. See the example below.
* @code{c}
* uint8_t responseUserBuffer[512] = { 0 };
* IotHttpsResponseInfo_t responseInfo = IOT_HTTPS_RESPONSE_INFO_INITIALIZER;
* responseInfo.userBuffer.pBuffer = responseUserBuffer;
* @endcode
*
* By the application providing the memory for the internal context, no memory is needed to be allocated internally to
* the library for the internal context. The application has control over the memory allocation related to the request,
* response, and connection.
*/
extern const uint32_t responseUserBufferMinimumSize;
/**
* @brief The minimum user buffer size for the HTTP connection context and headers.
*
* This helps to calculate the size of the buffer needed for #IotHttpsConnectionInfo_t.userBuffer.
*
* The buffer size is calculated to fit the HTTP connection context only. The buffer assigned by the application must be
* at least this size.
*
* A typical value for sizing the request user buffer for the connection context is 512 bytes. See the example below.
* @code{c}
* uint8_t connectionUserBuffer[512] = { 0 };
* IotHttpsConnectionInfo_t connectionInfo = IOT_HTTPS_CONNECTION_INFO_INITIALIZER;
* connectionInfo.userBuffer.pBuffer = connectionUserBuffer;
* @endcode
*
* By the application providing the memory for the internal context, no memory is needed to be allocated internally to
* the library for the internal context. The application has control over the memory allocation related to the request,
* response, and connection.
*/
extern const uint32_t connectionUserBufferMinimumSize;
/**
* @brief Flag for #IotHttpsConnectionInfo_t that disables TLS.
*
* Set this bit in #IotHttpsConnectionInfo_t.flags to disable use of TLS when the connection is created. This library
* creates secure connections by default.
*/
#define IOT_HTTPS_IS_NON_TLS_FLAG ( 0x00000001 )
/**
* @brief Flag for #IotHttpsConnectionInfo_t that disables Server Name Indication (SNI).
*
* Set this bit #IotHttpsConnectionInfo_t.flags to disable SNI. SNI is enabled by default in this library. When SNI is
* enabled #IotHttpsConnectionInfo_t.pAddress will be used for the server name verification.
*/
#define IOT_HTTPS_DISABLE_SNI ( 0x00000008 )
/* @[define_https_initializers] */
/** @brief Initializer for #IotHttpsConnectionHandle_t. */
#define IOT_HTTPS_CONNECTION_HANDLE_INITIALIZER NULL
/** @brief Initializer for #IotHttpsRequestHandle_t. */
#define IOT_HTTPS_REQUEST_HANDLE_INITIALIZER NULL
/** @brief Initializer for #IotHttpsResponseHandle_t. */
#define IOT_HTTPS_RESPONSE_HANDLE_INITIALIZER NULL
/** @brief Initializer for #IotHttpsUserBuffer_t. */
#define IOT_HTTPS_USER_BUFFER_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsSyncInfo_t. */
#define IOT_HTTPS_SYNC_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsAsyncInfo_t. */
#define IOT_HTTPS_ASYNC_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsConnectionInfo_t. */
#define IOT_HTTPS_CONNECTION_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsRequestInfo_t. */
#define IOT_HTTPS_REQUEST_INFO_INITIALIZER { 0 }
/** @brief Initializer for #IotHttpsResponseInfo_t. */
#define IOT_HTTPS_RESPONSE_INFO_INITIALIZER { 0 }
/* @[define_https_initializers] */
/* Network include for the network types below. */
#include "platform/iot_network.h"
/**
* @brief Type for the network interface containing the operations to send, receive, connect, and disconnect from
* the network.
*/
#define IOT_HTTPS_NETWORK_INTERFACE_TYPE const IotNetworkInterface_t *
/*---------------------------- HTTPS handle types ----------------------------*/
/**
* @handles{https_client,HTTPS Client library}
*/
/**
* @ingroup https_client_datatypes_handles
* @brief Opaque handle of an HTTP connection.
*
* A connection handle is needed to send many requests over a single persistent connection. This handle is valid after
* a successful call to @ref https_client_function_connect or @ref https_client_function_sendsync or
* @ref https_client_function_sendasync. A variable of this type is passed to @ref https_client_function_sendsync,
* @ref https_client_function_sendasync, and @ref https_client_function_disconnect to identify which connection that
* function acts on.
*
* A call to @ref https_client_function_disconnect makes a connection handle invalid. Once @ref https_client_function_disconnect
* returns, the connection handle should no longer be used. The application must call @ref https_client_function_connect
* again to retrieve a new handle and a new connection.
*
* Typical web servers disconnect the client in around 30-60 seconds. The application needs to be aware of this, when
* taking time between requests in a persistent connection.
*
* A connection handle is not thread safe. Multiple threads cannot connect and disconnect with the same handle at the
* same time.
*
* Multiple threads can call @ref https_client_function_sendasync or @ref https_client_function_sendsync with the same
* connection handle.
*/
typedef struct _httpsConnection * IotHttpsConnectionHandle_t;
/**
* @ingroup https_client_datatypes_handles
* @brief Opaque handle of an HTTP request.
*
* Having a separate handle for the HTTP request allows the application to re-use a request.
*
* This handle is valid after a successful call to @ref https_client_function_initializerequest. A variable of this type
* is passed to @ref https_client_function_sendasync or @ref https_client_function_sendsync.
*
* A request handle cannot be sent on multiple connections at the same time.
*
* A request handle is not thread safe. Multiple threads cannot write headers to the same request handle.
*/
typedef struct _httpsRequest * IotHttpsRequestHandle_t;
/**
* @ingroup https_client_datatypes_handles
* @brief Opaque handle of an HTTP response.
*
* This handle is valid after a successful call to @ref https_client_function_sendsync or
* @ref https_client_function_sendasync. A variable of this type is passed to
* @ref https_client_function_readresponsestatus, @ref https_client_function_readcontentlength,
* @ref https_client_function_readheader, and @ref https_client_function_readresponsebody.
*
* When returned from @ref https_client_function_sendsync or @ref https_client_function_sendasync, there is an
* associated #IotHttpsRequestHandle_t. If the #IotHttpsRequestHandle_t associated with this response is re-initialized
* with @ref https_client_function_initializerequest, then this response handle is no longer valid.
*
* A response handle is not thread safe. Multiple threads cannot read the headers in a response at the same time.
*/
typedef struct _httpsResponse * IotHttpsResponseHandle_t;
/*-------------------------- HTTPS enumerated types --------------------------*/
/**
* @enums{https_client,HTTPS Client library}
*/
/**
* @ingroup https_client_datatypes_enums
* @brief Return codes of [HTTPS Client functions](@ref https_client_functions).
*/
typedef enum IotHttpsReturnCode
{
/**
* @brief Returned for a successful operation.
*/
IOT_HTTPS_OK = 0,
/**
* @brief An invalid parameter was passed into an API function.
*/
IOT_HTTPS_INVALID_PARAMETER = 101,
/**
* @brief Invalid payload.
*/
IOT_HTTPS_INVALID_PAYLOAD = 102,
/**
* @brief HTTPS message was too large to fit into a configured synchronous body buffer.
*/
IOT_HTTPS_MESSAGE_TOO_LARGE = 103,
/**
* @brief Overflow occurred somewhere.
*/
IOT_HTTPS_OVERFLOW = 104,
/**
* @brief A buffer provided could not hold data required by the library.
*/
IOT_HTTPS_INSUFFICIENT_MEMORY = 105,
/**
* @brief Queue full.
*/
IOT_HTTPS_QUEUE_FULL = 106,
/**
* @brief Operation retry.
*/
IOT_HTTPS_RETRY = 107,
/**
* @brief Could not find an item specified by an API.
*
* Returned for not being able to find the address in a URL, the path in a URL, or a header field from the response
* headers.
*/
IOT_HTTPS_NOT_FOUND = 108,
/**
* @brief The HTTP request message was finished being written and we cannot write more with @ref https_client_function_writerequestbody.
*/
IOT_HTTPS_MESSAGE_FINISHED = 109,
/**
* @brief An error occurred internally to the library.
*/
IOT_HTTPS_INTERNAL_ERROR = 201,
/**
* @brief A network error occurred.
*/
IOT_HTTPS_NETWORK_ERROR = 202,
/**
* @brief A network connection error occurred.
*/
IOT_HTTPS_CONNECTION_ERROR = 203,
/**
* @brief A stream error occurred.
*/
IOT_HTTPS_STREAM_ERROR = 204,
/**
* @brief An authentication error occurred.
*/
IOT_HTTPS_AUTHENTICATION_ERROR = 205,
/**
* @brief A TLS error occurred.
*/
IOT_HTTPS_TLS_ERROR = 206,
/**
* @brief An error occurred during the user callback.
*/
IOT_HTTPS_USER_CALLBACK_ERROR = 207,
/**
* @brief The synchronous response could not be received in the specified timeout in @ref https_client_function_sendsync.
*/
IOT_HTTPS_TIMEOUT_ERROR = 208,
/**
* @brief An error in the HTTP protocol.
*/
IOT_HTTPS_PROTOCOL_ERROR = 209,
/**
* @brief The HTTPS request send was cancelled.
*/
IOT_HTTPS_SEND_ABORT = 210,
/**
* @brief The HTTPS response receiving was cancelled.
*/
IOT_HTTPS_RECEIVE_ABORT = 211,
/**
* @brief The asynchronous request had an error being scheduled.
*/
IOT_HTTPS_ASYNC_SCHEDULING_ERROR = 212,
/**
* @brief There was an error parsing the HTTP response.
*/
IOT_HTTPS_PARSING_ERROR = 213,
/**
* @brief Fatal HTTP library error.
*/
IOT_HTTPS_FATAL = 901,
/**
* @brief The connection is busy and cannot be cleaned up.
*
* The connection was closed, but @ref https_client_function_disconnect must be called again to cleanup connection
* resources.
*/
IOT_HTTPS_BUSY = 902,
/**
* @brief Try again.
*/
IOT_HTTPS_TRY_AGAIN = 903,
/**
* @brief Data exists.
*/
IOT_HTTPS_DATA_EXIST = 904,
/**
* @brief The operation on the public API is not supported.
*/
IOT_HTTPS_NOT_SUPPORTED = 905
} IotHttpsReturnCode_t;
/**
* @ingroup https_client_datatypes_enums
* @brief Types of HTTP methods.
*
* The HTTP method is configured in #IotHttpsRequestInfo_t.method.
*/
typedef enum IotHttpsMethod
{
IOT_HTTPS_METHOD_GET = 0, /* Client-to-server method GET */
IOT_HTTPS_METHOD_HEAD, /* Client-to-server method HEAD */
IOT_HTTPS_METHOD_PUT, /* Client-to-server method PUT */
IOT_HTTPS_METHOD_POST /* Client-to-server method POST. */
} IotHttpsMethod_t;
/**
* @ingroup https_client_datatypes_enums
* @brief Types of standard HTTP Response status codes.
*
* These status codes are taken from RFC 2616. Please see RFC 2616 for a description of each response status.
*/
enum IotHttpsResponseStatus
{
IOT_HTTPS_STATUS_CONTINUE = 100,
IOT_HTTPS_STATUS_SWITCHING_PROTOCOLS,
IOT_HTTPS_STATUS_OK = 200,
IOT_HTTPS_STATUS_CREATED,
IOT_HTTPS_STATUS_ACCEPTED,
IOT_HTTPS_STATUS_NON_AUTHORITIVE_INFORMATION,
IOT_HTTPS_STATUS_NO_CONTENT,
IOT_HTTPS_STATUS_RESET_CONTENT,
IOT_HTTPS_STATUS_PARTIAL_CONTENT,
IOT_HTTPS_STATUS_MULTIPLE_CHOICES = 300,
IOT_HTTPS_STATUS_MOVED_PERMANENTLY,
IOT_HTTPS_STATUS_FOUND,
IOT_HTTPS_STATUS_SEE_OTHER,
IOT_HTTPS_STATUS_NOT_MODIFIED,
IOT_HTTPS_STATUS_USE_PROXY,
IOT_HTTPS_STATUS_UNUSED,
IOT_HTTPS_STATUS_TEMPORARY_REDIRECT,
IOT_HTTPS_STATUS_BAD_REQUEST = 400,
IOT_HTTPS_STATUS_UNAUTHORIZED,
IOT_HTTPS_STATUS_PAYMENT_REQUIRED,
IOT_HTTPS_STATUS_FORBIDDEN,
IOT_HTTPS_STATUS_NOT_FOUND,
IOT_HTTPS_STATUS_METHOD_NOT_ALLOWED,
IOT_HTTPS_STATUS_NOT_ACCEPTABLE,
IOT_HTTPS_STATUS_PROXY_AUTHENTICATION_REQUIRED,
IOT_HTTPS_STATUS_REQUEST_TIMEOUT,
IOT_HTTPS_STATUS_CONFLICT,
IOT_HTTPS_STATUS_GONE,
IOT_HTTPS_STATUS_LENGTH_REQUIRED,
IOT_HTTPS_STATUS_PRECONDITION_FAILED,
IOT_HTTPS_STATUS_REQUEST_ENTITY_TOO_LARGE,
IOT_HTTPS_STATUS_REQUEST_URI_TOO_LONG,
IOT_HTTPS_STATUS_UNSUPPORTED_MEDIA_TYPE,
IOT_HTTPS_STATUS_REQUEST_RANGE_NOT_SATISFIABLE,
IOT_HTTPS_STATUS_EXPECTATION_FAILED,
IOT_HTTPS_STATUS_INTERNAL_SERVER_ERROR = 500,
IOT_HTTPS_STATUS_NOT_IMPLEMENTED,
IOT_HTTPS_STATUS_BAD_GATEWAY,
IOT_HTTPS_STATUS_SERVICE_UNAVAILABLE,
IOT_HTTPS_STATUS_GATEWAY_TIMEOUT,
IOT_HTTPS_STATUS_HTTP_VERSION_NOT_SUPPORTED
};
/*------------------------- HTTPS parameter structs --------------------------*/
/**
* @paramstructs{https_client,HTTPS Client library}
*/
/**
* @ingroup https_client_datatypes_paramstructs
*
* @brief HTTPS Client library callbacks for asynchronous requests.
*
* @paramfor @ref https_client_function_initializerequest
*
* This type is a parameter in #IotHttpsResponseInfo_t.
*
* If any of the members in this type are set to NULL, then they will not be invoked during the asynchronous
* request/response process.
*
* See @ref Asynchronous_Callback_Order for the order of the order of the callbacks and when they will be invoked.
*/
typedef struct IotHttpsClientCallbacks
{
/**
* @brief User-provided callback function signature for appending a header to current asynchronous request.
*
* If this is set to NULL, then it will not be invoked.
* See @ref https_client_function_addheader for more information on adding a header in this callback.
*
* Appending the header when request is in progress is good for things like time limited authentication tokens.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] reqHandle - The handle for the current HTTP request in progress.
*/
void ( * appendHeaderCallback )( void * pPrivData,
IotHttpsRequestHandle_t reqHandle );
/**
* @brief User-provided callback function signature for writing data to the network for a current asynchronous
* request.
*
* If this is set to NULL, then it will not be invoked.
* See @ref https_client_function_writerequestbody for more information on writing request body.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] reqHandle - The handle for the current HTTP request in progress.
*/
void ( * writeCallback )( void * pPrivData,
IotHttpsRequestHandle_t reqHandle );
/**
* @brief User-provided callback function signature for reading data from the network for a current asynchronous
* response.
*
* The network indicated that after sending the associated request, the response is available for reading.
* If this is set to NULL, then it will not be invoked and any response body received will be ignored.
* See @ref https_client_function_readresponsebody for more information about reading the response body in this
* callback.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] respHandle - The handle for the current HTTP response in progress.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
* @param[in] status - The HTTP response status code of the current response in progress.
*/
void ( * readReadyCallback )( void * pPrivData,
IotHttpsResponseHandle_t respHandle,
IotHttpsReturnCode_t rc,
uint16_t status );
/**
* @brief User-provided callback function signature to indicate that the asynchronous response is completed.
*
* If this is set to NULL, then it will not be invoked.
*
* This callback is invoked when the response is fully received from the network and the request/response pair is
* complete.
* If there was an error in sending the request or an error in receiving the associated response, this callback will
* be invoked, if the error caused the request or associated response to finish.
* #IotHttpsClientCallbacks_t.errorCallback will be invoked first before this callback.
* This callback is invoked to let the application know that memory used by #IotHttpsRequestInfo_t.userBuffer and
* #IotHttpsResponseInfo_t.userBuffer can be freed, modified, or reused.
*
* For a non-persistent connection, the connection will be closed first before invoking this callback.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] respHandle - The handle for the current HTTP response in progress.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
* @param[in] status - The HTTP response status code of the current response in progress.
*/
void ( * responseCompleteCallback )( void * pPrivData,
IotHttpsResponseHandle_t respHandle,
IotHttpsReturnCode_t rc,
uint16_t status );
/**
* @brief User-provided callback function signature to indicate that the connection has been close in an asynchronous
* request process.
*
* If this is set to NULL, then it will not be invoked.
* If there are errors during sending/receiving in the asynchronous process, the connection is not automatically
* closed. If the server closes the connection during the asynchronous process, this callback is not invoked.
* This callback is invoked only if the connection was flagged as non-persistent in
* #IotHttpsConnectionInfo_t.flags.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] connHandle - The handle for the current HTTP connection.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
*/
void ( * connectionClosedCallback )( void * pPrivData,
IotHttpsConnectionHandle_t connHandle,
IotHttpsReturnCode_t rc );
/**
* @brief User-provided callback function signature to indicate that an error occurred during the asynchronous
* request process.
*
* If respHandle is NULL, then reqHandle will not be NULL and vise-versa. This signals which handle the error
* occurred and if it is during the sending or receiving.
*
* @param[in] pPrivData - User context configured in #IotHttpsAsyncInfo_t.pPrivData
* @param[in] respHandle - The handle for the current HTTP response.
* @param[in] reqHandle - The handle for the current HTTP request.
* @param[in] rc - A return code indicating any errors before this callback was invoked.
*/
void ( * errorCallback )( void * pPrivData,
IotHttpsRequestHandle_t reqHandle,
IotHttpsResponseHandle_t respHandle,
IotHttpsReturnCode_t rc );
} IotHttpsClientCallbacks_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief User-provided buffer for storing the HTTPS headers and library internal context.
*
* @paramfor @ref https_client_function_initializerequest.
*
* The user buffer is configured in #IotHttpsConnectionInfo_t.userBuffer, #IotHttpsRequestInfo_t.userBuffer and
* #IotHttpsResponseInfo_t.userBuffer.
*
* The minimum size that the buffer must be configured to is indicated by requestUserBufferMinimumSize,
* responseUserBufferMinimumSize, connectionUserBufferMinimumSize.
*/
typedef struct IotHttpsUserBuffer
{
uint8_t * pBuffer; /**< @brief Application provided buffer pointer. */
uint32_t bufferLen; /**< @brief The length of the application provided buffer. */
} IotHttpsUserBuffer_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTPS Client synchronous request information.
*
* @paramfor @ref https_client_function_initializerequest, @ref https_client_function_sendsync,
* @ref https_client_function_sendasync
*
* This structure is configured in #IotHttpsRequestInfo_t.u and #IotHttpsResponseInfo_t.
*
* A synchronous request will block until the response is fully received from the network.
* This structure defines memory locations to store the response body.
*/
typedef struct IotHttpsSyncRequestInfo
{
/**
* Pointer to the HTTP message body.
*
* For a request this is the file or data we want to send. The data is separated from the headers for the
* flexibility to point to an already established file elsewhere in memory.
*
* For a response this is where to receive the response entity body.
* If the length of the buffer provided to store the response body is smaller than the amount of body received,
* then @ref https_client_function_sendsync will return a IOT_HTTPS_INSUFFICIENT_MEMORY error code. Although an error
* was returned, the first #IotHttpsSyncInfo_t.bodyLen of the response received on the network will
* still be available in the buffer.
*/
uint8_t * pBody;
uint32_t bodyLen; /**< @brief The length of the HTTP message body. */
} IotHttpsSyncInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTPS Client asynchronous request information.
*
* @paramfor @ref https_client_function_initializerequest.
*
* This is parameter in #IotHttpsRequestInfo_t.u.
*
* An asynchronous request will ask the application for headers and body right before the request is ready
* to be sent onto the network.
* An asynchronous request will have the application read headers and body as soon as the response is received
* on the network.
*/
typedef struct IotHttpsAsyncInfo
{
/**
* @brief Callbacks are used for an asynchronous request.
* See #IotHttpsClientCallbacks_t for more information.
*/
IotHttpsClientCallbacks_t callbacks;
void * pPrivData; /**< @brief User private data to provide context to the asynchronous callbacks. */
} IotHttpsAsyncInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTP connection configuration.
*
* @paramfor @ref https_client_function_connect or @ref https_client_function_sendsync or
* @ref https_client_function_sendasync.
*
* This parameter is used to connection in @ref https_client_function_connect.
*
* @note The lengths of the strings in this struct should not include the NULL
* terminator. Strings in this struct do not need to be NULL-terminated.
*/
typedef struct IotHttpsConnectionInfo
{
/**
* @brief Remote server address that is DNS discoverable.
*
* For example: avs-alexa-na.amazon.com.
*/
const char * pAddress;
uint32_t addressLen; /**< @brief remote address length. */
uint16_t port; /**< @brief Remote port number */
/**
* @brief Flags to configure the HTTPS connection.
*
* See @ref https_connection_flags for the available flags.
*
* Unknown flags are ignored.
*/
uint32_t flags; /**< @brief Flags to configure the HTTPS connection. */
/**
* @brief Timeout waiting for a response from the network in milliseconds.
*
* If this is set to zero, it will default to @ref IOT_HTTPS_RESPONSE_WAIT_MS.
*/
uint32_t timeout;
const char * pCaCert; /**< @brief Server trusted certificate store for this connection. */
uint32_t caCertLen; /**< @brief Server trusted certificate store size. */
const char * pClientCert; /**< @brief Client certificate store for this connection. */
uint32_t clientCertLen; /**< @brief Client certificate store size. */
const char * pPrivateKey; /**< @brief Client private key store for this connection. */
uint32_t privateKeyLen; /**< @brief Client private key store size. */
/**
* @brief String of all the ALPN protocols separated by commas needed for this connection.
*
* For the protocols needed for the AWS Iot Message broker endpoint please see:
* https://docs.aws.amazon.com/iot/latest/developerguide/protocols.html
*/
char * pAlpnProtocols;
uint32_t alpnProtocolsLen; /**< @brief ALPN protocol string length. */
/**
* @brief User buffer to store the internal connection context.
*
* See @ref connectionUserBufferMinimumSize for information about the user buffer configured in
* #IotHttpsConnectionInfo_t.userBuffer needed to create a valid connection handle.
*/
IotHttpsUserBuffer_t userBuffer;
/**
* @brief The IOT network abstraction interface.
*
* This contains the interface to connect, disconnect, send data, and receive data from the network.
*
* In Amazon FreeRTOS this should be of the type IotNetworkInterface_t.
*/
IOT_HTTPS_NETWORK_INTERFACE_TYPE pNetworkInterface;
} IotHttpsConnectionInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTP request configuration.
*
* @paramfor @ref https_client_function_initializerequest.
*
* This parameter is used to configure the request in https_client_function_initializerequest.
*
* @note The lengths of the strings in this struct should not include the NULL
* terminator. Strings in this struct do not need to be NULL-terminated.
*/
typedef struct IotHttpsRequestInfo
{
/**
* @brief The absolute path to the HTTP request object.
*
* The absolute path includes the path to the file AND the optional query.
* An example URI path: "/v20160207/directives?query".
*
* If this is NULL, a "/" will be added to the Request-Line automatically.
*
* This is used to generate the Request-Line in the HTTP request message, see
* @ref https_client_function_initializerequest for more information.
*/
const char * pPath;
uint32_t pathLen; /**< @brief URI path length */
/**
* @brief On of the HTTP method tokens defined in #IotHttpsMethod_t.
*
* This is used to generate the Request-Line in the HTTP request message, see
* @ref https_client_function_initializerequest for more information.
*/
IotHttpsMethod_t method;
/**
* @brief Host address this request is intended for, e.g., "awsamazon.com".
*
* This is the same as the address in #IotHttpsConnectionInfo_t.pAddress. This is here in the request structure to
* automatically generate the "Host" header field in the header buffer space configured in
* #IotHttpsRequestInfo_t.userBuffer. See @ref https_client_function_initializerequest for more information.
*/
const char * pHost;
uint32_t hostLen; /**< @brief Host address length. */
/**
* @brief Flag denoting if the connection should be non-persistent.
*
* If this flag is set to false, then the connection is persistent. When the connection is persistent, the HTTP
* header "Connection: keep-alive" is automatically added to the headers to send to the server. This header
* asks the server to not close the connection after sending the response.
*
* If this flag is set to true, then the connection is non-persistent. When the connection is non-persistent, then
* HTTP header "Connection: close" is automatically added to the headers to send to the server. This header asks
* the server to close the connection after sending the response.
*
* Please see https://tools.ietf.org/html/rfc2616#section-8.1.1 for more details.
*/
bool isNonPersistent;
/**
* @brief Application owned buffer for storing the request headers and internal request context.
*
* For an asynchronous request, if the application owns the memory for this buffer, then it must not be modified,
* freed, or reused until the the #IotHttpsClientCallbacks_t.responseCompleteCallback is invoked.
*
* Please see #IotHttpsUserBuffer_t for more information.
*/
IotHttpsUserBuffer_t userBuffer;
/**
* @brief Indicator if this request is sync or async.
*
* Set this to false to use a synchronous request. Set this to true to use an asynchronous request.
*/
bool isAsync;
/**
* @brief Specific information for either a synchronous request or an asynchronous request.
*
* See #IotHttpsAsyncInfo_t for information on pAsyncInfo.
* See #IotHttpsSyncInfo_t for information on u.pSyncInfo.
*/
union
{
IotHttpsAsyncInfo_t * pAsyncInfo; /**< @brief Information specifically for Asynchronous requests. */
IotHttpsSyncInfo_t * pSyncInfo; /**< @brief Information specifically for synchronous requests. */
} u;
} IotHttpsRequestInfo_t;
/**
* @ingroup https_client_datatypes_paramstructs
* @brief HTTP request configuration.
*
* @paramfor @ref https_client_function_sendsync and @ref https_client_function_sendasync
*
* A separate response info is defined so that the application can re-initialize a request for re-use while still
* processing a response that was already completed.
*/
typedef struct IotHttpsResponseInfo
{
/**
* The application owned buffer for storing the response headers and internal response context.
*
* For an asynchronous request, if the application owns the memory for this buffer, then it must not be modified,
* freed, or reused until the the #IotHttpsClientCallbacks_t.responseCompleteCallback is invoked.
*
* Please see #IotHttpsUserBuffer_t for more information.
*/
IotHttpsUserBuffer_t userBuffer;
/**
* @brief Specific information for a synchronously received response.
*
* Set this to NULL if the response is to be received asynchronously.
*
* See #IotHttpsSyncInfo_t for more information.
*/
IotHttpsSyncInfo_t * pSyncInfo;
} IotHttpsResponseInfo_t;
#endif /* ifndef IOT_HTTPS_TYPES_H_ */

View file

@ -0,0 +1,137 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 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.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file iot_https_utils.c
* @brief Implements functions for HTTPS Client library utilities.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* iot_https_includes */
#include "iot_https_utils.h"
#include "http_parser.h"
#include "private/iot_https_internal.h"
/*-----------------------------------------------------------*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlPath( const char * pUrl,
size_t urlLen,
const char ** pPath,
size_t * pPathLen )
{
/* http-parser status. Initialized to 0 to signify success. */
int parserStatus = 0;
struct http_parser_url urlParser;
IotHttpsReturnCode_t returnStatus = IOT_HTTPS_OK;
/* Sets all members in urlParser to 0. */
http_parser_url_init( &urlParser );
if( ( pUrl == NULL ) || ( pPath == NULL ) || ( pPathLen == NULL ) )
{
IotLogError( "NULL parameter passed to IotHttpsClient_GetUrlPath()." );
returnStatus = IOT_HTTPS_INVALID_PARAMETER;
}
if( returnStatus == IOT_HTTPS_OK )
{
parserStatus = http_parser_parse_url( pUrl, urlLen, 0, &urlParser );
if( parserStatus != 0 )
{
IotLogError( "Error parsing the input URL %.*s. Error code: %d.", urlLen, pUrl, parserStatus );
returnStatus = IOT_HTTPS_PARSING_ERROR;
}
}
if( returnStatus == IOT_HTTPS_OK )
{
*pPathLen = ( size_t ) ( urlParser.field_data[ UF_PATH ].len );
if( *pPathLen == 0 )
{
returnStatus = IOT_HTTPS_NOT_FOUND;
*pPath = NULL;
}
else
{
*pPath = &pUrl[ urlParser.field_data[ UF_PATH ].off ];
}
}
return returnStatus;
}
/*-----------------------------------------------------------*/
IotHttpsReturnCode_t IotHttpsClient_GetUrlAddress( const char * pUrl,
size_t urlLen,
const char ** pAddress,
size_t * pAddressLen )
{
/* http-parser status. Initialized to 0 to signify success. */
int parserStatus = 0;
struct http_parser_url urlParser;
IotHttpsReturnCode_t returnStatus = IOT_HTTPS_OK;
/* Sets all members in urlParser to 0. */
http_parser_url_init( &urlParser );
if( ( pUrl == NULL ) || ( pAddress == NULL ) || ( pAddressLen == NULL ) )
{
IotLogError( "NULL parameter passed to IotHttpsClient_GetUrlAddress()." );
returnStatus = IOT_HTTPS_INVALID_PARAMETER;
}
if( returnStatus == IOT_HTTPS_OK )
{
parserStatus = http_parser_parse_url( pUrl, urlLen, 0, &urlParser );
if( parserStatus != 0 )
{
IotLogError( "Error parsing the input URL %.*s. Error code: %d.", urlLen, pUrl, parserStatus );
returnStatus = IOT_HTTPS_PARSING_ERROR;
}
}
if( returnStatus == IOT_HTTPS_OK )
{
*pAddressLen = ( size_t ) ( urlParser.field_data[ UF_HOST ].len );
if( *pAddressLen == 0 )
{
returnStatus = IOT_HTTPS_NOT_FOUND;
*pAddress = NULL;
}
else
{
*pAddress = &pUrl[ urlParser.field_data[ UF_HOST ].off ];
}
}
return returnStatus;
}

View file

@ -0,0 +1,497 @@
/*
* Amazon FreeRTOS HTTPS Client V1.1.0
* Copyright (C) 2019 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.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
#ifndef IOT_HTTPS_INTERNAL_H_
#define IOT_HTTPS_INTERNAL_H_
/* The config header is always included first. */
#include "iot_config.h"
/* Standard Includes. */
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
/* Third party http-parser include. */
#include "http_parser.h"
/* HTTPS Client library includes. */
#include "iot_https_client.h"
/* Task pool include. */
#include "iot_taskpool_freertos.h"
/* Linear containers (lists and queues) include. */
#include "iot_linear_containers.h"
/* Types include. */
#include "types/iot_taskpool_types_freertos.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
#include "platform/iot_network.h"
/* Error handling include. */
#include "iot_error.h"
/*-----------------------------------------------------------*/
/* Convenience macros for handling errors in a standard way. */
/**
* @brief Every public API return an enumeration value with an underlying value of 0 in case of success.
*/
#define HTTPS_SUCCEEDED( x ) ( ( x ) == IOT_HTTPS_OK )
/**
* @brief Every public API returns an enumeration value with an underlying value different than 0 in case of success.
*/
#define HTTPS_FAILED( x ) ( ( x ) != IOT_HTTPS_OK )
/**
* @brief Declare the storage for the error status variable.
*/
#define HTTPS_FUNCTION_ENTRY( result ) IOT_FUNCTION_ENTRY( IotHttpsReturnCode_t, result )
/**
* @brief Jump to the cleanup area.
*/
#define HTTPS_GOTO_CLEANUP() IOT_GOTO_CLEANUP()
/**
* @brief Set error and leave.
*/
#define HTTPS_SET_AND_GOTO_CLEANUP( statusValue ) IOT_SET_AND_GOTO_CLEANUP( statusValue )
/**
* @brief Initialize error and declare start of cleanup area.
*/
#define HTTPS_FUNCTION_CLEANUP_BEGIN() IOT_FUNCTION_CLEANUP_BEGIN()
/**
* @brief Initialize error and declare end of cleanup area.
*/
#define HTTPS_FUNCTION_CLEANUP_END() IOT_FUNCTION_CLEANUP_END()
/**
* @brief Create an empty cleanup area.
*/
#define HTTPS_FUNCTION_EXIT_NO_CLEANUP() IOT_FUNCTION_EXIT_NO_CLEANUP()
/**
* @brief Exit if an argument is NULL.
*/
#define HTTPS_ON_NULL_ARG_GOTO_CLEANUP( ptr ) \
if( ( ptr == NULL ) ) \
{ \
IotLogError( # ptr " was NULL." ); \
IOT_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INVALID_PARAMETER ); \
}
/**
* @brief Exit if an condition is false.
*/
#define HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( expr ) \
if( ( expr ) == false ) \
{ \
IotLogError( # expr " must be true." ); \
IOT_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INVALID_PARAMETER ); \
}
/**
* @brief Exit if an argument is false with a message.
*/
#define HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( expr, statusValue, ... ) \
if( ( expr ) == false ) \
{ \
IotLogError( __VA_ARGS__ ); \
IOT_SET_AND_GOTO_CLEANUP( statusValue ); \
}
/* Configure logs for HTTPS Client functions. */
#ifdef IOT_LOG_LEVEL_HTTPS
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_HTTPS
#else
#ifdef IOT_LOG_LEVEL_GLOBAL
#define LIBRARY_LOG_LEVEL IOT_LOG_LEVEL_GLOBAL
#else
#define LIBRARY_LOG_LEVEL IOT_LOG_NONE
#endif
#endif
#define LIBRARY_LOG_NAME ( "HTTPS Client" )
#include "iot_logging_setup.h"
/*
* Provide default values for undefined memory allocation functions based on
* the usage of dynamic memory allocation.
*/
#if IOT_STATIC_MEMORY_ONLY == 1
#include "iot_static_memory.h"
#endif
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Provide default values for undefined configuration constants.
*/
#ifndef AWS_IOT_HTTPS_ENABLE_METRICS
#define AWS_IOT_HTTPS_ENABLE_METRICS ( 1 )
#endif
#ifndef IOT_HTTPS_USER_AGENT
#define IOT_HTTPS_USER_AGENT "FreeRTOS"
#endif
#ifndef IOT_HTTPS_MAX_FLUSH_BUFFER_SIZE
#define IOT_HTTPS_MAX_FLUSH_BUFFER_SIZE ( 1024 )
#endif
#ifndef IOT_HTTPS_RESPONSE_WAIT_MS
#define IOT_HTTPS_RESPONSE_WAIT_MS ( 1000 )
#endif
#ifndef IOT_HTTPS_MAX_HOST_NAME_LENGTH
#define IOT_HTTPS_MAX_HOST_NAME_LENGTH ( 255 ) /* Per FQDN, the maximum host name length is 255 bytes. */
#endif
#ifndef IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH
#define IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH ( 255 ) /* The maximum alpn protocols length is chosen arbitrarily. */
#endif
/** @endcond */
/**
* @brief The HTTP protocol version of this library is HTTP/1.1.
*/
#define HTTPS_PROTOCOL_VERSION "HTTP/1.1"
/**
* @brief An empty path for a NULL specified path in the request initialization configuration.
*/
#define HTTPS_EMPTY_PATH "/"
/**
* @brief HTTPS "CONNECT" method, defined as the longest string length method.
*/
#define HTTPS_CONNECT_METHOD "CONNECT"
/*
* Constants for the values of the HTTP "Connection" header field.
*
* This is used for writing headers automatically during the sending of the HTTP request.
* "Connection: keep-alive\r\n" is written automatically for a persistent connection.
* "Connection: close\r\n" is written automatically for a non-persistent connection.
*/
#define HTTPS_CONNECTION_KEEP_ALIVE_HEADER_VALUE "keep-alive"
#define HTTPS_CONNECTION_CLOSE_HEADER_VALUE "close"
/**
* Constants for HTTP header formatting.
*
* ": " separates and header field from the header value.
*/
#define HTTPS_HEADER_FIELD_SEPARATOR ": "
#define HTTPS_HEADER_FIELD_SEPARATOR_LENGTH ( 2 )
#define COLON_CHARACTER ':'
#define SPACE_CHARACTER ' '
/**
* Constants for HTTP header formatting.
*
* "\r\n" Ends the header line.
*/
#define HTTPS_END_OF_HEADER_LINES_INDICATOR "\r\n"
#define HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH ( 2 )
#define CARRIAGE_RETURN_CHARACTER '\r'
#define NEWLINE_CHARACTER '\n'
/*
* Constants for header fields added automatically during the request initialization.
*/
#define HTTPS_USER_AGENT_HEADER "User-Agent"
#define HTTPS_HOST_HEADER "Host"
/*
* Constants for the header fields added automatically during the sending of the HTTP request.
*/
#define HTTPS_CONTENT_LENGTH_HEADER "Content-Length"
#define HTTPS_CONNECTION_HEADER "Connection"
/**
* @brief The maximum Content-Length header line size.
*
* This is the length of header line string: "Content-Length: 4294967296\r\n". 4294967296 is 2^32. This number is chosen
* because it is the maximum file size that can be represented in a 32 bit system.
*
* This is used to initialize a local array for the final headers to send.
*/
#define HTTPS_MAX_CONTENT_LENGTH_LINE_LENGTH ( 26 )
/**
* @brief Macro for fast string length calculation of string macros.
*
* We subtract 1 to subtract the NULL terminating character.
* We do not assume that the size of a character is a single byte or 8 bits with this calculation.
*/
#define FAST_MACRO_STRLEN( x ) ( ( sizeof( x ) / sizeof( char ) ) - 1 )
/*-----------------------------------------------------------*/
/**
* @brief The state of the HTTP response parsing.
*
* This state notes what has been parsed in the HTTP response. As soon as any part of the HTTP response is received from
* the network, it is sent to be parsed.
*
* The states move as follows:
* PARSER_STATE_NONE --> PARSER_STATE_IN_HEADERS --> PARSER_STATE_HEADERS_COMPLETE --> PARSER_STATE_BODY_COMPLETE
*
* The parser callbacks are called in the following order:
* 1. _httpParserOnMessageBeginCallback()
* 2. _httpParserOnStatusCallback()
* 3. _httpParserOnHeaderFieldCallback()
* 4. _httpParserOnHeaderValueCallback()
* 5. _httpParserOnHeadersCompleteCallback()
* 6. _httpParserOnChunkHeaderCallback() (optional only if the response is chunked)
* 7. _httpParserOnBodyCallback()
* 8. _httpParserOnChunkCompleteCallback() (optional only if the response is chunked)
* 9. _httpParserOnMessageCompleteCallback()
*
* Theses states are set in the parser callbacks and used outside the callbacks to determine action.
*
* PARSER_STATE_NONE is assigned to #_httpsResponse_t.parserState when the _httpsResponse_t.parserState is initialized
* in @ref IotHttpsClient_InitializeRequest and before parsing a new respone message from the server.
*
* PARSER_STATE_IN_HEADERS is assigned at the start of the HTTP Response message. This occurs in the
* _httpParserOnMessageBeginCallback(). HTTP headers are always first and there is always the response status line
* and some headers in a response message according to RFC 2616.
*
* PARSER_STATE_HEADERS_COMPLETE is assigned when all of the headers are finished being parsed in the HTTP response
* message. This occurs in the _httpParserOnHeadersCompleteCallback(). The state can end here if the response has no
* body, like for a response to a HEAD request.
* If this state is not reached after receiving headers from the network into the user configured header buffer and
* running it through the parser, then we know that not all of the headers from the response could fit into the buffer.
*
* PARSER_STATE_IN_BODY is assigned each time the parser reaches HTTP response body. This occurs in the
* _httpParserOnBodyCallback().
*
* PARSER_STATE_BODY_COMPLETE is assigned when the parser has finished with the whole HTTP response message. This
* happens when _httpParserOnMessageCompleteCallback() is invoked.
* If this state is not reached after receiving body from the network into the user configured body buffer and
* running it through the parser, then we know that not all of the body from the response could fit into the buffer.
*/
typedef enum IotHttpsResponseParserState
{
PARSER_STATE_NONE = 0, /**< @brief The parser has not started so we are neither in the headers or the body. */
PARSER_STATE_IN_HEADERS, /**< @brief The parser is currently parsing the HTTP respone headers. */
PARSER_STATE_HEADERS_COMPLETE, /**< @brief The parser has finished parsing the headers. */
PARSER_STATE_IN_BODY, /**< @brief The parser is currently parsing the HTTP response body. */
PARSER_STATE_BODY_COMPLETE /**< @brief The parser has completed parsing the HTTP response body. */
} IotHttpsResponseParserState_t;
/**
* @brief The state denoting which buffer (the header buffer or the body buffer) is currently being processed
* and for what.
*
* This state is set outside of the parser callbacks and used inside the of parser callbacks to determine actions.
*
* The state moves as follows:
* Receiving and parsing a response: PROCESSING_STATE_NONE --> PROCESSING_STATE_FILLING_HEADER_BUFFER --> PROCESSING_STATE_FILLING_BODY_BUFFER --> PROCESSING_STATE_FINISHED
* Searching a response for headers: ((enter state)) --> PROCESSING_STATE_SEARCHING_HEADER_BUFFER --> ((enter state))
*
* PROCESSING_STATE_NONE is assigned when #_httpsResponse_t.bufferProcessingState is initialized in
* @ref IotHttpsClient_InitializeRequest.
*
* PROCESSING_STATE_FILLING_HEADER_BUFFER is assigned at the start of receiving HTTP response headers from the network
* into the header buffer, before processing the received headers with the parser.
* This state is then used in the parser callbacks _httpParserOnStatusCallback(), _httpParserOnHeaderFieldCallback(),
* _httpParserOnHeaderValueCallback(), and _httpParserOnHeadersCompleteCallback() to move the
* #_httpsResponse_t.headersCur pointer along in the header buffer.
* Since the server sends the HTTP response as a single continuous message, sometimes during receiving of the HTTP
* headers we may receive part or all of the HTTP response body:
* ((example header buffer))[headers headers headers headers body body body]
* When parsing this header buffer the parser will execute _httpParserOnBodyCallback() in the
* PROCESSING_STATE_FILLING_HEADER_BUFFER state. The state is used here, for an asynchronous response, to save where
* and how much body is inside the of the header buffer. When a body buffer becomes available, the body in the header
* buffer will be copied to the body buffer.
*
* PROCESSING_STATE_FILLING_BODY_BUFFER is assigned at the start of receiving the HTTP response body form the network
* into the body buffer, before processing the received body with the parser.
*
* PROCESSING_STATE_FINISHED is assigned at the end of IotHttpsClient_SendSync() or at the end of
* IotHttpsClient_SendAsync() when both the header and body buffer are finished being filled with network data and
* parsed.
*
* PROCESSING_STATE_SEARCHING_HEADER_BUFFER is assigned in IotHttpsClient_ReadHeader() when searching for a header
* in the header buffer.
* This state is used in the parser callback _httpParserOnHeaderFieldCallback() to check if the current header field
* parsed equals the header we are searching for. It is used in parser callback _httpParserOnHeaderValueCallback() to
* return the header value if the corresponding field we are searching for was found. It is used in parser callback
* _httpParserOnHeadersCompleteCallback() to stop parsing the header buffer if the header we are searching for was not
* found.
*
* The header buffer is separate from the body buffer.
* The header buffer is configured in #IotHttpRequestInfo_t.respUserBuff. The body buffer is configured in
* #IotHttpRequestInfo_t.syncInfo->respData or as buffer provided asynchronously during the
* #IotHttpsClientCallbacks_t.readReadyCallback() to call to @ref IotHttpsClient_ReadResponseBody().
*/
typedef enum IotHttpsResponseBufferState
{
PROCESSING_STATE_NONE, /**< @brief There is no buffer processing currently. */
PROCESSING_STATE_FILLING_HEADER_BUFFER, /**< @brief The header buffer is being filled and parsed. */
PROCESSING_STATE_FILLING_BODY_BUFFER, /**< @brief The body buffer is being filled and parsed. */
PROCESSING_STATE_FINISHED, /**< @brief Filling and parsing of both buffers is finished. */
PROCESSING_STATE_SEARCHING_HEADER_BUFFER /**< @brief The header buffer is being searched. */
} IotHttpsResponseBufferState_t;
/*-----------------------------------------------------------*/
/**
* @brief Represents an HTTP connection.
*/
typedef struct _httpsConnection
{
const IotNetworkInterface_t * pNetworkInterface; /**< @brief Network interface with calls for connect, disconnect, send, and receive. */
IotNetworkConnection_t pNetworkConnection; /**< @brief Pointer to the network connection to use pNetworkInterface calls on. */
uint32_t timeout; /**< @brief Timeout for a connection and waiting for a response from the network. */
/**
* @brief true if a connection was successful most recently on this context
*
* We have no way of knowing if the server closed the connection because that error is unique to the underlying TLS
* layer. This is set to false initially, then set to true for a successful intentional call to connect.
* Post connection, this is set to false only after an implicit disconnect with a non-persistent request, an implicit
* disconnect with a network error, or an explicit disconnect with a call to @ref https_client_function_disconnect.
*/
bool isConnected;
bool isDestroyed; /**< @brief true if the connection is already destroyed and we should call anymore */
IotMutex_t connectionMutex; /**< @brief Mutex protecting operations on this entire connection context. */
IotDeQueue_t reqQ; /**< @brief The queue for the requests that are not finished yet. */
IotDeQueue_t respQ; /**< @brief The queue for the responses that are waiting to be processed. */
IotTaskPoolJobStorage_t taskPoolJobStorage; /**< @brief An asynchronous operation requires storage for the task pool job. */
IotTaskPoolJob_t taskPoolJob; /**< @brief The task pool job identifier for an asynchronous request. */
} _httpsConnection_t;
/**
* @brief Third party library http-parser information.
*
* There are two separate structures for http_parser state information. This is so that the application can read
* a header during it's readReadyCallback. The readReadyCallback could be invoked many times and the parser will
* therefore be invoked many times for each response read from the network. In order to ensure that the state of
* the parser remains intact whilst headers may be read, two structures holding the state are kept.
*/
typedef struct _httpParserInfo
{
http_parser responseParser; /**< @brief http_parser state information for parsing the response. */
size_t ( * parseFunc )( http_parser * parser,
const http_parser_settings * settings,
const char * data,
size_t len ); /**< @brief http_parser_execute function is to be plugged in here during initialization of the response. */
http_parser readHeaderParser; /**< @brief http_parser state information for parsing the header buffer for reading a header. */
} _httpParserInfo_t;
/**
* @brief Represents an HTTP response.
*/
typedef struct _httpsResponse
{
IotLink_t link; /**< @brief The link to insert the job in the connection's respQ. */
uint8_t * pHeaders; /**< @brief Pointer to the start of the headers buffer. */
uint8_t * pHeadersEnd; /**< @brief Pointer to the end of the headers buffer. */
uint8_t * pHeadersCur; /**< @brief Pointer to the next location to write in the headers buffer. */
uint8_t * pBody; /**< @brief Pointer to the start of the body buffer. */
uint8_t * pBodyEnd; /**< @brief Pointer to the end of the body buffer. */
uint8_t * pBodyCur; /**< @brief Pointer to the next location to write in the body buffer. */
_httpParserInfo_t httpParserInfo; /**< @brief Third party http-parser information. */
uint16_t status; /**< @brief The HTTP response status code of this response. */
IotHttpsMethod_t method; /**< @brief The method of the originating request. */
IotHttpsResponseParserState_t parserState; /**< @brief The current state of the parser. See IotHttpsResponseParserState_t documentation for more details. */
IotHttpsResponseBufferState_t bufferProcessingState; /**< @brief Which buffer is currently being processed and for what. See IotHttpsResponseBufferState_t documentation. */
char * pReadHeaderField; /**< @brief Header field that we want to read from the headers buffer when IotHttpsClient_ReadHeader() is called. */
size_t readHeaderFieldLength; /**< @brief Length of pReadHeaderField */
char * pReadHeaderValue; /**< @brief Header value that we read from the headers buffer when IotHttpsClient_ReadHeader() is called. */
size_t readHeaderValueLength; /**< @brief Length of pReadHeaderValue. */
bool foundHeaderField; /**< @brief State to use during parsing to let us know when we found the header field in the https-parser callbacks.
* This is set to true when the header field is found in parser callback _httpParserOnHeaderFieldCallback().
* On the following parser callback _httpParserOnHeaderValueCallback() we will store the value in pReadHeaderValue and then exit the parsing. */
struct _httpsConnection * pHttpsConnection; /**< @brief Connection associated with response. This is set during IotHttpsClient_SendAsync(). This is needed during the asynchronous workflow to receive data given the respHandle only in the callback. */
bool isAsync; /**< @brief This is set to true if this response is to be retrieved asynchronously. Set to false otherwise. */
uint8_t * pBodyInHeaderBuf; /**< @brief Pointer to the start of body inside the header buffer for copying to a body buffer provided later by the asynchronous response process. */
uint8_t * pBodyCurInHeaderBuf; /**< @brief Pointer to the next location to write body data during processing of the header buffer. This is necessary in case there is a chunk encoded HTTP response. */
IotHttpsReturnCode_t bodyRxStatus; /**< @brief The status of network receiving the HTTPS body to be returned during the #IotHttpsClientCallbacks_t.readReadyCallback. */
bool cancelled; /**< @brief This is set to true to stop the request/response processing in the asynchronous request workflow. */
IotSemaphore_t respFinishedSem; /**< @brief This is for synchronous response to post that is finished being received. It is better to use a task event signal, but that is not implemented yet in the iot_threads.h API. */
IotHttpsReturnCode_t syncStatus; /**< @brief The status of the synchronous response. */
/**
* @brief This is set to true to when the request is finished being sent on the network
*
* A request is not shared with multiple tasks, so only one task will update this. This is to let the let the
* network receive callback know that the request is fully pushed out to the server. This is also to let the
* disconnect know that the request is not using the network interface resources anymore.
*/
bool reqFinishedSending;
IotHttpsClientCallbacks_t * pCallbacks; /**< @brief Pointer to the asynchronous request callbacks. */
void * pUserPrivData; /**< @brief User private data to hand back in the asynchronous callbacks for context. */
bool isNonPersistent; /**< @brief Non-persistent flag to indicate closing the connection immediately after receiving the response. */
} _httpsResponse_t;
/**
* @brief Represents and HTTP request.
*/
typedef struct _httpsRequest
{
IotLink_t link; /**< @brief The link to insert the job in the connection's reqQ. */
uint8_t * pHeaders; /**< @brief Pointer to the start of the headers buffer. */
uint8_t * pHeadersEnd; /**< @brief Pointer to the end of the headers buffer. */
uint8_t * pHeadersCur; /**< @brief Pointer to the next location to write in the headers buffer. */
uint8_t * pBody; /**< @brief Pointer to the start of the body buffer. */
uint32_t bodyLength; /**< @brief Length of request body buffer. */
IotHttpsMethod_t method; /**< @brief The method of the originating request. */
IotHttpsConnectionInfo_t * pConnInfo; /**< @brief Connection info associated with this request. For an implicit connection. */
struct _httpsResponse * pHttpsResponse; /**< @brief Response associated with request. This is initialized during IotHttpsClient_InitializeRequest(), then returned to the application in IotHttpsClient_SendAsync() and IotHttpsClient_SendSync(). */
struct _httpsConnection * pHttpsConnection; /**< @brief Connection associated with request. This is set during IotHttpsClient_SendAsync(). It is needed for the asynchronous workflow to use to send data given the reqHandle only in the callback. */
bool isNonPersistent; /**< @brief Non-persistent flag to indicate closing the connection immediately after receiving the response. */
bool isAsync; /**< @brief This is set to true if this request is to be sent asynchronously. Set to false otherwise. */
void * pUserPrivData; /**< @brief User private data to hand back in the asynchronous callbacks for context. */
IotHttpsClientCallbacks_t * pCallbacks; /**< @brief Pointer to the asynchronous request callbacks. */
bool cancelled; /**< @brief Set this to true to stop the response processing in the asynchronous workflow. */
IotHttpsReturnCode_t bodyTxStatus; /**< @brief The status of network sending the HTTPS body to be returned during the #IotHttpsClientCallbacks_t.writeCallback. */
bool scheduled; /**< @brief Set to true when this request has already been scheduled to the task pool. */
} _httpsRequest_t;
/*-----------------------------------------------------------*/
/**
* @brief A map of the method enum to strings
*
* These are in order to the HTTP request method enums defined in IotHttpsMethod_t.
*/
static const char * _pHttpsMethodStrings[] = {
"GET",
"HEAD",
"PUT",
"POST"
};
#endif /* IOT_HTTPS_INTERNAL_H_ */

View file

@ -0,0 +1,859 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_mqtt.h
* @brief User-facing functions of the MQTT 3.1.1 library.
*/
#ifndef IOT_MQTT_H_
#define IOT_MQTT_H_
/* The config header is always included first. */
#include "iot_config.h"
/* MQTT types include. */
#include "types/iot_mqtt_types.h"
/*------------------------- MQTT library functions --------------------------*/
/**
* @functionspage{mqtt,MQTT library}
* - @functionname{mqtt_function_init}
* - @functionname{mqtt_function_cleanup}
* - @functionname{mqtt_function_receivecallback}
* - @functionname{mqtt_function_connect}
* - @functionname{mqtt_function_disconnect}
* - @functionname{mqtt_function_subscribeasync}
* - @functionname{mqtt_function_subscribesync}
* - @functionname{mqtt_function_unsubscribeasync}
* - @functionname{mqtt_function_unsubscribesync}
* - @functionname{mqtt_function_publishasync}
* - @functionname{mqtt_function_publishsync}
* - @functionname{mqtt_function_wait}
* - @functionname{mqtt_function_strerror}
* - @functionname{mqtt_function_operationtype}
* - @functionname{mqtt_function_issubscribed}
*/
/**
* @functionpage{IotMqtt_Init,mqtt,init}
* @functionpage{IotMqtt_Cleanup,mqtt,cleanup}
* @functionpage{IotMqtt_ReceiveCallback,mqtt,receivecallback}
* @functionpage{IotMqtt_Connect,mqtt,connect}
* @functionpage{IotMqtt_Disconnect,mqtt,disconnect}
* @functionpage{IotMqtt_SubscribeAsync,mqtt,subscribeasync}
* @functionpage{IotMqtt_SubscribeSync,mqtt,subscribesync}
* @functionpage{IotMqtt_UnsubscribeAsync,mqtt,unsubscribeasync}
* @functionpage{IotMqtt_UnsubscribeSync,mqtt,unsubscribesync}
* @functionpage{IotMqtt_PublishAsync,mqtt,publishasync}
* @functionpage{IotMqtt_PublishSync,mqtt,publishsync}
* @functionpage{IotMqtt_Wait,mqtt,wait}
* @functionpage{IotMqtt_strerror,mqtt,strerror}
* @functionpage{IotMqtt_OperationType,mqtt,operationtype}
* @functionpage{IotMqtt_IsSubscribed,mqtt,issubscribed}
*/
/**
* @brief One-time initialization function for the MQTT library.
*
* This function performs setup of the MQTT library. <b>It must be called
* once (and only once) before calling any other MQTT function.</b> Calling this
* function more than once without first calling @ref mqtt_function_cleanup
* may result in a crash.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
*
* @warning No thread-safety guarantees are provided for this function.
*
* @see @ref mqtt_function_cleanup
*/
/* @[declare_mqtt_init] */
IotMqttError_t IotMqtt_Init( void );
/* @[declare_mqtt_init] */
/**
* @brief One-time deinitialization function for the MQTT library.
*
* This function frees resources taken in @ref mqtt_function_init. It should be
* called after [closing all MQTT connections](@ref mqtt_function_disconnect) to
* clean up the MQTT library. After this function returns, @ref mqtt_function_init
* must be called again before calling any other MQTT function.
*
* @warning No thread-safety guarantees are provided for this function. Do not
* call this function if any MQTT connections are open!
*
* @see @ref mqtt_function_init
*/
/* @[declare_mqtt_cleanup] */
void IotMqtt_Cleanup( void );
/* @[declare_mqtt_cleanup] */
/**
* @brief Network receive callback for the MQTT library.
*
* This function should be called by the system whenever data is available for
* the MQTT library.
*
* @param[in] pNetworkConnection The network connection associated with the MQTT
* connection, passed by the network stack.
* @param[in] pReceiveContext A pointer to the MQTT connection handle for which
* the packet was received.
*/
/* @[declare_mqtt_receivecallback] */
void IotMqtt_ReceiveCallback( IotNetworkConnection_t pNetworkConnection,
void * pReceiveContext );
/* @[declare_mqtt_receivecallback] */
/**
* @brief Establish a new MQTT connection.
*
* This function opens a connection between a new MQTT client and an MQTT server
* (also called a <i>broker</i>). MQTT connections are established on top of transport
* layer protocols (such as TCP/IP), and optionally, application layer security
* protocols (such as TLS). The MQTT packet that establishes a connection is called
* the MQTT CONNECT packet. After @ref mqtt_function_init, this function must be
* called before any other MQTT library function.
*
* If [pConnectInfo->cleanSession](@ref IotMqttConnectInfo_t.cleanSession) is `true`,
* this function establishes a clean MQTT session. Subscriptions and unacknowledged
* PUBLISH messages will be discarded when the connection is closed.
*
* If [pConnectInfo->cleanSession](@ref IotMqttConnectInfo_t.cleanSession) is `false`,
* this function establishes (or re-establishes) a persistent MQTT session. The parameters
* [pConnectInfo->pPreviousSubscriptions](@ref IotMqttConnectInfo_t.pPreviousSubscriptions)
* and [pConnectInfo->previousSubscriptionCount](@ref IotMqttConnectInfo_t.previousSubscriptionCount)
* may be used to restore subscriptions present in a re-established persistent session.
* Any restored subscriptions <b>MUST</b> have been present in the persistent session;
* <b>this function does not send an MQTT SUBSCRIBE packet!</b>
*
* [pConnectInfo->pPreviousSubscriptions](@ref IotMqttConnectInfo_t.pPreviousSubscriptions)
* and [pConnectInfo->previousSubscriptionCount](@ref IotMqttConnectInfo_t.previousSubscriptionCount) can
* also be used to pass a list of subscriptions to be stored locally without a SUBSCRIBE packet being
* sent to the broker. These subscriptions are useful to invoke application level callbacks for messages received
* on unsolicited topics from the broker.
*
* This MQTT library is network agnostic, meaning it has no knowledge of the
* underlying network protocol carrying the MQTT packets. It interacts with the
* network through a network abstraction layer, allowing it to be used with many
* different network stacks. The network abstraction layer is established
* per-connection, allowing every #IotMqttConnection_t to use a different network
* stack. The parameter `pNetworkInterface` sets up the network abstraction layer
* for an MQTT connection; see the documentation on #IotMqttNetworkInfo_t for details
* on its members.
*
* The `pConnectInfo` parameter provides the contents of the MQTT CONNECT packet.
* Most members [are defined by the MQTT spec.](@ref IotMqttConnectInfo_t). The
* [pConnectInfo->pWillInfo](@ref IotMqttConnectInfo_t.pWillInfo) member provides
* information on a Last Will and Testament (LWT) message to be published if the
* MQTT connection is closed without [sending a DISCONNECT packet]
* (@ref mqtt_function_disconnect). Unlike other PUBLISH
* messages, a LWT message payload is limited to 65535 bytes in length. Additionally,
* the retry [interval](@ref IotMqttPublishInfo_t.retryMs) and [limit]
* (@ref IotMqttPublishInfo_t.retryLimit) members of #IotMqttPublishInfo_t
* are ignored for LWT messages. The LWT message is optional; `pWillInfo` may be NULL.
*
* Unlike @ref mqtt_function_publishasync, @ref mqtt_function_subscribeasync, and
* @ref mqtt_function_unsubscribeasync, this function is always blocking. Additionally,
* because the MQTT connection acknowledgement packet (CONNACK packet) does not
* contain any information on <i>which</i> CONNECT packet it acknowledges, only one
* CONNECT operation may be in progress at any time. This means that parallel
* threads making calls to @ref mqtt_function_connect will be serialized to send
* their CONNECT packets one-by-one.
*
* @param[in] pNetworkInfo Information on the transport-layer network connection
* to use with the MQTT connection.
* @param[in] pConnectInfo MQTT connection setup parameters.
* @param[in] timeoutMs If the MQTT server does not accept the connection within
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
* @param[out] pMqttConnection Set to a newly-initialized MQTT connection handle
* if this function succeeds.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_TIMEOUT
* - #IOT_MQTT_SERVER_REFUSED
*
* <b>Example</b>
* @code{c}
*
* // Callback function to receive messages from the broker on an unsolicited topic.
* void unsolicitedMessageCallback( void * pArgument, IotMqttCallbackParam_t * pPublish );
*
* // Parameters to MQTT connect.
* IotMqttConnection_t mqttConnection = IOT_MQTT_CONNECTION_INITIALIZER;
* IotMqttNetworkInfo_t networkInfo = IOT_MQTT_NETWORK_INFO_INITIALIZER;
* IotMqttConnectInfo_t connectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER;
* IotMqttPublishInfo_t willInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
*
* // A local subscription to receive messages from the broker on an unsolicited topic.
* IotMqttSubscription_t subscription = IOT_MQTT_SUBSCRIPTION_INITIALIZER;
*
* // Example network abstraction types.
* IotNetworkServerInfo_t serverInfo = { ... };
* IotNetworkCredentials_t credentialInfo = { ... };
* IotNetworkInterface_t networkInterface = { ... };
*
* // Example using a generic network implementation.
* networkInfo.createNetworkConnection = true;
* networkInfo.u.setup.pNetworkServerInfo = &serverInfo;
* networkInfo.u.setup.pNetworkCredentialInfo = &credentialInfo;
* networkInfo.pNetworkInterface = &networkInterface;
*
* // Set the members of the connection info (password and username not used).
* connectInfo.cleanSession = true;
* connectInfo.keepAliveSeconds = 30;
* connectInfo.pClientIdentifier = "uniqueclientidentifier";
* connectInfo.clientIdentifierLength = 22;
*
* // Set the members of the will info (retain and retry not used).
* willInfo.qos = IOT_MQTT_QOS_1;
* willInfo.pTopicName = "will/topic/name";
* willInfo.topicNameLength = ( uint16_t ) strlen( willInfo.pTopicName );
* willInfo.pPayload = "MQTT client unexpectedly disconnected.";
* willInfo.payloadLength = strlen( willInfo.pPayload );
*
* // Set the pointer to the will info.
* connectInfo.pWillInfo = &willInfo;
*
* // [Optional] Set a local subscription to receive the broker messages on an unsolicited topic.
* subscription.qos = IOT_MQTT_QOS_0;
* subscription.pTopicFilter = "some/unsolicited/topic";
* subscription.topicLength = ( uint16_t ) strlen( subscription.pTopicFilter );
* subscription.callback.function = unsolicitedMessageCallback;
* connectInfo.pPreviousSubscriptions = &subscription;
* connectInfo.previousSubscriptionCount = 1;
*
*
* // Call CONNECT with a 5 second block time. Should return
* // IOT_MQTT_SUCCESS when successful.
* IotMqttError_t result = IotMqtt_Connect( &networkInfo,
* &connectInfo,
* 5000,
* &mqttConnection );
*
* if( result == IOT_MQTT_SUCCESS )
* {
* // Do something with the MQTT connection...
*
* // Clean up and close the MQTT connection once it's no longer needed.
* IotMqtt_Disconnect( mqttConnection, 0 );
* }
* @endcode
*/
/* @[declare_mqtt_connect] */
IotMqttError_t IotMqtt_Connect( const IotMqttNetworkInfo_t * pNetworkInfo,
const IotMqttConnectInfo_t * pConnectInfo,
uint32_t timeoutMs,
IotMqttConnection_t * const pMqttConnection );
/* @[declare_mqtt_connect] */
/**
* @brief Closes an MQTT connection and frees resources.
*
* This function closes an MQTT connection and should only be called once
* the MQTT connection is no longer needed. Its exact behavior depends on the
* `flags` parameter.
*
* Normally, `flags` should be `0`. This gracefully shuts down an MQTT
* connection by sending an MQTT DISCONNECT packet. Any [network close function]
* (@ref IotNetworkInterface_t::close) provided [when the connection was established]
* (@ref mqtt_function_connect) will also be called. Note that because the MQTT server
* will not acknowledge a DISCONNECT packet, the client has no way of knowing if
* the server received the DISCONNECT packet. In the case where the DISCONNECT
* packet is lost in transport, any Last Will and Testament (LWT) message established
* with the connection may be published. However, if the DISCONNECT reaches the
* MQTT server, the LWT message will be discarded and not published.
*
* Should the underlying network connection become unusable, this function should
* be called with `flags` set to #IOT_MQTT_FLAG_CLEANUP_ONLY. In this case, no
* DISCONNECT packet will be sent, though the [network close function](@ref IotNetworkInterface_t::close)
* will still be called. This function will only free the resources used by the MQTT
* connection; it still must be called even if the network is offline to avoid leaking
* resources.
*
* @ref mqtt_function_disconnect modifies `mqttConnection`, so it shouldn't
* be used after calling this function.
*
* @param[in] mqttConnection The MQTT connection to close and clean up.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
*/
/* @[declare_mqtt_disconnect] */
void IotMqtt_Disconnect( IotMqttConnection_t mqttConnection,
uint32_t flags );
/* @[declare_mqtt_disconnect] */
/**
* @brief Subscribes to the given array of topic filters and optionally
* receive an asynchronous notification when the subscribe completes.
*
* This function sends an MQTT SUBSCRIBE packet to the server. A SUBSCRIBE
* packet notifies the server to send any matching PUBLISH messages to this client.
* A single SUBSCRIBE packet may carry more than one topic filter, hence the
* parameters to this function include an array of [subscriptions]
* (@ref IotMqttSubscription_t).
*
* An MQTT subscription has two pieces:
* 1. The subscription topic filter registered with the MQTT server. The MQTT
* SUBSCRIBE packet sent from this client to server notifies the server to send
* messages matching the given topic filters to this client.
* 2. The [callback function](@ref IotMqttCallbackInfo_t.function) that this
* client will invoke when an incoming message is received. The callback function
* notifies applications of an incoming PUBLISH message.
*
* The helper function @ref mqtt_function_issubscribed can be used to check if a
* [callback function](@ref IotMqttCallbackInfo_t.function) is registered for
* a particular topic filter.
*
* To modify an already-registered subscription callback, call this function with
* a new `pSubscriptionList`. Any topic filters in `pSubscriptionList` that already
* have a registered callback will be replaced with the new values in `pSubscriptionList`.
*
* @attention QoS 2 subscriptions are currently unsupported. Only 0 or 1 are valid
* for subscription QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion (`NULL` to disable).
* @param[out] pSubscribeOperation Set to a handle by which this operation may be
* referenced after this function returns. This reference is invalidated once
* the subscription operation completes.
*
* @return This function will return #IOT_MQTT_STATUS_PENDING upon success.
* @return Upon completion of the subscription (either through an
* #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_SERVER_REFUSED
* @return If this function fails before queuing a subscribe operation, it will return
* one of:
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
*
* @see @ref mqtt_function_subscribesync for a blocking variant of this function.
* @see @ref mqtt_function_unsubscribeasync for the function that removes subscriptions.
*
* <b>Example</b>
* @code{c}
* #define NUMBER_OF_SUBSCRIPTIONS ...
*
* // Subscription callback function.
* void subscriptionCallback( void * pArgument, IotMqttCallbackParam_t * pPublish );
*
* // An initialized and connected MQTT connection.
* IotMqttConnection_t mqttConnection;
*
* // Subscription information.
* pSubscriptions[ NUMBER_OF_SUBSCRIPTIONS ] = { IOT_MQTT_SUBSCRIPTION_INITIALIZER };
* IotMqttOperation_t lastOperation = IOT_MQTT_OPERATION_INITIALIZER;
*
* // Set the subscription information.
* for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
* {
* pSubscriptions[ i ].qos = IOT_MQTT_QOS_1;
* pSubscriptions[ i ].pTopicFilter = "some/topic/filter";
* pSubscriptions[ i ].topicLength = ( uint16_t ) strlen( pSubscriptions[ i ].pTopicFilter );
* pSubscriptions[ i ].callback.function = subscriptionCallback;
* }
*
* IotMqttError_t result = IotMqtt_SubscribeAsync( mqttConnection,
* pSubscriptions,
* NUMBER_OF_SUBSCRIPTIONS,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &lastOperation );
*
* // Subscribe returns IOT_MQTT_STATUS_PENDING when successful. Wait up to
* // 5 seconds for the operation to complete.
* if( result == IOT_MQTT_STATUS_PENDING )
* {
* result = IotMqtt_Wait( subscriptionRef, 5000 );
* }
*
* // Check that the subscriptions were successful.
* if( result == IOT_MQTT_SUCCESS )
* {
* // Wait for messages on the subscription topic filters...
*
* // Unsubscribe once the subscriptions are no longer needed.
* result = IotMqtt_UnsubscribeAsync( mqttConnection,
* pSubscriptions,
* NUMBER_OF_SUBSCRIPTIONS,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &lastOperation );
*
* // UNSUBSCRIBE returns IOT_MQTT_STATUS_PENDING when successful.
* // Wait up to 5 seconds for the operation to complete.
* if( result == IOT_MQTT_STATUS_PENDING )
* {
* result = IotMqtt_Wait( lastOperation, 5000 );
* }
* }
* // Check which subscriptions were rejected by the server.
* else if( result == IOT_MQTT_SERVER_REFUSED )
* {
* for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
* {
* if( IotMqtt_IsSubscribed( mqttConnection,
* pSubscriptions[ i ].pTopicFilter,
* pSubscriptions[ i ].topicFilterLength,
* NULL ) == false )
* {
* // This subscription was rejected.
* }
* }
* }
* @endcode
*/
/* @[declare_mqtt_subscribeasync] */
IotMqttError_t IotMqtt_SubscribeAsync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
const IotMqttCallbackInfo_t * pCallbackInfo,
IotMqttOperation_t * const pSubscribeOperation );
/* @[declare_mqtt_subscribeasync] */
/**
* @brief Subscribes to the given array of topic filters with a timeout.
*
* This function sends an MQTT SUBSCRIBE packet to the server, then waits for
* a server response to the packet. Internally, this function is a call to @ref
* mqtt_function_subscribeasync followed by @ref mqtt_function_wait. See @ref
* mqtt_function_subscribeasync for more information about the MQTT SUBSCRIBE operation.
*
* @attention QoS 2 subscriptions are currently unsupported. Only 0 or 1 are valid
* for subscription QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* Currently, flags are ignored by this function; this parameter is for
* future-compatibility.
* @param[in] timeoutMs If the MQTT server does not acknowledge the subscriptions within
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_TIMEOUT
* - #IOT_MQTT_SERVER_REFUSED
*/
/* @[declare_mqtt_subscribesync] */
IotMqttError_t IotMqtt_SubscribeSync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_mqtt_subscribesync] */
/**
* @brief Unsubscribes from the given array of topic filters and optionally
* receive an asynchronous notification when the unsubscribe completes.
*
* This function sends an MQTT UNSUBSCRIBE packet to the server. An UNSUBSCRIBE
* packet removes registered topic filters from the server. After unsubscribing,
* the server will no longer send messages on these topic filters to the client.
*
* Corresponding [subscription callback functions](@ref IotMqttCallbackInfo_t.function)
* are also removed from the MQTT connection. These subscription callback functions
* will be removed even if the MQTT UNSUBSCRIBE packet fails to send.
*
* @param[in] mqttConnection The MQTT connection used for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion (`NULL` to disable).
* @param[out] pUnsubscribeOperation Set to a handle by which this operation may be
* referenced after this function returns. This reference is invalidated once
* the unsubscribe operation completes.
*
* @return This function will return #IOT_MQTT_STATUS_PENDING upon success.
* @return Upon completion of the unsubscribe (either through an
* #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* @return If this function fails before queuing an unsubscribe operation, it will return
* one of:
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
*
* @see @ref mqtt_function_unsubscribesync for a blocking variant of this function.
* @see @ref mqtt_function_subscribeasync for the function that adds subscriptions.
*/
/* @[declare_mqtt_unsubscribeasync] */
IotMqttError_t IotMqtt_UnsubscribeAsync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
const IotMqttCallbackInfo_t * pCallbackInfo,
IotMqttOperation_t * const pUnsubscribeOperation );
/* @[declare_mqtt_unsubscribeasync] */
/**
* @brief Unsubscribes from a given array of topic filters with a timeout.
*
* This function sends an MQTT UNSUBSCRIBE packet to the server, then waits
* for a server response to the packet. Internally, this function is a call to
* @ref mqtt_function_unsubscribeasync followed by @ref mqtt_function_wait. See @ref
* mqtt_function_unsubscribeasync for more information about the MQTT UNSUBSCRIBE
* operation.
*
* @param[in] mqttConnection The MQTT connection used for the subscription.
* @param[in] pSubscriptionList Pointer to the first element in the array of
* subscriptions.
* @param[in] subscriptionCount The number of elements in pSubscriptionList.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* Flags are currently ignored but reserved for future use.
* @param[in] timeoutMs If the MQTT server does not acknowledge the UNSUBSCRIBE within
* this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
*/
/* @[declare_mqtt_unsubscribesync] */
IotMqttError_t IotMqtt_UnsubscribeSync( IotMqttConnection_t mqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_mqtt_unsubscribesync] */
/**
* @brief Publishes a message to the given topic name and optionally
* receive an asynchronous notification when the publish completes.
*
* This function sends an MQTT PUBLISH packet to the server. A PUBLISH packet
* contains a payload and a topic name. Any clients with a subscription on a
* topic filter matching the PUBLISH topic name will receive a copy of the
* PUBLISH packet from the server.
*
* If a PUBLISH packet fails to reach the server and it is not a QoS 0 message,
* it will be retransmitted. See #IotMqttPublishInfo_t for a description
* of the retransmission strategy.
*
* @attention QoS 2 messages are currently unsupported. Only 0 or 1 are valid
* for message QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the publish.
* @param[in] pPublishInfo MQTT publish parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* @param[in] pCallbackInfo Asynchronous notification of this function's completion (`NULL` to disable).
* @param[out] pPublishOperation Set to a handle by which this operation may be
* referenced after this function returns. This reference is invalidated once
* the publish operation completes.
*
* @return This function will return #IOT_MQTT_STATUS_PENDING upon success for
* QoS 1 publishes. For a QoS 0 publish it returns #IOT_MQTT_SUCCESS upon
* success.
* @return Upon completion of a QoS 1 publish (either through an
* #IotMqttCallbackInfo_t or @ref mqtt_function_wait), the status will be one of:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_RETRY_NO_RESPONSE (if [pPublishInfo->retryMs](@ref IotMqttPublishInfo_t.retryMs)
* and [pPublishInfo->retryLimit](@ref IotMqttPublishInfo_t.retryLimit) were set).
* @return If this function fails before queuing an publish operation (regardless
* of QoS), it will return one of:
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
*
* @note The parameters `pCallbackInfo` and `pPublishOperation` should only be used for QoS
* 1 publishes. For QoS 0, they should both be `NULL`.
*
* @see @ref mqtt_function_publishsync for a blocking variant of this function.
*
* <b>Example</b>
* @code{c}
* // An initialized and connected MQTT connection.
* IotMqttConnection_t mqttConnection;
*
* // Publish information.
* IotMqttPublishInfo_t publishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;
*
* // Set the publish information. QoS 0 example (retain not used):
* publishInfo.qos = IOT_MQTT_QOS_0;
* publishInfo.pTopicName = "some/topic/name";
* publishInfo.topicNameLength = ( uint16_t ) strlen( publishInfo.pTopicName );
* publishInfo.pPayload = "payload";
* publishInfo.payloadLength = strlen( publishInfo.pPayload );
*
* // QoS 0 publish should return IOT_MQTT_SUCCESS upon success.
* IotMqttError_t qos0Result = IotMqtt_PublishAsync( mqttConnection,
* &publishInfo,
* 0,
* NULL,
* NULL );
*
* // QoS 1 with retry example (using same topic name and payload as QoS 0 example):
* IotMqttOperation_t qos1Operation = IOT_MQTT_OPERATION_INITIALIZER;
* publishInfo.qos = IOT_MQTT_QOS_1;
* publishInfo.retryMs = 1000; // Retry if no response is received in 1 second.
* publishInfo.retryLimit = 5; // Retry up to 5 times.
*
* // QoS 1 publish should return IOT_MQTT_STATUS_PENDING upon success.
* IotMqttError_t qos1Result = IotMqtt_PublishAsync( mqttConnection,
* &publishInfo,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &qos1Operation );
*
* // Wait up to 5 seconds for the publish to complete.
* if( qos1Result == IOT_MQTT_STATUS_PENDING )
* {
* qos1Result = IotMqtt_Wait( qos1Operation, 5000 );
* }
* @endcode
*/
/* @[declare_mqtt_publishasync] */
IotMqttError_t IotMqtt_PublishAsync( IotMqttConnection_t mqttConnection,
const IotMqttPublishInfo_t * pPublishInfo,
uint32_t flags,
const IotMqttCallbackInfo_t * pCallbackInfo,
IotMqttOperation_t * const pPublishOperation );
/* @[declare_mqtt_publishasync] */
/**
* @brief Publish a message to the given topic name with a timeout.
*
* This function sends an MQTT PUBLISH packet to the server, then waits for
* a server response to the packet. Internally, this function is a call to @ref
* mqtt_function_publishasync followed by @ref mqtt_function_wait. See @ref
* mqtt_function_publishasync for more information about the MQTT PUBLISH operation.
*
* @attention QoS 2 messages are currently unsupported. Only 0 or 1 are valid
* for message QoS.
*
* @param[in] mqttConnection The MQTT connection to use for the publish.
* @param[in] pPublishInfo MQTT publish parameters.
* @param[in] flags Flags which modify the behavior of this function. See @ref mqtt_constants_flags.
* Currently, flags are ignored by this function; this parameter is for
* future-compatibility.
* @param[in] timeoutMs If the MQTT server does not acknowledge a QoS 1 PUBLISH
* within this timeout in milliseconds, this function returns #IOT_MQTT_TIMEOUT.
* This parameter is ignored for QoS 0 PUBLISH messages.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_NOT_INITIALIZED
* - #IOT_MQTT_BAD_PARAMETER
* - #IOT_MQTT_NO_MEMORY
* - #IOT_MQTT_NETWORK_ERROR
* - #IOT_MQTT_SCHEDULING_ERROR
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_RETRY_NO_RESPONSE (if [pPublishInfo->retryMs](@ref IotMqttPublishInfo_t.retryMs)
* and [pPublishInfo->retryLimit](@ref IotMqttPublishInfo_t.retryLimit) were set).
*/
/* @[declare_mqtt_publishsync] */
IotMqttError_t IotMqtt_PublishSync( IotMqttConnection_t mqttConnection,
const IotMqttPublishInfo_t * pPublishInfo,
uint32_t flags,
uint32_t timeoutMs );
/* @[declare_mqtt_publishsync] */
/**
* @brief Waits for an operation to complete.
*
* This function blocks to wait for a [subscribe](@ref mqtt_function_subscribeasync),
* [unsubscribe](@ref mqtt_function_unsubscribeasync), or [publish]
* (@ref mqtt_function_publishasync) to complete. These operations are by default
* asynchronous; the function calls queue an operation for processing, and a
* callback is invoked once the operation is complete.
*
* To use this function, the flag #IOT_MQTT_FLAG_WAITABLE must have been
* set in the operation's function call. Additionally, this function must always
* be called with any waitable operation to clean up resources.
*
* Regardless of its return value, this function always clean up resources used
* by the waitable operation. This means `reference` is invalidated as soon as
* this function returns, even if it returns #IOT_MQTT_TIMEOUT or another error.
*
* @param[in] operation Reference to the operation to wait for. The flag
* #IOT_MQTT_FLAG_WAITABLE must have been set for this operation.
* @param[in] timeoutMs How many milliseconds to wait before returning
* #IOT_MQTT_TIMEOUT.
*
* @return The return value of this function depends on the MQTT operation associated
* with `reference`. See #IotMqttError_t for possible return values.
*
* <b>Example</b>
* @code{c}
* // Operation reference and timeout.
* IotMqttOperation_t publishOperation = IOT_MQTT_OPERATION_INITIALIZER;
* uint32_t timeoutMs = 5000; // 5 seconds
*
* // MQTT operation to wait for.
* IotMqttError_t result = IotMqtt_PublishAsync( mqttConnection,
* &publishInfo,
* IOT_MQTT_FLAG_WAITABLE,
* NULL,
* &publishOperation );
*
* // Publish should have returned IOT_MQTT_STATUS_PENDING. The call to wait
* // returns once the result of the publish is available or the timeout expires.
* if( result == IOT_MQTT_STATUS_PENDING )
* {
* result = IotMqtt_Wait( publishOperation, timeoutMs );
*
* // After the call to wait, the result of the publish is known
* // (not IOT_MQTT_STATUS_PENDING).
* assert( result != IOT_MQTT_STATUS_PENDING );
* }
* @endcode
*/
/* @[declare_mqtt_wait] */
IotMqttError_t IotMqtt_Wait( IotMqttOperation_t operation,
uint32_t timeoutMs );
/* @[declare_mqtt_wait] */
/*-------------------------- MQTT helper functions --------------------------*/
/**
* @brief Returns a string that describes an #IotMqttError_t.
*
* Like the POSIX `strerror`, this function returns a string describing a
* return code. In this case, the return code is an MQTT library error code,
* `status`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] status The status to describe.
*
* @return A read-only string that describes `status`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_mqtt_strerror] */
const char * IotMqtt_strerror( IotMqttError_t status );
/* @[declare_mqtt_strerror] */
/**
* @brief Returns a string that describes an #IotMqttOperationType_t.
*
* This function returns a string describing an MQTT operation type, `operation`.
*
* The string returned by this function <b>MUST</b> be treated as read-only: any
* attempt to modify its contents may result in a crash. Therefore, this function
* is limited to usage in logging.
*
* @param[in] operation The operation to describe.
*
* @return A read-only string that describes `operation`.
*
* @warning The string returned by this function must never be modified.
*/
/* @[declare_mqtt_operationtype] */
const char * IotMqtt_OperationType( IotMqttOperationType_t operation );
/* @[declare_mqtt_operationtype] */
/**
* @brief Check if an MQTT connection has a subscription for a topic filter.
*
* This function checks whether an MQTT connection `mqttConnection` has a
* subscription callback registered for a topic filter `pTopicFilter`. If a
* subscription callback is found, its details are copied into the output parameter
* `pCurrentSubscription`. This subscription callback will be invoked for incoming
* PUBLISH messages on `pTopicFilter`.
*
* <b>The check for a matching subscription is only performed client-side</b>;
* therefore, this function should not be relied upon for perfect accuracy. For
* example, this function may return an incorrect result if the MQTT server
* crashes and drops subscriptions without informing the client.
*
* Note that an MQTT connection's subscriptions might change between the time this
* function checks the subscription list and its caller tests the return value.
* This function certainly should not be used concurrently with any pending SUBSCRIBE
* or UNSUBSCRIBE operations.
*
* One suitable use of this function is to check <i>which</i> subscriptions were rejected
* if @ref mqtt_function_subscribeasync returns #IOT_MQTT_SERVER_REFUSED; that return
* code only means that <i>at least one</i> subscription was rejected.
*
* @param[in] mqttConnection The MQTT connection to check.
* @param[in] pTopicFilter The topic filter to check.
* @param[in] topicFilterLength Length of `pTopicFilter`.
* @param[out] pCurrentSubscription If a subscription is found, its details are
* copied here. This output parameter is only valid if this function returns `true` (`NULL` to disable).
*
* @return `true` if a subscription was found; `false` otherwise.
*
* @note The subscription QoS is not stored by the MQTT library; therefore,
* `pCurrentSubscription->qos` will always be set to #IOT_MQTT_QOS_0.
*/
/* @[declare_mqtt_issubscribed] */
bool IotMqtt_IsSubscribed( IotMqttConnection_t mqttConnection,
const char * pTopicFilter,
uint16_t topicFilterLength,
IotMqttSubscription_t * const pCurrentSubscription );
/* @[declare_mqtt_issubscribed] */
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Backwards compatibility macros for previous function names.
*/
#define IotMqtt_Subscribe IotMqtt_SubscribeAsync
#define IotMqtt_TimedSubscribe IotMqtt_SubscribeSync
#define IotMqtt_Unsubscribe IotMqtt_UnsubscribeAsync
#define IotMqtt_TimedUnsubscribe IotMqtt_UnsubscribeSync
#define IotMqtt_Publish IotMqtt_PublishAsync
#define IotMqtt_TimedPublish IotMqtt_PublishSync
/** @endcond */
#endif /* ifndef IOT_MQTT_H_ */

View file

@ -0,0 +1,702 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_mqtt_serialize.h
* @brief User-facing functions for serializing MQTT 3.1.1 packets. This header should
* be included for building a single threaded light-weight MQTT client bypassing
* stateful CSDK MQTT library.
*/
#ifndef _IOT_MQTT_SERIALIZE_H_
#define _IOT_MQTT_SERIALIZE_H_
/* The config header is always included first. */
#include "iot_config.h"
/* MQTT types include. */
#include "types/iot_mqtt_types.h"
/*------------------------- MQTT library functions --------------------------*/
/**
* @functionspage{mqtt,MQTT library}
* - @functionname{mqtt_function_getconnectpacketsize}
* - @functionname{mqtt_function_serializeconnect}
* - @functionname{mqtt_function_getsubscriptionpacketsize}
* - @functionname{mqtt_function_serializesubscribe}
* - @functionname{mqtt_function_serializeunsubscribe}
* - @functionname{mqtt_function_getpublishpacketsize}
* - @functionname{mqtt_function_serializepublish}
* - @functionname{mqtt_function_serializedisconnect}
* - @functionname{mqtt_function_serializepingreq}
* - @functionname{mqtt_function_getincomingmqttpackettypeandlength}
* - @functionname{mqtt_function_deserializeresponse}
* - @functionname{mqtt_function_deserializepublish}
*/
/**
* @functionpage{IotMqtt_GetConnectPacketSize,mqtt,getconnectpacketsize}
* @functionpage{IotMqtt_SerializeConnect,mqtt,serializeconnect}
* @functionpage{IotMqtt_GetSubscriptionPacketSize,mqtt,getsubscriptionpacketsize}
* @functionpage{IotMqtt_SerializeSubscribe,mqtt,serializesubscribe}
* @functionpage{IotMqtt_SerializeUnsubscribe,mqtt,serializeunsubscribe}
* @functionpage{IotMqtt_GetPublishPacketSize,mqtt,getpublishpacketsize}
* @functionpage{IotMqtt_SerializePublish,mqtt,serializepublish}
* @functionpage{IotMqtt_SerializeDisconnect,mqtt,serializedisconnect}
* @functionpage{IotMqtt_SerializePingreq,mqtt,serializepingreq}
* @functionpage{IotMqtt_GetIncomingMQTTPacketTypeAndLength,mqtt,getincomingmqttpackettypeandlength}
* @functionpage{IotMqtt_DeserializeResponse,mqtt,deserializeresponse}
* @functionpage{IotMqtt_DeserializePublish,mqtt,deserializepublish}
*/
/**
* @brief Calculate the size and "Remaining length" of a CONNECT packet generated
* from the given parameters.
*
* @param[in] pConnectInfo User-provided CONNECT information struct.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return IOT_MQTT_SUCCESS if the packet is within the length allowed by MQTT 3.1.1 spec;
* IOT_MQTT_BAD_PARAMETER otherwise. If this function returns `IOT_MQTT_BAD_PARAMETER`,
* the output parameters should be ignored.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_GetConnectPacketSize() should be used to calculate
* // the size of connect request.
*
* IotMqttConnectInfo_t xConnectInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
*
* // start with everything set to zero
* memset( ( void * ) &xConnectInfo, 0x00, sizeof( xConnectInfo ) );
*
* // Initialize connection info, details are out of scope for this example.
* _initializeConnectInfo( &xConnectInfo );
* // Get size requirement for the connect packet
* xResult = IotMqtt_GetConnectPacketSize( &xConnectInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // Application should allocate buffer with size == xPacketSize or use static buffer
* // with size >= xPacketSize to serialize connect request.
* @endcode
*/
/* @[declare_mqtt_getconnectpacketsize] */
IotMqttError_t IotMqtt_GetConnectPacketSize( const IotMqttConnectInfo_t * pConnectInfo,
size_t * pRemainingLength,
size_t * pPacketSize );
/* @[declare_mqtt_getconnectpacketsize] */
/**
* @brief Generate a CONNECT packet from the given parameters.
*
* @param[in] pConnectInfo User-provided CONNECT information.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[in, out] pBuffer User provided buffer where the CONNECT packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.
*
* @note pBuffer must be allocated by caller. Use @ref mqtt_function_getconnectpacketsize
* to determine the required size.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializeConnect() should be used to serialize
* // MQTT connect packet and send it to MQTT broker.
* // Example uses static memory but dynamically allocated memory can be used as well.
* // Get size requirement for the connect packet.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendConnectPacket( int xMQTTSocket )
* {
* IotMqttConnectInfo_t xConnectInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
* size_t xSentBytes = 0;
* // Get size requirement for MQTT connect packet.
* xResult = IotMqtt_GetConnectPacketSize( &xConnectInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
* // Serialize MQTT connect packet into provided buffer
* xResult = IotMqtt_SerializeConnect( &xConnectInfo, xRemainingLength, ucSharedBuffer, xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializeconnect] */
IotMqttError_t IotMqtt_SerializeConnect( const IotMqttConnectInfo_t * pConnectInfo,
size_t remainingLength,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializeconnect] */
/**
* @brief Calculate the size and "Remaining length" of a SUBSCRIBE or UNSUBSCRIBE
* packet generated from the given parameters.
*
* @param[in] type Either IOT_MQTT_SUBSCRIBE or IOT_MQTT_UNSUBSCRIBE.
* @param[in] pSubscriptionList User-provided array of subscriptions.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return IOT_MQTT_SUCCESS if the packet is within the length allowed by MQTT 3.1.1 spec;
* IOT_MQTT_BAD_PARAMETER otherwise. If this function returns IOT_MQTT_BAD_PARAMETER,
* the output parameters should be ignored.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_GetSubscriptionPacketSize() should be used to calculate
* // the size of subscribe or unsubscribe request.
*
* IotMqttError_t xResult;
* IotMqttSubscription_t xMQTTSubscription[ 1 ];
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
*
* // Initialize Subscribe parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter and topic filter length.
* _initializeSubscribe( xMQTTSubscription );
*
* xResult = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
* xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // Application should allocate buffer with size == xPacketSize or use static buffer
* // with size >= xPacketSize to serialize connect request.
* @endcode
*/
/* @[declare_mqtt_getsubscriptionpacketsize] */
IotMqttError_t IotMqtt_GetSubscriptionPacketSize( IotMqttOperationType_t type,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t * pRemainingLength,
size_t * pPacketSize );
/* @[declare_mqtt_getsubscriptionpacketsize] */
/**
* @brief Generate a SUBSCRIBE packet from the given parameters.
*
* @param[in] pSubscriptionList User-provided array of subscriptions.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[out] pPacketIdentifier The packet identifier generated for this SUBSCRIBE.
* @param[in, out] pBuffer User provide buffer where the SUBSCRIBE packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.
*
* @note pBuffer must be allocated by caller.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializeSubscribe() should be used to serialize
* // MQTT Subscribe packet and send it to MQTT broker.
* // Example uses static memory, but dynamically allocated memory can be used as well.
* // Get size requirement for the MQTT subscribe packet.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendSubscribePacket( int xMQTTSocket )
* {
* IotMqttSubscription_t xMQTTSubscription[ 1 ];
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
* size_t xSentBytes = 0;
*
* // Initialize Subscribe parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter and topic filter length.
* _initializeSubscribe( xMQTTSubscription );
* // Get size requirement for MQTT Subscribe packet.
* xResult = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_SUBSCRIBE,
* xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size.
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
*
* // Serialize subscribe into statically allocated ucSharedBuffer.
* xResult = IotMqtt_SerializeSubscribe( xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* xRemainingLength,
* &usPacketIdentifier,
* ucSharedBuffer,
* xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializesubscribe] */
IotMqttError_t IotMqtt_SerializeSubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializesubscribe] */
/**
* @brief Generate a UNSUBSCRIBE packet from the given parameters.
*
* @param[in] pSubscriptionList User-provided array of subscriptions to remove.
* @param[in] subscriptionCount Size of `pSubscriptionList`.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[out] pPacketIdentifier The packet identifier generated for this UNSUBSCRIBE.
* @param[in, out] pBuffer User provide buffer where the UNSUBSCRIBE packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_NO_MEMORY.
*
* @note pBuffer must be allocated by caller.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializeUnsubscribe() should be used to serialize
* // MQTT unsubscribe packet and send it to MQTT broker.
* // Example uses static memory, but dynamically allocated memory can be used as well.
* // Get size requirement for the Unsubscribe packet.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendUnsubscribePacket( int xMQTTSocket )
* {
* // Following example shows one topic example.
* IotMqttSubscription_t xMQTTSubscription[ 1 ];
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* IotMqttError_t xResult;
* size_t xSentBytes = 0;
* // Get size requirement for MQTT unsubscribe packet.
* xResult = IotMqtt_GetSubscriptionPacketSize( IOT_MQTT_UNSUBSCRIBE,
* xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size.
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
* // Serialize subscribe into statically allocated ucSharedBuffer.
* xResult = IotMqtt_SerializeUnsubscribe( xMQTTSubscription,
* sizeof( xMQTTSubscription ) / sizeof( IotMqttSubscription_t ),
* xRemainingLength,
* &usPacketIdentifier,
* ucSharedBuffer,
* xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializeunsubscribe] */
IotMqttError_t IotMqtt_SerializeUnsubscribe( const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializeunsubscribe] */
/**
* @brief Calculate the size and "Remaining length" of a PUBLISH packet generated
* from the given parameters.
*
* @param[in] pPublishInfo User-provided PUBLISH information struct.
* @param[out] pRemainingLength Output for calculated "Remaining length" field.
* @param[out] pPacketSize Output for calculated total packet size.
*
* @return IOT_MQTT_SUCCESS if the packet is within the length allowed by MQTT 3.1.1 spec;
* IOT_MQTT_BAD_PARAMETER otherwise. If this function returns IOT_MQTT_BAD_PARAMETER,
* the output parameters should be ignored.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_GetPublishPacketSize() should be used to calculate
* // the size of MQTT publish request.
*
* IotMqttError_t xResult;
* IotMqttPublishInfo_t xMQTTPublishInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
*
* // Initialize Publish parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter, topic filter length, payload
* // payload length
* _initializePublish( &xMQTTPublishInfo );
*
* // Find out length of Publish packet size.
* xResult = IotMqtt_GetPublishPacketSize( &xMQTTPublishInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // Application should allocate buffer with size == xPacketSize or use static buffer
* // with size >= xPacketSize to serialize connect request.
* @endcode
*/
/* @[declare_mqtt_getpublishpacketsize] */
IotMqttError_t IotMqtt_GetPublishPacketSize( IotMqttPublishInfo_t * pPublishInfo,
size_t * pRemainingLength,
size_t * pPacketSize );
/* @[declare_mqtt_getpublishpacketsize] */
/**
* @brief Generate a PUBLISH packet from the given parameters.
*
* @param[in] pPublishInfo User-provided PUBLISH information.
* @param[in] remainingLength remaining length of the packet to be serialized.
* @param[out] pPacketIdentifier The packet identifier generated for this PUBLISH.
* @param[out] pPacketIdentifierHigh Where the high byte of the packet identifier
* is written.
* @param[in, out] pBuffer User provide buffer where the PUBLISH packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_BAD_PARAMETER.
*
* @note pBuffer must be allocated by caller.
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how IotMqtt_SerializePublish() should be used to serialize
* // MQTT Publish packet and send it to broker.
* // Example uses static memory, but dynamically allocated memory can be used as well.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendUnsubscribePacket( int xMQTTSocket )
* {
* IotMqttError_t xResult;
* IotMqttPublishInfo_t xMQTTPublishInfo;
* size_t xRemainingLength = 0;
* size_t xPacketSize = 0;
* size_t xSentBytes = 0;
* uint16_t usPacketIdentifier;
* uint8_t * pusPacketIdentifierHigh;
*
* // Initialize Publish parameters. Details are out of scope for this example.
* // It will involve setting QOS, topic filter, topic filter length, payload
* // payload length.
* _initializePublish( &xMQTTPublishInfo );
*
* // Find out length of Publish packet size.
* xResult = IotMqtt_GetPublishPacketSize( &xMQTTPublishInfo, &xRemainingLength, &xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* // Make sure the packet size is less than static buffer size
* IotMqtt_Assert( xPacketSize < mqttexampleSHARED_BUFFER_SIZE );
*
* xResult = IotMqtt_SerializePublish( &xMQTTPublishInfo,
* xRemainingLength,
* &usPacketIdentifier,
* &pusPacketIdentifierHigh,
* ucSharedBuffer,
* xPacketSize );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentBytes = send( xMQTTSocket, ( void * ) ucSharedBuffer, xPacketSize, 0 );
* IotMqtt_Assert( xSentBytes == xPacketSize );
* }
* @endcode
*/
/* @[declare_mqtt_serializepublish] */
IotMqttError_t IotMqtt_SerializePublish( IotMqttPublishInfo_t * pPublishInfo,
size_t remainingLength,
uint16_t * pPacketIdentifier,
uint8_t ** pPacketIdentifierHigh,
uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializepublish] */
/**
* @brief Generate a DISCONNECT packet
*
* @param[in, out] pBuffer User provide buffer where the DISCONNECT packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return returns #IOT_MQTT_SUCCESS or #IOT_MQTT_BAD_PARAMETER
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_SerializeDisconnect() should be used.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendDisconnectRequest( int xMQTTSocket )
* {
* size_t xSentBytes = 0;
*
* // Disconnect is fixed length packet, therefore there is no need to calculate the size,
* // just makes sure static buffer can accommodate disconnect request.
* IotMqtt_Assert( MQTT_PACKET_DISCONNECT_SIZE <= mqttexampleSHARED_BUFFER_SIZE );
*
* // Serialize Disconnect packet into static buffer (dynamically allocated buffer can be used as well)
* xResult = IotMqtt_SerializeDisconnect( ucSharedBuffer, MQTT_PACKET_DISCONNECT_SIZE );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentByte = send( xMQTTSocket, ( void * ) ucSharedBuffer, MQTT_PACKET_DISCONNECT_SIZE, 0 );
* IotMqtt_Assert( xSentByte == MQTT_PACKET_DISCONNECT_SIZE );
* }
*
* @endcode
*/
/* @[declare_mqtt_serializedisconnect] */
IotMqttError_t IotMqtt_SerializeDisconnect( uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializedisconnect] */
/**
* @brief Generate a PINGREQ packet.
*
* @param[in, out] pBuffer User provide buffer where the PINGREQ packet is written.
* @param[in] bufferSize Size of the buffer pointed to by pBuffer.
*
* @return #IOT_MQTT_SUCCESS or #IOT_MQTT_BAD_PARAMETER.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_SerializePingReq() should be used.
*
* #define mqttexampleSHARED_BUFFER_SIZE 100
* static ucSharedBuffer[mqttexampleSHARED_BUFFER_SIZE];
* void sendPingRequest( int xMQTTSocket )
* {
* size_t xSentBytes = 0;
*
* // PingReq is fixed length packet, therefore there is no need to calculate the size,
* // just makes sure static buffer can accommodate Ping request.
* IotMqtt_Assert( MQTT_PACKET_PINGREQ_SIZE <= mqttexampleSHARED_BUFFER_SIZE );
*
* xResult = IotMqtt_SerializePingreq( ucSharedBuffer, MQTT_PACKET_PINGREQ_SIZE );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
*
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* xSentByte = send( xMQTTSocket, ( void * ) ucSharedBuffer, MQTT_PACKET_DISCONNECT_SIZE, 0 );
* IotMqtt_Assert( xSentByte == MQTT_PACKET_PINGREQ_SIZE);
* }
* @endcode
*/
/* @[declare_mqtt_serializepingreq] */
IotMqttError_t IotMqtt_SerializePingreq( uint8_t * pBuffer,
size_t bufferSize );
/* @[declare_mqtt_serializepingreq] */
/**
* @brief Extract MQTT packet type and length from incoming packet
*
* @param[in, out] pIncomingPacket Pointer to IotMqttPacketInfo_t structure
* where type, remaining length and packet identifier are stored.
* @param[in] getNextByte Pointer to platform specific function which is used
* to extract type and length from incoming received stream (see example ).
* @param[in] pNetworkConnection Pointer to platform specific network connection
* which is used by getNextByte to receive network data
*
* @return #IOT_MQTT_SUCCESS on successful extraction of type and length,
* #IOT_MQTT_BAD_RESPONSE on failure.
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example code below shows how to implement getNetxByte function with posix sockets.
* // Note: IotMqttGetNextByte_t typedef IotMqttError_t (* IotMqttGetNextByte_t)( void * pNetworkContext,
* // uint8_t * pNextByte );
* // Note: It is assumed that socket is already created and connected,
*
* IotMqttError_t getNextByte( void * pContext,
* uint8_t * pNextByte )
* {
* int socket = ( int ) ( *pvContext );
* int receivedBytes;
* IotMqttError_t result;
*
* receivedBytes = recv( socket, ( void * ) pNextByte, sizeof( uint8_t ), 0 );
*
* if( receivedBytes == sizeof( uint8_t ) )
* {
* result = IOT_MQTT_SUCCESS;
* }
* else
* {
* result = IOT_MQTT_TIMEOUT;
* }
*
* return result;
* }
*
* // Example below shows how IotMqtt_GetIncomingMQTTPacketTypeAndLength() is used to extract type
* // and length from incoming ping response.
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* void getTypeAndLengthFromIncomingMQTTPingResponse( int xMQTTSocket )
* {
* IotMqttPacketInfo_t xIncomingPacket;
* IotMqttError_t xResult = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &xIncomingPacket, getNextByte, ( void * ) xMQTTSocket );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* IotMqtt_Assert( xIncomingPacket.type == MQTT_PACKET_TYPE_PINGRESP );
* }
* @endcode
*
*/
/* @[declare_mqtt_getincomingmqttpackettypeandlength] */
IotMqttError_t IotMqtt_GetIncomingMQTTPacketTypeAndLength( IotMqttPacketInfo_t * pIncomingPacket,
IotMqttGetNextByte_t getNextByte,
void * pNetworkConnection );
/* @[declare_mqtt_getincomingmqttpackettypeandlength] */
/**
* @brief Deserialize incoming publish packet.
*
* @param[in, out] pMqttPacket The caller of this API sets type, remainingLength and pRemainingData.
* On success, packetIdentifier and pubInfo will be set by the function.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_SERVER_REFUSED
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_DeserializePublish() used to extract contents of incoming
* // Publish. xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* void processIncomingPublish( int xMQTTSocket )
* {
* IotMqttError_t xResult;
* IotMqttPacketInfo_t xIncomingPacket;
*
* xResult = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &xIncomingPacket, getNextByte, ( void * ) xMQTTSocket );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* IotMqtt_Assert( ( xIncomingPacket.type & 0xf0 ) == MQTT_PACKET_TYPE_PUBLISH );
* IotMqtt_Assert( xIncomingPacket.remainingLength <= mqttexampleSHARED_BUFFER_SIZE );
*
* // Receive the remaining bytes.
* if( recv( xMQTTSocket, ( void * ) ucSharedBuffer, xIncomingPacket.remainingLength, 0 ) == xIncomingPacket.remainingLength )
* {
* xIncomingPacket.pRemainingData = ucSharedBuffer;
*
* if( IotMqtt_DeserializePublish( &xIncomingPacket ) != IOT_MQTT_SUCCESS )
* {
* xResult = IOT_MQTT_BAD_RESPONSE;
* }
* else
* {
* // Process incoming Publish.
* IotLogInfo( "Incoming QOS : %d\n", xIncomingPacket.pubInfo.qos );
* IotLogInfo( "Incoming Publish Topic Name: %.*s\n", xIncomingPacket.pubInfo.topicNameLength, xIncomingPacket.pubInfo.pTopicName );
* IotLogInfo( "Incoming Publish Message : %.*s\n", xIncomingPacket.pubInfo.payloadLength, xIncomingPacket.pubInfo.pPayload );
* }
* }
* else
* {
* xResult = IOT_MQTT_NETWORK_ERROR;
* }
*
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* }
* @endcode
*/
/* @[declare_mqtt_deserializepublish] */
IotMqttError_t IotMqtt_DeserializePublish( IotMqttPacketInfo_t * pMqttPacket );
/* @[declare_mqtt_deserializepublish] */
/**
* @brief Deserialize incoming ack packets.
*
* @param[in, out] pMqttPacket The caller of this API sets type, remainingLength and pRemainingData.
* On success, packetIdentifier will be set.
*
* @return One of the following:
* - #IOT_MQTT_SUCCESS
* - #IOT_MQTT_BAD_RESPONSE
* - #IOT_MQTT_SERVER_REFUSED
*
* @note This call is part of serializer API used for implementing light-weight MQTT client.
*
* <b>Example</b>
* @code{c}
* // Example below shows how IotMqtt_DeserializeResponse() is used to process unsubscribe ack.
* // xMQTTSocket here is posix socket created and connected to MQTT broker outside of this function.
* void processUnsubscribeAck( int xMQTTSocket )
* {
* IotMqttError_t xResult;
* IotMqttPacketInfo_t xIncomingPacket;
*
* xResult = IotMqtt_GetIncomingMQTTPacketTypeAndLength( &xIncomingPacket, getNextByte, ( void * ) xMQTTSocket );
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* IotMqtt_Assert( xIncomingPacket.type == MQTT_PACKET_TYPE_UNSUBACK );
* IotMqtt_Assert( xIncomingPacket.remainingLength <= sizeof( ucSharedBuffer ) );
*
* // Receive the remaining bytes.
* if( recv( xMQTTSocket, ( void * ) ucSharedBuffer, xIncomingPacket.remainingLength, 0 ) == xIncomingPacket.remainingLength )
* {
* xIncomingPacket.pRemainingData = ucSharedBuffer;
*
* if( IotMqtt_DeserializeResponse( &xIncomingPacket ) != IOT_MQTT_SUCCESS )
* {
* xResult = IOT_MQTT_BAD_RESPONSE;
* }
* }
* else
* {
* xResult = IOT_MQTT_NETWORK_ERROR;
* }
* IotMqtt_Assert( xResult == IOT_MQTT_SUCCESS );
* }
* @endcode
*/
/* @[declare_mqtt_deserializeresponse] */
IotMqttError_t IotMqtt_DeserializeResponse( IotMqttPacketInfo_t * pMqttPacket );
/* @[declare_mqtt_deserializeresponse] */
#endif /* ifndef IOT_MQTT_SERIALIZE_H_ */

View file

@ -0,0 +1,884 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2019 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.
*/
/**
* @file iot_mqtt_network.c
* @brief Implements functions involving transport layer connections.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <string.h>
/* Error handling include. */
#include "iot_error.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/* Atomics include. */
#include "iot_atomic.h"
/*-----------------------------------------------------------*/
/**
* @brief Check if an incoming packet type is valid.
*
* @param[in] packetType The packet type to check.
*
* @return `true` if the packet type is valid; `false` otherwise.
*/
static bool _incomingPacketValid( uint8_t packetType );
/**
* @brief Get an incoming MQTT packet from the network.
*
* @param[in] pNetworkConnection Network connection to use for receive, which
* may be different from the network connection associated with the MQTT connection.
* @param[in] pMqttConnection The associated MQTT connection.
* @param[out] pIncomingPacket Output parameter for the incoming packet.
*
* @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY or #IOT_MQTT_BAD_RESPONSE.
*/
static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket );
/**
* @brief Deserialize a packet received from the network.
*
* @param[in] pMqttConnection The associated MQTT connection.
* @param[in] pIncomingPacket The packet received from the network.
*
* @return #IOT_MQTT_SUCCESS, #IOT_MQTT_NO_MEMORY, #IOT_MQTT_NETWORK_ERROR,
* #IOT_MQTT_SCHEDULING_ERROR, #IOT_MQTT_BAD_RESPONSE, or #IOT_MQTT_SERVER_REFUSED.
*/
static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket );
/**
* @brief Send a PUBACK for a received QoS 1 PUBLISH packet.
*
* @param[in] pMqttConnection Which connection the PUBACK should be sent over.
* @param[in] packetIdentifier Which packet identifier to include in PUBACK.
*/
static void _sendPuback( _mqttConnection_t * pMqttConnection,
uint16_t packetIdentifier );
/**
* @brief Flush a packet from the stream of incoming data.
*
* This function is called when memory for a packet cannot be allocated. The
* packet is flushed from the stream of incoming data so that the next packet
* may be read.
*
* @param[in] pNetworkConnection Network connection to use for receive, which
* may be different from the network connection associated with the MQTT connection.
* @param[in] pMqttConnection The associated MQTT connection.
* @param[in] length The length of the packet to flush.
*/
static void _flushPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
size_t length );
/**
* @cond DOXYGEN_IGNORE
* Doxygen should ignore this section.
*
* Declaration of local MQTT serializer override selectors
*/
#if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttGetPacketType_t,
_getPacketTypeFunc,
_IotMqtt_GetPacketType,
getPacketType )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttGetRemainingLength_t,
_getRemainingLengthFunc,
_IotMqtt_GetRemainingLength,
getRemainingLength )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getConnackDeserializer,
_IotMqtt_DeserializeConnack,
deserialize.connack )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getPublishDeserializer,
_IotMqtt_DeserializePublish,
deserialize.publish )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getPubackDeserializer,
_IotMqtt_DeserializePuback,
deserialize.puback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getSubackDeserializer,
_IotMqtt_DeserializeSuback,
deserialize.suback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getUnsubackDeserializer,
_IotMqtt_DeserializeUnsuback,
deserialize.unsuback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttDeserialize_t,
_getPingrespDeserializer,
_IotMqtt_DeserializePingresp,
deserialize.pingresp )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttSerializePuback_t,
_getMqttPubackSerializer,
_IotMqtt_SerializePuback,
serialize.puback )
_SERIALIZER_OVERRIDE_SELECTOR( IotMqttFreePacket_t,
_getMqttFreePacketFunc,
_IotMqtt_FreePacket,
freePacket )
#else /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */
#define _getPacketTypeFunc( pSerializer ) _IotMqtt_GetPacketType
#define _getRemainingLengthFunc( pSerializer ) _IotMqtt_GetRemainingLength
#define _getConnackDeserializer( pSerializer ) _IotMqtt_DeserializeConnack
#define _getPublishDeserializer( pSerializer ) _IotMqtt_DeserializePublish
#define _getPubackDeserializer( pSerializer ) _IotMqtt_DeserializePuback
#define _getSubackDeserializer( pSerializer ) _IotMqtt_DeserializeSuback
#define _getUnsubackDeserializer( pSerializer ) _IotMqtt_DeserializeUnsuback
#define _getPingrespDeserializer( pSerializer ) _IotMqtt_DeserializePingresp
#define _getMqttPubackSerializer( pSerializer ) _IotMqtt_SerializePuback
#define _getMqttFreePacketFunc( pSerializer ) _IotMqtt_FreePacket
#endif /* if IOT_MQTT_ENABLE_SERIALIZER_OVERRIDES == 1 */
/** @endcond */
/*-----------------------------------------------------------*/
static bool _incomingPacketValid( uint8_t packetType )
{
bool status = true;
/* Check packet type. Mask out lower bits to ignore flags. */
switch( packetType & 0xf0 )
{
/* Valid incoming packet types. */
case MQTT_PACKET_TYPE_CONNACK:
case MQTT_PACKET_TYPE_PUBLISH:
case MQTT_PACKET_TYPE_PUBACK:
case MQTT_PACKET_TYPE_SUBACK:
case MQTT_PACKET_TYPE_UNSUBACK:
case MQTT_PACKET_TYPE_PINGRESP:
break;
/* Any other packet type is invalid. */
default:
status = false;
break;
}
return status;
}
/*-----------------------------------------------------------*/
static IotMqttError_t _getIncomingPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket )
{
IOT_FUNCTION_ENTRY( IotMqttError_t, IOT_MQTT_SUCCESS );
size_t dataBytesRead = 0;
/* No buffer for remaining data should be allocated. */
IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );
IotMqtt_Assert( pIncomingPacket->remainingLength == 0 );
/* Read the packet type, which is the first byte available. */
pIncomingPacket->type = _getPacketTypeFunc( pMqttConnection->pSerializer )( pNetworkConnection,
pMqttConnection->pNetworkInterface );
/* Check that the incoming packet type is valid. */
if( _incomingPacketValid( pIncomingPacket->type ) == false )
{
IotLogError( "(MQTT connection %p) Unknown packet type %02x received.",
pMqttConnection,
pIncomingPacket->type );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Read the remaining length. */
pIncomingPacket->remainingLength = _getRemainingLengthFunc( pMqttConnection->pSerializer )( pNetworkConnection,
pMqttConnection->pNetworkInterface );
if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Allocate a buffer for the remaining data and read the data. */
if( pIncomingPacket->remainingLength > 0 )
{
pIncomingPacket->pRemainingData = IotMqtt_MallocMessage( pIncomingPacket->remainingLength );
if( pIncomingPacket->pRemainingData == NULL )
{
IotLogError( "(MQTT connection %p) Failed to allocate buffer of length "
"%lu for incoming packet type %lu.",
pMqttConnection,
( unsigned long ) pIncomingPacket->remainingLength,
( unsigned long ) pIncomingPacket->type );
_flushPacket( pNetworkConnection, pMqttConnection, pIncomingPacket->remainingLength );
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_NO_MEMORY );
}
else
{
EMPTY_ELSE_MARKER;
}
dataBytesRead = pMqttConnection->pNetworkInterface->receive( pNetworkConnection,
pIncomingPacket->pRemainingData,
pIncomingPacket->remainingLength );
if( dataBytesRead != pIncomingPacket->remainingLength )
{
IOT_SET_AND_GOTO_CLEANUP( IOT_MQTT_BAD_RESPONSE );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Clean up on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status != IOT_MQTT_SUCCESS )
{
if( pIncomingPacket->pRemainingData != NULL )
{
IotMqtt_FreeMessage( pIncomingPacket->pRemainingData );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_CLEANUP_END();
}
/*-----------------------------------------------------------*/
static IotMqttError_t _deserializeIncomingPacket( _mqttConnection_t * pMqttConnection,
_mqttPacket_t * pIncomingPacket )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
_mqttOperation_t * pOperation = NULL;
/* A buffer for remaining data must be allocated if remaining length is not 0. */
IotMqtt_Assert( ( pIncomingPacket->remainingLength > 0 ) ==
( pIncomingPacket->pRemainingData != NULL ) );
/* Only valid packets should be given to this function. */
IotMqtt_Assert( _incomingPacketValid( pIncomingPacket->type ) == true );
/* Mask out the low bits of packet type to ignore flags. */
switch( ( pIncomingPacket->type & 0xf0 ) )
{
case MQTT_PACKET_TYPE_CONNACK:
IotLogDebug( "(MQTT connection %p) CONNACK in data stream.", pMqttConnection );
/* Deserialize CONNACK and notify of result. */
status = _getConnackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_CONNECT,
NULL );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_PUBLISH:
IotLogDebug( "(MQTT connection %p) PUBLISH in data stream.", pMqttConnection );
/* Allocate memory to handle the incoming PUBLISH. */
pOperation = IotMqtt_MallocOperation( sizeof( _mqttOperation_t ) );
if( pOperation == NULL )
{
IotLogWarn( "Failed to allocate memory for incoming PUBLISH." );
status = IOT_MQTT_NO_MEMORY;
break;
}
else
{
/* Set the members of the incoming PUBLISH operation. */
( void ) memset( pOperation, 0x00, sizeof( _mqttOperation_t ) );
pOperation->incomingPublish = true;
pOperation->pMqttConnection = pMqttConnection;
pIncomingPacket->u.pIncomingPublish = pOperation;
}
/* Deserialize incoming PUBLISH. */
status = _getPublishDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
if( status == IOT_MQTT_SUCCESS )
{
/* Send a PUBACK for QoS 1 PUBLISH. */
if( pOperation->u.publish.publishInfo.qos == IOT_MQTT_QOS_1 )
{
_sendPuback( pMqttConnection, pIncomingPacket->packetIdentifier );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Transfer ownership of the received MQTT packet to the PUBLISH operation. */
pOperation->u.publish.pReceivedData = pIncomingPacket->pRemainingData;
pIncomingPacket->pRemainingData = NULL;
/* Add the PUBLISH to the list of operations pending processing. */
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
IotListDouble_InsertHead( &( pMqttConnection->pendingProcessing ),
&( pOperation->link ) );
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
/* Increment the MQTT connection reference count before scheduling an
* incoming PUBLISH. */
if( _IotMqtt_IncrementConnectionReferences( pMqttConnection ) == true )
{
/* Schedule PUBLISH for callback invocation. */
status = _IotMqtt_ScheduleOperation( pOperation, _IotMqtt_ProcessIncomingPublish, 0 );
}
else
{
status = IOT_MQTT_NETWORK_ERROR;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Free PUBLISH operation on error. */
if( status != IOT_MQTT_SUCCESS )
{
/* Check ownership of the received MQTT packet. */
if( pOperation->u.publish.pReceivedData != NULL )
{
/* Retrieve the pointer MQTT packet pointer so it may be freed later. */
IotMqtt_Assert( pIncomingPacket->pRemainingData == NULL );
pIncomingPacket->pRemainingData = ( uint8_t * ) pOperation->u.publish.pReceivedData;
}
else
{
EMPTY_ELSE_MARKER;
}
/* Remove operation from pending processing list. */
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
if( IotLink_IsLinked( &( pOperation->link ) ) == true )
{
IotListDouble_Remove( &( pOperation->link ) );
}
else
{
EMPTY_ELSE_MARKER;
}
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
IotMqtt_Assert( pOperation != NULL );
IotMqtt_FreeOperation( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_PUBACK:
IotLogDebug( "(MQTT connection %p) PUBACK in data stream.", pMqttConnection );
/* Deserialize PUBACK and notify of result. */
status = _getPubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_PUBLISH_TO_SERVER,
&( pIncomingPacket->packetIdentifier ) );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_SUBACK:
IotLogDebug( "(MQTT connection %p) SUBACK in data stream.", pMqttConnection );
/* Deserialize SUBACK and notify of result. */
pIncomingPacket->u.pMqttConnection = pMqttConnection;
status = _getSubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_SUBSCRIBE,
&( pIncomingPacket->packetIdentifier ) );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
case MQTT_PACKET_TYPE_UNSUBACK:
IotLogDebug( "(MQTT connection %p) UNSUBACK in data stream.", pMqttConnection );
/* Deserialize UNSUBACK and notify of result. */
status = _getUnsubackDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
pOperation = _IotMqtt_FindOperation( pMqttConnection,
IOT_MQTT_UNSUBSCRIBE,
&( pIncomingPacket->packetIdentifier ) );
if( pOperation != NULL )
{
pOperation->u.operation.status = status;
_IotMqtt_Notify( pOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
break;
default:
/* The only remaining valid type is PINGRESP. */
IotMqtt_Assert( ( pIncomingPacket->type & 0xf0 ) == MQTT_PACKET_TYPE_PINGRESP );
IotLogDebug( "(MQTT connection %p) PINGRESP in data stream.", pMqttConnection );
/* Deserialize PINGRESP. */
status = _getPingrespDeserializer( pMqttConnection->pSerializer )( pIncomingPacket );
if( status == IOT_MQTT_SUCCESS )
{
if( Atomic_CompareAndSwap_u32( &( pMqttConnection->pingreq.u.operation.periodic.ping.failure ),
0,
1 ) == 1 )
{
IotLogDebug( "(MQTT connection %p) PINGRESP successfully parsed.",
pMqttConnection );
}
else
{
IotLogWarn( "(MQTT connection %p) Unexpected PINGRESP received.",
pMqttConnection );
}
}
else
{
EMPTY_ELSE_MARKER;
}
break;
}
if( status != IOT_MQTT_SUCCESS )
{
IotLogError( "(MQTT connection %p) Packet parser status %s.",
pMqttConnection,
IotMqtt_strerror( status ) );
}
else
{
EMPTY_ELSE_MARKER;
}
return status;
}
/*-----------------------------------------------------------*/
static void _sendPuback( _mqttConnection_t * pMqttConnection,
uint16_t packetIdentifier )
{
IotMqttError_t status = IOT_MQTT_STATUS_PENDING;
_mqttOperation_t * pPubackOperation = NULL;
IotLogDebug( "(MQTT connection %p) Sending PUBACK for received PUBLISH %hu.",
pMqttConnection,
packetIdentifier );
/* Create a PUBACK operation. */
status = _IotMqtt_CreateOperation( pMqttConnection,
0,
NULL,
&pPubackOperation );
if( status != IOT_MQTT_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
/* Set the operation type. */
pPubackOperation->u.operation.type = IOT_MQTT_PUBACK;
/* Generate a PUBACK packet from the packet identifier. */
status = _getMqttPubackSerializer( pMqttConnection->pSerializer )( packetIdentifier,
&( pPubackOperation->u.operation.pMqttPacket ),
&( pPubackOperation->u.operation.packetSize ) );
if( status != IOT_MQTT_SUCCESS )
{
IOT_GOTO_CLEANUP();
}
/* Add the PUBACK operation to the send queue for network transmission. */
status = _IotMqtt_ScheduleOperation( pPubackOperation,
_IotMqtt_ProcessSend,
0 );
if( status != IOT_MQTT_SUCCESS )
{
IotLogError( "(MQTT connection %p) Failed to enqueue PUBACK for sending.",
pMqttConnection );
IOT_GOTO_CLEANUP();
}
else
{
EMPTY_ELSE_MARKER;
}
/* Clean up on error. */
IOT_FUNCTION_CLEANUP_BEGIN();
if( status != IOT_MQTT_SUCCESS )
{
if( pPubackOperation != NULL )
{
_IotMqtt_DestroyOperation( pPubackOperation );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
/*-----------------------------------------------------------*/
static void _flushPacket( void * pNetworkConnection,
const _mqttConnection_t * pMqttConnection,
size_t length )
{
size_t bytesFlushed = 0;
uint8_t receivedByte = 0;
for( bytesFlushed = 0; bytesFlushed < length; bytesFlushed++ )
{
( void ) _IotMqtt_GetNextByte( pNetworkConnection,
pMqttConnection->pNetworkInterface,
&receivedByte );
}
}
/*-----------------------------------------------------------*/
bool _IotMqtt_GetNextByte( void * pNetworkConnection,
const IotNetworkInterface_t * pNetworkInterface,
uint8_t * pIncomingByte )
{
bool status = false;
uint8_t incomingByte = 0;
size_t bytesReceived = 0;
/* Attempt to read 1 byte. */
bytesReceived = pNetworkInterface->receive( pNetworkConnection,
&incomingByte,
1 );
/* Set the output parameter and return success if 1 byte was read. */
if( bytesReceived == 1 )
{
*pIncomingByte = incomingByte;
status = true;
}
else
{
/* Network receive must return 0 on failure. */
IotMqtt_Assert( bytesReceived == 0 );
}
return status;
}
/*-----------------------------------------------------------*/
void _IotMqtt_CloseNetworkConnection( IotMqttDisconnectReason_t disconnectReason,
_mqttConnection_t * pMqttConnection )
{
IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;
IotNetworkError_t closeStatus = IOT_NETWORK_SUCCESS;
IotMqttCallbackParam_t callbackParam = { .u.message = { 0 } };
void * pNetworkConnection = NULL, * pDisconnectCallbackContext = NULL;
/* Disconnect callback function. */
void ( * disconnectCallback )( void *,
IotMqttCallbackParam_t * ) = NULL;
/* Network close function. */
IotNetworkError_t ( * closeConnection) ( IotNetworkConnection_t ) = NULL;
/* Mark the MQTT connection as disconnected and the keep-alive as failed. */
IotMutex_Lock( &( pMqttConnection->referencesMutex ) );
pMqttConnection->disconnected = true;
if( pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs != 0 )
{
/* Keep-alive must have a PINGREQ allocated. */
IotMqtt_Assert( pMqttConnection->pingreq.u.operation.pMqttPacket != NULL );
IotMqtt_Assert( pMqttConnection->pingreq.u.operation.packetSize != 0 );
/* PINGREQ provides a reference to the connection, so reference count must
* be nonzero. */
IotMqtt_Assert( pMqttConnection->references > 0 );
/* Attempt to cancel the keep-alive job. */
taskPoolStatus = IotTaskPool_TryCancel( IOT_SYSTEM_TASKPOOL,
pMqttConnection->pingreq.job,
NULL );
/* Clean up keep-alive if its job was successfully canceled. Otherwise,
* the executing keep-alive job will clean up itself. */
if( taskPoolStatus == IOT_TASKPOOL_SUCCESS )
{
/* Free the packet */
_getMqttFreePacketFunc( pMqttConnection->pSerializer )( pMqttConnection->pingreq.u.operation.pMqttPacket );
/* Clear data about the keep-alive. */
pMqttConnection->pingreq.u.operation.periodic.ping.keepAliveMs = 0;
pMqttConnection->pingreq.u.operation.pMqttPacket = NULL;
pMqttConnection->pingreq.u.operation.packetSize = 0;
/* Keep-alive is cleaned up; decrement reference count. Since this
* function must be followed with a call to DISCONNECT, a check to
* destroy the connection is not done here. */
pMqttConnection->references--;
IotLogDebug( "(MQTT connection %p) Keep-alive job canceled and cleaned up.",
pMqttConnection );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Copy the function pointers and contexts, as the MQTT connection may be
* modified after the mutex is released. */
disconnectCallback = pMqttConnection->disconnectCallback.function;
pDisconnectCallbackContext = pMqttConnection->disconnectCallback.pCallbackContext;
closeConnection = pMqttConnection->pNetworkInterface->close;
pNetworkConnection = pMqttConnection->pNetworkConnection;
IotMutex_Unlock( &( pMqttConnection->referencesMutex ) );
/* Close the network connection. */
if( closeConnection != NULL )
{
closeStatus = closeConnection( pNetworkConnection );
if( closeStatus == IOT_NETWORK_SUCCESS )
{
IotLogInfo( "(MQTT connection %p) Network connection closed.", pMqttConnection );
}
else
{
IotLogWarn( "(MQTT connection %p) Failed to close network connection, error %d.",
pMqttConnection,
closeStatus );
}
}
else
{
IotLogWarn( "(MQTT connection %p) No network close function was set. Network connection"
" not closed.", pMqttConnection );
}
/* Invoke the disconnect callback. */
if( disconnectCallback != NULL )
{
/* Set the members of the callback parameter. */
callbackParam.mqttConnection = pMqttConnection;
callbackParam.u.disconnectReason = disconnectReason;
disconnectCallback( pDisconnectCallbackContext,
&callbackParam );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/*-----------------------------------------------------------*/
void IotMqtt_ReceiveCallback( IotNetworkConnection_t pNetworkConnection,
void * pReceiveContext )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
_mqttPacket_t incomingPacket = { .u.pMqttConnection = NULL };
/* Cast context to correct type. */
_mqttConnection_t * pMqttConnection = ( _mqttConnection_t * ) pReceiveContext;
/* Read an MQTT packet from the network. */
status = _getIncomingPacket( pNetworkConnection,
pMqttConnection,
&incomingPacket );
if( status == IOT_MQTT_SUCCESS )
{
/* Deserialize the received packet. */
status = _deserializeIncomingPacket( pMqttConnection,
&incomingPacket );
/* Free any buffers allocated for the MQTT packet. */
if( incomingPacket.pRemainingData != NULL )
{
IotMqtt_FreeMessage( incomingPacket.pRemainingData );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Close the network connection on a bad response. */
if( status == IOT_MQTT_BAD_RESPONSE )
{
IotLogError( "(MQTT connection %p) Error processing incoming data. Closing connection.",
pMqttConnection );
_IotMqtt_CloseNetworkConnection( IOT_MQTT_BAD_PACKET_RECEIVED,
pMqttConnection );
}
else
{
EMPTY_ELSE_MARKER;
}
}
/*-----------------------------------------------------------*/
IotMqttError_t IotMqtt_GetIncomingMQTTPacketTypeAndLength( IotMqttPacketInfo_t * pIncomingPacket,
IotMqttGetNextByte_t getNextByte,
void * pNetworkConnection )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
/* Read the packet type, which is the first byte available. */
if( getNextByte( pNetworkConnection, &( pIncomingPacket->type ) ) == IOT_MQTT_SUCCESS )
{
/* Check that the incoming packet type is valid. */
if( _incomingPacketValid( pIncomingPacket->type ) == false )
{
IotLogError( "(MQTT connection) Unknown packet type %02x received.",
pIncomingPacket->type );
status = IOT_MQTT_BAD_RESPONSE;
}
else
{
/* Read the remaining length. */
pIncomingPacket->remainingLength = _IotMqtt_GetRemainingLength_Generic( pNetworkConnection,
getNextByte );
if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
{
status = IOT_MQTT_BAD_RESPONSE;
}
}
}
else
{
status = IOT_MQTT_NETWORK_ERROR;
}
return status;
}
/*-----------------------------------------------------------*/

View file

@ -0,0 +1,645 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_mqtt_subscription.c
* @brief Implements functions that manage subscriptions for an MQTT connection.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Standard includes. */
#include <stdbool.h>
#include <string.h>
/* Error handling include. */
#include "iot_error.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/* Platform layer includes. */
#include "platform/iot_threads.h"
/*-----------------------------------------------------------*/
/**
* @brief First parameter to #_topicMatch.
*/
typedef struct _topicMatchParams
{
const char * pTopicName; /**< @brief The topic name to parse. */
uint16_t topicNameLength; /**< @brief Length of #_topicMatchParams_t.pTopicName. */
bool exactMatchOnly; /**< @brief Whether to allow wildcards or require exact matches. */
} _topicMatchParams_t;
/**
* @brief First parameter to #_packetMatch.
*/
typedef struct _packetMatchParams
{
uint16_t packetIdentifier; /**< Packet identifier to match. */
int32_t order; /**< Order to match. Set to #MQTT_REMOVE_ALL_SUBSCRIPTIONS to ignore. */
} _packetMatchParams_t;
/*-----------------------------------------------------------*/
/**
* @brief Matches a topic name (from a publish) with a topic filter (from a
* subscription).
*
* @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.
* @param[in] pMatch Pointer to a #_topicMatchParams_t.
*
* @return `true` if the arguments match the subscription topic filter; `false`
* otherwise.
*/
static bool _topicMatch( const IotLink_t * pSubscriptionLink,
void * pMatch );
/**
* @brief Matches a packet identifier and order.
*
* @param[in] pSubscriptionLink Pointer to the link member of an #_mqttSubscription_t.
* @param[in] pMatch Pointer to a #_packetMatchParams_t.
*
* @return `true` if the arguments match the subscription's packet info; `false`
* otherwise.
*/
static bool _packetMatch( const IotLink_t * pSubscriptionLink,
void * pMatch );
/*-----------------------------------------------------------*/
static bool _topicMatch( const IotLink_t * pSubscriptionLink,
void * pMatch )
{
IOT_FUNCTION_ENTRY( bool, false );
uint16_t nameIndex = 0, filterIndex = 0;
/* Because this function is called from a container function, the given link
* must never be NULL. */
IotMqtt_Assert( pSubscriptionLink != NULL );
_mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,
pSubscriptionLink,
link );
_topicMatchParams_t * pParam = ( _topicMatchParams_t * ) pMatch;
/* Extract the relevant strings and lengths from parameters. */
const char * pTopicName = pParam->pTopicName;
const char * pTopicFilter = pSubscription->pTopicFilter;
const uint16_t topicNameLength = pParam->topicNameLength;
const uint16_t topicFilterLength = pSubscription->topicFilterLength;
/* Check for an exact match. */
if( topicNameLength == topicFilterLength )
{
status = ( strncmp( pTopicName, pTopicFilter, topicNameLength ) == 0 );
IOT_GOTO_CLEANUP();
}
else
{
EMPTY_ELSE_MARKER;
}
/* If the topic lengths are different but an exact match is required, return
* false. */
if( pParam->exactMatchOnly == true )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
while( ( nameIndex < topicNameLength ) && ( filterIndex < topicFilterLength ) )
{
/* Check if the character in the topic name matches the corresponding
* character in the topic filter string. */
if( pTopicName[ nameIndex ] == pTopicFilter[ filterIndex ] )
{
/* Handle special corner cases as documented by the MQTT protocol spec. */
/* Filter "sport/#" also matches "sport" since # includes the parent level. */
if( nameIndex == topicNameLength - 1 )
{
if( filterIndex == topicFilterLength - 3 )
{
if( pTopicFilter[ filterIndex + 1 ] == '/' )
{
if( pTopicFilter[ filterIndex + 2 ] == '#' )
{
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Filter "sport/+" also matches the "sport/" but not "sport". */
if( nameIndex == topicNameLength - 1 )
{
if( filterIndex == topicFilterLength - 2 )
{
if( pTopicFilter[ filterIndex + 1 ] == '+' )
{
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
/* Check for wildcards. */
if( pTopicFilter[ filterIndex ] == '+' )
{
/* Move topic name index to the end of the current level.
* This is identified by '/'. */
while( nameIndex < topicNameLength && pTopicName[ nameIndex ] != '/' )
{
nameIndex++;
}
/* Increment filter index to skip '/'. */
filterIndex++;
continue;
}
else if( pTopicFilter[ filterIndex ] == '#' )
{
/* Subsequent characters don't need to be checked if the for the
* multi-level wildcard. */
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
/* Any character mismatch other than '+' or '#' means the topic
* name does not match the topic filter. */
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
/* Increment indexes. */
nameIndex++;
filterIndex++;
}
/* If the end of both strings has been reached, they match. */
if( ( nameIndex == topicNameLength ) && ( filterIndex == topicFilterLength ) )
{
IOT_SET_AND_GOTO_CLEANUP( true );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
static bool _packetMatch( const IotLink_t * pSubscriptionLink,
void * pMatch )
{
bool match = false;
/* Because this function is called from a container function, the given link
* must never be NULL. */
IotMqtt_Assert( pSubscriptionLink != NULL );
_mqttSubscription_t * pSubscription = IotLink_Container( _mqttSubscription_t,
pSubscriptionLink,
link );
_packetMatchParams_t * pParam = ( _packetMatchParams_t * ) pMatch;
/* Compare packet identifiers. */
if( pParam->packetIdentifier == pSubscription->packetInfo.identifier )
{
/* Compare orders if order is not MQTT_REMOVE_ALL_SUBSCRIPTIONS. */
if( pParam->order == MQTT_REMOVE_ALL_SUBSCRIPTIONS )
{
match = true;
}
else
{
match = ( ( size_t ) pParam->order ) == pSubscription->packetInfo.order;
}
}
/* If this subscription should be removed, check the reference count. */
if( match == true )
{
/* Reference count must not be negative. */
IotMqtt_Assert( pSubscription->references >= 0 );
/* If the reference count is positive, this subscription cannot be
* removed yet because there are subscription callbacks using it. */
if( pSubscription->references > 0 )
{
match = false;
/* Set the unsubscribed flag. The last active subscription callback
* will remove and clean up this subscription. */
pSubscription->unsubscribed = true;
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
return match;
}
/*-----------------------------------------------------------*/
IotMqttError_t _IotMqtt_AddSubscriptions( _mqttConnection_t * pMqttConnection,
uint16_t subscribePacketIdentifier,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount )
{
IotMqttError_t status = IOT_MQTT_SUCCESS;
size_t i = 0;
_mqttSubscription_t * pNewSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
_topicMatchParams_t topicMatchParams = { .exactMatchOnly = true };
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
for( i = 0; i < subscriptionCount; i++ )
{
/* Check if this topic filter is already registered. */
topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;
topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;
pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
NULL,
_topicMatch,
&topicMatchParams );
if( pSubscriptionLink != NULL )
{
pNewSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
/* The lengths of exactly matching topic filters must match. */
IotMqtt_Assert( pNewSubscription->topicFilterLength == pSubscriptionList[ i ].topicFilterLength );
/* Replace the callback and packet info with the new parameters. */
pNewSubscription->callback = pSubscriptionList[ i ].callback;
pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;
pNewSubscription->packetInfo.order = i;
}
else
{
/* Allocate memory for a new subscription. */
pNewSubscription = IotMqtt_MallocSubscription( sizeof( _mqttSubscription_t ) +
pSubscriptionList[ i ].topicFilterLength );
if( pNewSubscription == NULL )
{
status = IOT_MQTT_NO_MEMORY;
break;
}
else
{
/* Clear the new subscription. */
( void ) memset( pNewSubscription,
0x00,
sizeof( _mqttSubscription_t ) + pSubscriptionList[ i ].topicFilterLength );
/* Set the members of the new subscription and add it to the list. */
pNewSubscription->packetInfo.identifier = subscribePacketIdentifier;
pNewSubscription->packetInfo.order = i;
pNewSubscription->callback = pSubscriptionList[ i ].callback;
pNewSubscription->topicFilterLength = pSubscriptionList[ i ].topicFilterLength;
( void ) memcpy( pNewSubscription->pTopicFilter,
pSubscriptionList[ i ].pTopicFilter,
( size_t ) ( pSubscriptionList[ i ].topicFilterLength ) );
IotListDouble_InsertHead( &( pMqttConnection->subscriptionList ),
&( pNewSubscription->link ) );
}
}
}
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
/* If memory allocation failed, remove all previously added subscriptions. */
if( status != IOT_MQTT_SUCCESS )
{
_IotMqtt_RemoveSubscriptionByTopicFilter( pMqttConnection,
pSubscriptionList,
i );
}
else
{
EMPTY_ELSE_MARKER;
}
return status;
}
/*-----------------------------------------------------------*/
void _IotMqtt_InvokeSubscriptionCallback( _mqttConnection_t * pMqttConnection,
IotMqttCallbackParam_t * pCallbackParam )
{
_mqttSubscription_t * pSubscription = NULL;
IotLink_t * pCurrentLink = NULL, * pNextLink = NULL;
void * pCallbackContext = NULL;
void ( * callbackFunction )( void *,
IotMqttCallbackParam_t * ) = NULL;
_topicMatchParams_t topicMatchParams = { 0 };
/* Set the members of the search parameter. */
topicMatchParams.pTopicName = pCallbackParam->u.message.info.pTopicName;
topicMatchParams.topicNameLength = pCallbackParam->u.message.info.topicNameLength;
topicMatchParams.exactMatchOnly = false;
/* Prevent any other thread from modifying the subscription list while this
* function is searching. */
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
/* Search the subscription list for all matching subscriptions starting at
* the list head. */
while( true )
{
pCurrentLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
pCurrentLink,
_topicMatch,
&topicMatchParams );
/* No subscription found. Exit loop. */
if( pCurrentLink == NULL )
{
break;
}
else
{
EMPTY_ELSE_MARKER;
}
/* Subscription found. Calculate pointer to subscription object. */
pSubscription = IotLink_Container( _mqttSubscription_t, pCurrentLink, link );
/* Subscription validation should not have allowed a NULL callback function. */
IotMqtt_Assert( pSubscription->callback.function != NULL );
/* Increment the subscription's reference count. */
( pSubscription->references )++;
/* Copy the necessary members of the subscription before releasing the
* subscription list mutex. */
pCallbackContext = pSubscription->callback.pCallbackContext;
callbackFunction = pSubscription->callback.function;
/* Unlock the subscription list mutex. */
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
/* Set the members of the callback parameter. */
pCallbackParam->mqttConnection = pMqttConnection;
pCallbackParam->u.message.pTopicFilter = pSubscription->pTopicFilter;
pCallbackParam->u.message.topicFilterLength = pSubscription->topicFilterLength;
/* Invoke the subscription callback. */
callbackFunction( pCallbackContext, pCallbackParam );
/* Lock the subscription list mutex to decrement the reference count. */
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
/* Decrement the reference count. It must still be positive. */
( pSubscription->references )--;
IotMqtt_Assert( pSubscription->references >= 0 );
/* Save the pointer to the next link in case this subscription is freed. */
pNextLink = pCurrentLink->pNext;
/* Remove this subscription if it has no references and the unsubscribed
* flag is set. */
if( pSubscription->unsubscribed == true )
{
/* An unsubscribed subscription should have been removed from the list. */
IotMqtt_Assert( IotLink_IsLinked( &( pSubscription->link ) ) == false );
/* Free subscriptions with no references. */
if( pSubscription->references == 0 )
{
IotMqtt_FreeSubscription( pSubscription );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Move current link pointer. */
pCurrentLink = pNextLink;
}
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
_IotMqtt_DecrementConnectionReferences( pMqttConnection );
}
/*-----------------------------------------------------------*/
void _IotMqtt_RemoveSubscriptionByPacket( _mqttConnection_t * pMqttConnection,
uint16_t packetIdentifier,
int32_t order )
{
_packetMatchParams_t packetMatchParams = { 0 };
/* Set the members of the search parameter. */
packetMatchParams.packetIdentifier = packetIdentifier;
packetMatchParams.order = order;
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
IotListDouble_RemoveAllMatches( &( pMqttConnection->subscriptionList ),
_packetMatch,
( void * ) ( &packetMatchParams ),
IotMqtt_FreeSubscription,
offsetof( _mqttSubscription_t, link ) );
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
}
/*-----------------------------------------------------------*/
void _IotMqtt_RemoveSubscriptionByTopicFilter( _mqttConnection_t * pMqttConnection,
const IotMqttSubscription_t * pSubscriptionList,
size_t subscriptionCount )
{
size_t i = 0;
_mqttSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
_topicMatchParams_t topicMatchParams = { 0 };
/* Prevent any other thread from modifying the subscription list while this
* function is running. */
IotMutex_Lock( &( pMqttConnection->subscriptionMutex ) );
/* Find and remove each topic filter from the list. */
for( i = 0; i < subscriptionCount; i++ )
{
topicMatchParams.pTopicName = pSubscriptionList[ i ].pTopicFilter;
topicMatchParams.topicNameLength = pSubscriptionList[ i ].topicFilterLength;
topicMatchParams.exactMatchOnly = true;
pSubscriptionLink = IotListDouble_FindFirstMatch( &( pMqttConnection->subscriptionList ),
NULL,
_topicMatch,
&topicMatchParams );
if( pSubscriptionLink != NULL )
{
pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
/* Reference count must not be negative. */
IotMqtt_Assert( pSubscription->references >= 0 );
/* Remove subscription from list. */
IotListDouble_Remove( pSubscriptionLink );
/* Check the reference count. This subscription cannot be removed if
* there are subscription callbacks using it. */
if( pSubscription->references > 0 )
{
/* Set the unsubscribed flag. The last active subscription callback
* will remove and clean up this subscription. */
pSubscription->unsubscribed = true;
}
else
{
/* Free a subscription with no references. */
IotMqtt_FreeSubscription( pSubscription );
}
}
else
{
EMPTY_ELSE_MARKER;
}
}
IotMutex_Unlock( &( pMqttConnection->subscriptionMutex ) );
}
/*-----------------------------------------------------------*/
bool IotMqtt_IsSubscribed( IotMqttConnection_t mqttConnection,
const char * pTopicFilter,
uint16_t topicFilterLength,
IotMqttSubscription_t * const pCurrentSubscription )
{
bool status = false;
_mqttSubscription_t * pSubscription = NULL;
IotLink_t * pSubscriptionLink = NULL;
_topicMatchParams_t topicMatchParams = { 0 };
/* Set the members of the search parameter. */
topicMatchParams.pTopicName = pTopicFilter;
topicMatchParams.topicNameLength = topicFilterLength;
topicMatchParams.exactMatchOnly = true;
/* Prevent any other thread from modifying the subscription list while this
* function is running. */
IotMutex_Lock( &( mqttConnection->subscriptionMutex ) );
/* Search for a matching subscription. */
pSubscriptionLink = IotListDouble_FindFirstMatch( &( mqttConnection->subscriptionList ),
NULL,
_topicMatch,
&topicMatchParams );
/* Check if a matching subscription was found. */
if( pSubscriptionLink != NULL )
{
pSubscription = IotLink_Container( _mqttSubscription_t, pSubscriptionLink, link );
/* Copy the matching subscription to the output parameter. */
if( pCurrentSubscription != NULL )
{
pCurrentSubscription->pTopicFilter = pTopicFilter;
pCurrentSubscription->topicFilterLength = topicFilterLength;
pCurrentSubscription->qos = IOT_MQTT_QOS_0;
pCurrentSubscription->callback = pSubscription->callback;
}
else
{
EMPTY_ELSE_MARKER;
}
status = true;
}
else
{
EMPTY_ELSE_MARKER;
}
IotMutex_Unlock( &( mqttConnection->subscriptionMutex ) );
return status;
}
/*-----------------------------------------------------------*/
/* Provide access to internal functions and variables if testing. */
#if IOT_BUILD_TESTS == 1
#include "iot_test_access_mqtt_subscription.c"
#endif

View file

@ -0,0 +1,637 @@
/*
* IoT MQTT V2.1.0
* Copyright (C) 2018 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.
*/
/**
* @file iot_mqtt_validate.c
* @brief Implements functions that validate the structs of the MQTT library.
*/
/* The config header is always included first. */
#include "iot_config.h"
/* Error handling include. */
#include "iot_error.h"
/* MQTT internal include. */
#include "private/iot_mqtt_internal.h"
/**
* @brief Check that an #IotMqttPublishInfo_t is valid.
*
* @param[in] awsIotMqttMode Specifies if this PUBLISH packet is being sent to
* an AWS IoT MQTT server.
* @param[in] maximumPayloadLength Maximum payload length.
* @param[in] pPublishTypeDescription String describing the publish type.
* @param[in] pPublishInfo The #IotMqttPublishInfo_t to validate.
*
* @return `true` if `pPublishInfo` is valid; `false` otherwise.
*/
static bool _validatePublish( bool awsIotMqttMode,
size_t maximumPayloadLength,
const char * pPublishTypeDescription,
const IotMqttPublishInfo_t * pPublishInfo );
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateConnect( const IotMqttConnectInfo_t * pConnectInfo )
{
IOT_FUNCTION_ENTRY( bool, true );
uint16_t maxClientIdLength = MQTT_SERVER_MAX_CLIENTID_LENGTH;
bool enforceMaxClientIdLength = false;
/* Check for NULL. */
if( pConnectInfo == NULL )
{
IotLogError( "MQTT connection information cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that a client identifier was set. */
if( pConnectInfo->pClientIdentifier == NULL )
{
IotLogError( "Client identifier must be set." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for a zero-length client identifier. Zero-length client identifiers
* are not allowed with clean sessions. */
if( pConnectInfo->clientIdentifierLength == 0 )
{
IotLogWarn( "A zero-length client identifier was provided." );
if( pConnectInfo->cleanSession == true )
{
IotLogError( "A zero-length client identifier cannot be used with a clean session." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that the number of persistent session subscriptions is valid. */
if( pConnectInfo->pPreviousSubscriptions != NULL )
{
if( _IotMqtt_ValidateSubscriptionList( IOT_MQTT_SUBSCRIBE,
pConnectInfo->awsIotMqttMode,
pConnectInfo->pPreviousSubscriptions,
pConnectInfo->previousSubscriptionCount ) == false )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* If will info is provided, check that it is valid. */
if( pConnectInfo->pWillInfo != NULL )
{
if( _IotMqtt_ValidateLwtPublish( pConnectInfo->awsIotMqttMode,
pConnectInfo->pWillInfo ) == false )
{
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* The AWS IoT MQTT service enforces a client ID length limit. */
if( pConnectInfo->awsIotMqttMode == true )
{
maxClientIdLength = AWS_IOT_MQTT_SERVER_MAX_CLIENTID_LENGTH;
enforceMaxClientIdLength = true;
}
if( pConnectInfo->clientIdentifierLength > maxClientIdLength )
{
if( enforceMaxClientIdLength == false )
{
IotLogWarn( "A client identifier length of %hu is longer than %hu, "
"which is "
"the longest client identifier a server must accept.",
pConnectInfo->clientIdentifierLength,
maxClientIdLength );
}
else
{
IotLogError( "A client identifier length of %hu exceeds the "
"maximum supported length of %hu.",
pConnectInfo->clientIdentifierLength,
maxClientIdLength );
IOT_SET_AND_GOTO_CLEANUP( false );
}
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
static bool _validatePublish( bool awsIotMqttMode,
size_t maximumPayloadLength,
const char * pPublishTypeDescription,
const IotMqttPublishInfo_t * pPublishInfo )
{
IOT_FUNCTION_ENTRY( bool, true );
/* Check for NULL. */
if( pPublishInfo == NULL )
{
IotLogError( "Publish information cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check topic name for NULL or zero-length. */
if( pPublishInfo->pTopicName == NULL )
{
IotLogError( "Publish topic name must be set." );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pPublishInfo->topicNameLength == 0 )
{
IotLogError( "Publish topic name length cannot be 0." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pPublishInfo->payloadLength != 0 )
{
if( pPublishInfo->payloadLength > maximumPayloadLength )
{
IotLogError( "%s payload size of %zu exceeds maximum length of %zu.",
pPublishTypeDescription,
pPublishInfo->payloadLength,
maximumPayloadLength );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
if( pPublishInfo->pPayload == NULL )
{
IotLogError( "Nonzero payload length cannot have a NULL payload." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for a valid QoS. */
if( pPublishInfo->qos != IOT_MQTT_QOS_0 )
{
if( pPublishInfo->qos != IOT_MQTT_QOS_1 )
{
IotLogError( "Publish QoS must be either 0 or 1." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check the retry parameters. */
if( pPublishInfo->retryLimit > 0 )
{
if( pPublishInfo->retryMs == 0 )
{
IotLogError( "Publish retry time must be positive." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for compatibility with AWS IoT MQTT server. */
if( awsIotMqttMode == true )
{
/* Check for retained message. */
if( pPublishInfo->retain == true )
{
IotLogError( "AWS IoT does not support retained publish messages." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check topic name length. */
if( pPublishInfo->topicNameLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )
{
IotLogError( "AWS IoT does not support topic names longer than %d bytes.",
AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidatePublish( bool awsIotMqttMode,
const IotMqttPublishInfo_t * pPublishInfo )
{
size_t maximumPayloadLength = MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;
if( awsIotMqttMode == true )
{
maximumPayloadLength = AWS_IOT_MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;
}
else
{
EMPTY_ELSE_MARKER;
}
return _validatePublish( awsIotMqttMode,
maximumPayloadLength,
"Publish",
pPublishInfo );
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateLwtPublish( bool awsIotMqttMode,
const IotMqttPublishInfo_t * pLwtPublishInfo )
{
return _validatePublish( awsIotMqttMode,
MQTT_SERVER_MAX_LWT_PAYLOAD_LENGTH,
"LWT",
pLwtPublishInfo );
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateOperation( IotMqttOperation_t operation )
{
IOT_FUNCTION_ENTRY( bool, true );
/* Check for NULL. */
if( operation == NULL )
{
IotLogError( "Operation reference cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that reference is waitable. */
if( ( operation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) != IOT_MQTT_FLAG_WAITABLE )
{
IotLogError( "Operation is not waitable." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/
bool _IotMqtt_ValidateSubscriptionList( IotMqttOperationType_t operation,
bool awsIotMqttMode,
const IotMqttSubscription_t * pListStart,
size_t listSize )
{
IOT_FUNCTION_ENTRY( bool, true );
size_t i = 0;
uint16_t j = 0;
const IotMqttSubscription_t * pListElement = NULL;
/* Operation must be either subscribe or unsubscribe. */
IotMqtt_Assert( ( operation == IOT_MQTT_SUBSCRIBE ) ||
( operation == IOT_MQTT_UNSUBSCRIBE ) );
/* Check for empty list. */
if( pListStart == NULL )
{
IotLogError( "Subscription list pointer cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
if( listSize == 0 )
{
IotLogError( "Empty subscription list." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* AWS IoT supports at most 8 topic filters in a single SUBSCRIBE packet. */
if( awsIotMqttMode == true )
{
if( listSize > AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE )
{
IotLogError( "AWS IoT does not support more than %d topic filters per "
"subscription request.",
AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
for( i = 0; i < listSize; i++ )
{
pListElement = &( pListStart[ i ] );
/* Check for a valid QoS and callback function when subscribing. */
if( operation == IOT_MQTT_SUBSCRIBE )
{
if( pListElement->qos != IOT_MQTT_QOS_0 )
{
if( pListElement->qos != IOT_MQTT_QOS_1 )
{
IotLogError( "Subscription QoS must be either 0 or 1." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
if( pListElement->callback.function == NULL )
{
IotLogError( "Callback function must be set." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check subscription topic filter. */
if( pListElement->pTopicFilter == NULL )
{
IotLogError( "Subscription topic filter cannot be NULL." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
if( pListElement->topicFilterLength == 0 )
{
IotLogError( "Subscription topic filter length cannot be 0." );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check for compatibility with AWS IoT MQTT server. */
if( awsIotMqttMode == true )
{
/* Check topic filter length. */
if( pListElement->topicFilterLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )
{
IotLogError( "AWS IoT does not support topic filters longer than %d bytes.",
AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Check that the wildcards '+' and '#' are being used correctly. */
for( j = 0; j < pListElement->topicFilterLength; j++ )
{
switch( pListElement->pTopicFilter[ j ] )
{
/* Check that the single level wildcard '+' is used correctly. */
case '+':
/* Unless '+' is the first character in the filter, it must be preceded by '/'. */
if( j > 0 )
{
if( pListElement->pTopicFilter[ j - 1 ] != '/' )
{
IotLogError( "Invalid topic filter %.*s -- '+' must be preceded by '/'.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
/* Unless '+' is the last character in the filter, it must be succeeded by '/'. */
if( j < pListElement->topicFilterLength - 1 )
{
if( pListElement->pTopicFilter[ j + 1 ] != '/' )
{
IotLogError( "Invalid topic filter %.*s -- '+' must be succeeded by '/'.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
break;
/* Check that the multi-level wildcard '#' is used correctly. */
case '#':
/* '#' must be the last character in the filter. */
if( j != pListElement->topicFilterLength - 1 )
{
IotLogError( "Invalid topic filter %.*s -- '#' must be the last character.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
/* Unless '#' is standalone, it must be preceded by '/'. */
if( pListElement->topicFilterLength > 1 )
{
if( pListElement->pTopicFilter[ j - 1 ] != '/' )
{
IotLogError( "Invalid topic filter %.*s -- '#' must be preceded by '/'.",
pListElement->topicFilterLength,
pListElement->pTopicFilter );
IOT_SET_AND_GOTO_CLEANUP( false );
}
else
{
EMPTY_ELSE_MARKER;
}
}
else
{
EMPTY_ELSE_MARKER;
}
break;
default:
break;
}
}
}
IOT_FUNCTION_EXIT_NO_CLEANUP();
}
/*-----------------------------------------------------------*/