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_sock: add nanocoap_sock_block_request() #17958

Merged
merged 4 commits into from
May 18, 2022
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
22 changes: 20 additions & 2 deletions sys/include/net/nanocoap.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
#ifdef RIOT_VERSION
#include "bitfield.h"
#include "byteorder.h"
#include "iolist.h"
#include "net/coap.h"
#else
#include "coap.h"
Expand Down Expand Up @@ -193,13 +194,28 @@ typedef struct {

/**
* @brief CoAP PDU parsing context structure
*
* When this struct is used to assemble the header, @p payload is used as the
* write pointer and @p payload_len contains the number of free bytes left in
* then packet buffer pointed to by @ref coap_pkt_t::hdr
*
* When the header was written, @p payload must not be changed, it must remain
* pointing to the end of the header.
* @p payload_len must then be set to the size of the payload that was further
* copied into the packet buffer, after the header.
*
* @ref coap_pkt_t::snips can be used to attach further payload buffers without copying them
* into the CoAP packet buffer.
* If there are any, they will be attached in order after the last payload byte
* (or header byte) in the original CoAP packet buffer.
*/
typedef struct {
coap_hdr_t *hdr; /**< pointer to raw packet */
uint8_t *token; /**< pointer to token
* @deprecated Use coap_get_token(),
* Will be removed after 2022.10. */
uint8_t *payload; /**< pointer to payload */
uint8_t *payload; /**< pointer to end of the header */
iolist_t *snips; /**< payload snips (optional)*/
uint16_t payload_len; /**< length of payload */
uint16_t options_len; /**< length of options array */
coap_optpos_t options[CONFIG_NANOCOAP_NOPTS_MAX]; /**< option offset array */
Expand Down Expand Up @@ -430,7 +446,9 @@ static inline unsigned coap_get_total_hdr_len(const coap_pkt_t *pkt)
}

/**
* @brief Get the total length of a CoAP packet
* @brief Get the total length of a CoAP packet in the packet buffer
*
* @note This does not include possible payload snips.
*
* @param[in] pkt CoAP packet
*
Expand Down
93 changes: 93 additions & 0 deletions sys/include/net/nanocoap_sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@

#include "net/nanocoap.h"
#include "net/sock/udp.h"
#include "net/sock/util.h"

#ifdef __cplusplus
extern "C" {
Expand All @@ -145,6 +146,17 @@ extern "C" {
*/
typedef sock_udp_t nanocoap_sock_t;

/**
* @brief Blockwise request helper struct
*/
typedef struct {
nanocoap_sock_t sock; /**< socket used for the request */
const char *path; /**< path on the server */
uint32_t blknum; /**< current block number */
uint8_t method; /**< request method (GET, POST, PUT) */
uint8_t blksize; /**< CoAP blocksize exponent */
} coap_block_request_t;

/**
* @brief Start a nanocoap server instance
*
Expand Down Expand Up @@ -332,6 +344,87 @@ ssize_t nanocoap_request(coap_pkt_t *pkt, sock_udp_ep_t *local,
ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, void *buf,
size_t len);

/**
* @brief Initialize block request context
*
* @param[out] ctx The block request context to initialize
* @param[in] remote Server endpoint
* @param[in] path Server path for request
* @param[in] method Request method (`COAP_METHOD_{GET|PUT|POST}`)
* @param[in] blksize Request blocksize exponent
*
* @retval 0 Success
* @retval <0 Error (see @ref nanocoap_sock_connect for details)
*/
static inline int nanocoap_block_request_init(coap_block_request_t *ctx,
sock_udp_ep_t *remote,
const char *path,
uint8_t method,
coap_blksize_t blksize)
{
ctx->path = path;
ctx->blknum = 0;
ctx->method = method;
ctx->blksize = blksize;
return nanocoap_sock_connect(&ctx->sock, NULL, remote);
}

/**
* @brief Initialize block request context by URL
*
* @param[out] ctx The block request context to initialize
* @param[in] url The request URL
* @param[in] method Request method (`COAP_METHOD_{GET|PUT|POST}`)
* @param[in] blksize Request blocksize exponent
*
* @retval 0 Success
* @retval <0 Error (see @ref nanocoap_sock_url_connect for details)
*/
static inline int nanocoap_block_request_init_url(coap_block_request_t *ctx,
const char *url,
uint8_t method,
coap_blksize_t blksize)
{
ctx->path = sock_urlpath(url);
ctx->blknum = 0;
ctx->method = method;
ctx->blksize = blksize;
return nanocoap_sock_url_connect(url, &ctx->sock);
}

/**
* @brief Free block request context
*
* @param[out] ctx The block request context to finalize
*/
static inline void nanocoap_block_request_done(coap_block_request_t *ctx)
{
nanocoap_sock_close(&ctx->sock);
}

/**
* @brief Do a block-wise request, send a single block.
*
* This method is expected to be called in a loop until all
* payload blocks have been transferred.
*
* @pre @p ctx was initialized with @ref nanocoap_block_request_init or
* @ref nanocoap_block_request_init_url
*
* @param[in] ctx blockwise request context
* @param[in] data payload to send
* @param[in] len payload length
* @param[in] more more blocks after this one
* (will be set automatically if @p len > block size)
* @param[in] cb callback for response
* @param[in] arg callback context
*
* @return Number of payload bytes written on success
* Negative error on failure
*/
int nanocoap_sock_block_request(coap_block_request_t *ctx,
const void *data, size_t len, bool more,
coap_request_cb_t cb, void *arg);
#ifdef __cplusplus
}
#endif
Expand Down
1 change: 1 addition & 0 deletions sys/net/application_layer/nanocoap/nanocoap.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
pkt->payload = NULL;
pkt->payload_len = 0;
memset(pkt->opt_crit, 0, sizeof(pkt->opt_crit));
pkt->snips = NULL;

if (len < sizeof(coap_hdr_t)) {
DEBUG("msg too short\n");
Expand Down
64 changes: 58 additions & 6 deletions sys/net/application_layer/nanocoap/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,13 @@ ssize_t nanocoap_sock_request_cb(nanocoap_sock_t *sock, coap_pkt_t *pkt,
coap_request_cb_t cb, void *arg)
{
ssize_t tmp, res = 0;
const void *pdu = pkt->hdr;
const size_t pdu_len = coap_get_total_len(pkt);
const unsigned id = coap_get_id(pkt);
void *payload, *ctx = NULL;
const uint8_t *token = coap_get_token(pkt);
uint8_t token_len = coap_get_token_len(pkt);
uint8_t state = STATE_SEND_REQUEST;

unsigned state = STATE_SEND_REQUEST;

/* random timeout, deadline for receive retries */
uint32_t timeout = random_uint32_range(CONFIG_COAP_ACK_TIMEOUT_MS * US_PER_MS,
CONFIG_COAP_ACK_TIMEOUT_MS * CONFIG_COAP_RANDOM_FACTOR_1000);
uint32_t deadline = _deadline_from_interval(timeout);
Expand All @@ -143,16 +141,24 @@ ssize_t nanocoap_sock_request_cb(nanocoap_sock_t *sock, coap_pkt_t *pkt,
/* try receiving another packet without re-request */
bool retry_rx = false;

iolist_t head = {
.iol_next = pkt->snips,
.iol_base = pkt->hdr,
.iol_len = coap_get_total_len(pkt),
};

while (1) {
switch (state) {
case STATE_SEND_REQUEST:
DEBUG("nanocoap: send %u bytes (%u tries left)\n", (unsigned)pdu_len, tries_left);
DEBUG("nanocoap: send %u bytes (%u tries left)\n",
(unsigned)iolist_size(&head), tries_left);

if (--tries_left == 0) {
DEBUG("nanocoap: maximum retries reached\n");
return -ETIMEDOUT;
}

res = sock_udp_send(sock, pdu, pdu_len, NULL);
res = sock_udp_sendv(sock, &head, NULL);
if (res <= 0) {
DEBUG("nanocoap: error sending coap request, %d\n", (int)res);
return res;
Expand Down Expand Up @@ -386,6 +392,52 @@ static int _fetch_block(nanocoap_sock_t *sock, uint8_t *buf, size_t len,
return nanocoap_sock_request_cb(sock, &pkt, _block_cb, ctx);
}

int nanocoap_sock_block_request(coap_block_request_t *req,
const void *data, size_t len, bool more,
coap_request_cb_t callback, void *arg)
{
/* clip the payload at the block size */
if (len > coap_szx2size(req->blksize)) {
len = coap_szx2size(req->blksize);
more = true;
}

int res;
uint8_t buf[CONFIG_NANOCOAP_BLOCK_HEADER_MAX];
iolist_t snip = {
.iol_base = (void *)data,
.iol_len = len,
};

coap_pkt_t pkt = {
.hdr = (void *)buf,
.snips = &snip,
};

uint8_t *pktpos = (void *)pkt.hdr;
uint16_t lastonum = 0;

pktpos += coap_build_hdr(pkt.hdr, COAP_TYPE_CON, NULL, 0, req->method, _get_id());
pktpos += coap_opt_put_uri_pathquery(pktpos, &lastonum, req->path);
pktpos += coap_opt_put_uint(pktpos, lastonum, COAP_OPT_BLOCK1,
(req->blknum << 4) | req->blksize | (more ? 0x8 : 0));
if (len) {
/* set payload marker */
*pktpos++ = 0xFF;
}

pkt.payload = pktpos;
pkt.payload_len = 0;

res = nanocoap_sock_request_cb(&req->sock, &pkt, callback, arg);
if (res < 0) {
return res;
}

++req->blknum;
return len;
}

int nanocoap_sock_get_blockwise(nanocoap_sock_t *sock, const char *path,
coap_blksize_t blksize,
coap_blockwise_cb_t callback, void *arg)
Expand Down
2 changes: 2 additions & 0 deletions tests/nanocoap_cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
extern int nanotest_client_cmd(int argc, char **argv);
extern int nanotest_client_url_cmd(int argc, char **argv);
extern int nanotest_server_cmd(int argc, char **argv);
extern int nanotest_client_put_cmd(int argc, char **argv);
static int _list_all_inet6(int argc, char **argv);

static const shell_command_t shell_commands[] = {
{ "client", "CoAP client", nanotest_client_cmd },
{ "url", "CoAP client URL request", nanotest_client_url_cmd },
{ "put", "experimental put", nanotest_client_put_cmd },
{ "server", "CoAP server", nanotest_server_cmd },
{ "inet6", "IPv6 addresses", _list_all_inet6 },
{ NULL, NULL, NULL }
Expand Down
56 changes: 56 additions & 0 deletions tests/nanocoap_cli/nanocli_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "net/nanocoap.h"
#include "net/nanocoap_sock.h"
#include "net/sock/udp.h"
#include "net/sock/util.h"

#include "od.h"

Expand Down Expand Up @@ -210,3 +211,58 @@ int nanotest_client_url_cmd(int argc, char **argv)
printf("usage: %s <get|post|put> <url> [data]\n", argv[0]);
return -1;
}

static const char song[] =
"Join us now and share the software;\n"
"You'll be free, hackers, you'll be free.\n"
"Join us now and share the software;\n"
"You'll be free, hackers, you'll be free.\n"
"\n"
"Hoarders can get piles of money,\n"
"That is true, hackers, that is true.\n"
"But they cannot help their neighbors;\n"
"That's not good, hackers, that's not good.\n"
"\n"
"When we have enough free software\n"
"At our call, hackers, at our call,\n"
"We'll kick out those dirty licenses\n"
"Ever more, hackers, ever more.\n"
"\n"
"Join us now and share the software;\n"
"You'll be free, hackers, you'll be free.\n"
"Join us now and share the software;\n"
"You'll be free, hackers, you'll be free.\n";

int nanotest_client_put_cmd(int argc, char **argv)
{
int res;
coap_block_request_t ctx;

if (argc < 2) {
printf("usage: %s <url>\n", argv[0]);
return 1;
}

res = nanocoap_block_request_init_url(&ctx, argv[1],
COAP_METHOD_PUT, COAP_BLOCKSIZE_32);
if (res < 0) {
printf("error: %d\n", res);
return res;
}

const char *pos = song;
size_t len = sizeof(song);

while (len) {
res = nanocoap_sock_block_request(&ctx, pos, len, 0, NULL, NULL);
if (res <= 0) {
puts(strerror(-res));
break;
}
len -= res;
pos += res;
}

nanocoap_block_request_done(&ctx);
return res;
}