Merge pull request #11056 from kb2ma/coap/pkt_api_block_write
net/gcoap: add/use Packet API Block implementation
This commit is contained in:
commit
e942f86837
@ -26,6 +26,10 @@ BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-leonardo \
|
||||
#GCOAP_TOKENLEN = 2
|
||||
#CFLAGS += -DGCOAP_TOKENLEN=$(GCOAP_TOKENLEN)
|
||||
|
||||
# Increase from default for confirmable block2 follow-on requests
|
||||
GCOAP_RESEND_BUFS_MAX ?= 2
|
||||
CFLAGS += -DGCOAP_RESEND_BUFS_MAX=$(GCOAP_RESEND_BUFS_MAX)
|
||||
|
||||
# Include packages that pull up and auto-init the link layer.
|
||||
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
|
||||
USEMODULE += gnrc_netdev_default
|
||||
|
||||
@ -54,6 +54,12 @@ static gcoap_listener_t _listener = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Retain request path to re-request if response includes block. User must not
|
||||
* start a new request (with a new path) until any blockwise transfer
|
||||
* completes or times out. */
|
||||
#define _LAST_REQ_PATH_MAX (32)
|
||||
static char _last_req_path[_LAST_REQ_PATH_MAX];
|
||||
|
||||
/* Counts requests sent by CLI. */
|
||||
static uint16_t req_count = 0;
|
||||
|
||||
@ -92,6 +98,11 @@ static void _resp_handler(unsigned req_state, coap_pkt_t* pdu,
|
||||
return;
|
||||
}
|
||||
|
||||
coap_block1_t block;
|
||||
if (coap_get_block2(pdu, &block) && block.blknum == 0) {
|
||||
puts("--- blockwise start ---");
|
||||
}
|
||||
|
||||
char *class_str = (coap_get_code_class(pdu) == COAP_CLASS_SUCCESS)
|
||||
? "Success" : "Error";
|
||||
printf("gcoap: response %s, code %1u.%02u", class_str,
|
||||
@ -115,6 +126,30 @@ static void _resp_handler(unsigned req_state, coap_pkt_t* pdu,
|
||||
else {
|
||||
printf(", empty payload\n");
|
||||
}
|
||||
|
||||
/* ask for next block if present */
|
||||
if (coap_get_block2(pdu, &block)) {
|
||||
if (block.more) {
|
||||
unsigned msg_type = coap_get_type(pdu);
|
||||
if (block.blknum == 0 && !strlen(_last_req_path)) {
|
||||
puts("Path too long; can't complete blockwise");
|
||||
return;
|
||||
}
|
||||
|
||||
gcoap_req_init(pdu, (uint8_t *)pdu->hdr, GCOAP_PDU_BUF_SIZE,
|
||||
COAP_METHOD_GET, _last_req_path);
|
||||
if (msg_type == COAP_TYPE_ACK) {
|
||||
coap_hdr_set_type(pdu->hdr, COAP_TYPE_CON);
|
||||
}
|
||||
block.blknum++;
|
||||
coap_opt_add_block2_control(pdu, &block);
|
||||
int len = coap_opt_finish(pdu, COAP_OPT_FINISH_NONE);
|
||||
gcoap_req_send((uint8_t *)pdu->hdr, len, remote, _resp_handler);
|
||||
}
|
||||
else {
|
||||
puts("--- blockwise complete ---");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -280,6 +315,11 @@ int gcoap_cli_cmd(int argc, char **argv)
|
||||
gcoap_req_init(&pdu, &buf[0], GCOAP_PDU_BUF_SIZE, code_pos+1, argv[apos+2]);
|
||||
coap_hdr_set_type(pdu.hdr, msg_type);
|
||||
|
||||
memset(_last_req_path, 0, _LAST_REQ_PATH_MAX);
|
||||
if (strlen(argv[apos+2]) < _LAST_REQ_PATH_MAX) {
|
||||
memcpy(_last_req_path, argv[apos+2], strlen(argv[apos+2]));
|
||||
}
|
||||
|
||||
size_t paylen = (argc == apos + 4) ? strlen(argv[apos+3]) : 0;
|
||||
if (paylen) {
|
||||
coap_opt_add_format(&pdu, COAP_FORMAT_TEXT);
|
||||
|
||||
@ -24,15 +24,18 @@
|
||||
* port, which supports RFC 6282 compression. Internally, gcoap depends on the
|
||||
* nanocoap package for base level structs and functionality.
|
||||
*
|
||||
* gcoap also supports the Observe extension (RFC 7641) for a server. gcoap
|
||||
* provides functions to generate and send an observe notification that are
|
||||
* similar to the functions to send a client request.
|
||||
* gcoap supports the Observe extension (RFC 7641) for a server. gcoap provides
|
||||
* functions to generate and send an observe notification that are similar to
|
||||
* the functions to send a client request. gcoap also supports the Block
|
||||
* extension (RFC 7959) with block-specific option functions as well as some
|
||||
* helpers.
|
||||
*
|
||||
* *Contents*
|
||||
*
|
||||
* - Server Operation
|
||||
* - Client Operation
|
||||
* - Observe Server Operation
|
||||
* - Block Operation
|
||||
* - Implementation Notes
|
||||
* - Implementation Status
|
||||
*
|
||||
@ -193,6 +196,106 @@
|
||||
* the Observe option value set to 1. The server does not support cancellation
|
||||
* via a reset (RST) response to a non-confirmable notification.
|
||||
*
|
||||
* ## Block Operation ##
|
||||
*
|
||||
* gcoap provides for both server side and client side blockwise messaging for
|
||||
* requests and responses. This section outlines how to write a message for
|
||||
* each situation.
|
||||
*
|
||||
* ### CoAP server GET handling ###
|
||||
*
|
||||
* The server must slice the full response body into smaller payloads, and
|
||||
* identify the slice with a Block2 option. This implementation toggles the
|
||||
* actual writing of data as it passes over the code for the full response
|
||||
* body. See the _riot_block2_handler() example in
|
||||
* [gcoap-block-server](https://github.com/kb2ma/riot-apps/blob/kb2ma-master/gcoap-block-server/gcoap_block.c),
|
||||
* which implements the sequence described below.
|
||||
*
|
||||
* - Use coap_block2_init() to initialize a _slicer_ struct from the Block2
|
||||
* option in the request. The slicer tracks boundaries while writing the
|
||||
* payload. If no option present in the initial request, the init function
|
||||
* defaults to a payload size of 16 bytes.
|
||||
* - Use gcoap_resp_init() to begin the response.
|
||||
* - Use coap_opt_add_block2() to write the Block2 option from the slicer. Use
|
||||
* 1 as a default for the _more_ parameter. At this point, we don't know yet
|
||||
* if this message will be the last in the block exchange. However, we must
|
||||
* add the block option at this location in the message.
|
||||
* - Use coap_opt_finish() to add a payload marker.
|
||||
* - Add the payload using the `coap_blockwise_put_xxx()` functions. The slicer
|
||||
* knows the current position in the overall body of the response. It writes
|
||||
* only the portion of the body specified by the block number and block size
|
||||
* in the slicer.
|
||||
* - Finally, use coap_block2_finish() to finalize the block option with the
|
||||
* proper value for the _more_ parameter.
|
||||
*
|
||||
* ### CoAP server PUT/POST handling ###
|
||||
*
|
||||
* The server must ack each blockwise portion of the response body received
|
||||
* from the client by writing a Block1 option in the response. See the
|
||||
* _sha256_handler() example in
|
||||
* [gcoap-block-server](https://github.com/kb2ma/riot-apps/blob/kb2ma-master/gcoap-block-server/gcoap_block.c),
|
||||
* which implements the sequence described below.
|
||||
*
|
||||
* - Use coap_get_block1() to initialize a block1 struct from the request.
|
||||
* - Determine the response code. If the block1 _more_ attribute is 1, use
|
||||
* COAP_CODE_CONTINUE to request more responses. Otherwise, use
|
||||
* COAP_CODE_CHANGED to indicate a successful transfer.
|
||||
* - Use gcoap_resp_init() to begin the response, including the response code.
|
||||
* - Use coap_opt_add_block1_control() to write the Block1 option.
|
||||
* - Use coap_opt_finish() to determine the length of the PDU. If appropriate,
|
||||
* use the COAP_OPT_FINISH_PAYLOAD parameter and then write the payload.
|
||||
*
|
||||
* ### CoAP client GET request ###
|
||||
*
|
||||
* The client requests a specific blockwise payload from the overall body by
|
||||
* writing a Block2 option in the request. See _resp_handler() in the
|
||||
* [gcoap](https://github.com/RIOT-OS/RIOT/blob/master/examples/gcoap/gcoap_cli.c)
|
||||
* example in the RIOT distribution, which implements the sequence described
|
||||
* below.
|
||||
*
|
||||
* - For the first request, use coap_block_object_init() to initialize a new
|
||||
* block1 struct. For subsequent requests, first use coap_get_block2() to
|
||||
* read the Block2 option in the response to the previous request. If the
|
||||
* _more_ attribute indicates no more blocks, you are done.
|
||||
* - The gcoap example actually does _not_ include a Block2 option in the
|
||||
* original request, but the server response includes a blockwise response
|
||||
* with a Block2 option anyway. On the other hand, this example shows how
|
||||
* blockwise messaging can be supported in a generic way.
|
||||
* - If more blocks are available, use gcoap_req_init() to create a new
|
||||
* request.
|
||||
* - Increment the _blknum_ attribute in the block1 struct from the previous
|
||||
* response to request the next blockwise payload.
|
||||
* - Use coap_opt_put_block2_control() to write the Block2 option to the
|
||||
* request.
|
||||
* - Use coap_opt_finish() to determine the length of the PDU.
|
||||
*
|
||||
* ### CoAP client PUT/POST request ###
|
||||
*
|
||||
* The client pushes a specific blockwise payload from the overall body to the
|
||||
* server by writing a Block1 option in the request. See _do_block_post() in
|
||||
* the [gcoap-block-client](https://github.com/kb2ma/riot-apps/blob/kb2ma-master/gcoap-block-client/gcoap_block.c)
|
||||
* example, which implements the sequence described below.
|
||||
*
|
||||
* - For the first request, use coap_block_slicer_init() to initialize a
|
||||
* _slicer_ struct with the desired block number and block size. For
|
||||
* subsequent requests, first read the response from the server to the
|
||||
* previous request. If the response code is COAP_CODE_CONTINUE, then
|
||||
* increment the last block number sent when initializing the slicer struct
|
||||
* for the next request.
|
||||
* - Use gcoap_req_init() to initialize the request.
|
||||
* - Use coap_opt_add_block1() to add the Block1 option from the slicer. Use 1
|
||||
* as a default for the _more_ parameter. At this point, we don't know yet if
|
||||
* this message will be the last in the block exchange. However, we must add
|
||||
* the block option at this location in the message.
|
||||
* - Use coap_opt_finish() with COAP_OPT_FINISH_PAYLOAD to write the payload
|
||||
* marker.
|
||||
* - Add the payload using the `coap_blockwise_put_xxx()` functions. The slicer
|
||||
* knows the current position in the overall body of the response. It writes
|
||||
* only the portion of the body specified by the block number and block size
|
||||
* in the slicer.
|
||||
* - Finally, use coap_block1_finish() to finalize the block option with the
|
||||
* proper value for the _more_ parameter.
|
||||
*
|
||||
* ## Implementation Notes ##
|
||||
*
|
||||
* ### Waiting for a response ###
|
||||
|
||||
@ -780,6 +780,75 @@ static inline unsigned coap_szx2size(unsigned szx)
|
||||
*
|
||||
* Use a coap_pkt_t struct to manage writing Options to the PDU.
|
||||
*/
|
||||
/**
|
||||
* @brief Add block option in descriptive use from a slicer object
|
||||
*
|
||||
* When calling this function to initialize a packet with a block option, the
|
||||
* more flag must be set to prevent the creation of an option with a length too
|
||||
* small to contain the size bit.
|
||||
|
||||
* @post pkt.payload advanced to first byte after option
|
||||
* @post pkt.payload_len reduced by option length
|
||||
*
|
||||
* @param[in,out] pkt pkt referencing target buffer
|
||||
* @param[in] slicer coap blockwise slicer helper struct
|
||||
* @param[in] more more flag (1 or 0)
|
||||
* @param[in] option option number (block1 or block2)
|
||||
*
|
||||
* @return number of bytes written to buffer
|
||||
* @return <0 on error
|
||||
* @return -ENOSPC if no available options or insufficient buffer space
|
||||
*/
|
||||
ssize_t coap_opt_add_block(coap_pkt_t *pkt, coap_block_slicer_t *slicer,
|
||||
bool more, uint16_t option);
|
||||
|
||||
/**
|
||||
* @brief Add block1 option in descriptive use from a slicer object
|
||||
*
|
||||
* When calling this function to initialize a packet with a block option, the
|
||||
* more flag must be set to prevent the creation of an option with a length too
|
||||
* small to contain the size bit.
|
||||
|
||||
* @post pkt.payload advanced to first byte after option
|
||||
* @post pkt.payload_len reduced by option length
|
||||
*
|
||||
* @param[in,out] pkt pkt referencing target buffer
|
||||
* @param[in] slicer coap blockwise slicer helper struct
|
||||
* @param[in] more more flag (1 or 0)
|
||||
*
|
||||
* @return number of bytes written to buffer
|
||||
* @return <0 on error
|
||||
* @return -ENOSPC if no available options or insufficient buffer space
|
||||
*/
|
||||
static inline ssize_t coap_opt_add_block1(coap_pkt_t *pkt,
|
||||
coap_block_slicer_t *slicer, bool more)
|
||||
{
|
||||
return coap_opt_add_block(pkt, slicer, more, COAP_OPT_BLOCK1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add block2 option in descriptive use from a slicer object
|
||||
*
|
||||
* When calling this function to initialize a packet with a block option, the
|
||||
* more flag must be set to prevent the creation of an option with a length too
|
||||
* small to contain the size bit.
|
||||
|
||||
* @post pkt.payload advanced to first byte after option
|
||||
* @post pkt.payload_len reduced by option length
|
||||
*
|
||||
* @param[in,out] pkt pkt referencing target buffer
|
||||
* @param[in] slicer coap blockwise slicer helper struct
|
||||
* @param[in] more more flag (1 or 0)
|
||||
*
|
||||
* @return number of bytes written to buffer
|
||||
* @return <0 on error
|
||||
* @return -ENOSPC if no available options or insufficient buffer space
|
||||
*/
|
||||
static inline ssize_t coap_opt_add_block2(coap_pkt_t *pkt,
|
||||
coap_block_slicer_t *slicer, bool more)
|
||||
{
|
||||
return coap_opt_add_block(pkt, slicer, more, COAP_OPT_BLOCK2);
|
||||
}
|
||||
/**
|
||||
* @brief Encode the given uint option into pkt
|
||||
*
|
||||
@ -796,6 +865,43 @@ static inline unsigned coap_szx2size(unsigned szx)
|
||||
*/
|
||||
ssize_t coap_opt_add_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t value);
|
||||
|
||||
/**
|
||||
* @brief Encode the given block1 option in control use
|
||||
*
|
||||
* @post pkt.payload advanced to first byte after option
|
||||
* @post pkt.payload_len reduced by option length
|
||||
*
|
||||
* @param[in,out] pkt pkt referencing target buffer
|
||||
* @param[in] block block to encode
|
||||
*
|
||||
* @return number of bytes written to buffer
|
||||
* @return <0 on error
|
||||
* @return -ENOSPC if no available options or insufficient buffer space
|
||||
*/
|
||||
static inline ssize_t coap_opt_add_block1_control(coap_pkt_t *pkt, coap_block1_t *block) {
|
||||
return coap_opt_add_uint(pkt, COAP_OPT_BLOCK1,
|
||||
(block->blknum << 4) | block->szx | (block->more ? 0x8 : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode the given block2 option in control use
|
||||
*
|
||||
* @post pkt.payload advanced to first byte after option
|
||||
* @post pkt.payload_len reduced by option length
|
||||
*
|
||||
* @param[in,out] pkt pkt referencing target buffer
|
||||
* @param[in] block block to encode
|
||||
*
|
||||
* @return number of bytes written to buffer
|
||||
* @return <0 on error
|
||||
* @return -ENOSPC if no available options or insufficient buffer space
|
||||
*/
|
||||
static inline ssize_t coap_opt_add_block2_control(coap_pkt_t *pkt, coap_block1_t *block) {
|
||||
/* block.more must be zero, so no need to 'or' it in */
|
||||
return coap_opt_add_uint(pkt, COAP_OPT_BLOCK2,
|
||||
(block->blknum << 4) | block->szx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append a Content-Format option to the pkt buffer
|
||||
*
|
||||
|
||||
@ -897,6 +897,18 @@ ssize_t coap_opt_add_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t value)
|
||||
return _add_opt_pkt(pkt, optnum, (uint8_t *)&tmp, tmp_len);
|
||||
}
|
||||
|
||||
ssize_t coap_opt_add_block(coap_pkt_t *pkt, coap_block_slicer_t *slicer,
|
||||
bool more, uint16_t option)
|
||||
{
|
||||
uint32_t blkopt = (_slicer_blknum(slicer) << 4);
|
||||
blkopt |= _size2szx(slicer->end - slicer->start);
|
||||
blkopt |= (more ? 0x8 : 0);
|
||||
|
||||
slicer->opt = pkt->payload;
|
||||
|
||||
return coap_opt_add_uint(pkt, option, blkopt);
|
||||
}
|
||||
|
||||
ssize_t coap_opt_finish(coap_pkt_t *pkt, uint16_t flags)
|
||||
{
|
||||
if (flags & COAP_OPT_FINISH_PAYLOAD) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user