diff --git a/sys/include/net/coap.h b/sys/include/net/coap.h index 797d4d7f8a73..6b9323524a7b 100644 --- a/sys/include/net/coap.h +++ b/sys/include/net/coap.h @@ -540,6 +540,7 @@ typedef enum { * @brief Marks the boundary between header and payload */ #define COAP_PAYLOAD_MARKER (0xFF) +#define COAP_PAYLOAD_MARKER_SIZE (1U) /**< Size of the payload marker */ /** * @defgroup net_coap_conf CoAP compile configurations diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 4ae8107736e3..8f52b34ce7d3 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -2077,29 +2077,60 @@ ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, const void *token, * will create the reply packet header based on parameters from the request * (e.g., id, token). * - * Passing a non-zero @p payload_len will ensure the payload fits into the - * buffer along with the header. For this validation, payload_len must include - * any options, the payload marker, as well as the payload proper. - * - * @param[in] pkt packet to reply to - * @param[in] code reply code (e.g., COAP_CODE_204) - * @param[out] rbuf buffer to write reply to - * @param[in] rlen size of @p rbuf - * @param[in] payload_len length of payload - * - * @returns size of reply packet on success - * - * Note that this size can be severely shortened if due to a No-Response option there - * is only an empty ACK to be sent back. The caller may just continue populating the - * payload (the space was checked to suffice), but may also skip that needless step - * if the returned length is less than the requested payload length. - * - * @returns 0 if no response should be sent due to a No-Response option in the request - * @returns <0 on error - * @returns -ENOSPC if @p rbuf too small + * Passing a non-zero @p max_data_len will ensure the remaining data fits into + * the buffer along with the header. For this validation, @p max_data_len must + * include any CoAP Options, the payload marker, as well as the payload proper. + * + * @param[in] pkt packet to reply to + * @param[in] code reply code (e.g., COAP_CODE_204) + * @param[out] rbuf buffer to write reply to + * @param[in] rlen size of @p rbuf + * @param[in] max_data_len Length of additional CoAP options, the payload marker and payload + * + * @warning CoAP request handlers *must* check the return value for being + * negative. If it is, they must stop further processing of the + * request and pass on the return value unmodified. + * + * @return @p max_data_len + size of the header written in bytes + * + * @retval -ECANCELED No-Response Option present and matching + * @retval -ENOSPC @p rbuf too small + * @retval <0 other error + * + * Usage: + * + * ```C + * static ssize_t _foo_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, + * coap_request_ctx_t *context) + * { + * static const char *payload = "Hello World"; + * const payload_len = strlen(payload); + * // Worst case estimation of the data to add: + * size_t max_data_len = COAP_OPT_FOO_MAX_LEN + COAP_OPT_BAR_MAX_LEN + * + COAP_PAYLOAD_MARKER_SIZE + payload_len; + * ssize_t hdr_len = coap_build_reply(pkt, COAP_CODE_CONTENT, buf, len, max_data_len); + * + * if (hdr_len < 0) { + * return hdr_len; // pass through error + * } + * + * // This step is needed due to an API design flaw + * hdr_len -= max_data_len; + * + * uint8_t *pos = buf + hdr_len; + * uint16_t lastonum = 0; + * pos += coap_opt_put_uint(buf, lastonum, COAP_OPT_FOO, 42); + * lastonum = COAP_OPT_FOO; + * pos += coap_opt_put_uint(buf, lastonum, COAP_OPT_BAR, 1337); + * *pos++ = COAP_PAYLOAD_MARKER; + * memcpy(pos, payload, payload_len); + * pos += payload_len; + * return (uintptr_t)pos - (uintptr_t)buf; + * } + * ``` */ ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, - uint8_t *rbuf, unsigned rlen, unsigned payload_len); + uint8_t *rbuf, unsigned rlen, unsigned max_data_len); /** * @brief Build empty reply to CoAP request diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index 1d218bbb945a..46f830d2fd7d 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -508,8 +508,20 @@ ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_le if (pkt->hdr->code == 0) { return coap_build_reply(pkt, COAP_CODE_EMPTY, resp_buf, resp_buf_len, 0); } - return coap_tree_handler(pkt, resp_buf, resp_buf_len, ctx, - coap_resources, coap_resources_numof); + + ssize_t retval = coap_tree_handler(pkt, resp_buf, resp_buf_len, ctx, + coap_resources, coap_resources_numof); + + if (retval == -ECANCELED) { + DEBUG_PUTS("nanocoap: No-Reponse Option present and matching"); + /* ==> reply with empty ACK, if needed */ + if (coap_get_type(pkt) == COAP_TYPE_CON) { + return coap_build_empty_ack(pkt, (void *)resp_buf); + } + return 0; + } + + return retval; } ssize_t coap_subtree_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, @@ -591,10 +603,7 @@ ssize_t coap_build_reply_header(coap_pkt_t *pkt, unsigned code, payload = NULL; } - /* no-response requested, only send empty ACK or nothing */ - if (type != COAP_TYPE_ACK) { - return 0; - } + return -ECANCELED; } } @@ -690,18 +699,7 @@ ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, /* option contains bitmap of disinterest */ if (no_response & mask) { - switch (coap_get_type(pkt)) { - case COAP_TYPE_NON: - /* no response and no ACK */ - return 0; - default: - /* There is an immediate ACK response, but it is an empty response */ - code = COAP_CODE_EMPTY; - len = sizeof(coap_hdr_t); - tkl = 0; - payload_len = 0; - break; - } + return -ECANCELED; } }