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-2019 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 1.9.6
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 #include "error.h"
41 
42 //Check TCP/IP stack configuration
43 #if (COAP_CLIENT_SUPPORT == ENABLED && COAP_CLIENT_BLOCK_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Set preferred block for transmission path
48  * @param[in] request CoAP request handle
49  * @param[in] blockSize Preferred block size, in bytes
50  * @return Error code
51  **/
52 
54 {
55  //Make sure the CoAP request handle is valid
56  if(request == NULL)
58 
59  //Acquire exclusive access to the CoAP client context
60  osAcquireMutex(&request->context->mutex);
61 
62  //Set TX block size
63  if(blockSize == 16)
64  request->txBlockSzx = COAP_BLOCK_SIZE_16;
65  else if(blockSize == 32)
66  request->txBlockSzx = COAP_BLOCK_SIZE_32;
67  else if(blockSize == 64)
68  request->txBlockSzx = COAP_BLOCK_SIZE_64;
69  else if(blockSize == 128)
70  request->txBlockSzx = COAP_BLOCK_SIZE_128;
71  else if(blockSize == 256)
72  request->txBlockSzx = COAP_BLOCK_SIZE_256;
73  else if(blockSize == 512)
74  request->txBlockSzx = COAP_BLOCK_SIZE_512;
75  else
76  request->txBlockSzx = COAP_BLOCK_SIZE_1024;
77 
78  //Ensure the block size is acceptable
79  if(request->txBlockSzx > coapClientGetMaxBlockSize())
80  request->txBlockSzx = coapClientGetMaxBlockSize();
81 
82  //Release exclusive access to the CoAP client context
83  osReleaseMutex(&request->context->mutex);
84 
85  //Successful processing
86  return NO_ERROR;
87 }
88 
89 
90 /**
91  * @brief Set preferred block for reception path
92  * @param[in] request CoAP request handle
93  * @param[in] blockSize Preferred block size, in bytes
94  * @return Error code
95  **/
96 
98 {
99  //Make sure the CoAP request handle is valid
100  if(request == NULL)
102 
103  //Acquire exclusive access to the CoAP client context
104  osAcquireMutex(&request->context->mutex);
105 
106  //Set RX block size
107  if(blockSize == 16)
108  request->rxBlockSzx = COAP_BLOCK_SIZE_16;
109  else if(blockSize == 32)
110  request->rxBlockSzx = COAP_BLOCK_SIZE_32;
111  else if(blockSize == 64)
112  request->rxBlockSzx = COAP_BLOCK_SIZE_64;
113  else if(blockSize == 128)
114  request->rxBlockSzx = COAP_BLOCK_SIZE_128;
115  else if(blockSize == 256)
116  request->rxBlockSzx = COAP_BLOCK_SIZE_256;
117  else if(blockSize == 512)
118  request->rxBlockSzx = COAP_BLOCK_SIZE_512;
119  else
120  request->rxBlockSzx = COAP_BLOCK_SIZE_1024;
121 
122  //Ensure the block size is acceptable
123  if(request->rxBlockSzx > coapClientGetMaxBlockSize())
124  request->rxBlockSzx = coapClientGetMaxBlockSize();
125 
126  //Release exclusive access to the CoAP client context
127  osReleaseMutex(&request->context->mutex);
128 
129  //Successful processing
130  return NO_ERROR;
131 }
132 
133 
134 /**
135  * @brief Write resource body using block-wise mode
136  * @param[in] request CoAP request handle
137  * @param[in] data Pointer to a buffer containing the data to be transmitted
138  * @param[in] length Number of bytes to be transmitted
139  * @param[out] written Actual number of bytes written (optional parameter)
140  * @param[in] last Flag indicating whether this message fragment is the last
141  * @return Error code
142  **/
143 
145  const void *data, size_t length, size_t *written, bool_t last)
146 {
147  error_t error;
148  size_t n;
149  uint32_t value;
150  uint32_t blockPos;
151  uint32_t blockSzx;
152  size_t payloadLen;
153  const uint8_t *payload;
154  CoapMessage *requestMsg;
155  CoapMessage *responseMsg;
156  CoapCode responseCode;
157 
158  //Initialize status code
159  error = NO_ERROR;
160 
161  //Total number of bytes that have been written
162  if(written != NULL)
163  *written = 0;
164 
165  //Block-wise transfers are realized as combinations of exchanges, each
166  //of which is performed according to the CoAP base protocol
167  while(length > 0 || last)
168  {
169  //Point to the request message
170  requestMsg = coapClientGetRequestMessage(request);
171 
172  //Block1 option is used in descriptive usage in a request
173  error = coapGetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, &value);
174 
175  //Block1 option found?
176  if(!error)
177  {
178  //Retrieve current block parameters
179  blockPos = COAP_GET_BLOCK_POS(value);
180  blockSzx = COAP_GET_BLOCK_SZX(value);
181  }
182  else
183  {
184  //Initialize block parameters
185  blockPos = 0;
186  blockSzx = request->txBlockSzx;
187  }
188 
189  //Retrieve message payload length
190  error = coapClientGetPayload(requestMsg, &payload, &payloadLen);
191  //Any error to report?
192  if(error)
193  break;
194 
195  //Write as much data as possible
196  if(length > 0 && payloadLen < COAP_GET_BLOCK_SIZE(blockSzx))
197  {
198  //Limit the number of data to copy at a time
199  n = MIN(length, COAP_GET_BLOCK_SIZE(blockSzx) - payloadLen);
200 
201  //Write payload data
202  error = coapClientWritePayload(requestMsg, data, n);
203  //Any error to report?
204  if(error)
205  break;
206 
207  //Advance data pointer
208  data = (uint8_t *) data + n;
209  length -= n;
210 
211  //Total number of bytes that have been written
212  if(written != NULL)
213  *written += n;
214  }
215  else
216  {
217  //Block-wise transfer?
218  if(blockPos > 0 || length > 0 || !last)
219  {
220  //The NUM field in the option value describes what block number
221  //is contained in the payload of this message
222  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
223 
224  //The M bit indicates whether further blocks need to be transferred
225  //to complete the transfer of the body
226  if(length == 0 && last)
228  else
230 
231  //Set block size
232  COAP_SET_BLOCK_SZX(value, blockSzx);
233 
234  //Block1 option is used in descriptive usage in a request
235  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, value);
236  //Any error to report?
237  if(error)
238  break;
239  }
240 
241  //Last block?
242  if(length == 0 && last)
243  {
244  //Any preferred block size defined?
245  if(request->rxBlockSzx < COAP_BLOCK_SIZE_RESERVED)
246  {
247  //Set preferred block size
250  COAP_SET_BLOCK_SZX(value, request->rxBlockSzx);
251 
252  //Perform early negotiation
253  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK2, 0, value);
254  //Any error to report?
255  if(error)
256  break;
257  }
258  }
259 
260  //Send request
261  error = coapClientSendRequest(request, NULL, NULL);
262  //Any error to report?
263  if(error)
264  break;
265 
266  //Point to the response message
267  responseMsg = coapClientGetResponseMessage(request);
268 
269  //Retrieve response code
270  error = coapClientGetResponseCode(responseMsg, &responseCode);
271  //Any error to report?
272  if(error)
273  break;
274 
275  //Check response code
276  if(COAP_GET_CODE_CLASS(responseCode) != COAP_CODE_CLASS_SUCCESS)
277  {
278  error = ERROR_INVALID_STATUS;
279  break;
280  }
281 
282  //Block-wise transfer?
283  if(blockPos > 0 || length > 0 || !last)
284  {
285  //A Block1 option is used in control usage in a response
286  error = coapClientGetUintOption(responseMsg, COAP_OPT_BLOCK1, 0, &value);
287  //Any error to report?
288  if(error)
289  break;
290 
291  //The value 7 for SZX is reserved
293  {
294  error = ERROR_FAILURE;
295  break;
296  }
297 
298  //The NUM field of the Block1 option indicates what block number is
299  //being acknowledged
300  if(COAP_GET_BLOCK_POS(value) != blockPos)
301  {
302  error = ERROR_FAILURE;
303  break;
304  }
305 
306  //A server receiving a block-wise PUT or POST may want to indicate a
307  //smaller block size preference (late negotiation)
308  if(blockSzx > COAP_GET_BLOCK_SZX(value))
309  blockSzx = COAP_GET_BLOCK_SZX(value);
310 
311  //Next block
312  blockPos += COAP_GET_BLOCK_SIZE(blockSzx);
313 
314  //The NUM field in the option value describes what block number
315  //is contained in the payload of this message
316  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
317 
318  //Set block size
319  COAP_SET_BLOCK_SZX(value, blockSzx);
320 
321  //Block1 option is used in descriptive usage in a request
322  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK1, 0, value);
323  //Any error to report?
324  if(error)
325  break;
326  }
327 
328  //Trim the existing payload
329  error = coapClientSetPayload(requestMsg, NULL, 0);
330  //Any error to report?
331  if(error)
332  break;
333 
334  //Last block?
335  if(length == 0 && last)
336  {
337  //Delete Block1 option
338  error = coapClientDeleteOption(requestMsg, COAP_OPT_BLOCK1, 0);
339  //We are done
340  break;
341  }
342  }
343  }
344 
345  //Return status code
346  return error;
347 }
348 
349 
350 /**
351  * @brief Read resource body using block-wise mode
352  * @param[in] request CoAP request handle
353  * @param[out] data Buffer into which received data will be placed
354  * @param[in] size Maximum number of bytes that can be received
355  * @param[out] received Number of bytes that have been received
356  * @return Error code
357  **/
358 
360  size_t size, size_t *received)
361 {
362  error_t error;
363  size_t n;
364  uint32_t value;
365  uint32_t blockPos;
366  uint32_t blockSzx;
367  size_t payloadLen;
368  const uint8_t *payload;
369  CoapMessage *requestMsg;
370  CoapMessage *responseMsg;
371  CoapCode responseCode;
372 
373  //Initialize status code
374  error = NO_ERROR;
375 
376  //Total number of bytes that have been received
377  *received = 0;
378 
379  //Block-wise transfers are realized as combinations of exchanges, each
380  //of which is performed according to the CoAP base protocol
381  while(*received < size)
382  {
383  //Point to the response message
384  responseMsg = coapClientGetResponseMessage(request);
385 
386  //Read payload data
387  error = coapClientReadPayload(responseMsg, data, size - *received, &n);
388 
389  //Check status code
390  if(error == NO_ERROR)
391  {
392  //Advance data pointer
393  data = (uint8_t *) data + n;
394 
395  //Total number of bytes that have been received
396  *received += n;
397  }
398  else if(error == ERROR_END_OF_STREAM)
399  {
400  //Point to the request message
401  requestMsg = coapClientGetRequestMessage(request);
402 
403  //A Block2 Option is used in control usage in a request
404  error = coapClientGetUintOption(requestMsg, COAP_OPT_BLOCK2, 0,
405  &value);
406 
407  //Block2 option found?
408  if(!error)
409  {
410  //Retrieve current block parameters
411  blockPos = COAP_GET_BLOCK_POS(value);
412  blockSzx = COAP_GET_BLOCK_SZX(value);
413  }
414  else
415  {
416  //Initialize block parameters
417  blockPos = 0;
418  blockSzx = request->rxBlockSzx;
419  }
420 
421  //Block2 option is used in descriptive usage in a response
422  error = coapClientGetUintOption(responseMsg, COAP_OPT_BLOCK2, 0,
423  &value);
424 
425  //Block-wise transfer?
426  if(!error)
427  {
428  //The value 7 for SZX is reserved
430  {
431  error = ERROR_FAILURE;
432  break;
433  }
434 
435  //The NUM field in the option value describes what block number
436  //is contained in the payload of this message
437  if(COAP_GET_BLOCK_POS(value) != blockPos)
438  {
439  //Report an error
440  error = ERROR_FAILURE;
441  break;
442  }
443 
444  //The M bit indicates whether further blocks need to be
445  //transferred to complete the transfer of that body
446  if(!COAP_GET_BLOCK_M(value))
447  {
448  //Exit immediately
449  error = ERROR_END_OF_STREAM;
450  break;
451  }
452 
453  //Retrieve the length of the payload
454  error = coapClientGetPayload(responseMsg, &payload, &payloadLen);
455  //Any error to report?
456  if(error)
457  break;
458 
459  //Make sure the length of the payload matches the block size
461  {
462  //Report an error
463  error = ERROR_FAILURE;
464  break;
465  }
466 
467  //If the first request uses a bigger block size than the receiver
468  //prefers, subsequent requests will use the preferred block size
469  if(blockSzx > COAP_GET_BLOCK_SZX(value))
470  blockSzx = COAP_GET_BLOCK_SZX(value);
471 
472  //Next block
473  blockPos += COAP_GET_BLOCK_SIZE(value);
474 
475  //The NUM field in the Block2 option gives the block number of the
476  //payload that is being requested to be returned in the response
477  COAP_SET_BLOCK_NUM(value, blockPos >> (blockSzx + 4));
478 
479  //the M bit has no function and must be set to zero
481 
482  //The block size given suggests a block size (in the case of block
483  //number 0) or repeats the block size of previous blocks received
484  //(in the case of a non-zero block number)
485  COAP_SET_BLOCK_SZX(value, blockSzx);
486 
487  //A Block2 Option is used in control usage in a request
488  error = coapClientSetUintOption(requestMsg, COAP_OPT_BLOCK2, 0,
489  value);
490  //Any error to report?
491  if(error)
492  break;
493 
494  //Perform message exchange
495  error = coapClientSendRequest(request, NULL, NULL);
496  //Any error to report?
497  if(error)
498  break;
499 
500  //Point to the response message
501  responseMsg = coapClientGetResponseMessage(request);
502 
503  //Retrieve response code
504  error = coapClientGetResponseCode(responseMsg, &responseCode);
505  //Any error to report?
506  if(error)
507  break;
508 
509  //Check response code
510  if(COAP_GET_CODE_CLASS(responseCode) != COAP_CODE_CLASS_SUCCESS)
511  {
512  error = ERROR_INVALID_STATUS;
513  break;
514  }
515  }
516  else
517  {
518  //The Block2 option is not present in the response
519  if(blockPos == 0)
520  error = ERROR_END_OF_STREAM;
521  else
522  error = ERROR_FAILURE;
523 
524  //Exit immediately
525  break;
526  }
527  }
528  else
529  {
530  //Failed to read payload data
531  break;
532  }
533  }
534 
535  //Any data received?
536  if(*received > 0)
537  {
538  //Catch exception
539  if(error == ERROR_END_OF_STREAM)
540  error = NO_ERROR;
541  }
542 
543  //Return status code
544  return error;
545 }
546 
547 
548 /**
549  * @brief Get maximum block size
550  * @return Block size
551  **/
552 
554 {
555  CoapBlockSize blockSize;
556 
557  //Retrieve maximum block size
558 #if (COAP_MAX_MSG_SIZE > 1024)
559  blockSize = COAP_BLOCK_SIZE_1024;
560 #elif (COAP_MAX_MSG_SIZE > 512)
561  blockSize = COAP_BLOCK_SIZE_512;
562 #elif (COAP_MAX_MSG_SIZE > 256)
563  blockSize = COAP_BLOCK_SIZE_256;
564 #elif (COAP_MAX_MSG_SIZE > 128)
565  blockSize = COAP_BLOCK_SIZE_128;
566 #elif (COAP_MAX_MSG_SIZE > 64)
567  blockSize = COAP_BLOCK_SIZE_64;
568 #elif (COAP_MAX_MSG_SIZE > 32)
569  blockSize = COAP_BLOCK_SIZE_32;
570 #else
571  blockSize = COAP_BLOCK_SIZE_16;
572 #endif
573 
574  //Return maximum block size
575  return blockSize;
576 }
577 
578 #endif
error_t coapClientSetPayload(CoapMessage *message, const void *payload, size_t payloadLen)
Set message payload.
@ COAP_BLOCK_SIZE_256
Definition: coap_option.h:164
uint8_t length
Definition: dtls_misc.h:149
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:49
CoapMessage * coapClientGetRequestMessage(CoapClientRequest *request)
Get request message.
error_t coapClientSendRequest(CoapClientRequest *request, CoapRequestCallback callback, void *param)
Send a CoAP request.
#define COAP_GET_BLOCK_SIZE(value)
Definition: coap_option.h:76
uint16_t last
Definition: ipv4_frag.h:96
@ ERROR_INVALID_STATUS
Definition: error.h:101
@ COAP_BLOCK_SIZE_1024
Definition: coap_option.h:166
#define COAP_SET_BLOCK_M(value, m)
Definition: coap_option.h:64
@ ERROR_END_OF_STREAM
Definition: error.h:208
#define CoapClientRequest
Definition: coap_client.h:143
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:42
#define COAP_GET_CODE_CLASS(code)
Definition: coap_common.h:59
@ 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:55
CoAP client.
@ COAP_OPT_BLOCK1
Definition: coap_option.h:106
CoapBlockSize coapClientGetMaxBlockSize(void)
Get maximum block size.
Error codes description.
@ COAP_BLOCK_SIZE_128
Definition: coap_option.h:163
error_t coapClientReadPayload(CoapMessage *message, void *data, size_t size, size_t *length)
Read payload data.
#define MIN(a, b)
Definition: os_port.h:62
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:160
uint8_t n
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
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:167
#define COAP_SET_BLOCK_NUM(value, n)
Definition: coap_option.h:62
@ COAP_BLOCK_SIZE_512
Definition: coap_option.h:165
uint8_t payloadLen
Definition: ipv6.h:343
uint8_t value[]
Definition: dtls_misc.h:150
@ COAP_CODE_CLASS_SUCCESS
Definition: coap_common.h:107
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
uint8_t data[]
Definition: dtls_misc.h:176
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:117
CoapBlockSize
Block size parameter.
Definition: coap_option.h:158
@ 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:162
@ COAP_BLOCK_SIZE_32
Definition: coap_option.h:161