From 6dcb32d7b3a247251930aebf7866e8411e7804b8 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 4 Jan 2022 18:38:01 +0100 Subject: [PATCH] examples/gcoap: split client and server implementation Move client and server side implementations into separate files to increase readability. Also get rid of a goto. --- examples/gcoap/{gcoap_cli.c => client.c} | 180 ++------------------ examples/gcoap/gcoap_example.h | 65 ++++++++ examples/gcoap/main.c | 7 +- examples/gcoap/server.c | 204 +++++++++++++++++++++++ examples/gcoap_dtls/client.c | 1 + examples/gcoap_dtls/gcoap_cli.c | 1 - examples/gcoap_dtls/gcoap_example.h | 1 + examples/gcoap_dtls/server.c | 1 + 8 files changed, 290 insertions(+), 170 deletions(-) rename examples/gcoap/{gcoap_cli.c => client.c} (64%) create mode 100644 examples/gcoap/gcoap_example.h create mode 100644 examples/gcoap/server.c create mode 120000 examples/gcoap_dtls/client.c delete mode 120000 examples/gcoap_dtls/gcoap_cli.c create mode 120000 examples/gcoap_dtls/gcoap_example.h create mode 120000 examples/gcoap_dtls/server.c diff --git a/examples/gcoap/gcoap_cli.c b/examples/gcoap/client.c similarity index 64% rename from examples/gcoap/gcoap_cli.c rename to examples/gcoap/client.c index d8dc9f56fbb8..d22c62eac0d3 100644 --- a/examples/gcoap/gcoap_cli.c +++ b/examples/gcoap/client.c @@ -23,92 +23,32 @@ #include #include #include + +#include "fmt.h" #include "net/gcoap.h" #include "net/utils.h" #include "od.h" -#include "fmt.h" + +#include "gcoap_example.h" #define ENABLE_DEBUG 0 #include "debug.h" #if IS_USED(MODULE_GCOAP_DTLS) -#include "net/credman.h" #include "net/dsm.h" -#include "tinydtls_keys.h" - -/* Example credential tag for credman. Tag together with the credential type needs to be unique. */ -#define GCOAP_DTLS_CREDENTIAL_TAG 10 - -static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY; -static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY; -static const credman_credential_t credential = { - .type = CREDMAN_TYPE_PSK, - .tag = GCOAP_DTLS_CREDENTIAL_TAG, - .params = { - .psk = { - .key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, }, - .id = { .s = psk_id_0, .len = sizeof(psk_id_0) - 1, }, - } - }, -}; #endif static bool _proxied = false; static sock_udp_ep_t _proxy_remote; static char proxy_uri[64]; -static ssize_t _encode_link(const coap_resource_t *resource, char *buf, - size_t maxlen, coap_link_encoder_ctx_t *context); -static void _resp_handler(const gcoap_request_memo_t *memo, coap_pkt_t* pdu, - const sock_udp_ep_t *remote); -static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx); -static ssize_t _riot_board_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx); - -/* CoAP resources. Must be sorted by path (ASCII order). */ -static const coap_resource_t _resources[] = { - { "/cli/stats", COAP_GET | COAP_PUT, _stats_handler, NULL }, - { "/riot/board", COAP_GET, _riot_board_handler, NULL }, -}; - -static const char *_link_params[] = { - ";ct=0;rt=\"count\";obs", - NULL -}; - -static gcoap_listener_t _listener = { - &_resources[0], - ARRAY_SIZE(_resources), - _encode_link, - NULL, - NULL -}; - /* Retain request path to re-request if response includes block. User must not * start a new request (with a new path) until any blockwise transfer * completes or times out. */ #define _LAST_REQ_PATH_MAX (64) static char _last_req_path[_LAST_REQ_PATH_MAX]; -/* Counts requests sent by CLI. */ -static uint16_t req_count = 0; - -/* Adds link format params to resource list */ -static ssize_t _encode_link(const coap_resource_t *resource, char *buf, - size_t maxlen, coap_link_encoder_ctx_t *context) { - ssize_t res = gcoap_encode_link(resource, buf, maxlen, context); - if (res > 0) { - if (_link_params[context->link_pos] - && (strlen(_link_params[context->link_pos]) < (maxlen - res))) { - if (buf) { - memcpy(buf+res, _link_params[context->link_pos], - strlen(_link_params[context->link_pos])); - } - return res + strlen(_link_params[context->link_pos]); - } - } - - return res; -} +uint16_t req_count = 0; /* * Response callback. @@ -201,66 +141,6 @@ static void _resp_handler(const gcoap_request_memo_t *memo, coap_pkt_t* pdu, } } -/* - * Server callback for /cli/stats. Accepts either a GET or a PUT. - * - * GET: Returns the count of packets sent by the CLI. - * PUT: Updates the count of packets. Rejects an obviously bad request, but - * allows any two byte value for example purposes. Semantically, the only - * valid action is to set the value to 0. - */ -static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx) -{ - (void)ctx; - - /* read coap method type in packet */ - unsigned method_flag = coap_method2flag(coap_get_code_detail(pdu)); - - switch (method_flag) { - case COAP_GET: - gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); - coap_opt_add_format(pdu, COAP_FORMAT_TEXT); - size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); - - /* write the response buffer with the request count value */ - resp_len += fmt_u16_dec((char *)pdu->payload, req_count); - return resp_len; - - case COAP_PUT: - /* convert the payload to an integer and update the internal - value */ - if (pdu->payload_len <= 5) { - char payload[6] = { 0 }; - memcpy(payload, (char *)pdu->payload, pdu->payload_len); - req_count = (uint16_t)strtoul(payload, NULL, 10); - return gcoap_response(pdu, buf, len, COAP_CODE_CHANGED); - } - else { - return gcoap_response(pdu, buf, len, COAP_CODE_BAD_REQUEST); - } - } - - return 0; -} - -static ssize_t _riot_board_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx) -{ - (void)ctx; - gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); - coap_opt_add_format(pdu, COAP_FORMAT_TEXT); - size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); - - /* write the RIOT board name in the response buffer */ - if (pdu->payload_len >= strlen(RIOT_BOARD)) { - memcpy(pdu->payload, RIOT_BOARD, strlen(RIOT_BOARD)); - return resp_len + strlen(RIOT_BOARD); - } - else { - puts("gcoap_cli: msg buffer too small"); - return gcoap_response(pdu, buf, len, COAP_CODE_INTERNAL_SERVER_ERROR); - } -} - static bool _parse_endpoint(sock_udp_ep_t *remote, const char *addr_str, const char *port_str) { @@ -307,6 +187,12 @@ static size_t _send(uint8_t *buf, size_t len, char *addr_str, char *port_str) return bytes_sent; } +static int _print_usage(char **argv) +{ + printf("usage: %s \n", argv[0]); + return 1; +} + int gcoap_cli_cmd(int argc, char **argv) { /* Ordered like the RFC method code numbers, but off by 1. GET is code 0. */ @@ -317,7 +203,7 @@ int gcoap_cli_cmd(int argc, char **argv) if (argc == 1) { /* show help for main commands */ - goto end; + return _print_usage(argv); } if (strcmp(argv[1], "info") == 0) { @@ -376,7 +262,7 @@ int gcoap_cli_cmd(int argc, char **argv) } } if (code_pos == -1) { - goto end; + return _print_usage(argv); } /* parse options */ @@ -447,22 +333,7 @@ int gcoap_cli_cmd(int argc, char **argv) } else { /* send Observe notification for /cli/stats */ - switch (gcoap_obs_init(&pdu, &buf[0], CONFIG_GCOAP_PDU_BUF_SIZE, - &_resources[0])) { - case GCOAP_OBS_INIT_OK: - DEBUG("gcoap_cli: creating /cli/stats notification\n"); - coap_opt_add_format(&pdu, COAP_FORMAT_TEXT); - len = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD); - len += fmt_u16_dec((char *)pdu.payload, req_count); - gcoap_obs_send(&buf[0], len, &_resources[0]); - break; - case GCOAP_OBS_INIT_UNUSED: - DEBUG("gcoap_cli: no observer for /cli/stats\n"); - break; - case GCOAP_OBS_INIT_ERR: - DEBUG("gcoap_cli: error initializing /cli/stats notification\n"); - break; - } + notify_observers(); } return 0; } @@ -475,26 +346,5 @@ int gcoap_cli_cmd(int argc, char **argv) return 1; } - end: - printf("usage: %s \n", argv[0]); - return 1; -} - -void gcoap_cli_init(void) -{ -#if IS_USED(MODULE_GCOAP_DTLS) - int res = credman_add(&credential); - if (res < 0 && res != CREDMAN_EXIST) { - /* ignore duplicate credentials */ - printf("gcoap: cannot add credential to system: %d\n", res); - return; - } - sock_dtls_t *gcoap_sock_dtls = gcoap_get_sock_dtls(); - res = sock_dtls_add_credential(gcoap_sock_dtls, GCOAP_DTLS_CREDENTIAL_TAG); - if (res < 0) { - printf("gcoap: cannot add credential to DTLS sock: %d\n", res); - } -#endif - - gcoap_register_listener(&_listener); + return _print_usage(argv); } diff --git a/examples/gcoap/gcoap_example.h b/examples/gcoap/gcoap_example.h new file mode 100644 index 000000000000..eeb683904081 --- /dev/null +++ b/examples/gcoap/gcoap_example.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief gcoap example + * + * @author Ken Bannister + */ + +#ifndef GCOAP_EXAMPLE_H +#define GCOAP_EXAMPLE_H + +#include +#include +#include +#include + +#include "fmt.h" +#include "net/gcoap.h" +#include "net/utils.h" +#include "od.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint16_t req_count; /**< Counts requests sent by CLI. */ + +/** + * @brief Shell interface exposing the client side features of gcoap + * @param argc Number of shell arguments (including shell command name) + * @param argv Shell argument values (including shell command name) + * @return Exit status of the shell command + */ +int gcoap_cli_cmd(int argc, char **argv); + +/** + * @brief Registers the CoAP resources exposed in the example app + * + * Run this exactly one during startup. + */ +void server_init(void); + +/** + * @brief Notifies all observers registered to /cli/stats - if any + * + * Call this whenever the count of successfully send client requests changes + */ +void notify_observers(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GCOAP_EXAMPLE_H */ +/** @} */ diff --git a/examples/gcoap/main.c b/examples/gcoap/main.c index 930964ca7814..612b98af5f57 100644 --- a/examples/gcoap/main.c +++ b/examples/gcoap/main.c @@ -24,12 +24,11 @@ #include "net/gcoap.h" #include "shell.h" +#include "gcoap_example.h" + #define MAIN_QUEUE_SIZE (4) static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; -extern int gcoap_cli_cmd(int argc, char **argv); -extern void gcoap_cli_init(void); - static const shell_command_t shell_commands[] = { { "coap", "CoAP example", gcoap_cli_cmd }, { NULL, NULL, NULL } @@ -39,7 +38,7 @@ int main(void) { /* for the thread running the shell */ msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); - gcoap_cli_init(); + server_init(); puts("gcoap example app"); /* start shell */ diff --git a/examples/gcoap/server.c b/examples/gcoap/server.c new file mode 100644 index 000000000000..54b065b8a287 --- /dev/null +++ b/examples/gcoap/server.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2015-2017 Ken Bannister. All rights reserved. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief gcoap CLI support + * + * @author Ken Bannister + * @author Hauke Petersen + * + * @} + */ + +#include +#include +#include +#include + +#include "fmt.h" +#include "net/gcoap.h" +#include "net/utils.h" +#include "od.h" + +#include "gcoap_example.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#if IS_USED(MODULE_GCOAP_DTLS) +#include "net/credman.h" +#include "net/dsm.h" +#include "tinydtls_keys.h" + +/* Example credential tag for credman. Tag together with the credential type needs to be unique. */ +#define GCOAP_DTLS_CREDENTIAL_TAG 10 + +static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY; +static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY; +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_PSK, + .tag = GCOAP_DTLS_CREDENTIAL_TAG, + .params = { + .psk = { + .key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, }, + .id = { .s = psk_id_0, .len = sizeof(psk_id_0) - 1, }, + } + }, +}; +#endif + +static ssize_t _encode_link(const coap_resource_t *resource, char *buf, + size_t maxlen, coap_link_encoder_ctx_t *context); +static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx); +static ssize_t _riot_board_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx); + +/* CoAP resources. Must be sorted by path (ASCII order). */ +static const coap_resource_t _resources[] = { + { "/cli/stats", COAP_GET | COAP_PUT, _stats_handler, NULL }, + { "/riot/board", COAP_GET, _riot_board_handler, NULL }, +}; + +static const char *_link_params[] = { + ";ct=0;rt=\"count\";obs", + NULL +}; + +static gcoap_listener_t _listener = { + &_resources[0], + ARRAY_SIZE(_resources), + _encode_link, + NULL, + NULL +}; + + +/* Adds link format params to resource list */ +static ssize_t _encode_link(const coap_resource_t *resource, char *buf, + size_t maxlen, coap_link_encoder_ctx_t *context) { + ssize_t res = gcoap_encode_link(resource, buf, maxlen, context); + if (res > 0) { + if (_link_params[context->link_pos] + && (strlen(_link_params[context->link_pos]) < (maxlen - res))) { + if (buf) { + memcpy(buf+res, _link_params[context->link_pos], + strlen(_link_params[context->link_pos])); + } + return res + strlen(_link_params[context->link_pos]); + } + } + + return res; +} + +/* + * Server callback for /cli/stats. Accepts either a GET or a PUT. + * + * GET: Returns the count of packets sent by the CLI. + * PUT: Updates the count of packets. Rejects an obviously bad request, but + * allows any two byte value for example purposes. Semantically, the only + * valid action is to set the value to 0. + */ +static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx) +{ + (void)ctx; + + /* read coap method type in packet */ + unsigned method_flag = coap_method2flag(coap_get_code_detail(pdu)); + + switch (method_flag) { + case COAP_GET: + gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); + coap_opt_add_format(pdu, COAP_FORMAT_TEXT); + size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); + + /* write the response buffer with the request count value */ + resp_len += fmt_u16_dec((char *)pdu->payload, req_count); + return resp_len; + + case COAP_PUT: + /* convert the payload to an integer and update the internal + value */ + if (pdu->payload_len <= 5) { + char payload[6] = { 0 }; + memcpy(payload, (char *)pdu->payload, pdu->payload_len); + req_count = (uint16_t)strtoul(payload, NULL, 10); + return gcoap_response(pdu, buf, len, COAP_CODE_CHANGED); + } + else { + return gcoap_response(pdu, buf, len, COAP_CODE_BAD_REQUEST); + } + } + + return 0; +} + +static ssize_t _riot_board_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx) +{ + (void)ctx; + gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); + coap_opt_add_format(pdu, COAP_FORMAT_TEXT); + size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); + + /* write the RIOT board name in the response buffer */ + if (pdu->payload_len >= strlen(RIOT_BOARD)) { + memcpy(pdu->payload, RIOT_BOARD, strlen(RIOT_BOARD)); + return resp_len + strlen(RIOT_BOARD); + } + else { + puts("gcoap_cli: msg buffer too small"); + return gcoap_response(pdu, buf, len, COAP_CODE_INTERNAL_SERVER_ERROR); + } +} + +void notify_observers(void) +{ + size_t len; + uint8_t buf[CONFIG_GCOAP_PDU_BUF_SIZE]; + coap_pkt_t pdu; + + /* send Observe notification for /cli/stats */ + switch (gcoap_obs_init(&pdu, &buf[0], CONFIG_GCOAP_PDU_BUF_SIZE, + &_resources[0])) { + case GCOAP_OBS_INIT_OK: + DEBUG("gcoap_cli: creating /cli/stats notification\n"); + coap_opt_add_format(&pdu, COAP_FORMAT_TEXT); + len = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD); + len += fmt_u16_dec((char *)pdu.payload, req_count); + gcoap_obs_send(&buf[0], len, &_resources[0]); + break; + case GCOAP_OBS_INIT_UNUSED: + DEBUG("gcoap_cli: no observer for /cli/stats\n"); + break; + case GCOAP_OBS_INIT_ERR: + DEBUG("gcoap_cli: error initializing /cli/stats notification\n"); + break; + } +} + +void server_init(void) +{ +#if IS_USED(MODULE_GCOAP_DTLS) + int res = credman_add(&credential); + if (res < 0 && res != CREDMAN_EXIST) { + /* ignore duplicate credentials */ + printf("gcoap: cannot add credential to system: %d\n", res); + return; + } + sock_dtls_t *gcoap_sock_dtls = gcoap_get_sock_dtls(); + res = sock_dtls_add_credential(gcoap_sock_dtls, GCOAP_DTLS_CREDENTIAL_TAG); + if (res < 0) { + printf("gcoap: cannot add credential to DTLS sock: %d\n", res); + } +#endif + + gcoap_register_listener(&_listener); +} diff --git a/examples/gcoap_dtls/client.c b/examples/gcoap_dtls/client.c new file mode 120000 index 000000000000..c6a81dc61710 --- /dev/null +++ b/examples/gcoap_dtls/client.c @@ -0,0 +1 @@ +../gcoap/client.c \ No newline at end of file diff --git a/examples/gcoap_dtls/gcoap_cli.c b/examples/gcoap_dtls/gcoap_cli.c deleted file mode 120000 index 22f3ebcab197..000000000000 --- a/examples/gcoap_dtls/gcoap_cli.c +++ /dev/null @@ -1 +0,0 @@ -../gcoap/gcoap_cli.c \ No newline at end of file diff --git a/examples/gcoap_dtls/gcoap_example.h b/examples/gcoap_dtls/gcoap_example.h new file mode 120000 index 000000000000..2862a8e533a4 --- /dev/null +++ b/examples/gcoap_dtls/gcoap_example.h @@ -0,0 +1 @@ +../gcoap/gcoap_example.h \ No newline at end of file diff --git a/examples/gcoap_dtls/server.c b/examples/gcoap_dtls/server.c new file mode 120000 index 000000000000..a082f6e616c0 --- /dev/null +++ b/examples/gcoap_dtls/server.c @@ -0,0 +1 @@ +../gcoap/server.c \ No newline at end of file