Skip to content

Commit

Permalink
Merge pull request RIOT-OS#12074 from kb2ma/coap/options_iterate
Browse files Browse the repository at this point in the history
net/nanocoap: iterate options
  • Loading branch information
smlng authored Sep 9, 2019
2 parents 6d800ed + 47ef5c1 commit c78ae0e
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 4 deletions.
36 changes: 34 additions & 2 deletions sys/include/net/nanocoap.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ static inline unsigned coap_get_id(coap_pkt_t *pkt)
*
* @returns length of token in the given message (0-8 byte)
*/
static inline unsigned coap_get_token_len(coap_pkt_t *pkt)
static inline unsigned coap_get_token_len(const coap_pkt_t *pkt)
{
return (pkt->hdr->ver_t_tkl & 0xf);
}
Expand All @@ -340,7 +340,7 @@ static inline unsigned coap_get_token_len(coap_pkt_t *pkt)
*
* @returns total header length
*/
static inline unsigned coap_get_total_hdr_len(coap_pkt_t *pkt)
static inline unsigned coap_get_total_hdr_len(const coap_pkt_t *pkt)
{
return sizeof(coap_hdr_t) + coap_get_token_len(pkt);
}
Expand Down Expand Up @@ -533,6 +533,38 @@ static inline ssize_t coap_get_uri_query(const coap_pkt_t *pkt, uint8_t *target)
return coap_opt_get_string(pkt, COAP_OPT_URI_QUERY, target,
NANOCOAP_URI_MAX, '&');
}

/**
* @brief Iterate over a packet's options
*
* To start iteration from the first option, set @p init_opt to true. To start
* iteration from a specific option, set @p init_opt to false, set
* @p opt->offset to the offset of the desired option from pkt->hdr, and
* @p opt->opt_num as required. See below for how @p opt->opt_num is modified.
*
* With each invocation, this function returns the length of the option value
* and sets @p value to point to the start of the value. The value for
* @p opt->opt_num is increased by the delta in the option number value over
* the preceding option in the packet. So, @p opt->opt_num is accurate if
* iteration started with the first option. Otherwise, it is useful for
* identification of repeated options. Finally, @p opt->offset is set to the
* offset for any following option, to prepare for the next iteration.
*
* The end of the options is indicated by a -ENOENT return value. In this case
* @p value and @p opt are unchanged from their input values.
*
* @param[in] pkt packet to read from
* @param[in,out] opt option attributes; read on input if @p init_opt
* is false
* @param[out] value start of the option value
* @param[in] init_opt true to retrieve first option; false to retrieve
* option at opt->offset
*
* @return length of option value
* @return -ENOENT if option not found
*/
ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt,
uint8_t **value, bool init_opt);
/**@}*/


Expand Down
29 changes: 27 additions & 2 deletions sys/net/application_layer/nanocoap/nanocoap.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,15 @@ uint8_t *coap_find_option(const coap_pkt_t *pkt, unsigned opt_num)
* opt_len[out] length of option value
*
* return next byte after option header, usually the option value
* return NULL if initial pkt_pos is past options
* return NULL if initial pkt_pos is payload marker or past options
*/
static uint8_t *_parse_option(const coap_pkt_t *pkt,
uint8_t *pkt_pos, uint16_t *delta, int *opt_len)
{
uint8_t *hdr_end = pkt->payload;

if (pkt_pos == hdr_end) {
if ((pkt_pos >= hdr_end)
|| (((pkt_pos + 1) == hdr_end) && (*pkt_pos == 0xFF))) {
return NULL;
}

Expand Down Expand Up @@ -268,6 +269,30 @@ unsigned coap_get_content_type(coap_pkt_t *pkt)
return content_type;
}

ssize_t coap_opt_get_next(const coap_pkt_t *pkt, coap_optpos_t *opt,
uint8_t **value, bool init_opt)
{
if (init_opt) {
opt->opt_num = 0;
opt->offset = coap_get_total_hdr_len(pkt);
}
uint8_t *start = (uint8_t*)pkt->hdr + opt->offset;

/* Find start of option value and value length. */
uint16_t delta;
int len;

start = _parse_option(pkt, start, &delta, &len);
if (!start) {
return -ENOENT;
}

*value = start;
opt->opt_num += delta;
opt->offset = start + len - (uint8_t*)pkt->hdr;
return len;
}

ssize_t coap_opt_get_string(const coap_pkt_t *pkt, uint16_t optnum,
uint8_t *target, size_t max_len, char separator)
{
Expand Down
74 changes: 74 additions & 0 deletions tests/unittests/tests-nanocoap/tests-nanocoap.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,79 @@ static void test_nanocoap__server_option_count_overflow(void)
TEST_ASSERT(res < 0);
}

/*
* Helper for options tests below.
* POST request to a CoRE RD server to update the entries for a node
* from RIOT cord_ep example. Generated by RIOT.
* Includes 4 options:
* Uri-Path: resourcedirectory
* Content-Format: 40 (0x28)
* Uri-Query: ep-RIOT-0C49232323232323
* Uri-Query: lt=60
* Payload: </node/info> (absent if omit_payload)
*/
static int _read_rd_post_req(coap_pkt_t *pkt, bool omit_payload)
{
static uint8_t pkt_data[] = {
0x42, 0x02, 0x20, 0x92, 0xb9, 0x27, 0xbd, 0x04,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72,
0x79, 0x11, 0x28, 0x3d, 0x0b, 0x65, 0x70, 0x3d,
0x52, 0x49, 0x4f, 0x54, 0x2d, 0x30, 0x43, 0x34,
0x39, 0x32, 0x33, 0x32, 0x33, 0x32, 0x33, 0x32,
0x33, 0x32, 0x33, 0x32, 0x33, 0x05, 0x6c, 0x74,
0x3d, 0x36, 0x30, 0xff, 0x3c, 0x2f, 0x6e, 0x6f,
0x64, 0x65, 0x2f, 0x69, 0x6e, 0x66, 0x6f, 0x3e
};

size_t len = omit_payload ? 59 : sizeof(pkt_data);
return coap_parse(pkt, pkt_data, len);
}

/*
* Tests use of coap_opt_get_next() to iterate over options.
*/
static void test_nanocoap__options_iterate(void)
{
coap_pkt_t pkt;
int res = _read_rd_post_req(&pkt, true);
TEST_ASSERT_EQUAL_INT(0, res);

/* read all options */
coap_optpos_t opt = {0, 0};
uint8_t *value;
ssize_t exp_len[] = {17, 1, 24, 5, -ENOENT};
ssize_t exp_optnum[] = {COAP_OPT_URI_PATH, COAP_OPT_CONTENT_FORMAT,
COAP_OPT_URI_QUERY, COAP_OPT_URI_QUERY};

for (int i = 0; i < 5; i++) {
ssize_t optlen = coap_opt_get_next(&pkt, &opt, &value, !i);
TEST_ASSERT_EQUAL_INT(exp_len[i], optlen);
if (optlen >= 0) {
TEST_ASSERT_EQUAL_INT(exp_optnum[i], opt.opt_num);
}
else {
TEST_ASSERT_EQUAL_INT(4, i);
}
}

/* test with no payload to verify end of options handling */
memset(&pkt, 0, sizeof(pkt));
res = _read_rd_post_req(&pkt, false);
TEST_ASSERT_EQUAL_INT(0, res);

for (int i = 0; i < 5; i++) {
ssize_t optlen = coap_opt_get_next(&pkt, &opt, &value, !i);
TEST_ASSERT_EQUAL_INT(exp_len[i], optlen);
if (optlen >= 0) {
TEST_ASSERT_EQUAL_INT(exp_optnum[i], opt.opt_num);
}
else {
TEST_ASSERT_EQUAL_INT(4, i);
}
}
}

Test *tests_nanocoap_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
Expand All @@ -561,6 +634,7 @@ Test *tests_nanocoap_tests(void)
new_TestFixture(test_nanocoap__get_query),
new_TestFixture(test_nanocoap__get_multi_query),
new_TestFixture(test_nanocoap__option_add_buffer_max),
new_TestFixture(test_nanocoap__options_iterate),
new_TestFixture(test_nanocoap__server_get_req),
new_TestFixture(test_nanocoap__server_reply_simple),
new_TestFixture(test_nanocoap__server_get_req_con),
Expand Down

0 comments on commit c78ae0e

Please sign in to comment.