From 32ab8cf7248d3a0e6c8f411740b41f1cf4cdba9d Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Tue, 25 Jul 2017 20:36:24 +0200 Subject: [PATCH 1/2] net/gcoap: expose creation of resource list to API Creating the resource list is useful outside the /.well-known/core handler, e.g. for the registration to resource directories. --- sys/include/net/gcoap.h | 20 +++++++ sys/net/application_layer/coap/gcoap.c | 76 ++++++++++++++++---------- 2 files changed, 68 insertions(+), 28 deletions(-) diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h index 375a57c49821..0c4c4c2070c7 100644 --- a/sys/include/net/gcoap.h +++ b/sys/include/net/gcoap.h @@ -634,6 +634,26 @@ size_t gcoap_obs_send(const uint8_t *buf, size_t len, */ uint8_t gcoap_op_state(void); +/** + * @brief Get the resource list, currently only `CoRE Link Format` + * (COAP_FORMAT_LINK) supported + * + * If @p buf := NULL, nothing will be written but the size of the resulting + * resource list is computed and returned. + * + * @param[out] buf output buffer to write resource list into, my be NULL + * @param[in] maxlen length of @p buf, ignored if @p buf is NULL + * @param[in] cf content format to use for the resource list, currently + * only COAP_FORMAT_LINK supported + * + * @todo add support for `JSON CoRE Link Format` + * @todo add support for 'CBOR CoRE Link Format` + * + * @return the number of bytes written to @p buf + * @return -1 on error + */ +int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf); + #ifdef __cplusplus } #endif diff --git a/sys/net/application_layer/coap/gcoap.c b/sys/net/application_layer/coap/gcoap.c index 1284b02c4e4e..a7fbcdc8dd4c 100644 --- a/sys/net/application_layer/coap/gcoap.c +++ b/sys/net/application_layer/coap/gcoap.c @@ -398,35 +398,10 @@ static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t le { /* write header */ gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); - - /* skip the first listener, gcoap itself */ - gcoap_listener_t *listener = _coap_state.listeners->next; - - /* write payload */ - uint8_t *bufpos = pdu->payload; - - while (listener) { - coap_resource_t *resource = listener->resources; - for (size_t i = 0; i < listener->resources_len; i++) { - /* Don't overwrite buffer if paths are too long. */ - if (bufpos + strlen(resource->path) + 3 > buf + len) { - break; - } - if (i) { - *bufpos++ = ','; - resource++; - } - *bufpos++ = '<'; - unsigned url_len = strlen(resource->path); - memcpy(bufpos, resource->path, url_len); - bufpos += url_len; - *bufpos++ = '>'; - } - listener = listener->next; - } - + int plen = gcoap_get_resource_list(pdu->payload, (size_t)pdu->payload_len, + COAP_FORMAT_LINK); /* response content */ - return gcoap_finish(pdu, bufpos - pdu->payload, COAP_FORMAT_LINK); + return gcoap_finish(pdu, (size_t)plen, COAP_FORMAT_LINK); } /* @@ -819,4 +794,49 @@ uint8_t gcoap_op_state(void) return count; } +int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf) +{ + assert(cf == COAP_CT_LINK_FORMAT); +#ifndef DEVELHELP + (void)cf; +#endif + + /* skip the first listener, gcoap itself (we skip /.well-known/core) */ + gcoap_listener_t *listener = _coap_state.listeners->next; + + char *out = (char *)buf; + size_t pos = 0; + + /* write payload */ + while (listener) { + coap_resource_t *resource = listener->resources; + + for (unsigned i = 0; i < listener->resources_len; i++) { + size_t path_len = strlen(resource->path); + if (out) { + /* only add new resources if there is space in the buffer */ + if ((pos + path_len + 3) > maxlen) { + break; + } + if (i) { + out[pos++] = ','; + } + out[pos++] = '<'; + memcpy(&out[pos], resource->path, path_len); + pos += path_len; + out[pos++] = '>'; + } + else { + pos += (i) ? 3 : 2; + pos += path_len; + } + ++resource; + } + + listener = listener->next; + } + + return (int)pos; +} + /** @} */ From 813c92e52ab7a6760564d3bcd2d41b7d7c9e8233 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Mon, 7 Aug 2017 14:03:09 +0200 Subject: [PATCH 2/2] unittests/gcoap: add tests for _get_resource_list --- tests/unittests/tests-gcoap/tests-gcoap.c | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/unittests/tests-gcoap/tests-gcoap.c b/tests/unittests/tests-gcoap/tests-gcoap.c index 6e9b57fb5a26..067d6efcbe25 100644 --- a/tests/unittests/tests-gcoap/tests-gcoap.c +++ b/tests/unittests/tests-gcoap/tests-gcoap.c @@ -22,6 +22,23 @@ #include "unittests-constants.h" #include "tests-gcoap.h" +/* + * A test set of dummy resources. The resource handlers are set to NULL. + */ +static const coap_resource_t ressources[] = { + { "/test/info/all", (COAP_GET), NULL }, + { "/sensor/temp", (COAP_GET), NULL }, + { "/act/switch", (COAP_GET | COAP_POST), NULL } +}; + +static gcoap_listener_t listener = { + .resources = (coap_resource_t *)&ressources[0], + .resources_len = (sizeof(ressources) / sizeof(ressources[0])), + .next = NULL +}; + +static const char *resource_list_str = ",,"; + /* * Client GET request success case. Test request generation. * Request /time resource from libcoap example @@ -226,6 +243,30 @@ static void test_gcoap__server_con_resp(void) TEST_ASSERT_EQUAL_INT(sizeof(resp_data), res); } +/* + * Test the export of configured resources as CoRE link format string + */ +static void test_gcoap__server_get_resource_list(void) +{ + char res[128]; + int size = 0; + + gcoap_register_listener(&listener); + + size = gcoap_get_resource_list(NULL, 0, COAP_CT_LINK_FORMAT); + TEST_ASSERT_EQUAL_INT(strlen(resource_list_str), size); + + res[0] = 'A'; + size = gcoap_get_resource_list(res, 0, COAP_CT_LINK_FORMAT); + TEST_ASSERT_EQUAL_INT(0, size); + TEST_ASSERT_EQUAL_INT((int)'A', (int)res[0]); + + size = gcoap_get_resource_list(res, 127, COAP_CT_LINK_FORMAT); + res[size] = '\0'; + TEST_ASSERT_EQUAL_INT(strlen(resource_list_str), size); + TEST_ASSERT_EQUAL_STRING(resource_list_str, (char *)res); +} + Test *tests_gcoap_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -235,6 +276,7 @@ Test *tests_gcoap_tests(void) new_TestFixture(test_gcoap__server_get_resp), new_TestFixture(test_gcoap__server_con_req), new_TestFixture(test_gcoap__server_con_resp), + new_TestFixture(test_gcoap__server_get_resource_list) }; EMB_UNIT_TESTCALLER(gcoap_tests, NULL, NULL, fixtures);