diff --git a/doc/connectivity/networking/api/coap_client.rst b/doc/connectivity/networking/api/coap_client.rst index 2393d953c0da0..d345f5a4be42c 100644 --- a/doc/connectivity/networking/api/coap_client.rst +++ b/doc/connectivity/networking/api/coap_client.rst @@ -106,6 +106,52 @@ RFC7959 Figure 3: Block-Wise GET with Early Negotiation). ret = coap_client_req(&client, sock, &address, &req, -1); +Optionally, the application can register a payload callback instead of providing a payload pointer +for the CoAP upload. In such cases, the CoAP client library will call this callback when preparing +a PUT/POST request, so that the application can provide the payload in blocks, instead of having to +provide a single contiguous buffer with the entire payload. An example callback, providing the +content of the Lorem Ipsum string can look like this: + +.. code-block:: c + + static int lorem_ipsum_cb(size_t offset, const uint8_t **payload, size_t *len, + bool *last_block, void *user_data) + { + size_t data_left; + + if (offset > LOREM_IPSUM_STRLEN) { + return -EINVAL; + } + + *payload = LOREM_IPSUM + offset; + + data_left = LOREM_IPSUM_STRLEN - offset; + if (data_left <= *len) { + *len = data_left; + *last_block = true; + } else { + *last_block = false; + } + + return 0; + } + +The callback can then be registered for the PUT/POST request instead of a payload pointer: + +.. code-block:: c + + struct coap_client_request req = { 0 }; + + req.method = COAP_METHOD_PUT; + req.confirmable = true; + req.path = "lorem-ipsum"; + req.fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN; + req.cb = response_cb; + req.payload_cb = lore_ipsum_cb, + + ret = coap_client_req(&client, sock, &address, &req, -1); + + API Reference ************* diff --git a/doc/releases/release-notes-4.3.rst b/doc/releases/release-notes-4.3.rst index be05de7656d64..e677288fb04d2 100644 --- a/doc/releases/release-notes-4.3.rst +++ b/doc/releases/release-notes-4.3.rst @@ -212,6 +212,7 @@ New APIs and options * CoAP * :c:struct:`coap_client_response_data` + * :c:member:`coap_client_request.payload_cb` * Sockets diff --git a/include/zephyr/net/coap_client.h b/include/zephyr/net/coap_client.h index 722bc2ec9ac85..16cc7da830e72 100644 --- a/include/zephyr/net/coap_client.h +++ b/include/zephyr/net/coap_client.h @@ -68,6 +68,37 @@ struct coap_client_response_data { typedef void (*coap_client_response_cb_t)(const struct coap_client_response_data *data, void *user_data); +/** + * @typedef coap_client_payload_cb_t + * @brief Callback for providing a payload for the CoAP request. + * + * An optional callback for providing a payload for CoAP client requests. If set in + * @ref coap_client_request, the CoAP client library will call this callback when + * preparing a PUT/POST request (note that this is also true for retransmissions). + * + * When called, the library provides the application with the current payload offset + * for the transfer and the payload block size. In return, the application sets the + * payload pointer, payload size and information whether more data blocks are expected. + * Setting the @p last_block parameter to false on the initial callback call triggers + * a block transfer upload. The library will keep calling the callback until the + * @p last_block parameter is set to false. + * + * @note If block transfer is used, the application is expected to provide full blocks of + * payload. Only the final block (i.e. when @p last_block is set to true) can be shorter + * than the requested block size. + * + * @param offset Payload offset from the beginning of a blockwise transfer. + * @param payload A pointer for the buffer containing the payload block. + * @param len Requested (maximum) block size on input. The actual payload length on output. + * @param last_block A pointer to the flag indicating whether more payload blocks are expected. + * @param user_data User provided context. + * + * @return Zero on success, a negative error code to abort upload. + */ +typedef int (*coap_client_payload_cb_t)(size_t offset, const uint8_t **payload, + size_t *len, bool *last_block, + void *user_data); + /** * @brief Representation of a CoAP client request. */ @@ -78,6 +109,7 @@ struct coap_client_request { enum coap_content_format fmt; /**< Content format to be used */ const uint8_t *payload; /**< User allocated buffer for send request */ size_t len; /**< Length of the payload */ + coap_client_payload_cb_t payload_cb; /**< Optional payload callback */ coap_client_response_cb_t cb; /**< Callback when response received */ const struct coap_client_option *options; /**< Extra options to be added to request */ uint8_t num_options; /**< Number of extra options */ diff --git a/samples/net/sockets/coap_upload/CMakeLists.txt b/samples/net/sockets/coap_upload/CMakeLists.txt new file mode 100644 index 0000000000000..15f73a1962b5d --- /dev/null +++ b/samples/net/sockets/coap_upload/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(coap_upload) + +target_sources(app PRIVATE + src/main.c +) + +include(${ZEPHYR_BASE}/samples/net/common/common.cmake) diff --git a/samples/net/sockets/coap_upload/Kconfig b/samples/net/sockets/coap_upload/Kconfig new file mode 100644 index 0000000000000..d953750638058 --- /dev/null +++ b/samples/net/sockets/coap_upload/Kconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +mainmenu "CoAP Upload Sample Application" + +config NET_SAMPLE_COAP_SERVER_PORT + int "CoAP server port" + default 5683 + help + CoAP server port that the application should send requests to. + +source "Kconfig.zephyr" diff --git a/samples/net/sockets/coap_upload/README.rst b/samples/net/sockets/coap_upload/README.rst new file mode 100644 index 0000000000000..a5d2e59af8bd3 --- /dev/null +++ b/samples/net/sockets/coap_upload/README.rst @@ -0,0 +1,119 @@ +.. zephyr:code-sample:: coap-upload + :name: CoAP upload + :relevant-api: coap + + Use the CoAP client API to upload data via a PUT request + +Overview +******** + +This sample demonstrates the use of the CoAP client API to upload a content to a +CoAP server with PUT requests. The sample showcases various methods of uploading +a content: + + * Short upload with payload pointer, + * Short upload with payload callback, + * Block transfer upload with payload pointer, + * Block transfer upload with payload callback. + +The sample is compatible with the :zephyr:code-sample:`coap-server` sample, or +can run against a standalone CoAP server, for example `aiocoap`_. + +Requirements +************ +- :ref:`networking_with_host`, :ref:`networking_with_native_sim` +- or, a board with hardware networking (tested on nrf7002dk board with ``wifi-ipv4`` snippet) +- Network connection between the board and a host running a CoAP server + +Build and Running +***************** +Build the CoAP upload sample application like this: + +.. zephyr-app-commands:: + :zephyr-app: samples/net/sockets/coap_upload + :board: + :goals: build + :compact: + +The easiest way to run this sample application is to build and run it as a +native_sim application. Some setup is required as described in +:ref:`networking_with_native_sim`. + +Download a CoAP server application, for example `aiocoap`_ (Python). + +Using ``aiocoap``: + +.. code-block:: bash + + python -m pip install "aiocoap[all]" + mkdir file_root + aiocoap-fileserver --write file_root + +Launch :command:`net-setup.sh` in net-tools: + +.. code-block:: bash + + ./net-setup.sh + +Build and run the CoAP upload sample application for native_sim like this: + +.. zephyr-app-commands:: + :zephyr-app: samples/net/sockets/coap_upload + :host-os: unix + :board: native_sim + :goals: run + :compact: + +Sample output +============= + +.. code-block:: console + + [00:00:00.000,000] net_config: Initializing network + [00:00:00.000,000] net_config: IPv4 address: 192.0.2.1 + [00:00:00.110,000] net_config: IPv6 address: 2001:db8::1 + [00:00:00.110,000] net_config: IPv6 address: 2001:db8::1 + [00:00:00.110,000] net_samples_common: Network connectivity established and IP address assigned + [00:00:00.110,000] net_samples_common: Waiting for network... + [00:00:00.110,000] coap_upload: + [00:00:00.110,000] coap_upload: * Starting CoAP upload using IPv4 + [00:00:00.110,000] coap_upload: + [00:00:00.110,000] coap_upload: ** CoAP upload short + [00:00:00.180,000] coap_upload: CoAP upload short done in 70 ms + [00:00:00.180,000] coap_upload: + [00:00:00.180,000] coap_upload: ** CoAP upload short with callback + [00:00:00.240,000] coap_upload: CoAP upload short with callback done in 60 ms + [00:00:00.240,000] coap_upload: + [00:00:00.240,000] coap_upload: ** CoAP upload blockwise + [00:00:00.300,000] coap_upload: CoAP upload blockwise ongoing, sent 128 bytes so far + [00:00:00.360,000] coap_upload: CoAP upload blockwise ongoing, sent 256 bytes so far + [00:00:00.420,000] coap_upload: CoAP upload blockwise ongoing, sent 384 bytes so far + [00:00:00.480,000] coap_upload: CoAP upload blockwise done in 240 ms + [00:00:00.480,000] coap_upload: + [00:00:00.480,000] coap_upload: ** CoAP upload blockwise with callback + [00:00:00.540,000] coap_upload: CoAP upload blockwise with callback ongoing, sent 128 bytes so far + [00:00:00.600,000] coap_upload: CoAP upload blockwise with callback ongoing, sent 256 bytes so far + [00:00:00.660,000] coap_upload: CoAP upload blockwise with callback ongoing, sent 384 bytes so far + [00:00:00.720,000] coap_upload: CoAP upload blockwise with callback done in 240 ms + [00:00:01.230,000] coap_upload: + [00:00:01.230,000] coap_upload: * Starting CoAP upload using IPv6 + [00:00:01.230,000] coap_upload: + [00:00:01.230,000] coap_upload: ** CoAP upload short + [00:00:01.320,000] coap_upload: CoAP upload short done in 90 ms + [00:00:01.320,000] coap_upload: + [00:00:01.320,000] coap_upload: ** CoAP upload short with callback + [00:00:01.380,000] coap_upload: CoAP upload short with callback done in 60 ms + [00:00:01.380,000] coap_upload: + [00:00:01.380,000] coap_upload: ** CoAP upload blockwise + [00:00:01.440,000] coap_upload: CoAP upload blockwise ongoing, sent 128 bytes so far + [00:00:01.500,000] coap_upload: CoAP upload blockwise ongoing, sent 256 bytes so far + [00:00:01.560,000] coap_upload: CoAP upload blockwise ongoing, sent 384 bytes so far + [00:00:01.620,000] coap_upload: CoAP upload blockwise done in 240 ms + [00:00:01.620,000] coap_upload: + [00:00:01.620,000] coap_upload: ** CoAP upload blockwise with callback + [00:00:01.680,000] coap_upload: CoAP upload blockwise with callback ongoing, sent 128 bytes so far + [00:00:01.740,000] coap_upload: CoAP upload blockwise with callback ongoing, sent 256 bytes so far + [00:00:01.800,000] coap_upload: CoAP upload blockwise with callback ongoing, sent 384 bytes so far + [00:00:01.860,000] coap_upload: CoAP upload blockwise with callback done in 240 ms + +.. _aiocoap: https://github.com/chrysn/aiocoap diff --git a/samples/net/sockets/coap_upload/prj.conf b/samples/net/sockets/coap_upload/prj.conf new file mode 100644 index 0000000000000..f46558ae1b5f9 --- /dev/null +++ b/samples/net/sockets/coap_upload/prj.conf @@ -0,0 +1,29 @@ +# Networking +CONFIG_NETWORKING=y +CONFIG_NET_UDP=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_CONNECTION_MANAGER=y + +# Logging +CONFIG_LOG=y +CONFIG_NET_LOG=y + +# Kernel options +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_MAIN_STACK_SIZE=2048 + +# CoAP +CONFIG_COAP=y +CONFIG_COAP_CLIENT=y +CONFIG_COAP_CLIENT_BLOCK_SIZE=128 +CONFIG_COAP_CLIENT_STACK_SIZE=2048 + +# Network application options and configuration +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2" +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2" diff --git a/samples/net/sockets/coap_upload/sample.yaml b/samples/net/sockets/coap_upload/sample.yaml new file mode 100644 index 0000000000000..2522b3851020f --- /dev/null +++ b/samples/net/sockets/coap_upload/sample.yaml @@ -0,0 +1,14 @@ +sample: + description: CoAP client upload example + name: socket_coap_upload +common: + harness: net + depends_on: netif + tags: + - net + - coap +tests: + sample.net.sockets.coap_upload: + platform_allow: + - native_sim + - qemu_x86 diff --git a/samples/net/sockets/coap_upload/src/main.c b/samples/net/sockets/coap_upload/src/main.c new file mode 100644 index 0000000000000..62eba2a922da6 --- /dev/null +++ b/samples/net/sockets/coap_upload/src/main.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "net_sample_common.h" + +LOG_MODULE_REGISTER(coap_upload, LOG_LEVEL_INF); + +#define SHORT_PAYLOAD "sample_payload" + +static K_SEM_DEFINE(coap_done_sem, 0, 1); +static int64_t start_time; + +static void response_cb(const struct coap_client_response_data *data, + void *user_data) +{ + + if (data->result_code < 0) { + goto error; + } + + if (data->last_block) { + int64_t elapsed_time = k_uptime_get() - start_time; + + if (data->result_code != COAP_RESPONSE_CODE_CHANGED) { + goto error; + } + + LOG_INF("CoAP upload %s done in %" PRId64 " ms", + (char *)user_data, elapsed_time); + + k_sem_give(&coap_done_sem); + } else { + if (data->result_code != COAP_RESPONSE_CODE_CONTINUE) { + goto error; + } + + LOG_INF("CoAP upload %s ongoing, sent %zu bytes so far", + (char *)user_data, data->offset); + } + + return; + +error: + LOG_ERR("Error during CoAP upload, result_code=%d", data->result_code); + k_sem_give(&coap_done_sem); +} + +static int short_payload_cb(size_t offset, const uint8_t **payload, size_t *len, + bool *last_block, void *user_data) +{ + if (*len < sizeof(SHORT_PAYLOAD) - 1) { + return -ENOMEM; + } + + *payload = SHORT_PAYLOAD; + *len = sizeof(SHORT_PAYLOAD) - 1; + *last_block = true; + + LOG_DBG("CoAP short payload callback, returning %zu bytes at offset %zu", + *len, offset); + + return 0; +} + +static int block_payload_cb(size_t offset, const uint8_t **payload, size_t *len, + bool *last_block, void *user_data) +{ + size_t data_left; + + if (offset > LOREM_IPSUM_SHORT_STRLEN) { + return -EINVAL; + } + + *payload = LOREM_IPSUM_SHORT + offset; + + data_left = LOREM_IPSUM_SHORT_STRLEN - offset; + if (data_left <= *len) { + *len = data_left; + *last_block = true; + } else { + *last_block = false; + } + + LOG_DBG("CoAP blockwise payload callback, returning %zu bytes at offset %zu", + *len, offset); + + return 0; +} + +static int coap_upload_single(struct coap_client *client, int sock, + struct coap_client_request *request) +{ + int ret; + + LOG_INF(""); + LOG_INF("** CoAP upload %s", (char *)request->user_data); + + start_time = k_uptime_get(); + + ret = coap_client_req(client, sock, NULL, request, NULL); + if (ret < 0) { + LOG_ERR("Failed to send CoAP request, err %d", ret); + return ret; + } + + /* Wait for CoAP request to complete */ + k_sem_take(&coap_done_sem, K_FOREVER); + + return ret; +} + +static void coap_upload(struct coap_client *client, struct sockaddr *sa, + socklen_t addrlen) +{ + struct coap_client_request requests[] = { + { + .method = COAP_METHOD_PUT, + .confirmable = true, + .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN, + .path = "test", + .payload = SHORT_PAYLOAD, + .len = sizeof(SHORT_PAYLOAD) - 1, + .cb = response_cb, + .user_data = "short", + }, + { + .method = COAP_METHOD_PUT, + .confirmable = true, + .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN, + .path = "test", + .cb = response_cb, + .payload_cb = short_payload_cb, + .user_data = "short with callback", + }, + { + .method = COAP_METHOD_PUT, + .confirmable = true, + .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN, + .path = "large-update", + .payload = LOREM_IPSUM_SHORT, + .len = LOREM_IPSUM_SHORT_STRLEN, + .cb = response_cb, + .user_data = "blockwise", + }, + { + .method = COAP_METHOD_PUT, + .confirmable = true, + .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN, + .path = "large-update", + .cb = response_cb, + .payload_cb = block_payload_cb, + .user_data = "blockwise with callback", + } + }; + int sock; + int ret; + + LOG_INF(""); + LOG_INF("* Starting CoAP upload using %s", (AF_INET == sa->sa_family) ? "IPv4" : "IPv6"); + + sock = zsock_socket(sa->sa_family, SOCK_DGRAM, 0); + if (sock < 0) { + LOG_ERR("Failed to create socket, err %d", errno); + return; + } + + ret = zsock_connect(sock, sa, addrlen); + if (ret < 0) { + LOG_ERR("Failed to connect socket, err %d", errno); + goto out; + + } + + ARRAY_FOR_EACH(requests, i) { + ret = coap_upload_single(client, sock, &requests[i]); + if (ret < 0) { + LOG_ERR("CoAP upload %s failed, err %d", + (char *)requests[i].user_data, ret); + goto out; + } + } + +out: + coap_client_cancel_requests(client); + + zsock_close(sock); +} + +int main(void) +{ + static struct coap_client client; + int ret; + + wait_for_network(); + + ret = coap_client_init(&client, NULL); + if (ret) { + LOG_ERR("Failed to init CoAP client, err %d", ret); + return ret; + } + + struct sockaddr sa; + +#if defined(CONFIG_NET_IPV4) + struct sockaddr_in *addr4 = (struct sockaddr_in *)&sa; + + addr4->sin_family = AF_INET; + addr4->sin_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT); + zsock_inet_pton(AF_INET, CONFIG_NET_CONFIG_PEER_IPV4_ADDR, &addr4->sin_addr); + + coap_upload(&client, &sa, sizeof(struct sockaddr_in)); +#endif + +#if defined(CONFIG_NET_IPV6) + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&sa; + + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT); + zsock_inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR, &addr6->sin6_addr); + + coap_upload(&client, &sa, sizeof(struct sockaddr_in6)); +#endif + + return 0; +} diff --git a/subsys/net/lib/coap/coap_client.c b/subsys/net/lib/coap/coap_client.c index 1fd5fe715c271..2c1df8c669519 100644 --- a/subsys/net/lib/coap/coap_client.c +++ b/subsys/net/lib/coap/coap_client.c @@ -231,7 +231,7 @@ static int coap_client_init_request(struct coap_client *client, } /* Add content format option only if there is a payload */ - if (req->payload) { + if (req->payload != NULL || req->payload_cb != NULL) { ret = coap_append_option_int(&internal_req->request, COAP_OPTION_CONTENT_FORMAT, req->fmt); @@ -272,14 +272,94 @@ static int coap_client_init_request(struct coap_client *client, } } - if (req->payload) { + if (req->payload != NULL || req->payload_cb != NULL) { + const uint8_t *payload = NULL; + bool block_transfer = false; uint16_t payload_len; - uint16_t offset; - /* Blockwise send ongoing, add block1 */ - if (internal_req->send_blk_ctx.total_size > 0 || - (req->len > CONFIG_COAP_CLIENT_MESSAGE_SIZE)) { + /* Block transfer already in progress */ + if (internal_req->send_blk_ctx.total_size > 0) { + block_transfer = true; + } + + if (req->payload_cb != NULL) { + size_t offset = 0; + size_t block_size = internal_req->send_blk_ctx.total_size > 0 ? + coap_block_size_to_bytes(internal_req->send_blk_ctx.block_size) : + CONFIG_COAP_CLIENT_BLOCK_SIZE; + size_t len = block_size; + bool last_block; + + if (block_transfer) { + offset = internal_req->send_blk_ctx.current; + } + + ret = req->payload_cb(offset, &payload, &len, &last_block, + req->user_data); + if (ret < 0) { + LOG_ERR("Payload callback reported error, %d", ret); + goto out; + } + + if (len > block_size || (len < block_size && !last_block)) { + LOG_ERR("Invalid payload size"); + ret = -EINVAL; + goto out; + } + + if (payload == NULL) { + LOG_ERR("No payload provided"); + ret = -EINVAL; + goto out; + } + + payload_len = len; + + if (block_transfer && last_block) { + /* If block transfer was used and it's a final + * block, adjust the total size value. + */ + internal_req->send_blk_ctx.total_size = + internal_req->send_blk_ctx.current + len; + } + + if (!last_block) { + /* Expecting more blocks, enable block transfer. + * Total length is yet unknown at this point, + * so use SIZE_MAX for now. + */ + block_transfer = true; + req->len = SIZE_MAX; + } + } else { + payload = req->payload; + payload_len = req->len; + + if (block_transfer) { + /* Block transfer already in progress, adjust + * payload pointer and length. + */ + uint16_t block_in_bytes = coap_block_size_to_bytes( + internal_req->send_blk_ctx.block_size); + + payload_len = internal_req->send_blk_ctx.total_size - + internal_req->send_blk_ctx.current; + if (payload_len > block_in_bytes) { + payload_len = block_in_bytes; + } + payload = req->payload + internal_req->send_blk_ctx.current; + } else if (req->len > CONFIG_COAP_CLIENT_MESSAGE_SIZE) { + /* Otherwise, if payload won't fit, initialize + * block transfer. + */ + block_transfer = true; + payload_len = CONFIG_COAP_CLIENT_BLOCK_SIZE; + } + } + + /* Blockwise send ongoing, add block1 */ + if (block_transfer) { if (internal_req->send_blk_ctx.total_size == 0) { coap_block_transfer_init(&internal_req->send_blk_ctx, coap_client_default_block_size(), @@ -289,6 +369,7 @@ static int coap_client_init_request(struct coap_client *client, memcpy(internal_req->request_tag, tag, COAP_TOKEN_MAX_LEN); } + ret = coap_append_block1_option(&internal_req->request, &internal_req->send_blk_ctx); @@ -314,22 +395,7 @@ static int coap_client_init_request(struct coap_client *client, goto out; } - if (internal_req->send_blk_ctx.total_size > 0) { - uint16_t block_in_bytes = - coap_block_size_to_bytes(internal_req->send_blk_ctx.block_size); - - payload_len = internal_req->send_blk_ctx.total_size - - internal_req->send_blk_ctx.current; - if (payload_len > block_in_bytes) { - payload_len = block_in_bytes; - } - offset = internal_req->send_blk_ctx.current; - } else { - payload_len = req->len; - offset = 0; - } - - ret = coap_packet_append_payload(&internal_req->request, req->payload + offset, + ret = coap_packet_append_payload(&internal_req->request, payload, payload_len); if (ret < 0) { @@ -935,6 +1001,8 @@ static int handle_response(struct coap_client *client, const struct coap_packet /* Check if this was a response to last blockwise send */ if (internal_req->send_blk_ctx.total_size > 0) { + int block1_option; + blockwise_transfer = true; internal_req->offset = internal_req->send_blk_ctx.current; if (internal_req->send_blk_ctx.total_size == internal_req->send_blk_ctx.current) { @@ -942,6 +1010,15 @@ static int handle_response(struct coap_client *client, const struct coap_packet } else { last_block = false; } + + block1_option = coap_get_option_int(response, COAP_OPTION_BLOCK1); + if (block1_option > 0) { + int block_size = GET_BLOCK_SIZE(block1_option); + + if (block_size < internal_req->send_blk_ctx.block_size) { + internal_req->send_blk_ctx.block_size = block_size; + } + } } /* Until the last block of a transfer, limit data size sent to the application to the block