Skip to content

Commit

Permalink
Merge pull request #8932 from bergzand/pr/nanocoap/block2
Browse files Browse the repository at this point in the history
nanocoap: add server-side block2 support
  • Loading branch information
kb2ma authored Oct 14, 2018
2 parents a73c499 + f3b4e44 commit 14c9b30
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 14 deletions.
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;

/**
* @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);

/**
* @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

0 comments on commit 14c9b30

Please sign in to comment.