From a6b49a178d5505f905af9dd424c66994a6af712b Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Sun, 27 Jan 2019 08:08:25 -0500 Subject: [PATCH 1/4] net/nanocoap: add inline function for Content-Format --- sys/include/net/nanocoap.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 00e716f50c1c..c39e982d283b 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -639,6 +639,23 @@ ssize_t coap_opt_add_string(coap_pkt_t *pkt, uint16_t optnum, const char *string */ ssize_t coap_opt_add_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t value); +/** + * @brief Append a Content-Format option to the pkt buffer + * + * @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] format COAP_FORMAT_xxx to use + * + * @return number of bytes written to buffer + * @return <0 reserved for error but not implemented yet + */ +static inline ssize_t coap_opt_add_format(coap_pkt_t *pkt, uint16_t format) +{ + return coap_opt_add_uint(pkt, COAP_OPT_CONTENT_FORMAT, format); +} + /** * @brief Finalizes options as required and prepares for payload * From 93da7372fd0c5db649292a988e6b11a534b9adc0 Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Sun, 27 Jan 2019 08:51:02 -0500 Subject: [PATCH 2/4] net/gcoap: use coap_opt_finish within gcoap --- sys/include/net/gcoap.h | 4 ++-- sys/net/application_layer/gcoap/gcoap.c | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h index 9bff25d526c8..159d9d49f1d8 100644 --- a/sys/include/net/gcoap.h +++ b/sys/include/net/gcoap.h @@ -582,7 +582,7 @@ static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code, char *path) { return (gcoap_req_init(pdu, buf, len, code, path) == 0) - ? gcoap_finish(pdu, 0, COAP_FORMAT_NONE) + ? coap_opt_finish(pdu, COAP_OPT_FINISH_NONE) : -1; } @@ -648,7 +648,7 @@ static inline ssize_t gcoap_response(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code) { return (gcoap_resp_init(pdu, buf, len, code) == 0) - ? gcoap_finish(pdu, 0, COAP_FORMAT_NONE) + ? coap_opt_finish(pdu, COAP_OPT_FINISH_NONE) : -1; } diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 3313db4af15a..c3fa0121675b 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -496,15 +496,18 @@ static void _expire_request(gcoap_request_memo_t *memo) * Handler for /.well-known/core. Lists registered handlers, except for * /.well-known/core itself. */ -static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx) +static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, + void *ctx) { (void)ctx; - /* write header */ + gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); - int plen = gcoap_get_resource_list(pdu->payload, (size_t)pdu->payload_len, - COAP_FORMAT_LINK); - /* response content */ - return gcoap_finish(pdu, (size_t)plen, COAP_FORMAT_LINK); + coap_opt_add_format(pdu, COAP_FORMAT_LINK); + ssize_t plen = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); + + plen += gcoap_get_resource_list(pdu->payload, (size_t)pdu->payload_len, + COAP_FORMAT_LINK); + return plen; } /* From e96209fddb032c2272efbeb23c5c04a6a7126f59 Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Sun, 27 Jan 2019 09:14:46 -0500 Subject: [PATCH 3/4] net/gcoap: use coap_opt_finish in unit tests --- tests/unittests/tests-gcoap/tests-gcoap.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/unittests/tests-gcoap/tests-gcoap.c b/tests/unittests/tests-gcoap/tests-gcoap.c index a7f82e0a7b5f..b67273ac5cb7 100644 --- a/tests/unittests/tests-gcoap/tests-gcoap.c +++ b/tests/unittests/tests-gcoap/tests-gcoap.c @@ -140,10 +140,11 @@ static void test_gcoap__client_put_req(void) char payload[] = "1"; gcoap_req_init(&pdu, buf, GCOAP_PDU_BUF_SIZE, COAP_METHOD_PUT, path); + coap_opt_add_format(&pdu, COAP_FORMAT_TEXT); + len = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD); memcpy(pdu.payload, payload, 1); - len = gcoap_finish(&pdu, 1, COAP_FORMAT_TEXT); - coap_parse(&pdu, buf, len); + coap_parse(&pdu, buf, len + 1); TEST_ASSERT_EQUAL_INT(COAP_METHOD_PUT, coap_get_code(&pdu)); TEST_ASSERT_EQUAL_INT(1, pdu.payload_len); @@ -172,7 +173,7 @@ static void test_gcoap__client_get_query(void) optlen = gcoap_add_qstring(&pdu, key2, NULL); TEST_ASSERT_EQUAL_INT(2, optlen); - size_t len = gcoap_finish(&pdu, 0, COAP_FORMAT_NONE); + size_t len = coap_opt_finish(&pdu, COAP_OPT_FINISH_NONE); coap_parse(&pdu, buf, len); @@ -198,7 +199,7 @@ static void test_gcoap__client_get_path_defer(void) coap_opt_add_string(&pdu, COAP_OPT_URI_PATH, path, '/'); optlen = 6; - len = gcoap_finish(&pdu, 0, COAP_FORMAT_NONE); + len = coap_opt_finish(&pdu, COAP_OPT_FINISH_NONE); TEST_ASSERT_EQUAL_INT(len, sizeof(coap_hdr_t) + GCOAP_TOKENLEN + optlen); coap_parse(&pdu, buf, len); @@ -262,9 +263,11 @@ static void test_gcoap__server_get_resp(void) /* generate response */ gcoap_resp_init(&pdu, &buf[0], sizeof(buf), COAP_CODE_CONTENT); + coap_opt_add_format(&pdu, COAP_FORMAT_TEXT); + ssize_t res = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD); + char resp_payload[] = "2"; memcpy(&pdu.payload[0], &resp_payload[0], strlen(resp_payload)); - ssize_t res = gcoap_finish(&pdu, strlen(resp_payload), COAP_FORMAT_TEXT); uint8_t resp_data[] = { 0x52, 0x45, 0x20, 0xb6, 0x35, 0x61, 0xc0, 0xff, @@ -275,8 +278,7 @@ static void test_gcoap__server_get_resp(void) TEST_ASSERT_EQUAL_INT(2, coap_get_token_len(&pdu)); TEST_ASSERT_EQUAL_INT(4 + 2, coap_get_total_hdr_len(&pdu)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_NON, coap_get_type(&pdu)); - TEST_ASSERT_EQUAL_INT(strlen(resp_payload), pdu.payload_len); - TEST_ASSERT_EQUAL_INT(sizeof(resp_data), res); + TEST_ASSERT_EQUAL_INT(sizeof(resp_data), res + 1); for (size_t i = 0; i < strlen(resp_payload); i++) { TEST_ASSERT_EQUAL_INT(resp_payload[i], pdu.payload[i]); @@ -326,9 +328,11 @@ static void test_gcoap__server_con_resp(void) /* generate response */ gcoap_resp_init(&pdu, &buf[0], sizeof(buf), COAP_CODE_CONTENT); + coap_opt_add_format(&pdu, COAP_FORMAT_TEXT); + ssize_t res = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD); + char resp_payload[] = "2"; memcpy(&pdu.payload[0], &resp_payload[0], strlen(resp_payload)); - ssize_t res = gcoap_finish(&pdu, strlen(resp_payload), COAP_FORMAT_TEXT); uint8_t resp_data[] = { 0x62, 0x45, 0x8e, 0x03, 0x35, 0x61, 0xc0, 0xff, @@ -337,7 +341,7 @@ static void test_gcoap__server_con_resp(void) TEST_ASSERT_EQUAL_INT(COAP_CLASS_SUCCESS, coap_get_code_class(&pdu)); TEST_ASSERT_EQUAL_INT(COAP_TYPE_ACK, coap_get_type(&pdu)); - TEST_ASSERT_EQUAL_INT(sizeof(resp_data), res); + TEST_ASSERT_EQUAL_INT(sizeof(resp_data), res + 1); } /* From f4a4f2419759699af683e420295a44901e4cf872 Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Mon, 28 Jan 2019 06:41:36 -0500 Subject: [PATCH 4/4] net/gcoap: update module doc for coap_opt_finish --- sys/include/net/gcoap.h | 81 +++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h index 159d9d49f1d8..752c4e56db86 100644 --- a/sys/include/net/gcoap.h +++ b/sys/include/net/gcoap.h @@ -17,9 +17,7 @@ * application only needs to focus on request/response handling. For a server, * gcoap accepts a list of resource paths with callbacks for writing the * response. For a client, gcoap provides a function to send a request, with a - * callback for reading the server response. Generation of the request or - * response requires from one to three well-defined steps, depending on - * inclusion of a payload. + * callback for reading the server response. * * gcoap allocates a RIOT message processing thread, so a single instance can * serve multiple applications. This approach also means gcoap uses a single UDP @@ -56,35 +54,37 @@ * ### Creating a response ### * * An application resource includes a callback function, a coap_handler_t. After - * reading the request, the callback must use one or two functions provided by - * gcoap to format the response, as described below. The callback *must* read - * the request thoroughly before calling the functions, because the response - * buffer likely reuses the request buffer. See `examples/gcoap/gcoap_cli.c` - * for a simple example of a callback. + * reading the request, the callback must use functions provided by gcoap to + * format the response, as described below. The callback *must* read the request + * thoroughly before calling the functions, because the response buffer likely + * reuses the request buffer. See `examples/gcoap/gcoap_cli.c` for a simple + * example of a callback. * * Here is the expected sequence for a callback function: * * Read request completely and parse request payload, if any. Use the * coap_pkt_t _payload_ and _payload_len_ attributes. * - * If there is a payload, follow the three steps below. + * If there is a payload, follow the steps below. * * -# Call gcoap_resp_init() to initialize the response. + * -# Use the coap_opt_add_xxx() functions to include any Options, for example + * coap_opt_add_format() for Content-Format of the payload. + * -# Call coap_opt_finish() to complete the PDU metadata. Retain the returned + * metadata length. * -# Write the response payload, starting at the updated _payload_ pointer - * in the coap_pkt_t. If some error occurs, return a negative errno - * code from the handler, and gcoap will send a server error (5.00). - * -# Call gcoap_finish() to complete the PDU after writing the payload, - * and return the result. gcoap will send the message. + * in the coap_pkt_t, for up to _payload_len_ bytes. + * -# Return the sum of the metadata length and payload length. If some error + * has occurred, return a negative errno code from the handler, and gcoap + * will send a server error (5.00). * - * If no payload, call only gcoap_response() to write the full response. - * Alternatively, you still can use gcoap_resp_init() and gcoap_finish(), as - * described above. In fact, the gcoap_response() function is inline, and uses - * those two functions. + * If no payload, call only gcoap_response() to write the full response. If you + * need to add Options, follow the first three steps in the list above instead. * * ## Client Operation ## * - * Client operation includes two phases: creating and sending a request, and - * handling the response aynchronously in a client supplied callback. See + * Client operation includes two phases: creating and sending a request, and + * handling the response aynchronously in a client supplied callback. See * `examples/gcoap/gcoap_cli.c` for a simple example of sending a request and * reading the response. * @@ -97,19 +97,21 @@ * If there is a payload, follow the steps below. * * -# Call gcoap_req_init() to initialize the request. - * -# Optionally, mark the request confirmable by calling - * coap_hdr_set_type() with COAP_TYPE_CON. + * -# Optionally, mark the request confirmable by calling coap_hdr_set_type() + * with COAP_TYPE_CON. + * -# Use the coap_opt_add_xxx() functions to include any Options beyond + * Uri-Path, which was added in the first step. + * -# Call coap_opt_finish() to complete the PDU metadata. Retain the returned + * metadata length. * -# Write the request payload, starting at the updated _payload_ pointer - * in the coap_pkt_t. - * -# Call gcoap_finish(), which updates the packet for the payload. + * in the coap_pkt_t, for up to _payload_len_ bytes. * - * If no payload, call only gcoap_request() to write the full request. - * Alternatively, you still can use gcoap_req_init() and gcoap_finish(), - * as described above. The gcoap_request() function is inline, and uses those - * two functions. + * If no payload, call only gcoap_request() to write the full request. If you + * need to add Options, follow the first four steps in the list above instead. * - * Finally, call gcoap_req_send2() for the destination endpoint, as well as a - * callback function for the host's response. + * Finally, call gcoap_req_send2() with the sum of the metadata length and + * payload length, the destination endpoint, and a callback function for the + * host's response. * * ### Handling the response ### * @@ -147,11 +149,15 @@ * -# Call gcoap_obs_init() to initialize the notification for a resource. * Test the return value, which may indicate there is not an observer for * the resource. If so, you are done. + * -# Use the coap_opt_add_xxx() functions to include any Options, for example + * coap_opt_add_format() for Content-Format of the payload. + * -# Call coap_opt_finish() to complete the PDU metadata. Retain the returned + * metadata length. * -# Write the notification payload, starting at the updated _payload_ pointer - * in the coap_pkt_t. - * -# Call gcoap_finish(), which updates the packet for the payload. + * in the coap_pkt_t, for up to _payload_len_ bytes. * - * Finally, call gcoap_obs_send() for the resource. + * Finally, call gcoap_obs_send() for the resource, with the sum of the + * metadata length and payload length for the representation. * * ### Other considerations ### * @@ -171,17 +177,6 @@ * * ## Implementation Notes ## * - * ### Building a packet ### - * - * The sequence and functions described above to build a request or response - * is designed to provide a relatively simple API for the user. - * - * The structure of a CoAP PDU requires that options are placed between the - * header and the payload. So, gcoap provides space in the buffer for them in - * the request/response ...init() function, and then writes them during - * gcoap_finish(). We trade some inefficiency/work in the buffer for - * simplicity in the API. - * * ### Waiting for a response ### * * We take advantage of RIOT's asynchronous messaging by using an xtimer to wait