diff --git a/examples/gcoap_block_server/Makefile b/examples/gcoap_block_server/Makefile new file mode 100644 index 0000000000..d4e2c6b26a --- /dev/null +++ b/examples/gcoap_block_server/Makefile @@ -0,0 +1,47 @@ +# Default Makefile, for host native GNRC-based networking + +# name of your application +APPLICATION = gcoap_example + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +## Uncomment to redefine port, for example use 61616 for RFC 6282 UDP compression. +#GCOAP_PORT = 5683 +#CFLAGS += -DGCOAP_PORT=$(GCOAP_PORT) + +## Uncomment to redefine request token length, max 8. +#GCOAP_TOKENLEN = 2 +#CFLAGS += -DGCOAP_TOKENLEN=$(GCOAP_TOKENLEN) + +# Include packages that pull up and auto-init the link layer. +# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present +USEMODULE += netdev_default +USEMODULE += auto_init_gnrc_netif +# Specify the mandatory networking modules +USEMODULE += gnrc_ipv6_default +USEMODULE += gcoap +# Additional networking modules that can be dropped if not needed +USEMODULE += gnrc_icmpv6_echo +#USEMODULE += gnrc_rpl + +# Required by handlers +USEMODULE += fmt +USEMODULE += hashes +# Add also the shell, some shell commands +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/gcoap_block_server/Makefile.ci b/examples/gcoap_block_server/Makefile.ci new file mode 100644 index 0000000000..43a6d8fd95 --- /dev/null +++ b/examples/gcoap_block_server/Makefile.ci @@ -0,0 +1,38 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-mega2560 \ + arduino-nano \ + arduino-uno \ + atmega1284p \ + atmega328p \ + atmega328p-xplained-mini \ + atxmega-a3bu-xplained \ + bluepill-stm32f030c8 \ + derfmega128 \ + i-nucleo-lrwan1 \ + m1284p \ + mega-xplained \ + microduino-corerf \ + msb-430 \ + msb-430h \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l011k4 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + samd10-xmini \ + slstk3400a \ + stk3200 \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32g0316-disco \ + stm32l0538-disco \ + telosb \ + waspmote-pro \ + z1 \ + zigduino \ + # diff --git a/examples/gcoap_block_server/README.md b/examples/gcoap_block_server/README.md new file mode 100644 index 0000000000..242a5586d9 --- /dev/null +++ b/examples/gcoap_block_server/README.md @@ -0,0 +1,8 @@ +This application provides examples of CoAP server handling for Block requests, build with gcoap using nanocoap block handling functions. + +This show the current status and is very likely to change with improvements made to gcoap and nanocoap and their API, see example/nanocoap_server for comparison. + +Relevant resources: + + * `/riot/ver` -- provides Block2 response payloads for GET request + * `/sha256` -- provides SHA-256 digest from Block1 POST request input diff --git a/examples/gcoap_block_server/gcoap_block.c b/examples/gcoap_block_server/gcoap_block.c new file mode 100644 index 0000000000..3b8d349215 --- /dev/null +++ b/examples/gcoap_block_server/gcoap_block.c @@ -0,0 +1,161 @@ +/* + * 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. + */ + +/** + * @{ + * + * @file + * @brief gcoap block server handler example + * + * @author Ken Bannister + * + * @} + */ + +#include +#include +#include +#include +#include "fmt.h" +#include "hashes/sha256.h" +#include "net/gcoap.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static ssize_t _sha256_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx); +static ssize_t _riot_block2_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx); + +/* CoAP resources */ +static const coap_resource_t _resources[] = { + { "/riot/ver", COAP_GET, _riot_block2_handler, NULL }, + { "/sha256", COAP_POST, _sha256_handler, NULL }, +}; + +static gcoap_listener_t _listener = { + .resources = _resources, + .resources_len = ARRAY_SIZE(_resources), +}; + +/* Constants for /riot/ver. */ +static const uint8_t block2_intro[] = "This is RIOT (Version: "; +static const uint8_t block2_board[] = " running on a "; +static const uint8_t block2_mcu[] = " board with a "; + +static ssize_t _riot_block2_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, void *ctx) +{ + (void)ctx; + coap_block_slicer_t slicer; + coap_block2_init(pdu, &slicer); + + gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); + coap_opt_add_format(pdu, COAP_FORMAT_TEXT); + coap_opt_add_block2(pdu, &slicer, 1); + ssize_t plen = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); + + /* Add actual content */ + plen += coap_blockwise_put_bytes(&slicer, buf+plen, block2_intro, sizeof(block2_intro)-1); + plen += coap_blockwise_put_bytes(&slicer, buf+plen, (uint8_t*)RIOT_VERSION, strlen(RIOT_VERSION)); + plen += coap_blockwise_put_char(&slicer, buf+plen, ')'); + plen += coap_blockwise_put_bytes(&slicer, buf+plen, block2_board, sizeof(block2_board)-1); + plen += coap_blockwise_put_bytes(&slicer, buf+plen, (uint8_t*)RIOT_BOARD, strlen(RIOT_BOARD)); + plen += coap_blockwise_put_bytes(&slicer, buf+plen, block2_mcu, sizeof(block2_mcu)-1); + plen += coap_blockwise_put_bytes(&slicer, buf+plen, (uint8_t*)RIOT_MCU, strlen(RIOT_MCU)); + /* To demonstrate individual chars */ + plen += coap_blockwise_put_char(&slicer, buf+plen, ' '); + plen += coap_blockwise_put_char(&slicer, buf+plen, 'M'); + plen += coap_blockwise_put_char(&slicer, buf+plen, 'C'); + plen += coap_blockwise_put_char(&slicer, buf+plen, 'U'); + plen += coap_blockwise_put_char(&slicer, buf+plen, '.'); + + coap_block2_finish(&slicer); + + return plen; +} + +/* + * Uses block1 POSTs to generate an sha256 digest. */ +static ssize_t _sha256_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, void *ctx) +{ + (void)ctx; + + /* using a shared sha256 context *will* break if two requests are handled + * at the same time. doing it anyways, as this is meant to showcase block1 + * support, not proper synchronisation. */ + static sha256_context_t sha256; + uint8_t digest[SHA256_DIGEST_LENGTH]; + coap_block1_t block1; + + int blockwise = coap_get_block1(pdu, &block1); + + printf("_sha256_handler: received data: offset=%u len=%u blockwise=%i more=%i\n", + (unsigned)block1.offset, pdu->payload_len, blockwise, block1.more); + + /* initialize sha256 calculation and add payload bytes */ + if (block1.blknum == 0) { + puts("_sha256_handler: init"); + sha256_init(&sha256); + } + sha256_update(&sha256, pdu->payload, pdu->payload_len); + + unsigned resp_code = COAP_CODE_CHANGED; + if (block1.more) { + resp_code = COAP_CODE_CONTINUE; + } + + /* start response */ + gcoap_resp_init(pdu, buf, len, resp_code); + + /* has payload */ + if (!blockwise || !block1.more) { + coap_opt_add_format(pdu, COAP_FORMAT_TEXT); + } + if (blockwise) { + coap_opt_add_block1_control(pdu, &block1); + } + + /* include digest if done, otherwise response code above asks for next block */ + size_t pdu_len = 0; + if (!blockwise || !block1.more) { + puts("_sha256_handler: finish"); + sha256_final(&sha256, digest); + pdu_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); + + pdu_len += fmt_bytes_hex((char *)pdu->payload, digest, sizeof(digest)); + } + else { + pdu_len = coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); + } + + return pdu_len; +} + +int gcoap_cli_cmd(int argc, char **argv) +{ + if (argc == 1) { + /* show help for main commands */ + goto end; + } + + if (strcmp(argv[1], "info") == 0) { + uint8_t open_reqs = gcoap_op_state(); + + printf("CoAP server is listening on port %u\n", CONFIG_GCOAP_PORT); + printf("CoAP open requests: %u\n", open_reqs); + return 0; + } + + end: + printf("usage: %s \n", argv[0]); + return 1; +} + +void gcoap_cli_init(void) +{ + gcoap_register_listener(&_listener); +} diff --git a/examples/gcoap_block_server/main.c b/examples/gcoap_block_server/main.c new file mode 100644 index 0000000000..48e8be3d67 --- /dev/null +++ b/examples/gcoap_block_server/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015-2016 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 example + * + * @author Ken Bannister + * + * @} + */ + +#include +#include "msg.h" + +#include "net/gcoap.h" +#include "shell.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 } +}; + +int main(void) +{ + /* for the thread running the shell */ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + gcoap_cli_init(); + puts("gcoap block handler"); + + /* start shell */ + puts("All up, running the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should never be reached */ + return 0; +}