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-2024 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.4.4
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  *written = 0;
195 
196  //Block-wise transfers are realized as combinations of exchanges, each
197  //of which is performed according to the CoAP base protocol
198  while(length > 0 || last)
199  {
200  //Point to the request message
201  requestMsg = coapClientGetRequestMessage(request);
202 
203  //Block1 option is used in descriptive usage in a request
204  error = coapGetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, &value);
205 
206  //Block1 option found?
207  if(!error)
208  {
209  //Retrieve current block parameters
210  blockPos = COAP_GET_BLOCK_POS(value);
211  blockSzx = COAP_GET_BLOCK_SZX(value);
212  }
213  else
214  {
215  //Initialize block parameters
216  blockPos = 0;
217  blockSzx = request->txBlockSzx;
218  }
219 
220  //Retrieve message payload length
221  error = coapClientGetPayload(requestMsg, &payload, &payloadLen);
222  //Any error to report?
223  if(error)
224  break;
225 
226  //Write as much data as possible
227  if(length > 0 && payloadLen < COAP_GET_BLOCK_SIZE(blockSzx))
228  {
229  //Limit the number of data to copy at a time
230  n = MIN(length, COAP_GET_BLOCK_SIZE(blockSzx) - payloadLen);
231 
232  //Write payload data
233  error = coapClientWritePayload(requestMsg, data, n);
234  //Any error to report?
235  if(error)
236  break;
237 
238  //Advance data pointer
239  data = (uint8_t *) data + n;
240  length -= n;
241 
242  //Total number of bytes that have been written
243  if(written != NULL)
244  *written += n;
245  }
246  else
247  {
248  //Block-wise transfer?
249  if(blockPos > 0 || length > 0 || !last)
250  {
251  //The NUM field in the option value describes what block number
252  //is contained in the payload of this message
253  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
254 
255  //The M bit indicates whether further blocks need to be transferred
256  //to complete the transfer of the body
257  if(length == 0 && last)
258  {
260  }
261  else
262  {
264  }
265 
266  //Set block size
267  COAP_SET_BLOCK_SZX(value, blockSzx);
268 
269  //Block1 option is used in descriptive usage in a request
270  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, value);
271  //Any error to report?
272  if(error)
273  break;
274  }
275 
276  //Last block?
277  if(length == 0 && last)
278  {
279  //Any preferred block size defined?
280  if(request->rxBlockSzx < COAP_BLOCK_SIZE_RESERVED)
281  {
282  //Set preferred block size
285  COAP_SET_BLOCK_SZX(value, request->rxBlockSzx);
286 
287  //Perform early negotiation
288  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK2, 0, value);
289  //Any error to report?
290  if(error)
291  break;
292  }
293  }
294 
295  //Send request
296  error = coapClientSendRequest(request, NULL, NULL);
297  //Any error to report?
298  if(error)
299  break;
300 
301  //Point to the response message
302  responseMsg = coapClientGetResponseMessage(request);
303 
304  //Retrieve response code
305  error = coapClientGetResponseCode(responseMsg, &responseCode);
306  //Any error to report?
307  if(error)
308  break;
309 
310  //Check response code
311  if(COAP_GET_CODE_CLASS(responseCode) != COAP_CODE_CLASS_SUCCESS)
312  {
313  error = ERROR_INVALID_STATUS;
314  break;
315  }
316 
317  //Block-wise transfer?
318  if(blockPos > 0 || length > 0 || !last)
319  {
320  //A Block1 option is used in control usage in a response
321  error = coapClientGetUintOption(responseMsg, COAP_OPT_BLOCK1, 0, &value);
322  //Any error to report?
323  if(error)
324  break;
325 
326  //The value 7 for SZX is reserved
328  {
329  error = ERROR_FAILURE;
330  break;
331  }
332 
333  //The NUM field of the Block1 option indicates what block number is
334  //being acknowledged
335  if(COAP_GET_BLOCK_POS(value) != blockPos)
336  {
337  error = ERROR_FAILURE;
338  break;
339  }
340 
341  //Next block
342  blockPos += COAP_GET_BLOCK_SIZE(blockSzx);
343 
344  //A server receiving a block-wise PUT or POST may want to indicate a
345  //smaller block size preference (late negotiation)
346  if(blockSzx > COAP_GET_BLOCK_SZX(value))
347  {
348  //The client should use this block size or a smaller one in all
349  //further requests in the transfer sequence, even if that means
350  //changing the block size (and possibly scaling the block number
351  //accordingly) from now on
352  blockSzx = COAP_GET_BLOCK_SZX(value);
353  }
354 
355  //The NUM field in the option value describes what block number
356  //is contained in the payload of this message
357  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
358 
359  //Set block size
360  COAP_SET_BLOCK_SZX(value, blockSzx);
361 
362  //Block1 option is used in descriptive usage in a request
363  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, value);
364  //Any error to report?
365  if(error)
366  break;
367  }
368 
369  //Trim the existing payload
370  error = coapClientSetPayload(requestMsg, NULL, 0);
371  //Any error to report?
372  if(error)
373  break;
374 
375  //Last block?
376  if(length == 0 && last)
377  {
378  //Delete Block1 option
379  error = coapClientDeleteOption(requestMsg, COAP_OPT_BLOCK1, 0);
380  //We are done
381  break;
382  }
383  }
384  }
385 
386  //Return status code
387  return error;
388 }
389 
390 
391 /**
392  * @brief Read resource body using block-wise mode
393  * @param[in] request CoAP request handle
394  * @param[out] data Buffer into which received data will be placed
395  * @param[in] size Maximum number of bytes that can be received
396  * @param[out] received Number of bytes that have been received
397  * @return Error code
398  **/
399 
401  size_t size, size_t *received)
402 {
403  error_t error;
404  size_t n;
405  uint32_t value;
406  uint32_t blockPos;
407  uint32_t blockSzx;
408  size_t payloadLen;
409  const uint8_t *payload;
410  CoapMessage *requestMsg;
411  CoapMessage *responseMsg;
412  CoapCode responseCode;
413 
414  //Initialize status code
415  error = NO_ERROR;
416 
417  //Total number of bytes that have been received
418  *received = 0;
419 
420  //Block-wise transfers are realized as combinations of exchanges, each
421  //of which is performed according to the CoAP base protocol
422  while(*received < size)
423  {
424  //Point to the response message
425  responseMsg = coapClientGetResponseMessage(request);
426 
427  //Read payload data
428  error = coapClientReadPayload(responseMsg, data, size - *received, &n);
429 
430  //Check status code
431  if(error == NO_ERROR)
432  {
433  //Advance data pointer
434  data = (uint8_t *) data + n;
435 
436  //Total number of bytes that have been received
437  *received += n;
438  }
439  else if(error == ERROR_END_OF_STREAM)
440  {
441  //Point to the request message
442  requestMsg = coapClientGetRequestMessage(request);
443 
444  //A Block2 Option is used in control usage in a request
445  error = coapClientGetUintOption(requestMsg, COAP_OPT_BLOCK2, 0,
446  &value);
447 
448  //Block2 option found?
449  if(!error)
450  {
451  //Retrieve current block parameters
452  blockPos = COAP_GET_BLOCK_POS(value);
453  blockSzx = COAP_GET_BLOCK_SZX(value);
454  }
455  else
456  {
457  //Initialize block parameters
458  blockPos = 0;
459  blockSzx = request->rxBlockSzx;
460  }
461 
462  //Block2 option is used in descriptive usage in a response
463  error = coapClientGetUintOption(responseMsg, COAP_OPT_BLOCK2, 0,
464  &value);
465 
466  //Block-wise transfer?
467  if(!error)
468  {
469  //The value 7 for SZX is reserved
471  {
472  error = ERROR_FAILURE;
473  break;
474  }
475 
476  //The NUM field in the option value describes what block number
477  //is contained in the payload of this message
478  if(COAP_GET_BLOCK_POS(value) != blockPos)
479  {
480  //Report an error
481  error = ERROR_FAILURE;
482  break;
483  }
484 
485  //The M bit indicates whether further blocks need to be
486  //transferred to complete the transfer of that body
487  if(!COAP_GET_BLOCK_M(value))
488  {
489  //Exit immediately
490  error = ERROR_END_OF_STREAM;
491  break;
492  }
493 
494  //Retrieve the length of the payload
495  error = coapClientGetPayload(responseMsg, &payload, &payloadLen);
496  //Any error to report?
497  if(error)
498  break;
499 
500  //Make sure the length of the payload matches the block size
502  {
503  //Report an error
504  error = ERROR_FAILURE;
505  break;
506  }
507 
508  //If the first request uses a bigger block size than the receiver
509  //prefers, subsequent requests will use the preferred block size
510  if(blockSzx > COAP_GET_BLOCK_SZX(value))
511  {
512  blockSzx = COAP_GET_BLOCK_SZX(value);
513  }
514 
515  //Next block
516  blockPos += COAP_GET_BLOCK_SIZE(value);
517 
518  //The NUM field in the Block2 option gives the block number of the
519  //payload that is being requested to be returned in the response
520  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
521 
522  //the M bit has no function and must be set to zero
524 
525  //The block size given suggests a block size (in the case of block
526  //number 0) or repeats the block size of previous blocks received
527  //(in the case of a non-zero block number)
528  COAP_SET_BLOCK_SZX(value, blockSzx);
529 
530  //A Block2 Option is used in control usage in a request
531  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK2, 0,
532  value);
533  //Any error to report?
534  if(error)
535  break;
536 
537  //Perform message exchange
538  error = coapClientSendRequest(request, NULL, NULL);
539  //Any error to report?
540  if(error)
541  break;
542 
543  //Point to the response message
544  responseMsg = coapClientGetResponseMessage(request);
545 
546  //Retrieve response code
547  error = coapClientGetResponseCode(responseMsg, &responseCode);
548  //Any error to report?
549  if(error)
550  break;
551 
552  //Check response code
553  if(COAP_GET_CODE_CLASS(responseCode) != COAP_CODE_CLASS_SUCCESS)
554  {
555  error = ERROR_INVALID_STATUS;
556  break;
557  }
558  }
559  else
560  {
561  //The Block2 option is not present in the response
562  if(blockPos == 0)
563  {
564  error = ERROR_END_OF_STREAM;
565  }
566  else
567  {
568  error = ERROR_FAILURE;
569  }
570 
571  //Exit immediately
572  break;
573  }
574  }
575  else
576  {
577  //Failed to read payload data
578  break;
579  }
580  }
581 
582  //Any data received?
583  if(*received > 0)
584  {
585  //Catch exception
586  if(error == ERROR_END_OF_STREAM)
587  error = NO_ERROR;
588  }
589 
590  //Return status code
591  return error;
592 }
593 
594 
595 /**
596  * @brief Get maximum block size
597  * @return Block size
598  **/
599 
601 {
602  CoapBlockSize blockSize;
603 
604  //Retrieve maximum block size
605 #if (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 1024))
606  blockSize = COAP_BLOCK_SIZE_1024;
607 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 512))
608  blockSize = COAP_BLOCK_SIZE_512;
609 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 256))
610  blockSize = COAP_BLOCK_SIZE_256;
611 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 128))
612  blockSize = COAP_BLOCK_SIZE_128;
613 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 64))
614  blockSize = COAP_BLOCK_SIZE_64;
615 #elif (COAP_MAX_MSG_SIZE >= (COAP_HEADER_SIZE + 32))
616  blockSize = COAP_BLOCK_SIZE_32;
617 #else
618  blockSize = COAP_BLOCK_SIZE_16;
619 #endif
620 
621  //Return maximum block size
622  return blockSize;
623 }
624 
625 #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:53
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:222
#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:210
#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:368
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:369
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:50
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