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