coap_client_block.c
Go to the documentation of this file.
1 /**
2  * @file coap_client_block.c
3  * @brief CoAP block-wise transfer
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.2
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL COAP_TRACE_LEVEL
33 
34 //Dependencies
35 #include <stdlib.h>
36 #include "core/net.h"
37 #include "coap/coap_client.h"
38 #include "coap/coap_client_block.h"
39 #include "debug.h"
40 
41 //Check TCP/IP stack configuration
42 #if (COAP_CLIENT_SUPPORT == ENABLED && COAP_CLIENT_BLOCK_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief Set preferred block for transmission path
47  * @param[in] request CoAP request handle
48  * @param[in] blockSize Preferred block size, in bytes
49  * @return Error code
50  **/
51 
53 {
54  //Make sure the CoAP request handle is valid
55  if(request == NULL)
57 
58  //Acquire exclusive access to the CoAP client context
59  osAcquireMutex(&request->context->mutex);
60 
61  //Set TX block size
62  if(blockSize == 16)
63  {
64  request->txBlockSzx = COAP_BLOCK_SIZE_16;
65  }
66  else if(blockSize == 32)
67  {
68  request->txBlockSzx = COAP_BLOCK_SIZE_32;
69  }
70  else if(blockSize == 64)
71  {
72  request->txBlockSzx = COAP_BLOCK_SIZE_64;
73  }
74  else if(blockSize == 128)
75  {
76  request->txBlockSzx = COAP_BLOCK_SIZE_128;
77  }
78  else if(blockSize == 256)
79  {
80  request->txBlockSzx = COAP_BLOCK_SIZE_256;
81  }
82  else if(blockSize == 512)
83  {
84  request->txBlockSzx = COAP_BLOCK_SIZE_512;
85  }
86  else
87  {
88  request->txBlockSzx = COAP_BLOCK_SIZE_1024;
89  }
90 
91  //Ensure the block size is acceptable
92  if(request->txBlockSzx > coapClientGetMaxBlockSize())
93  {
94  request->txBlockSzx = coapClientGetMaxBlockSize();
95  }
96 
97  //Release exclusive access to the CoAP client context
98  osReleaseMutex(&request->context->mutex);
99 
100  //Successful processing
101  return NO_ERROR;
102 }
103 
104 
105 /**
106  * @brief Set preferred block for reception path
107  * @param[in] request CoAP request handle
108  * @param[in] blockSize Preferred block size, in bytes
109  * @return Error code
110  **/
111 
113 {
114  //Make sure the CoAP request handle is valid
115  if(request == NULL)
117 
118  //Acquire exclusive access to the CoAP client context
119  osAcquireMutex(&request->context->mutex);
120 
121  //Set RX block size
122  if(blockSize == 16)
123  {
124  request->rxBlockSzx = COAP_BLOCK_SIZE_16;
125  }
126  else if(blockSize == 32)
127  {
128  request->rxBlockSzx = COAP_BLOCK_SIZE_32;
129  }
130  else if(blockSize == 64)
131  {
132  request->rxBlockSzx = COAP_BLOCK_SIZE_64;
133  }
134  else if(blockSize == 128)
135  {
136  request->rxBlockSzx = COAP_BLOCK_SIZE_128;
137  }
138  else if(blockSize == 256)
139  {
140  request->rxBlockSzx = COAP_BLOCK_SIZE_256;
141  }
142  else if(blockSize == 512)
143  {
144  request->rxBlockSzx = COAP_BLOCK_SIZE_512;
145  }
146  else
147  {
148  request->rxBlockSzx = COAP_BLOCK_SIZE_1024;
149  }
150 
151  //Ensure the block size is acceptable
152  if(request->rxBlockSzx > coapClientGetMaxBlockSize())
153  {
154  request->rxBlockSzx = coapClientGetMaxBlockSize();
155  }
156 
157  //Release exclusive access to the CoAP client context
158  osReleaseMutex(&request->context->mutex);
159 
160  //Successful processing
161  return NO_ERROR;
162 }
163 
164 
165 /**
166  * @brief Write resource body using block-wise mode
167  * @param[in] request CoAP request handle
168  * @param[in] data Pointer to a buffer containing the data to be transmitted
169  * @param[in] length Number of bytes to be transmitted
170  * @param[out] written Actual number of bytes written (optional parameter)
171  * @param[in] last Flag indicating whether this message fragment is the last
172  * @return Error code
173  **/
174 
176  const void *data, size_t length, size_t *written, bool_t last)
177 {
178  error_t error;
179  size_t n;
180  uint32_t value;
181  uint32_t blockPos;
182  uint32_t blockSzx;
183  size_t payloadLen;
184  const uint8_t *payload;
185  CoapMessage *requestMsg;
186  CoapMessage *responseMsg;
187  CoapCode responseCode;
188 
189  //Initialize status code
190  error = NO_ERROR;
191 
192  //Total number of bytes that have been written
193  if(written != NULL)
194  {
195  *written = 0;
196  }
197 
198  //Block-wise transfers are realized as combinations of exchanges, each
199  //of which is performed according to the CoAP base protocol
200  while(length > 0 || last)
201  {
202  //Point to the request message
203  requestMsg = coapClientGetRequestMessage(request);
204 
205  //Block1 option is used in descriptive usage in a request
206  error = coapGetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, &value);
207 
208  //Block1 option found?
209  if(!error)
210  {
211  //Retrieve current block parameters
212  blockPos = COAP_GET_BLOCK_POS(value);
213  blockSzx = COAP_GET_BLOCK_SZX(value);
214  }
215  else
216  {
217  //Initialize block parameters
218  blockPos = 0;
219  blockSzx = request->txBlockSzx;
220  }
221 
222  //Retrieve message payload length
223  error = coapClientGetPayload(requestMsg, &payload, &payloadLen);
224  //Any error to report?
225  if(error)
226  break;
227 
228  //Write as much data as possible
229  if(length > 0 && payloadLen < COAP_GET_BLOCK_SIZE(blockSzx))
230  {
231  //Limit the number of data to copy at a time
232  n = MIN(length, COAP_GET_BLOCK_SIZE(blockSzx) - payloadLen);
233 
234  //Write payload data
235  error = coapClientWritePayload(requestMsg, data, n);
236  //Any error to report?
237  if(error)
238  break;
239 
240  //Advance data pointer
241  data = (uint8_t *) data + n;
242  length -= n;
243 
244  //Total number of bytes that have been written
245  if(written != NULL)
246  {
247  *written += n;
248  }
249  }
250  else
251  {
252  //Block-wise transfer?
253  if(blockPos > 0 || length > 0 || !last)
254  {
255  //The NUM field in the option value describes what block number
256  //is contained in the payload of this message
257  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
258 
259  //The M bit indicates whether further blocks need to be transferred
260  //to complete the transfer of the body
261  if(length == 0 && last)
262  {
264  }
265  else
266  {
268  }
269 
270  //Set block size
271  COAP_SET_BLOCK_SZX(value, blockSzx);
272 
273  //Block1 option is used in descriptive usage in a request
274  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, value);
275  //Any error to report?
276  if(error)
277  break;
278  }
279 
280  //Last block?
281  if(length == 0 && last)
282  {
283  //Any preferred block size defined?
284  if(request->rxBlockSzx < COAP_BLOCK_SIZE_RESERVED)
285  {
286  //Set preferred block size
289  COAP_SET_BLOCK_SZX(value, request->rxBlockSzx);
290 
291  //Perform early negotiation
292  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK2, 0, value);
293  //Any error to report?
294  if(error)
295  break;
296  }
297  }
298 
299  //Send request
300  error = coapClientSendRequest(request, NULL, NULL);
301  //Any error to report?
302  if(error)
303  break;
304 
305  //Point to the response message
306  responseMsg = coapClientGetResponseMessage(request);
307 
308  //Retrieve response code
309  error = coapClientGetResponseCode(responseMsg, &responseCode);
310  //Any error to report?
311  if(error)
312  break;
313 
314  //Check response code
315  if(COAP_GET_CODE_CLASS(responseCode) != COAP_CODE_CLASS_SUCCESS)
316  {
317  error = ERROR_INVALID_STATUS;
318  break;
319  }
320 
321  //Block-wise transfer?
322  if(blockPos > 0 || length > 0 || !last)
323  {
324  //A Block1 option is used in control usage in a response
325  error = coapClientGetUintOption(responseMsg, COAP_OPT_BLOCK1, 0, &value);
326  //Any error to report?
327  if(error)
328  break;
329 
330  //The value 7 for SZX is reserved
332  {
333  error = ERROR_FAILURE;
334  break;
335  }
336 
337  //The NUM field of the Block1 option indicates what block number is
338  //being acknowledged
339  if(COAP_GET_BLOCK_POS(value) != blockPos)
340  {
341  error = ERROR_FAILURE;
342  break;
343  }
344 
345  //Next block
346  blockPos += COAP_GET_BLOCK_SIZE(blockSzx);
347 
348  //A server receiving a block-wise PUT or POST may want to indicate a
349  //smaller block size preference (late negotiation)
350  if(blockSzx > COAP_GET_BLOCK_SZX(value))
351  {
352  //The client should use this block size or a smaller one in all
353  //further requests in the transfer sequence, even if that means
354  //changing the block size (and possibly scaling the block number
355  //accordingly) from now on
356  blockSzx = COAP_GET_BLOCK_SZX(value);
357  }
358 
359  //The NUM field in the option value describes what block number
360  //is contained in the payload of this message
361  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
362 
363  //Set block size
364  COAP_SET_BLOCK_SZX(value, blockSzx);
365 
366  //Block1 option is used in descriptive usage in a request
367  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, value);
368  //Any error to report?
369  if(error)
370  break;
371  }
372 
373  //Trim the existing payload
374  error = coapClientSetPayload(requestMsg, NULL, 0);
375  //Any error to report?
376  if(error)
377  break;
378 
379  //Last block?
380  if(length == 0 && last)
381  {
382  //Delete Block1 option
383  error = coapClientDeleteOption(requestMsg, COAP_OPT_BLOCK1, 0);
384  //We are done
385  break;
386  }
387  }
388  }
389 
390  //Return status code
391  return error;
392 }
393 
394 
395 /**
396  * @brief Read resource body using block-wise mode
397  * @param[in] request CoAP request handle
398  * @param[out] data Buffer into which received data will be placed
399  * @param[in] size Maximum number of bytes that can be received
400  * @param[out] received Number of bytes that have been received
401  * @return Error code
402  **/
403 
405  size_t size, size_t *received)
406 {
407  error_t error;
408  size_t n;
409  uint32_t value;
410  uint32_t blockPos;
411  uint32_t blockSzx;
412  size_t payloadLen;
413  const uint8_t *payload;
414  CoapMessage *requestMsg;
415  CoapMessage *responseMsg;
416  CoapCode responseCode;
417 
418  //Initialize status code
419  error = NO_ERROR;
420 
421  //Total number of bytes that have been received
422  *received = 0;
423 
424  //Block-wise transfers are realized as combinations of exchanges, each
425  //of which is performed according to the CoAP base protocol
426  while(*received < size)
427  {
428  //Point to the response message
429  responseMsg = coapClientGetResponseMessage(request);
430 
431  //Read payload data
432  error = coapClientReadPayload(responseMsg, data, size - *received, &n);
433 
434  //Check status code
435  if(error == NO_ERROR)
436  {
437  //Advance data pointer
438  data = (uint8_t *) data + n;
439 
440  //Total number of bytes that have been received
441  *received += n;
442  }
443  else if(error == ERROR_END_OF_STREAM)
444  {
445  //Point to the request message
446  requestMsg = coapClientGetRequestMessage(request);
447 
448  //A Block2 Option is used in control usage in a request
449  error = coapClientGetUintOption(requestMsg, COAP_OPT_BLOCK2, 0,
450  &value);
451 
452  //Block2 option found?
453  if(!error)
454  {
455  //Retrieve current block parameters
456  blockPos = COAP_GET_BLOCK_POS(value);
457  blockSzx = COAP_GET_BLOCK_SZX(value);
458  }
459  else
460  {
461  //Initialize block parameters
462  blockPos = 0;
463  blockSzx = request->rxBlockSzx;
464  }
465 
466  //Block2 option is used in descriptive usage in a response
467  error = coapClientGetUintOption(responseMsg, COAP_OPT_BLOCK2, 0,
468  &value);
469 
470  //Block-wise transfer?
471  if(!error)
472  {
473  //The value 7 for SZX is reserved
475  {
476  error = ERROR_FAILURE;
477  break;
478  }
479 
480  //The NUM field in the option value describes what block number
481  //is contained in the payload of this message
482  if(COAP_GET_BLOCK_POS(value) != blockPos)
483  {
484  //Report an error
485  error = ERROR_FAILURE;
486  break;
487  }
488 
489  //The M bit indicates whether further blocks need to be
490  //transferred to complete the transfer of that body
491  if(!COAP_GET_BLOCK_M(value))
492  {
493  //Exit immediately
494  error = ERROR_END_OF_STREAM;
495  break;
496  }
497 
498  //Retrieve the length of the payload
499  error = coapClientGetPayload(responseMsg, &payload, &payloadLen);
500  //Any error to report?
501  if(error)
502  break;
503 
504  //Make sure the length of the payload matches the block size
506  {
507  //Report an error
508  error = ERROR_FAILURE;
509  break;
510  }
511 
512  //If the first request uses a bigger block size than the receiver
513  //prefers, subsequent requests will use the preferred block size
514  if(blockSzx > COAP_GET_BLOCK_SZX(value))
515  {
516  blockSzx = COAP_GET_BLOCK_SZX(value);
517  }
518 
519  //Next block
520  blockPos += COAP_GET_BLOCK_SIZE(value);
521 
522  //The NUM field in the Block2 option gives the block number of the
523  //payload that is being requested to be returned in the response
524  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
525 
526  //the M bit has no function and must be set to zero
528 
529  //The block size given suggests a block size (in the case of block
530  //number 0) or repeats the block size of previous blocks received
531  //(in the case of a non-zero block number)
532  COAP_SET_BLOCK_SZX(value, blockSzx);
533 
534  //A Block2 Option is used in control usage in a request
535  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK2, 0,
536  value);
537  //Any error to report?
538  if(error)
539  break;
540 
541  //Perform message exchange
542  error = coapClientSendRequest(request, NULL, NULL);
543  //Any error to report?
544  if(error)
545  break;
546 
547  //Point to the response message
548  responseMsg = coapClientGetResponseMessage(request);
549 
550  //Retrieve response code
551  error = coapClientGetResponseCode(responseMsg, &responseCode);
552  //Any error to report?
553  if(error)
554  break;
555 
556  //Check response code
557  if(COAP_GET_CODE_CLASS(responseCode) != COAP_CODE_CLASS_SUCCESS)
558  {
559  error = ERROR_INVALID_STATUS;
560  break;
561  }
562  }
563  else
564  {
565  //The Block2 option is not present in the response
566  if(blockPos == 0)
567  {
568  error = ERROR_END_OF_STREAM;
569  }
570  else
571  {
572  error = ERROR_FAILURE;
573  }
574 
575  //Exit immediately
576  break;
577  }
578  }
579  else
580  {
581  //Failed to read payload data
582  break;
583  }
584  }
585 
586  //Any data received?
587  if(*received > 0)
588  {
589  //Catch exception
590  if(error == ERROR_END_OF_STREAM)
591  error = NO_ERROR;
592  }
593 
594  //Return status code
595  return error;
596 }
597 
598 
599 /**
600  * @brief Get maximum block size
601  * @return Block size
602  **/
603 
605 {
606  CoapBlockSize blockSize;
607 
608  //Retrieve maximum block size
609 #if (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 1024))
610  blockSize = COAP_BLOCK_SIZE_1024;
611 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 512))
612  blockSize = COAP_BLOCK_SIZE_512;
613 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 256))
614  blockSize = COAP_BLOCK_SIZE_256;
615 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 128))
616  blockSize = COAP_BLOCK_SIZE_128;
617 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 64))
618  blockSize = COAP_BLOCK_SIZE_64;
619 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 32))
620  blockSize = COAP_BLOCK_SIZE_32;
621 #else
622  blockSize = COAP_BLOCK_SIZE_16;
623 #endif
624 
625  //Return maximum block size
626  return blockSize;
627 }
628 
629 #endif
error_t coapClientSetPayload(CoapMessage *message, const void *payload, size_t payloadLen)
Set message payload.
@ COAP_BLOCK_SIZE_256
Definition: coap_option.h:205
error_t coapGetUintOption(const CoapMessage *message, uint16_t optionNum, uint_t optionIndex, uint32_t *optionValue)
Get the value of the specified uint option.
Definition: coap_option.c:651
int bool_t
Definition: compiler_port.h:61
CoapMessage * coapClientGetRequestMessage(CoapClientRequest *request)
Get request message.
error_t coapClientSendRequest(CoapClientRequest *request, CoapRequestCallback callback, void *param)
Send a CoAP request.
uint8_t data[]
Definition: ethernet.h:224
#define COAP_GET_BLOCK_SIZE(value)
Definition: coap_option.h:76
uint16_t last
Definition: ipv4_frag.h:105
@ ERROR_INVALID_STATUS
Definition: error.h:102
@ COAP_BLOCK_SIZE_1024
Definition: coap_option.h:207
#define COAP_SET_BLOCK_M(value, m)
Definition: coap_option.h:64
@ ERROR_END_OF_STREAM
Definition: error.h:211
#define CoapClientRequest
Definition: coap_client.h:148
CoapMessage * coapClientGetResponseMessage(CoapClientRequest *request)
Get response message.
#define COAP_SET_BLOCK_SZX(value, s)
Definition: coap_option.h:66
error_t coapClientSetTxBlockSize(CoapClientRequest *request, uint_t blockSize)
Set preferred block for transmission path.
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
error_t
Error codes.
Definition: error.h:43
#define COAP_GET_CODE_CLASS(code)
Definition: coap_common.h:54
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
error_t coapClientGetPayload(const CoapMessage *message, const uint8_t **payload, size_t *payloadLen)
Get message payload.
#define COAP_GET_BLOCK_M(value)
Definition: coap_option.h:71
CoAP message.
Definition: coap_message.h:56
CoAP client.
@ COAP_OPT_BLOCK1
Definition: coap_option.h:106
CoapBlockSize coapClientGetMaxBlockSize(void)
Get maximum block size.
@ COAP_BLOCK_SIZE_128
Definition: coap_option.h:204
uint8_t length
Definition: tcp.h:375
error_t coapClientReadPayload(CoapMessage *message, void *data, size_t size, size_t *length)
Read payload data.
#define MIN(a, b)
Definition: os_port.h:63
error_t coapClientWriteBody(CoapClientRequest *request, const void *data, size_t length, size_t *written, bool_t last)
Write resource body using block-wise mode.
error_t coapClientReadBody(CoapClientRequest *request, void *data, size_t size, size_t *received)
Read resource body using block-wise mode.
CoAP block-wise transfer.
@ COAP_BLOCK_SIZE_16
Definition: coap_option.h:201
uint8_t n
uint8_t payload[]
Definition: ipv6.h:286
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
error_t coapClientSetRxBlockSize(CoapClientRequest *request, uint_t blockSize)
Set preferred block for reception path.
@ COAP_OPT_BLOCK2
Definition: coap_option.h:105
error_t coapClientWritePayload(CoapMessage *message, const void *data, size_t length)
Write payload data.
#define COAP_GET_BLOCK_POS(value)
Definition: coap_option.h:78
uint8_t value[]
Definition: tcp.h:376
error_t coapClientGetUintOption(const CoapMessage *message, uint16_t optionNum, uint_t optionIndex, uint32_t *optionValue)
Read an uint option from the CoAP message.
error_t coapClientSetUintOption(CoapMessage *message, uint16_t optionNum, uint_t optionIndex, uint32_t optionValue)
Add a uint option to the CoAP message.
@ COAP_BLOCK_SIZE_RESERVED
Definition: coap_option.h:208
#define COAP_SET_BLOCK_NUM(value, n)
Definition: coap_option.h:62
@ COAP_BLOCK_SIZE_512
Definition: coap_option.h:206
uint16_t payloadLen
Definition: ipv6.h:281
@ COAP_CODE_CLASS_SUCCESS
Definition: coap_common.h:102
unsigned int uint_t
Definition: compiler_port.h:57
TCP/IP stack core.
error_t coapClientDeleteOption(CoapMessage *message, uint16_t optionNum, uint_t optionIndex)
Remove an option from the CoAP message.
#define COAP_GET_BLOCK_SZX(value)
Definition: coap_option.h:73
CoapCode
CoAP method and response codes.
Definition: coap_common.h:113
CoapBlockSize
Block size parameter.
Definition: coap_option.h:200
@ NO_ERROR
Success.
Definition: error.h:44
error_t coapClientGetResponseCode(const CoapMessage *message, CoapCode *code)
Get response code.
Debugging facilities.
@ COAP_BLOCK_SIZE_64
Definition: coap_option.h:203
@ COAP_BLOCK_SIZE_32
Definition: coap_option.h:202