1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-15 09:33:50 +01:00
RIOT/sys/include/net/unicoap/message.h
2025-07-07 17:27:39 +02:00

1362 lines
45 KiB
C

/*
* Copyright (C) 2024-2025 Carl Seifert
* Copyright (C) 2024-2025 TU Dresden
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for
* more details.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "iolist.h"
#include "net/unicoap/constants.h"
#include "net/unicoap/options.h"
/**
* @addtogroup net_unicoap_message
* @{
*/
/**
* @file
* @brief CoAP Message API
* @author Carl Seifert <carl.seifert1@mailbox.tu-dresden.de>
*/
#ifdef __cplusplus
extern "C" {
#endif
/* MARK: - CoAP message */
/**
* @name CoAP message
* @{
*/
/**
* @brief A type indicating how a message's payload is represented
*/
typedef enum {
/**
* @brief Contiguous payload buffer
*/
UNICOAP_PAYLOAD_CONTIGUOUS = 0,
/**
* @brief @ref iolist_t payload vector
*/
UNICOAP_PAYLOAD_NONCONTIGUOUS = 1
} unicoap_payload_representation_t;
/**
* @brief Generic CoAP message
*
* The payload of this message can either be represented as a vector (@ref iolist_t) or a buffer.
* You use @ref unicoap_message_payload_get and @ref unicoap_message_payload_set to work with
* contiguous payloads. For iolists, refer to @ref unicoap_message_payload_get_chunks,
* @ref unicoap_message_payload_set_chunks, and @ref unicoap_message_payload_append_chunk.
*
* Regardless of the representation, you can always use
* @ref unicoap_message_payload_get_size. For vectored payload, this entails an overhead of `O(n)`.
* If you just want to check whether there is _any_ payload, use
* @ref unicoap_message_payload_is_empty.
* You can also make the payload contiguous by calling @ref unicoap_message_payload_make_contiguous,
* which mutates the
* message struct. If you purely want to obtain a contiguous representation stored in another
* buffer, use @ref unicoap_message_payload_copy.
*
*/
typedef struct {
/**
* @brief Message options
*
* Key-value entries akin to HTTP headers
*
* @see @ref net_unicoap_options
*/
unicoap_options_t* options;
/**
* @brief Payload representation
*/
union {
/**
* @brief Contiguous payload
*/
struct {
/**
* @brief Message payload
*
* Access via @ref unicoap_message_payload_get and @ref unicoap_message_payload_set.
*/
uint8_t* payload;
/**
* @brief Size of message payload
*
* Number of bytes in @ref unicoap_message_t::payload. Access via
* @ref unicoap_message_payload_get_size.
*/
size_t payload_size;
};
/**
* @brief Noncontiguous payload
*
* Use one of these APIs:
* - @ref unicoap_message_payload_get_chunks
* - @ref unicoap_message_payload_set_chunks
* - @ref unicoap_message_payload_append_chunk
*/
iolist_t* payload_chunks;
};
union {
/**
* @brief CoAP message code
*
* A message code is divided into the class bits and detail bits, where `C` is a class bit
* and `D` is a detail bit.
* The class bits form a single-digit number, the detail bits represents a two-digit number.
* Hence, message codes are written as `c.dd` where `c` is the unsigned integer encoded in
* the three `C` bits and `dd` is the beforementioned two-digit number encoded in the five
* `D` bits.
*/
uint8_t code;
/* Note:
* Method, status, and signal are guaranteed to have the same size as uint8_t,
* see static_asserts in definitions.h
*/
/**
* @brief CoAP request method
*
* @pre Check if this is a request message before reading this property using
* @ref unicoap_message_is_request.
*/
unicoap_method_t method;
/**
* @brief CoAP response status
*
* @pre Check if this is a response message before reading this property using
* @ref unicoap_message_is_response.
*/
unicoap_status_t status;
/**
* @brief CoAP signal
*
* Check if this is a signaling message before reading this property using
* @ref unicoap_message_is_signal.
*/
unicoap_signal_t signal;
};
/**
* @brief A value indicating how the payload is represented
*
* If the payload is noncontiguous, the @ref unicoap_message_t::payload_chunks property must be
* accessed. Otherwise, only @ref unicoap_message_t::payload must be read and written to.
*/
unicoap_payload_representation_t payload_representation : 1;
} unicoap_message_t;
/**
* @brief Retrieves contiguous message payload, if available
* @memberof unicoap_message_t
* @pre Payload representation must be @ref UNICOAP_PAYLOAD_CONTIGUOUS
*
* @param message Message to retrieve payload buffer from
*
* @returns Payload buffer pointer
*/
static inline uint8_t* unicoap_message_payload_get(unicoap_message_t* message)
{
assert(message->payload_representation == UNICOAP_PAYLOAD_CONTIGUOUS);
return message->payload;
}
/**
* @brief Assigns the given message a contiguous payload
* @memberof unicoap_message_t
*
* @param message Message
* @param[in] payload Payload buffer
* @param size Size of @p payload in bytes
*/
static inline void unicoap_message_payload_set(unicoap_message_t* message, uint8_t* payload,
size_t size)
{
message->payload_representation = UNICOAP_PAYLOAD_CONTIGUOUS;
message->payload = payload;
message->payload_size = size;
}
/**
* @brief Retrieves noncontiguous message payload, if available
* @memberof unicoap_message_t
* @pre Payload representation must be @ref UNICOAP_PAYLOAD_NONCONTIGUOUS
*
* @param message Message to retrieve payload vector from
*
* @returns Payload vector pointer
*/
static inline iolist_t* unicoap_message_payload_get_chunks(unicoap_message_t* message)
{
assert(message->payload_representation == UNICOAP_PAYLOAD_NONCONTIGUOUS);
return message->payload_chunks;
}
/**
* @brief Assigns the given message a noncontiguous payload
* @memberof unicoap_message_t
*
* @param message Message
* @param[in] chunks Payload vector
*/
static inline void unicoap_message_payload_set_chunks(unicoap_message_t* message, iolist_t* chunks)
{
message->payload_representation = UNICOAP_PAYLOAD_NONCONTIGUOUS;
message->payload_chunks = chunks;
}
/**
* @brief Appends a payload chunk to a message
* @memberof unicoap_message_t
* @pre Message's payload must be noncontiguous
*
* @param message Message
* @param[in] chunk Payload chunk
*/
void unicoap_message_payload_append_chunk(unicoap_message_t* message, iolist_t* chunk);
/**
* @brief Retrieves payload size, regardless of payload representation
* @memberof unicoap_message_t
* @param message Message whose payload size to compute (if noncontiguous payload) or
* retrieve (contiguous payload).
* @returns Payload size in bytes
*
* @note This function calls @ref iolist_size if payload is noncontiguous, thus iterates over all
* iolist elements. The complexity is $O(n)$.
*/
static inline size_t unicoap_message_payload_get_size(const unicoap_message_t* message)
{
return message->payload_representation == UNICOAP_PAYLOAD_NONCONTIGUOUS ?
iolist_size(message->payload_chunks) :
message->payload_size;
}
/**
* @brief Determines whether message has any payload
* @memberof unicoap_message_t
* @param message Message with payload in question
* @returns A boolean indicating whether any payload bytes are present
*
* The given message may still have a buffer or iolist vector associated, yet this function returns
* `false` if the buffer or vector is empty.
*/
bool unicoap_message_payload_is_empty(const unicoap_message_t* message);
/**
* @brief Copies payload into given buffer
* @memberof unicoap_message_t
*
* @param message Message whose payload to copy
* @param[in,out] buffer Destination payload buffer
* @param capacity Capacity of @p buffer in bytes
*
* @returns Payload size in bytes, `-ENOBUFS` if given buffer is too small
*
* You can call this method regardless of whether payload is stored contiguously or noncontiguously.
*/
ssize_t unicoap_message_payload_copy(const unicoap_message_t* message, uint8_t* buffer,
size_t capacity);
/**
* @brief Copies noncontiguous payload into contiguous storage buffer
* @memberof unicoap_message_t
* @pre None, if payload is already contiguous, this method does not copy.
* @param[in,out] message Message whose payload to make contiguous
* @param[in,out] buffer Destination payload buffer
* @param capacity Capacity of @p buffer in bytes
*
* @returns Payload size in bytes, `-ENOBUFS` if given buffer is too small
*/
ssize_t unicoap_message_payload_make_contiguous(unicoap_message_t* message, uint8_t* buffer,
size_t capacity);
/**
* @brief Properties of a CoAP message
*/
typedef struct {
/** @brief CoAP token used to correlate requests to responses */
uint8_t* token;
/** @brief Length of @ref unicoap_message_properties_t::token */
uint8_t token_length : UNICOAP_TOKEN_LENGTH_FIXED_WIDTH;
/**
* @brief Determines if the corresponding message is considered an `Observe` registration
*
* This property avoids the need to read the `Observe` option every time this value is needed
*/
bool is_registration : 1;
/**
* @brief Determines if the corresponding message is considered an `Observe` notification
*
* This property avoids the need to read the `Observe` option every time this value is needed
*/
bool is_notification : 1;
/** @brief RFC 7252 only properties */
struct {
/** @brief RFC 7252 message type */
unicoap_rfc7252_message_type_t type : UNICOAP_RFC7252_MESSAGE_TYPE_FIXED_WIDTH;
/** @brief RFC 7252 message ID */
uint16_t id;
} rfc7252;
/** @brief CoAP message token */
} unicoap_message_properties_t;
/**
* @brief Returns a null-terminated label for the CoAP over UDP/DTLS message type
*
* @param type CoAP over UDP/DTLS message.
*
* @returns Message type label, never `NULL`
* @retval `"NON"` A non-confirmable message
* @retval `"CON"` A confirmable message
* @retval `"ACK"` An acknowledgement message
* @retval `"RST"` A reset message
* @retval `"?"` Unknown message type
*/
const char* unicoap_string_from_rfc7252_type(unicoap_rfc7252_message_type_t type);
/**
* @brief Retrieves options storage buffer
* @memberof unicoap_message_t
* @param[in] message Message
* @returns Pointer to first byte of storage buffer, can be `NULL`
*/
static inline uint8_t* unicoap_message_options_data(const unicoap_message_t* message)
{
return message->options ? message->options->entries->data : NULL;
}
/**
* @brief Retrieves total size of options in buffer
* @memberof unicoap_message_t
* @param[in] message Message
* @returns Size of options in buffer in bytes
*/
static inline size_t unicoap_message_options_size(const unicoap_message_t* message)
{
return message->options ? message->options->storage_size : 0;
}
/** @} */
/* MARK: - CoAP code */
/**
* @name CoAP code
* @{
*/
/**
* @brief Reads the code class number encoded in the given code
*
* A message code is divided into the three class bits and five detail bits.
* The class bits form a single-digit number, the detail bits represents a two-digit number. Hence,
* message codes are written as `c.dd`. In the following listing, `C` is a class bit and `D` is a
* detail bit. The `C` bits represent the class `c`, and the `D` bits form the detail number `d`.
* ```
* Bit
* 7 6 5 4 3 2 1 0
* [ C C C | D D D D D ]
* ```
*
* @param code Message code
* @return Code class number
*
* @see @ref unicoap_code_detail
*/
static inline uint8_t unicoap_code_class(uint8_t code)
{
return code >> 5;
}
/**
* @brief Reads the code detail number encoded in the given code
*
* A message code is divided into the three class bits and five detail bits.
* The class bits form a single-digit number, the detail bits represents a two-digit number. Hence,
* message codes are written as `c.dd`. In the following listing, `C` is a class bit and `D` is a
* detail bit. The `C` bits represent the class `c`, and the `D` bits form the detail number `d`.
* ```
* Bit
* 7 6 5 4 3 2 1 0
* [ C C C | D D D D D ]
* ```
*
* @param code Message code
* @return Code detail number
*
* @see @ref unicoap_code_class
*/
static inline uint8_t unicoap_code_detail(uint8_t code)
{
return code & 0x1f;
}
/**
* @brief Prints a `c.dd` class-detail string into the given buffer.
* @pre @p string must have a capacity of at least 4 characters
* @param code Message code
* @param[out] string Buffer to write string into
*
*/
int unicoap_print_code(uint8_t code, char* string);
/**
* @brief Returns label for given CoAP message code
*
* @param code CoAP message code
*
* @returns Label, such as `"POST"`, `"Abort"`, or `"Bad Request"`
* @returns `"?"` if code is unknown
*/
const char* unicoap_string_from_code(uint8_t code);
/**
* @brief Returns label for given CoAP message code class
*
* @param code CoAP message code
*
* @returns Label, `"REQ"`, `"RES"`, `"SIGNAL"`, or `"EMPTY"`
* @returns `"?"` if code class is unknown
*/
const char* unicoap_string_from_code_class(uint8_t code);
/** @} */
/* MARK: - Message initializers */
/**
* @name Message initializers
* @{
*/
/**
* @brief Initializes message with no payload and no options
* @memberof unicoap_message_t
* @param code Message code
* @param[in,out] message Pre-allocated message structure
* @pre @p message is allocated
*/
static inline void unicoap_message_init_empty(unicoap_message_t* message, uint8_t code)
{
message->code = code;
message->options = NULL;
message->payload = NULL;
message->payload_size = 0;
}
/**
* @brief Creates message with no payload and no options
* @param code Message code
* @return Message structure
*/
static inline unicoap_message_t unicoap_message_alloc_empty(uint8_t code)
{
return (unicoap_message_t){ .code = code };
}
/**
* @brief Initializes message with payload but no options
* @memberof unicoap_message_t
* @param[in,out] message Pre-allocated message structure
* @param code Message code
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @pre @p message is allocated
*/
static inline void unicoap_message_init(unicoap_message_t* message, uint8_t code, uint8_t* payload,
size_t payload_size)
{
message->code = code;
message->options = NULL;
message->payload = payload;
message->payload_size = payload_size;
}
/**
* @brief Creates message with no payload and no options
* @param code Message code
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @return Message structure
*/
static inline unicoap_message_t unicoap_message_alloc(uint8_t code, uint8_t* payload,
size_t payload_size)
{
return (unicoap_message_t){ .code = code, .payload = payload, .payload_size = payload_size };
}
/**
* @brief Initializes message with payload from null-terminated UTF-8 string but no options
* @memberof unicoap_message_t
*
* @param[in,out] message Pre-allocated message structure
* @param code Message code
* @param[in] payload Payload string (nullable), must be null-terminated
* @pre @p message is allocated
*/
static inline void unicoap_message_init_string(unicoap_message_t* message, uint8_t code,
const char* payload)
{
message->code = code;
message->options = NULL;
message->payload = (uint8_t*)payload;
message->payload_size = payload ? strlen(payload) : 0;
}
/**
* @brief Creates message with payload from null-terminated but no options
* @param code Message code
* @param[in] payload Payload string (nullable), must be null-terminated
* @return Message structure
*/
static inline unicoap_message_t unicoap_message_alloc_string(uint8_t code, const char* payload)
{
return (unicoap_message_t){ .code = code,
.payload = (uint8_t*)payload,
.payload_size = payload ? strlen(payload) : 0 };
}
/**
* @brief Initializes message with byte payload and options
* @memberof unicoap_message_t
*
* @param[in,out] message Pre-allocated message structure
* @param code Message code
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @pre @p message is allocated
* @pre @p options is allocated
*/
static inline void unicoap_message_init_with_options(unicoap_message_t* message, uint8_t code,
uint8_t* payload, size_t payload_size,
unicoap_options_t* options)
{
message->code = code;
message->options = options;
message->payload = payload;
message->payload_size = payload_size;
}
/**
* @brief Creates message with byte payload and options
* @param code Message code
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @return Message structure
* @pre @p options is allocated
*/
static inline unicoap_message_t unicoap_message_alloc_with_options(uint8_t code, uint8_t* payload,
size_t payload_size,
unicoap_options_t* options)
{
return (unicoap_message_t){
.code = code, .options = options, .payload = payload, .payload_size = payload_size
};
}
/**
* @brief Initializes message with payload from null-terminated UTF-8 string and options
* @memberof unicoap_message_t
*
* @param[in,out] message Pre-allocated message structure
* @param code Message code
* @param[in] payload Payload string (nullable), must be null-terminated
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @pre @p message is allocated
* @pre @p options is allocated
*/
static inline
void unicoap_message_init_string_with_options(unicoap_message_t* message, uint8_t code,
const char* payload,
unicoap_options_t* options)
{
unicoap_message_init_with_options(message, code, (uint8_t*)payload,
payload ? strlen(payload) : 0, options);
}
/**
* @brief Creates message with payload from null-terminated UTF-8 string and options
* @param code Message code
* @param[in] payload Payload string (nullable), must be null-terminated
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @returns Message structure
* @pre @p options is allocated
*/
static inline
unicoap_message_t unicoap_message_alloc_string_with_options(uint8_t code,
const char* payload,
unicoap_options_t* options)
{
return (unicoap_message_t){ .code = code,
.options = options,
.payload = (uint8_t*)payload,
.payload_size = payload ? strlen(payload) : 0 };
}
/** @} */
/* MARK: - Signaling messages */
/**
* @name Signaling messages
* @{
*/
/**
* @brief Determines if the given message's code is a a signaling code
*
* This function internally reads the code class encoded in the code's upper three bits.
*
* @param code Message code
*
* @return A boolean value indicating whether the given message is a signal
*/
static inline bool unicoap_message_is_signal(uint8_t code)
{
return unicoap_code_class(code) == UNICOAP_CODE_CLASS_SIGNAL;
}
/**
* @brief Retrieves signal code from given signal message
* @memberof unicoap_message_t
*
* @param[in] message CoAP signal
* @returns Message code as @ref unicoap_signal_t
*/
static inline unicoap_signal_t unicoap_message_get_signal(const unicoap_message_t* message)
{
return message->signal;
}
/**
* @brief Sets signal code of given signal message
* @memberof unicoap_message_t
*
* @param[in,out] message CoAP signal
* @param signal Signal code
*/
static inline void unicoap_message_set_signal(unicoap_message_t* message, unicoap_signal_t signal)
{
message->signal = signal;
}
/**
* @brief Returns label for given CoAP signal code
*
* @param signal CoAP signal code
*
* @returns Label, such as `"Ping"`, `"Abort"`, or `"CSM"`
* @returns `"?"` if code is unknown
*/
const char* unicoap_string_from_signal(unicoap_signal_t signal);
/** @} */
/* MARK: - Request messages */
/**
* @name Request messages
* @{
*/
/**
* @brief Determines if the the given message's code is a a request code
*
* This function internally reads the code class encoded in the code's upper three bits.
*
* @param code Message code
* @return A boolean value indicating whether the given message is a request
*/
static inline bool unicoap_message_is_request(uint8_t code)
{
return unicoap_code_class(code) == UNICOAP_CODE_CLASS_REQUEST;
}
/**
* @brief Obtains request method from the given message's code
* @memberof unicoap_message_t
*
* @param[in] request Request message, pre-initialized
* @returns Message code as @ref unicoap_method_t
*/
static inline unicoap_method_t unicoap_request_get_method(const unicoap_message_t* request)
{
return request->method;
}
/**
* @brief Sets request method
* @memberof unicoap_message_t
*
* @param[in,out] request Request message, pre-allocated
* @param method Request method to set
* @pre @p request is allocated
*/
static inline void unicoap_request_set_method(unicoap_message_t* request, unicoap_method_t method)
{
request->method = method;
}
/**
* @brief Obtains label for given request method
*
* Returns `"GET"`, `"PUT"`, `"POST"`, ...
*
* @param method Request method to return label for
* @returns String label
*/
const char* unicoap_string_from_method(unicoap_method_t method);
/**
* @brief Initializes request with no payload and no options
* @memberof unicoap_message_t
*
* @param method Request method
* @param[in,out] request Pre-allocated request structure
* @pre @p request is allocated
*/
static inline void unicoap_request_init_empty(unicoap_message_t* request, unicoap_method_t method)
{
unicoap_message_init_empty(request, (uint8_t)method);
}
/**
* @brief Creates request with no payload and no options
* @param method Request method
* @return Request structure
*/
static inline unicoap_message_t unicoap_request_alloc_empty(unicoap_method_t method)
{
return unicoap_message_alloc_empty((uint8_t)method);
}
/**
* @brief Initializes request with payload but no options
* @memberof unicoap_message_t
*
* @param[in,out] request Pre-allocated request structure
* @param method Request method
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @pre @p request is allocated
*/
static inline void unicoap_request_init(unicoap_message_t* request, unicoap_method_t method,
uint8_t* payload, size_t payload_size)
{
unicoap_message_init(request, (uint8_t)method, payload, payload_size);
}
/**
* @brief Creates request with no payload and no options
* @param method Request method
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @return Request structure
*/
static inline unicoap_message_t unicoap_request_alloc(unicoap_method_t method, uint8_t* payload,
size_t payload_size)
{
return unicoap_message_alloc((uint8_t)method, payload, payload_size);
}
/**
* @brief Initializes request with payload from null-terminated UTF-8 string but no options
* @memberof unicoap_message_t
*
* @param[in,out] request Pre-allocated request structure
* @param method Request method
* @param[in] payload Payload string (nullable), must be null-terminated
* @pre @p request is allocated
*/
static inline void unicoap_request_init_string(unicoap_message_t* request, unicoap_method_t method,
const char* payload)
{
unicoap_message_init_string(request, (uint8_t)method, payload);
}
/**
* @brief Creates request with payload from null-terminated UTF-8 string but no options
* @param method Request method
* @param[in] payload Payload string (nullable), must be null-terminated
* @return Request structure
*/
static inline unicoap_message_t unicoap_request_alloc_string(unicoap_method_t method,
const char* payload)
{
return unicoap_message_alloc_string((uint8_t)method, payload);
}
/**
* @brief Initializes request with byte payload and options
* @memberof unicoap_message_t
*
* @param[in,out] request Pre-allocated request structure
* @param method Request method
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @pre @p request is allocated
* @pre @p options is allocated
*/
static inline void unicoap_request_init_with_options(unicoap_message_t* request,
unicoap_method_t method,
uint8_t* payload, size_t payload_size,
unicoap_options_t* options)
{
unicoap_message_init_with_options(request, (uint8_t)method, payload, payload_size, options);
}
/**
* @brief Creates request with byte payload and options
* @param method Request method
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @return Request structure
* @pre @p options is allocated
*/
static inline unicoap_message_t unicoap_request_alloc_with_options(unicoap_method_t method,
uint8_t* payload,
size_t payload_size,
unicoap_options_t* options)
{
return unicoap_message_alloc_with_options((uint8_t)method, payload, payload_size, options);
}
/**
* @brief Initializes request with payload from null-terminated UTF-8 string and options
* @memberof unicoap_message_t
*
* @param[in,out] request Pre-allocated request structure
* @param method Request method
* @param[in] payload Payload string (nullable), must be null-terminated
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @pre @p request is allocated
* @pre @p options is allocated
*/
static inline void unicoap_request_init_string_with_options(unicoap_message_t* request,
unicoap_method_t method,
const char* payload,
unicoap_options_t* options)
{
unicoap_message_init_string_with_options(request, (uint8_t)method, payload, options);
}
/**
* @brief Creates request with payload from null-terminated UTF-8 string and options
* @param method Request method
* @param[in] payload Payload string (nullable), must be null-terminated
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @return Request structure
* @pre @p options is allocated
*/
static inline
unicoap_message_t unicoap_request_alloc_string_with_options(unicoap_method_t method,
const char* payload,
unicoap_options_t* options)
{
return unicoap_message_alloc_string_with_options((uint8_t)method, payload, options);
}
/** @} */
/* MARK: - Response messages */
/**
* @name Response messages
* @{
*/
/**
* @brief Determines if the the given message's code is a a response code
*
* This function internally reads the code class encoded in the code's upper three bits.
*
* @param code Message code
*
* @return A boolean value indicating whether the given message is a response
*/
bool unicoap_message_is_response(uint8_t code);
/**
* @brief Obtains response status from the given message's code
* @memberof unicoap_message_t
*
* @param[in] response Response message, pre-initialized
* @returns Message code as @ref unicoap_status_t
*/
static inline unicoap_status_t unicoap_response_get_status(const unicoap_message_t* response)
{
return response->status;
}
/**
* @brief Sets response status
* @memberof unicoap_message_t
*
* @param[in,out] response Response message, pre-allocated
* @param status Response status to set
* @pre @p response is allocated
*/
static inline void unicoap_response_set_status(unicoap_message_t* response, unicoap_status_t status)
{
response->status = status;
}
/**
* @brief Generates short descriptive label from CoAP status code
* @param status CoAP status code
* @returns Status string or `"?"` if status code is unknown
*/
const char* unicoap_string_from_status(unicoap_status_t status);
/**
* @brief Initializes response with no payload and no options
* @memberof unicoap_message_t
*
* @param status Response status
* @param[in,out] response Pre-allocated response structure
* @pre @p response is allocated
*/
static inline void unicoap_response_init_empty(unicoap_message_t* response, unicoap_status_t status)
{
unicoap_message_init_empty(response, (uint8_t)status);
}
/**
* @brief Creates response with no payload and no options
* @param status Response status
* @return Response structure
*/
static inline unicoap_message_t unicoap_response_alloc_empty(unicoap_status_t status)
{
return unicoap_message_alloc_empty((uint8_t)status);
}
/**
* @brief Initializes response with payload but no options
* @memberof unicoap_message_t
*
* @param[in,out] response Pre-allocated response structure
* @param status Response status
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @pre @p response is allocated
*/
static inline void unicoap_response_init(unicoap_message_t* response, unicoap_status_t status,
uint8_t* payload, size_t payload_size)
{
unicoap_message_init(response, (uint8_t)status, payload, payload_size);
}
/**
* @brief Creates response with no payload and no options
* @param status Response status
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @return Response structure
*/
static inline unicoap_message_t unicoap_response_alloc(unicoap_status_t status, uint8_t* payload,
size_t payload_size)
{
return unicoap_message_alloc((uint8_t)status, payload, payload_size);
}
/**
* @brief Initializes response with payload from null-terminated UTF-8 string but no options
* @memberof unicoap_message_t
*
* @param[in,out] response Pre-allocated response structure
* @param status Response status
* @param[in] payload Payload string (nullable), must be null-terminated
* @pre @p response is allocated
*/
static inline void unicoap_response_init_string(unicoap_message_t* response,
unicoap_status_t status, const char* payload)
{
unicoap_message_init_string(response, (uint8_t)status, payload);
}
/**
* @brief Creates response with payload from null-terminated UTF-8 string but no options
* @param status Response status
* @param[in] payload Payload string (nullable), must be null-terminated
* @return Response structure
*/
static inline unicoap_message_t unicoap_response_alloc_string(unicoap_status_t status,
const char* payload)
{
return unicoap_message_alloc_string((uint8_t)status, payload);
}
/**
* @brief Initializes response with byte payload and options
* @memberof unicoap_message_t
*
* @param[in,out] response Pre-allocated response structure
* @param status Response status
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @pre @p response is allocated
* @pre @p options is allocated
*/
static inline void unicoap_response_init_with_options(unicoap_message_t* response,
unicoap_status_t status,
uint8_t* payload,
size_t payload_size,
unicoap_options_t* options)
{
unicoap_message_init_with_options(response, (uint8_t)status, payload, payload_size, options);
}
/**
* @brief Creates response with string byte and options
* @param status Response status
* @param[in] payload Payload bytes (nullable)
* @param payload_size Payload size
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @return Response structure
* @pre @p options is allocated
*/
static inline unicoap_message_t unicoap_response_alloc_with_options(unicoap_status_t status,
uint8_t* payload,
size_t payload_size,
unicoap_options_t* options)
{
return unicoap_message_alloc_with_options((uint8_t)status, payload, payload_size, options);
}
/**
* @brief Initializes response with payload from null-terminated UTF-8 string and options
* @memberof unicoap_message_t
*
* @param[in,out] response Pre-allocated response structure
* @param status Response status
* @param[in] payload Payload string (nullable), must be null-terminated
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @pre @p response is allocated
* @pre @p options is allocated
*/
static inline void unicoap_response_init_string_with_options(unicoap_message_t* response,
unicoap_status_t status,
const char* payload,
unicoap_options_t* options)
{
unicoap_message_init_string_with_options(response, (uint8_t)status, payload, options);
}
/**
* @brief Creates response with payload from null-terminated UTF-8 string and options
* @param status Response status
* @param[in] payload Payload string (nullable), must be null-terminated
* @param[in] options Message options, must be pre-allocated and pre-initialized
* @return Response structure
* @pre @p options is allocated
*/
static inline
unicoap_message_t unicoap_response_alloc_string_with_options(unicoap_status_t status,
const char* payload,
unicoap_options_t* options)
{
return unicoap_message_alloc_string_with_options((uint8_t)status, payload, options);
}
/** @} */
/** @} */
/* MARK: - Parsing and Serialization ------------------------------------------------------- */
/**
* @addtogroup net_unicoap_pdu
* @{
*/
/* MARK: - Parsing */
/**
* @name Parsing
* @{
*/
/**
* @brief Common PDU parser
*
* The parser should call @ref unicoap_pdu_parse_options_and_payload when it's finished parsing the
* PDU header.
*
* @param[in] pdu PDU buffer
* @param size PDU size
* @param[in,out] message Pre-allocated message
* @param[in,out] properties Pre-allocated properties
*
* @pre @p message is allocated
* @pre @p properties is allocated
*
*/
typedef ssize_t (*unicoap_parser_t)(const uint8_t* pdu, size_t size, unicoap_message_t* message,
unicoap_message_properties_t* properties);
/**
* @brief Helper structure for parsing PDUs manually
*
* Use this struct to allocate all necessary artifacts for invoking a parser
*/
typedef struct {
/** @brief Message */
unicoap_message_t message;
/** @brief Parsed options */
unicoap_options_t options;
/** @brief Parsed message properties */
unicoap_message_properties_t properties;
} unicoap_parser_result_t;
/**
* @brief Parses PDU starting at options
*
* Call this method after you have parsed the CoAP header.
*
* @param[in] cursor Start of options buffer
* @param[out] end Pointer to after last buffer element
* @param[in,out] message Pre-allocated message to write options and payload into. Must have valid
* options pointer
*
* @pre @p message is allocated
*
* @returns `0` on success
* @returns Negative errno on failure
*
* @note This function does not mutate or copy the buffer pointed at by @p cursor. However,
* it **does escape** pointers into the buffer pointed at by @p cursor in @p message . This is
* necessary to create a lookup array for options, i.e., to avoid re-parsing the options buffer.
* You will need to decide whether you treat the message's options as constant or not.
* This depends on whether the buffer @p cursor passed to this function is considered constant
* _by you_.
*
* As `unicoap` cannot guarantee you won't add/insert/remove options later, @p cursor is not
* qualified by `const`. That hypothetical `const` depends on your usage of the message and its
* options. That hypothetical `const` depends on your usage of the message and its options.
*/
ssize_t unicoap_pdu_parse_options_and_payload(uint8_t* cursor, const uint8_t* end,
unicoap_message_t* message);
/** @} */
/* MARK: - Serializing */
/**
* @name Serializing
* @{
*/
/**
* @brief Populates the given buffer with options and payload
*
* Call this method with the remaining capacity after you have written the CoAP header into a
* PDU buffer
*
* @param[in] cursor Pointer to first byte after header
* @param remaining_capacity Capacity remaining for options, payload marker, and payload
* @param[in] message Message containing options and payload
*
* @returns Number of bytes written
* @returns `-ENOBUFS` if buffer is too small
*/
ssize_t unicoap_pdu_build_options_and_payload(uint8_t* cursor, size_t remaining_capacity,
const unicoap_message_t* message);
/**
* @brief Number of iolists in the iolist buffer that must be passed to
* @ref unicoap_pdu_buildv_options_and_payload
*/
#define UNICOAP_PDU_IOLIST_COUNT (4)
/**
* @brief Populates the given iolist with header, options, payload separator and payload
*
* Use this method to construct a vector message for vectored send functions in your transport
* driver.
*
* @param[in] header Encoded CoAP header
* @param header_size Size of @p header
* @param[in] message Message containing options and payload
* @param[in,out] iolists Buffer of iolists, pre-allocated, size must be
* @ref UNICOAP_PDU_IOLIST_COUNT
*
* @pre @p iolists is allocated
*
* @returns Zero on success, negative error number otherwise
*
*/
int unicoap_pdu_buildv_options_and_payload(uint8_t* header, size_t header_size,
const unicoap_message_t* message,
iolist_t iolists[UNICOAP_PDU_IOLIST_COUNT]);
/** @} */
/** @} */
/**
* @addtogroup net_unicoap_drivers_rfc7252_pdu
* @{
*/
/**
* @name Parsing
* @{
*/
/**
* @brief Parses RFC 7252 PDU
*
* @param pdu Buffer containing PDU to parse
* @param size Size of PDU in bytes
* @param[out] message Pre-allocated message to populate, should have options set
* @param[out] properties Pre-allocated properties structure to populate
*
* @pre @p message is allocated
* @pre @p properties is allocated
*
* @returns Zero on success or negative errno on failure
* @retval `-EBADOPT` Bad option
* @retval `-ENOBUFS` Options buffer in @ref unicoap_message_t::options (@ref unicoap_options_t)
* too small
*
* @remark To allocate everything needed in one go, use @ref unicoap_pdu_parse_rfc7252_result
* instead.
*
* @note This function does not mutate or copy the buffer pointed at by @p pdu. However,
* it **does escape** pointers into the buffer pointed at by @p pdu in @p message . This is
* necessary to create a lookup array for options, i.e., to avoid re-parsing the options buffer.
* You will need to decide whether you treat the message's options as constant or not.
* This depends on whether the buffer @p pdu passed to this function is considered constant
* _by you_.
*
* As `unicoap` cannot guarantee you won't add/insert/remove options later, @p pdu is not qualified
* by `const`. That hypothetical `const` depends on your usage of the message and its options.
*/
ssize_t unicoap_pdu_parse_rfc7252(uint8_t* pdu, size_t size, unicoap_message_t* message,
unicoap_message_properties_t* properties);
/**
* @brief Helper method for manually parsing a PDU
*
* @param[in] pdu PDU buffer
* @param size PDU size in bytes
* @param[out] parsed Pre-allocated parsed message structure
*
* @pre @p parsed is allocated
*
* @returns Zero on success or negative errno on failure
* @retval `-EBADOPT` Bad option
* @retval `-ENOBUFS` Options buffer in @ref unicoap_message_t::options (@ref unicoap_options_t)
* too small
*
* @note This function does not mutate or copy the buffer pointed at by @p pdu. However,
* it **does escape** pointers into the buffer pointed at by @p pdu in @p message . This is
* necessary to create a lookup array for options, i.e., to avoid re-parsing the options buffer.
* You will need to decide whether you treat the message's options as constant or not.
* This depends on whether the buffer @p pdu passed to this function is considered constant
* _by you_.
*
* As `unicoap` cannot guarantee you won't add/insert/remove options later, @p pdu is not qualified
* by `const`. That hypothetical `const` depends on your usage of the message and its options.
*/
static inline ssize_t unicoap_pdu_parse_rfc7252_result(uint8_t* pdu, size_t size,
unicoap_parser_result_t* parsed)
{
parsed->message.options = &parsed->options;
return unicoap_pdu_parse_rfc7252(pdu, size, &parsed->message, &parsed->properties);
}
/** @} */
/**
* @name Serializing
* @{
*/
/**
* @brief Writes RFC 7252 PDU header in the given buffer
*
* @param[in,out] header Buffer the header will be written into
* @param capacity Number of usable bytes in the @p header buffer
* @param[in] message Message to construct header from (use code or payload_size)
* @param[in] properties Message properties to serialize into the header
*
* @returns Header size
* @retval `-ENOBUFS` Buffer too small
*/
ssize_t unicoap_pdu_build_header_rfc7252(uint8_t* header, size_t capacity,
const unicoap_message_t* message,
const unicoap_message_properties_t* properties);
/**
* @brief Writes RFC 7252 PDU into buffer
*
* @param[in,out] pdu Buffer
* @param capacity PDU buffer capacity
* @param[in] message Message
* @param[in] properties Message properties containing ID and type
*
* @returns Size of PDU
* @returns Negative integer one error
* @retval `-ENOBUFS` Buffer too small
*/
static inline ssize_t unicoap_pdu_build_rfc7252(uint8_t* pdu, size_t capacity,
const unicoap_message_t* message,
const unicoap_message_properties_t* properties)
{
ssize_t res = 0;
if ((res = unicoap_pdu_build_header_rfc7252(pdu, capacity, message, properties)) < 0) {
return res;
}
return unicoap_pdu_build_options_and_payload(pdu + res, capacity - res, message) + res;
}
/* MARK: unicoap_driver_extension_point */
/**
* @brief Populates the given iolist with header according to RFC 7252, options, and payload
*
* @param[in] header Header buffer
* @param header_capacity Capacity of header buffer
* @param[in] message Message containing options and payload
* @param[in] properties Message properties containing ID and type
* @param[in,out] iolists Buffer of iolists, pre-allocated, size must be be
* @ref UNICOAP_PDU_IOLIST_COUNT
*
* @pre @p iolists is allocated
*
* @returns `0` on success
* @returns Negative integer one error
* @retval `-ENOBUFS` Buffer too small
*/
static inline ssize_t unicoap_pdu_buildv_rfc7252(uint8_t* header, size_t header_capacity,
const unicoap_message_t* message,
const unicoap_message_properties_t* properties,
iolist_t iolists[UNICOAP_PDU_IOLIST_COUNT])
{
ssize_t res = 0;
if ((res =
unicoap_pdu_build_header_rfc7252(header, header_capacity, message, properties)) < 0) {
return res;
}
return unicoap_pdu_buildv_options_and_payload(header, res, message, iolists);
}
/** @} */
/** @} */
/* MARK: unicoap_driver_extension_point */
#ifdef __cplusplus
}
#endif