Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nanocoap: add server-side block2 support #8932

Merged
merged 2 commits into from
Oct 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/nanocoap_server/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-mega2560 arduino-uno \
chronos msb-430 msb-430h nucleo-f031k6 \
nucleo-f042k6 nucleo-l031k6 nucleo-f030r8 \
nucleo-f303k8 nucleo-l053r8 stm32f0discovery \
telosb waspmote-pro z1
telosb waspmote-pro wsn430-v1_3b wsn430-v1_4 z1

# Include packages that pull up and auto-init the link layer.
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
Expand Down
39 changes: 39 additions & 0 deletions examples/nanocoap_server/coap_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,51 @@
/* internal value that can be read/written via CoAP */
static uint8_t internal_value = 0;

static const uint8_t block2_intro[] = "This is RIOT (Version: ";
static const uint8_t block2_board[] = " running on a ";
static const uint8_t block2_mcu[] = " board with a ";

static ssize_t _riot_board_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context)
{
(void)context;
return coap_reply_simple(pkt, COAP_CODE_205, buf, len,
COAP_FORMAT_TEXT, (uint8_t*)RIOT_BOARD, strlen(RIOT_BOARD));
}

static ssize_t _riot_block2_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context)
{
(void)context;
coap_block_slicer_t slicer;
coap_block2_init(pkt, &slicer);
uint8_t *payload = buf + coap_get_total_hdr_len(pkt);

uint8_t *bufpos = payload;

bufpos += coap_put_option_ct(bufpos, 0, COAP_FORMAT_TEXT);
bufpos += coap_opt_put_block2(bufpos, COAP_OPT_CONTENT_FORMAT, &slicer, 1);
*bufpos++ = 0xff;

/* Add actual content */
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, block2_intro, sizeof(block2_intro));
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, (uint8_t*)RIOT_VERSION, sizeof(RIOT_VERSION));
bufpos += coap_blockwise_put_char(&slicer, bufpos, ')');
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, block2_board, sizeof(block2_board));
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, (uint8_t*)RIOT_BOARD, sizeof(RIOT_BOARD));
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, block2_mcu, sizeof(block2_mcu));
bufpos += coap_blockwise_put_bytes(&slicer, bufpos, (uint8_t*)RIOT_MCU, sizeof(RIOT_MCU));
/* To demonstrate individual chars */
bufpos += coap_blockwise_put_char(&slicer, bufpos, ' ');
bufpos += coap_blockwise_put_char(&slicer, bufpos, 'M');
bufpos += coap_blockwise_put_char(&slicer, bufpos, 'C');
bufpos += coap_blockwise_put_char(&slicer, bufpos, 'U');
bufpos += coap_blockwise_put_char(&slicer, bufpos, '.');


unsigned payload_len = bufpos - payload;
return coap_block2_build_reply(pkt, COAP_CODE_205,
buf, len, payload_len, &slicer);
}

static ssize_t _riot_value_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, void *context)
{
(void) context;
Expand Down Expand Up @@ -109,6 +147,7 @@ const coap_resource_t coap_resources[] = {
COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER,
{ "/riot/board", COAP_GET, _riot_board_handler, NULL },
{ "/riot/value", COAP_GET | COAP_PUT | COAP_POST, _riot_value_handler, NULL },
{ "/riot/ver", COAP_GET, _riot_block2_handler, NULL },
{ "/sha256", COAP_POST, _sha256_handler, NULL },
};

Expand Down
146 changes: 144 additions & 2 deletions sys/include/net/nanocoap.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,31 @@
* @ingroup net
* @brief Provides CoAP functionality optimized for minimal resource usage
*
* # Create a Block-wise Response (Block2)
*
* Block-wise is a CoAP extension (RFC 7959) to divide a large payload across
* multiple physical packets. This section describes how to write a block-wise
* payload for a response, and is known as Block2. (Block1 is for a block-wise
* payload in a request.) See _riot_board_handler() in the nanocoap_server
* example for an example handler implementation.
*
* Start with coap_block2_init() to read the client request and initialize a
* coap_slicer_t struct with the size and location for this slice of the
* overall payload. Then write the block2 option in the response with
* coap_opt_put_block2(). The option includes an indicator ("more") that a
* slice completes the overall payload transfer. You may not know the value for
* _more_ at this point, but you must initialize the space in the packet for
* the option before writing the payload. The option is rewritten later.
*
* Next, use the coap_blockwise_put_xxx() functions to write the payload
* content. These functions use the coap_block_slicer_t to enable or disable
* actually writing the content, depending on the current position within the
* overall payload transfer.
*
* Finally, use the convenience function coap_block2_build_reply(), which
* finalizes the packet and calls coap_block2_finish() internally to update
* the block2 option.
*
* @{
*
* @file
Expand Down Expand Up @@ -62,8 +87,10 @@ extern "C" {
* @name Nanocoap specific maximum values
* @{
*/
#define NANOCOAP_NOPTS_MAX (16)
#define NANOCOAP_URI_MAX (64)
#define NANOCOAP_NOPTS_MAX (16)
#define NANOCOAP_URI_MAX (64)
#define NANOCOAP_BLOCK_SIZE_EXP_MAX (6) /**< Maximum size for a blockwise
* transfer as power of 2 */
/** @} */

#ifdef MODULE_GCOAP
Expand Down Expand Up @@ -145,6 +172,16 @@ typedef struct {
1 for more blocks coming */
} coap_block1_t;

/**
kb2ma marked this conversation as resolved.
Show resolved Hide resolved
* @brief Blockwise transfer helper struct
*/
typedef struct {
size_t start; /**< Start offset of the current block */
size_t end; /**< End offset of the current block */
size_t cur; /**< Offset of the generated content */
uint8_t *opt; /**< Pointer to the placed option */
} coap_block_slicer_t;

/**
* @brief Global CoAP resource list
*/
Expand Down Expand Up @@ -411,6 +448,17 @@ int coap_get_blockopt(coap_pkt_t *pkt, uint16_t option, uint32_t *blknum, unsign
*/
int coap_get_block1(coap_pkt_t *pkt, coap_block1_t *block1);

/**
* @brief Block2 option getter
*
* @param[in] pkt pkt to work on
* @param[out] block2 ptr to preallocated coap_block1_t structure
*
* @returns 0 if block2 option not present
* @returns 1 if structure has been filled
*/
int coap_get_block2(coap_pkt_t *pkt, coap_block1_t *block2);

/**
* @brief Insert block1 option into buffer
*
Expand Down Expand Up @@ -490,6 +538,23 @@ ssize_t coap_opt_add_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t value);
*/
ssize_t coap_opt_finish(coap_pkt_t *pkt, uint16_t flags);

/**
* @brief Insert block2 option into buffer
*
* When calling this function to initialize a packet with a block2 option, the
* more flag must be set to prevent the creation of an option with a length too
* small to contain the size bit.
*
* @param[out] buf buffer to write to
* @param[in] lastonum number of previous option (for delta calculation),
* must be < 23
* @param[in] slicer coap blockwise slicer helper struct
* @param[in] more more flag (1 or 0)
*
* @returns amount of bytes written to @p buf
*/
size_t coap_opt_put_block2(uint8_t *buf, uint16_t lastonum, coap_block_slicer_t *slicer, bool more);

/**
* @brief Get content type from packet
*
Expand Down Expand Up @@ -604,6 +669,83 @@ static inline ssize_t coap_get_location_query(const coap_pkt_t *pkt,
target, max_len, '&');
}

/**
* @brief Initialize a block2 slicer struct for writing the payload
*
* This function determines the size of the response payload based on the
* size requested by the client in @p pkt.
*
* @param[in] pkt packet to work on
* @param[out] slicer Preallocated slicer struct to fill
*/
void coap_block2_init(coap_pkt_t *pkt, coap_block_slicer_t *slicer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation out of sync with declaration. I fixed in b6b452d.


/**
* @brief Finish a block2 response
*
* This function finalizes the block2 response header
*
* Checks whether the `more` bit should be set in the block2 option and
* sets/clears it if required. Doesn't return the number of bytes as this
* overwrites bytes in the packet, it doesn't add new bytes to the packet.
*
* @param[inout] slicer Preallocated slicer struct to use
*/
void coap_block2_finish(coap_block_slicer_t *slicer);

/**
* @brief Build reply to CoAP block2 request
*
* This function can be used to create a reply to a CoAP block2 request
* packet. In addition to @ref coap_build_reply, this function checks the
* block2 option and returns an error message to the client if necessary.
*
* @param[in] pkt packet to reply to
* @param[in] code reply code (e.g., COAP_CODE_204)
* @param[out] rbuf buffer to write reply to
* @param[in] rlen size of @p rbuf
* @param[in] payload_len length of payload
* @param[in] slicer slicer to use
*
* @returns size of reply packet on success
* @returns <0 on error
*/
ssize_t coap_block2_build_reply(coap_pkt_t *pkt, unsigned code,
uint8_t *rbuf, unsigned rlen, unsigned payload_len,
coap_block_slicer_t *slicer);

/**
* @brief Add a single character to a block2 reply.
*
* This function is used to add single characters to a CoAP block2 reply. It
* checks whether the character should be added to the buffer and ignores it
* when the character is outside the current block2 request.
*
* @param[in] slicer slicer to use
* @param[in] bufpos pointer to the current payload buffer position
* @param[in] c character to write
*
* @returns Number of bytes writen to @p bufpos
*/
size_t coap_blockwise_put_char(coap_block_slicer_t *slicer, uint8_t *bufpos, char c);

/**
* @brief Add a byte array to a block2 reply.
*
* This function is used to add an array of bytes to a CoAP block2 reply. it
* checks which parts of the string should be added to the reply and ignores
* parts that are outside the current block2 request.
*
* @param[in] slicer slicer to use
* @param[in] bufpos pointer to the current payload buffer position
* @param[in] c byte array to copy
* @param[in] len length of the byte array
*
* @returns Number of bytes writen to @p bufpos
*/
size_t coap_blockwise_put_bytes(coap_block_slicer_t *slicer, uint8_t *bufpos,
const uint8_t *c, size_t len);

/**
* @brief Helper to decode SZX value to size in bytes
*
Expand Down
Loading