diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 4ae8107736e3..9d1f27b6b368 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -2077,29 +2077,56 @@ 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 + * + * @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..a5126f626106 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,