http_client.c
Go to the documentation of this file.
1 /**
2  * @file http_client.c
3  * @brief HTTP client (HyperText Transfer Protocol)
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  * @section Description
28  *
29  * The Hypertext Transfer Protocol (HTTP) is an application-level protocol for
30  * distributed, collaborative, hypermedia information systems. Refer to the
31  * following RFCs for complete details:
32  * - RFC 1945: Hypertext Transfer Protocol - HTTP/1.0
33  * - RFC 2616: Hypertext Transfer Protocol - HTTP/1.1
34  * - RFC 2818: HTTP Over TLS
35  * - RFC 7230: Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
36  * - RFC 7231: Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
37  *
38  * @author Oryx Embedded SARL (www.oryx-embedded.com)
39  * @version 1.9.4
40  **/
41 
42 //Switch to the appropriate trace level
43 #define TRACE_LEVEL HTTP_TRACE_LEVEL
44 
45 //Dependencies
46 #include <limits.h>
47 #include "core/net.h"
48 #include "http/http_client.h"
49 #include "http/http_client_auth.h"
51 #include "http/http_client_misc.h"
52 #include "str.h"
53 #include "debug.h"
54 
55 //Check TCP/IP stack configuration
56 #if (HTTP_CLIENT_SUPPORT == ENABLED)
57 
58 
59 /**
60  * @brief Initialize HTTP client context
61  * @param[in] context Pointer to the HTTP client context
62  * @return Error code
63  **/
64 
66 {
67 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
68  error_t error;
69 #endif
70 
71  //Make sure the HTTP client context is valid
72  if(context == NULL)
74 
75  //Clear HTTP client context
76  memset(context, 0, sizeof(HttpClientContext));
77 
78 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
79  //Initialize TLS session state
80  error = tlsInitSessionState(&context->tlsSession);
81  //Any error to report?
82  if(error)
83  return error;
84 #endif
85 
86  //Initialize HTTP connection state
87  context->state = HTTP_CLIENT_STATE_DISCONNECTED;
88  //Initialize HTTP request state
89  context->requestState = HTTP_REQ_STATE_INIT;
90 
91  //Default protocol version
92  context->version = HTTP_VERSION_1_1;
93  //Default timeout
94  context->timeout = HTTP_CLIENT_DEFAULT_TIMEOUT;
95 
96  //Successful initialization
97  return NO_ERROR;
98 }
99 
100 
101 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
102 
103 /**
104  * @brief Register TLS initialization callback function
105  * @param[in] context Pointer to the HTTP client context
106  * @param[in] callback TLS initialization callback function
107  * @return Error code
108  **/
109 
111  HttpClientTlsInitCallback callback)
112 {
113  //Make sure the HTTP client context is valid
114  if(context == NULL)
116 
117  //Save callback function
118  context->tlsInitCallback = callback;
119 
120  //Successful processing
121  return NO_ERROR;
122 }
123 
124 #endif
125 
126 
127 /**
128  * @brief Register random data generation callback function
129  * @param[in] context Pointer to the HTTP client context
130  * @param[in] callback Random data generation callback function
131  * @return Error code
132  **/
133 
135  HttpClientRandCallback callback)
136 {
137 #if (HTTP_CLIENT_DIGEST_AUTH_SUPPORT == ENABLED)
138  //Make sure the HTTP client context is valid
139  if(context == NULL)
141 
142  //Save callback function
143  context->randCallback = callback;
144 
145  //Successful processing
146  return NO_ERROR;
147 #else
148  //Digest authentication is not implemented
149  return ERROR_NOT_IMPLEMENTED;
150 #endif
151 }
152 
153 
154 /**
155  * @brief Set the HTTP protocol version to be used
156  * @param[in] context Pointer to the HTTP client context
157  * @param[in] version HTTP protocol version (1.0 or 1.1)
158  * @return Error code
159  **/
160 
162 {
163  //Make sure the HTTP client context is valid
164  if(context == NULL)
166 
167  //Check HTTP version
169  return ERROR_INVALID_VERSION;
170 
171  //Save the protocol version to be used
172  context->version = version;
173 
174  //Successful processing
175  return NO_ERROR;
176 }
177 
178 
179 /**
180  * @brief Set communication timeout
181  * @param[in] context Pointer to the HTTP client context
182  * @param[in] timeout Timeout value, in milliseconds
183  * @return Error code
184  **/
185 
187 {
188  //Make sure the HTTP client context is valid
189  if(context == NULL)
191 
192  //Save timeout value
193  context->timeout = timeout;
194 
195  //Successful processing
196  return NO_ERROR;
197 }
198 
199 
200 /**
201  * @brief Set authentication information
202  * @param[in] context Pointer to the HTTP client context
203  * @param[in] username NULL-terminated string containing the user name to be used
204  * @param[in] password NULL-terminated string containing the password to be used
205  * @return Error code
206  **/
207 
209  const char_t *username, const char_t *password)
210 {
211 #if (HTTP_CLIENT_AUTH_SUPPORT == ENABLED)
212  //Check parameters
213  if(context == NULL || username == NULL || password == NULL)
215 
216  //Make sure the length of the user name is acceptable
217  if(strlen(username) > HTTP_CLIENT_MAX_USERNAME_LEN)
218  return ERROR_INVALID_LENGTH;
219 
220  //Save user name
221  strcpy(context->authParams.username, username);
222 
223  //Make sure the length of the password is acceptable
224  if(strlen(password) > HTTP_CLIENT_MAX_PASSWORD_LEN)
225  return ERROR_INVALID_LENGTH;
226 
227  //Save password
228  strcpy(context->authParams.password, password);
229 
230  //Successful processing
231  return NO_ERROR;
232 #else
233  //HTTP authentication is not implemented
234  return ERROR_NOT_IMPLEMENTED;
235 #endif
236 }
237 
238 
239 /**
240  * @brief Bind the HTTP client to a particular network interface
241  * @param[in] context Pointer to the HTTP client context
242  * @param[in] interface Network interface to be used
243  * @return Error code
244  **/
245 
247  NetInterface *interface)
248 {
249  //Make sure the HTTP client context is valid
250  if(context == NULL)
252 
253  //Explicitly associate the HTTP client with the specified interface
254  context->interface = interface;
255 
256  //Successful processing
257  return NO_ERROR;
258 }
259 
260 
261 /**
262  * @brief Establish a connection with the specified HTTP server
263  * @param[in] context Pointer to the HTTP client context
264  * @param[in] serverIpAddr IP address of the HTTP server to connect to
265  * @param[in] serverPort Port number
266  * @return Error code
267  **/
268 
270  const IpAddr *serverIpAddr, uint16_t serverPort)
271 {
272  error_t error;
273 
274  //Make sure the HTTP client context is valid
275  if(context == NULL)
277 
278  //Initialize status code
279  error = NO_ERROR;
280 
281  //Establish connection with the HTTP server
282  while(!error)
283  {
284  //Check HTTP connection state
285  if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
286  {
287  //First connection attempt?
288  if(serverIpAddr != NULL)
289  {
290  //Save the IP address of the HTTP server
291  context->serverIpAddr = *serverIpAddr;
292  //Save the TCP port number to be used
293  context->serverPort = serverPort;
294 
295 #if (HTTP_CLIENT_AUTH_SUPPORT == ENABLED)
296  //HTTP authentication is not used for the first connection attempt
297  httpClientInitAuthParams(&context->authParams);
298 #endif
299  }
300 
301  //Open network connection
302  error = httpClientOpenConnection(context);
303 
304  //Check status code
305  if(!error)
306  {
307  //Establish network connection
309  }
310  }
311  else if(context->state == HTTP_CLIENT_STATE_CONNECTING)
312  {
313  //Establish network connection
314  error = httpClientEstablishConnection(context, &context->serverIpAddr,
315  context->serverPort);
316 
317  //Check status code
318  if(error == NO_ERROR)
319  {
320  //The client is connected to the HTTP server
322  }
323  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
324  {
325  //Check whether the timeout has elapsed
326  error = httpClientCheckTimeout(context);
327  }
328  else
329  {
330  //A communication error has occured
331  }
332  }
333  else if(context->state == HTTP_CLIENT_STATE_CONNECTED)
334  {
335  //The HTTP client is connected
336  break;
337  }
338  else
339  {
340  //Invalid state
341  error = ERROR_WRONG_STATE;
342  }
343  }
344 
345  //Failed to establish connection with the HTTP server?
346  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
347  {
348  //Clean up side effects
349  httpClientCloseConnection(context);
350  //Update HTTP connection state
352  }
353 
354  //Return status code
355  return error;
356 }
357 
358 
359 /**
360  * @brief Create a new HTTP request
361  * @param[in] context Pointer to the HTTP client context
362  * @return Error code
363  **/
364 
366 {
367  error_t error;
368 
369  //Make sure the HTTP client context is valid
370  if(context == NULL)
372 
373  //Format default HTTP request header
374  error = httpClientFormatRequestHeader(context);
375 
376  //Check status code
377  if(!error)
378  {
379  //The HTTP request body is empty
380  context->bodyLen = 0;
381  context->bodyPos = 0;
382 
383  //Reset status code
384  context->statusCode = 0;
385 
386  //Update HTTP request state
388  }
389 
390  //Return status code
391  return error;
392 }
393 
394 
395 /**
396  * @brief Set HTTP request method
397  * @param[in] context Pointer to the HTTP client context
398  * @param[in] method NULL-terminating string containing the HTTP method
399  * @return Error code
400  **/
401 
403 {
404  size_t m;
405  size_t n;
406  char_t *p;
407 
408  //Check parameters
409  if(context == NULL || method == NULL)
411 
412  //Compute the length of the HTTP method
413  n = strlen(method);
414 
415  //Make sure the length of the user name is acceptable
416  if(n == 0 || n > HTTP_CLIENT_MAX_METHOD_LEN)
417  return ERROR_INVALID_LENGTH;
418 
419  //Make sure the buffer contains a valid HTTP request
420  if(context->bufferLen > HTTP_CLIENT_BUFFER_SIZE)
421  return ERROR_INVALID_SYNTAX;
422 
423  //Properly terminate the string with a NULL character
424  context->buffer[context->bufferLen] = '\0';
425 
426  //The Request-Line begins with a method token
427  p = strchr(context->buffer, ' ');
428  //Any parsing error?
429  if(p == NULL)
430  return ERROR_INVALID_SYNTAX;
431 
432  //Compute the length of the current method token
433  m = p - context->buffer;
434 
435  //Make sure the buffer is large enough to hold the new HTTP request method
436  if((context->bufferLen + n - m) > HTTP_CLIENT_BUFFER_SIZE)
437  return ERROR_BUFFER_OVERFLOW;
438 
439  //Make room for the new method token
440  memmove(context->buffer + n, p, context->bufferLen + 1 - m);
441  //Copy the new method token
442  strncpy(context->buffer, method, n);
443 
444  //Adjust the length of the request header
445  context->bufferLen = context->bufferLen + n - m;
446 
447  //Save HTTP request method
448  strcpy(context->method, method);
449 
450  //Successful processing
451  return NO_ERROR;
452 }
453 
454 
455 /**
456  * @brief Set request URI
457  * @param[in] context Pointer to the HTTP client context
458  * @param[in] uri NULL-terminated string that contains the resource name
459  * @return Error code
460  **/
461 
463 {
464  size_t m;
465  size_t n;
466  char_t *p;
467  char_t *q;
468 
469  //Check parameters
470  if(context == NULL || uri == NULL)
472 
473  //The resource name must not be empty
474  if(uri[0] == '\0')
476 
477  //Check HTTP request state
478  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER)
479  return ERROR_WRONG_STATE;
480 
481  //Make sure the buffer contains a valid HTTP request
482  if(context->bufferLen > HTTP_CLIENT_BUFFER_SIZE)
483  return ERROR_INVALID_SYNTAX;
484 
485  //Properly terminate the string with a NULL character
486  context->buffer[context->bufferLen] = '\0';
487 
488  //The Request-Line begins with a method token
489  p = strchr(context->buffer, ' ');
490  //Any parsing error?
491  if(p == NULL)
492  return ERROR_INVALID_SYNTAX;
493 
494  //The method token is followed by the Request-URI
495  p++;
496 
497  //Point to the end of the Request-URI
498  q = strpbrk(p, " ?");
499  //Any parsing error?
500  if(q == NULL)
501  return ERROR_INVALID_SYNTAX;
502 
503  //Compute the length of the current URI
504  m = q - p;
505  //Compute the length of the new URI
506  n = strlen(uri);
507 
508  //Make sure the buffer is large enough to hold the new resource name
509  if((context->bufferLen + n - m) > HTTP_CLIENT_BUFFER_SIZE)
510  return ERROR_BUFFER_OVERFLOW;
511 
512  //Make room for the new resource name
513  memmove(p + n, q, context->buffer + context->bufferLen + 1 - q);
514  //Copy the new resource name
515  strncpy(p, uri, n);
516 
517  //Adjust the length of the request header
518  context->bufferLen = context->bufferLen + n - m;
519 
520  //Successful processing
521  return NO_ERROR;
522 }
523 
524 
525 /**
526  * @brief Set the hostname and port number of the resource being requested
527  * @param[in] context Pointer to the HTTP client context
528  * @param[in] host NULL-terminated string containing the hostname
529  * @param[in] port TCP port number
530  * @return Error code
531  **/
532 
534  uint16_t port)
535 {
536  size_t m;
537  size_t n;
538  char_t temp[7];
539 
540  //Check parameters
541  if(context == NULL || host == NULL)
543 
544  //Check HTTP request state
545  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER)
546  return ERROR_WRONG_STATE;
547 
548  //Make sure the buffer contains a valid HTTP request
549  if(context->bufferLen < 2 || context->bufferLen > HTTP_CLIENT_BUFFER_SIZE)
550  return ERROR_INVALID_SYNTAX;
551 
552  //Default port number?
553  if(port == HTTP_PORT)
554  {
555  //A host without any trailing port information implies the default port
556  //for the service requested
557  strcpy(temp, "");
558  }
559  else
560  {
561  //Format port number information
562  sprintf(temp, ":%" PRIu16, port);
563  }
564 
565  //Compute the length of the hostname
566  n = strlen(host);
567  //Compute the length of the trailing port information
568  m = strlen(temp);
569 
570  //Make sure the buffer is large enough to hold the new header field
571  if((context->bufferLen + n + m + 8) > HTTP_CLIENT_BUFFER_SIZE)
572  return ERROR_BUFFER_OVERFLOW;
573 
574  //The Host request-header field specifies the Internet host and port number
575  //of the resource being requested
576  sprintf(context->buffer + context->bufferLen - 2, "Host: %s%s\r\n\r\n",
577  host, temp);
578 
579  //Adjust the length of the request header
580  context->bufferLen = context->bufferLen + n + m + 8;
581 
582  //Successful processing
583  return NO_ERROR;
584 }
585 
586 
587 /**
588  * @brief Set query string
589  * @param[in] context Pointer to the HTTP client context
590  * @param[in] queryString NULL-terminated string that contains the query string
591  * @return Error code
592  **/
593 
595  const char_t *queryString)
596 {
597  size_t m;
598  size_t n;
599  char_t *p;
600  char_t *q;
601 
602  //Check parameters
603  if(context == NULL || queryString == NULL)
605 
606  //Check HTTP request state
607  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER)
608  return ERROR_WRONG_STATE;
609 
610  //Make sure the buffer contains a valid HTTP request
611  if(context->bufferLen > HTTP_CLIENT_BUFFER_SIZE)
612  return ERROR_INVALID_SYNTAX;
613 
614  //Properly terminate the string with a NULL character
615  context->buffer[context->bufferLen] = '\0';
616 
617  //The Request-Line begins with a method token
618  p = strchr(context->buffer, ' ');
619  //Any parsing error?
620  if(p == NULL)
621  return ERROR_INVALID_SYNTAX;
622 
623  //The method token is followed by the Request-URI
624  p = strpbrk(p + 1, " ?");
625  //Any parsing error?
626  if(p == NULL)
627  return ERROR_INVALID_SYNTAX;
628 
629  //The question mark is used as a separator
630  if(*p == '?')
631  {
632  //Point to the end of the query string
633  q = strchr(p + 1, ' ');
634  //Any parsing error?
635  if(q == NULL)
636  return ERROR_INVALID_SYNTAX;
637 
638  //Compute the length of the actual query string
639  m = q - p;
640  }
641  else
642  {
643  //The query string is not present
644  q = p;
645  m = 0;
646  }
647 
648  //Compute the length of the new query string
649  n = strlen(queryString);
650 
651  //Empty query string?
652  if(n == 0)
653  {
654  //Remove the query string
655  memmove(p, p + m, context->buffer + context->bufferLen + 1 - q);
656  }
657  else
658  {
659  //The question mark is not part of the query string
660  n++;
661 
662  //Make sure the buffer is large enough to hold the new query string
663  if((context->bufferLen + n - m) > HTTP_CLIENT_BUFFER_SIZE)
664  return ERROR_BUFFER_OVERFLOW;
665 
666  //Make room for the new query string
667  memmove(p + n, q, context->buffer + context->bufferLen + 1 - q);
668 
669  //The question mark is used as a separator
670  p[0] = '?';
671  //Copy the new query string
672  strncpy(p + 1, queryString, n - 1);
673  }
674 
675  //Adjust the length of the request header
676  context->bufferLen = context->bufferLen + n - m;
677 
678  //Successful processing
679  return NO_ERROR;
680 }
681 
682 
683 /**
684  * @brief Add a key/value pair to the query string
685  * @param[in] context Pointer to the HTTP client context
686  * @param[in] name NULL-terminated string that holds the parameter name
687  * @param[in] value NULL-terminated string that holds the parameter value
688  * @return Error code
689  **/
690 
692  const char_t *name, const char_t *value)
693 {
694  size_t nameLen;
695  size_t valueLen;
696  char_t separator;
697  char_t *p;
698 
699  //Check parameters
700  if(context == NULL || name == NULL)
702 
703  //The parameter name must not be empty
704  if(name[0] == '\0')
706 
707  //Check HTTP request state
708  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER)
709  return ERROR_WRONG_STATE;
710 
711  //Make sure the buffer contains a valid HTTP request
712  if(context->bufferLen > HTTP_CLIENT_BUFFER_SIZE)
713  return ERROR_INVALID_SYNTAX;
714 
715  //Properly terminate the string with a NULL character
716  context->buffer[context->bufferLen] = '\0';
717 
718  //The Request-Line begins with a method token
719  p = strchr(context->buffer, ' ');
720  //Any parsing error?
721  if(p == NULL)
722  return ERROR_INVALID_SYNTAX;
723 
724  //The method token is followed by the Request-URI
725  p = strpbrk(p + 1, " ?");
726  //Any parsing error?
727  if(p == NULL)
728  return ERROR_INVALID_SYNTAX;
729 
730  //The question mark is used as a separator
731  if(*p == '?')
732  {
733  //Point to the end of the query string
734  p = strchr(p + 1, ' ');
735  //Any parsing error?
736  if(p == NULL)
737  return ERROR_INVALID_SYNTAX;
738 
739  //multiple query parameters are separated by an ampersand
740  separator = '&';
741  }
742  else
743  {
744  //The query string is not present
745  separator = '?';
746  }
747 
748  //Compute the length of the parameter value
749  nameLen = strlen(name);
750 
751  //Empty parameter value?
752  if(value == NULL)
753  {
754  //Make sure the buffer is large enough to hold the new query parameter
755  if((context->bufferLen + nameLen + 1) > HTTP_CLIENT_BUFFER_SIZE)
756  return ERROR_BUFFER_OVERFLOW;
757 
758  //Make room for the new query parameter
759  memmove(p + nameLen + 1, p, context->buffer + context->bufferLen + 1 - p);
760 
761  //Multiple query parameters are separated by a delimiter
762  p[0] = separator;
763  //Copy parameter name
764  strncpy(p + 1, name, nameLen);
765 
766  //Adjust the length of the request header
767  context->bufferLen = context->bufferLen + nameLen + 1;
768  }
769  else
770  {
771  //Compute the length of the parameter value
772  valueLen = strlen(value);
773 
774  //Make sure the buffer is large enough to hold the new query parameter
775  if((context->bufferLen + nameLen + valueLen + 2) > HTTP_CLIENT_BUFFER_SIZE)
776  return ERROR_BUFFER_OVERFLOW;
777 
778  //Make room for the new query parameter
779  memmove(p + nameLen + valueLen + 2, p, context->buffer +
780  context->bufferLen + 1 - p);
781 
782  //Multiple query parameters are separated by a delimiter
783  p[0] = separator;
784  //Copy parameter name
785  strncpy(p + 1, name, nameLen);
786  //The field name and value are separated by an equals sign
787  p[nameLen + 1] = '=';
788  //Copy parameter value
789  strncpy(p + nameLen + 2, value, valueLen);
790 
791  //Adjust the length of the request header
792  context->bufferLen = context->bufferLen + nameLen + valueLen + 2;
793  }
794 
795  //Successful processing
796  return NO_ERROR;
797 }
798 
799 
800 /**
801  * @brief Add a header field to the HTTP request
802  * @param[in] context Pointer to the HTTP client context
803  * @param[in] name NULL-terminated string that holds the header field name
804  * @param[in] value NULL-terminated string that holds the header field value
805  * @return Error code
806  **/
807 
809  const char_t *name, const char_t *value)
810 {
811  error_t error;
812  size_t n;
813  size_t nameLen;
814  size_t valueLen;
815 
816  //Check parameters
817  if(context == NULL || name == NULL || value == NULL)
819 
820  //The field name must not be empty
821  if(name[0] == '\0')
823 
824  //Check HTTP request state
825  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER &&
826  context->requestState != HTTP_REQ_STATE_FORMAT_TRAILER)
827  {
828  return ERROR_WRONG_STATE;
829  }
830 
831  //Make sure the buffer contains a valid HTTP request
832  if(context->bufferLen < 2 || context->bufferLen > HTTP_CLIENT_BUFFER_SIZE)
833  return ERROR_INVALID_SYNTAX;
834 
835  //Retrieve the length of the field name and value
836  nameLen = strlen(name);
837  valueLen = strlen(value);
838 
839  //Determine the length of the new header field
840  n = nameLen + valueLen + 4;
841 
842  //Make sure the buffer is large enough to hold the new header field
843  if((context->bufferLen + n) > HTTP_CLIENT_BUFFER_SIZE)
844  return ERROR_BUFFER_OVERFLOW;
845 
846  //Each header field consists of a case-insensitive field name followed
847  //by a colon, optional leading whitespace and the field value
848  sprintf(context->buffer + context->bufferLen - 2, "%s: %s\r\n\r\n",
849  name, value);
850 
851  //Adjust the length of the request header
852  context->bufferLen = context->bufferLen + n;
853 
854  //Check header field name
855  if(!strcasecmp(name, "Connection"))
856  {
857  //Parse Connection header field
858  error = httpClientParseConnectionField(context, value);
859  }
860  else if(!strcasecmp(name, "Transfer-Encoding"))
861  {
862  //Parse Transfer-Encoding header field
864  }
865  else if(!strcasecmp(name, "Content-Length"))
866  {
867  //Parse Content-Length header field
868  error = httpClientParseContentLengthField(context, value);
869  }
870  else
871  {
872  //Discard unknown header fields
873  error = NO_ERROR;
874  }
875 
876  //Return status code
877  return error;
878 }
879 
880 
881 /**
882  * @brief Set the length of the HTTP request body
883  * @param[in] context Pointer to the HTTP client context
884  * @param[in] length Length of the HTTP request body, in bytes
885  * @return Error code
886  **/
887 
889 {
890  char_t temp[11];
891 
892  //Make sure the HTTP client context is valid
893  if(context == NULL)
895 
896  //Check HTTP request state
897  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER)
898  return ERROR_WRONG_STATE;
899 
900  //The Content-Length header field indicates the size of the body, in
901  //decimal number of octets
902  sprintf(temp, "%" PRIuSIZE, length);
903 
904  //Add the Content-Length header field
905  return httpClientAddHeaderField(context, "Content-Length", temp);
906 }
907 
908 
909 /**
910  * @brief Write HTTP request header
911  * @param[in] context Pointer to the HTTP client context
912  * @return Error code
913  **/
914 
916 {
917  error_t error;
918  size_t n;
919 
920  //Make sure the HTTP client context is valid
921  if(context == NULL)
923 
924  //Initialize status code
925  error = NO_ERROR;
926 
927  //Send HTTP request header
928  while(!error)
929  {
930  //Check HTTP connection state
931  if(context->state == HTTP_CLIENT_STATE_DISCONNECTED ||
932  context->state == HTTP_CLIENT_STATE_CONNECTING)
933  {
934  //If the HTTP connection is not persistent, then a new connection
935  //must be established
936  error = httpClientConnect(context, NULL, 0);
937  }
938  else if(context->state == HTTP_CLIENT_STATE_CONNECTED)
939  {
940  //Check HTTP request state
941  if(context->requestState == HTTP_REQ_STATE_FORMAT_HEADER)
942  {
943 #if (HTTP_CLIENT_AUTH_SUPPORT == ENABLED)
944  //HTTP authentication requested by the server?
945  if(context->authParams.mode != HTTP_AUTH_MODE_NONE &&
946  context->authParams.username[0] != '\0')
947  {
948  //Format Authorization header field
949  error = httpClientFormatAuthorizationField(context);
950  }
951 #endif
952  //Check status code
953  if(!error)
954  {
955  //Dump HTTP request header
956  TRACE_DEBUG("HTTP request header:\r\n%s", context->buffer);
957 
958  //Initiate the sending process
960  }
961  }
962  else if(context->requestState == HTTP_REQ_STATE_SEND_HEADER)
963  {
964  //Any remaining data to be sent?
965  if(context->bufferPos < context->bufferLen)
966  {
967  //Send more data
968  error = httpClientSendData(context,
969  context->buffer + context->bufferPos,
970  context->bufferLen - context->bufferPos, &n, 0);
971 
972  //Check status code
973  if(error == NO_ERROR || error == ERROR_TIMEOUT)
974  {
975  //Advance data pointer
976  context->bufferPos += n;
977  }
978  }
979  else
980  {
981  //The request header has been successfully transmitted
982  if(!strcasecmp(context->method, "POST") ||
983  !strcasecmp(context->method, "PUT") ||
984  !strcasecmp(context->method, "PATCH"))
985  {
986  //POST, PUT and PATCH requests have a body
988  }
989  else
990  {
991  //GET, HEAD and DELETE requests do not have a body
994  }
995  }
996  }
997  else if(context->requestState == HTTP_REQ_STATE_SEND_BODY ||
998  context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
999  {
1000  //We are done
1001  break;
1002  }
1003  else
1004  {
1005  //Invalid HTTP request state
1006  error = ERROR_WRONG_STATE;
1007  }
1008  }
1009  else
1010  {
1011  //Invalid HTTP connection state
1012  error = ERROR_WRONG_STATE;
1013  }
1014  }
1015 
1016  //Check status code
1017  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1018  {
1019  //Check whether the timeout has elapsed
1020  error = httpClientCheckTimeout(context);
1021  }
1022 
1023  //Return status code
1024  return error;
1025 }
1026 
1027 
1028 /**
1029  * @brief Write HTTP request body
1030  * @param[in] context Pointer to the HTTP client context
1031  * @param[in] data Pointer to the buffer containing the data to be transmitted
1032  * @param[in] length Number of data bytes to send
1033  * @param[out] written Actual number of bytes written (optional parameter)
1034  * @param[in] flags Set of flags that influences the behavior of this function
1035  * @return Error code
1036  **/
1037 
1039  size_t length, size_t *written, uint_t flags)
1040 {
1041  error_t error;
1042  size_t n;
1043  size_t totalLength;
1044 
1045  //Make sure the HTTP client context is valid
1046  if(context == NULL)
1047  return ERROR_INVALID_PARAMETER;
1048 
1049  //Check HTTP connection state
1050  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1051  return ERROR_WRONG_STATE;
1052 
1053  //Initialize status code
1054  error = NO_ERROR;
1055 
1056  //Actual number of bytes written
1057  totalLength = 0;
1058 
1059  //Send as much data as possible
1060  while(totalLength < length && !error)
1061  {
1062  //Check HTTP request state
1063  if(context->requestState == HTTP_REQ_STATE_SEND_BODY)
1064  {
1065  //Chunked transfer encoding?
1066  if(context->chunkedEncoding)
1067  {
1068  //The chunked encoding modifies the body in order to transfer it
1069  //as a series of chunks, each with its own size indicator
1070  error = httpClientFormatChunkSize(context, length - totalLength);
1071  }
1072  else
1073  {
1074  //Number of bytes left to send
1075  n = length - totalLength;
1076 
1077  //The length of the body shall not exceed the value specified in
1078  //the Content-Length field
1079  if(n <= (context->bodyLen - context->bodyPos))
1080  {
1081  //Send request body
1082  error = httpClientSendData(context, data, n, &n, flags);
1083 
1084  //Check status code
1085  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1086  {
1087  //Any data transmitted?
1088  if(n > 0)
1089  {
1090  //Advance data pointer
1091  data = (uint8_t *) data + n;
1092  totalLength += n;
1093  context->bodyPos += n;
1094 
1095  //Save current time
1096  context->timestamp = osGetSystemTime();
1097  }
1098  }
1099  }
1100  else
1101  {
1102  //Report an error
1103  error = ERROR_INVALID_LENGTH;
1104  }
1105  }
1106  }
1107  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_SIZE)
1108  {
1109  //Send the chunk-size field
1110  if(context->bufferPos < context->bufferLen)
1111  {
1112  //Send more data
1113  error = httpClientSendData(context,
1114  context->buffer + context->bufferPos,
1115  context->bufferLen - context->bufferPos, &n, 0);
1116 
1117  //Check status code
1118  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1119  {
1120  //Advance data pointer
1121  context->bufferPos += n;
1122  }
1123  }
1124  else
1125  {
1126  //Send chunk data
1128  }
1129  }
1130  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1131  {
1132  //The data stream is divided into a series of chunks
1133  if(context->bodyPos < context->bodyLen)
1134  {
1135  //The length of the chunk shall not exceed the value specified in
1136  //the chunk-size field
1137  n = MIN(length - totalLength, context->bodyLen - context->bodyPos);
1138 
1139  //Send chunk data
1140  error = httpClientSendData(context, data, n, &n, flags);
1141 
1142  //Check status code
1143  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1144  {
1145  //Any data transmitted?
1146  if(n > 0)
1147  {
1148  //Advance data pointer
1149  data = (uint8_t *) data + n;
1150  totalLength += n;
1151  context->bodyPos += n;
1152 
1153  //Save current time
1154  context->timestamp = osGetSystemTime();
1155  }
1156  }
1157  }
1158  else
1159  {
1160  //The chunked encoding modifies the body in order to transfer it
1161  //as a series of chunks, each with its own size indicator
1162  error = httpClientFormatChunkSize(context, length - totalLength);
1163  }
1164  }
1165  else
1166  {
1167  //Invalid state
1168  error = ERROR_WRONG_STATE;
1169  }
1170  }
1171 
1172  //Check status code
1173  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1174  {
1175  //Check whether the timeout has elapsed
1176  error = httpClientCheckTimeout(context);
1177  }
1178 
1179  //Total number of data that have been written
1180  if(written != NULL)
1181  *written = totalLength;
1182 
1183  //Return status code
1184  return error;
1185 }
1186 
1187 
1188 /**
1189  * @brief Write HTTP trailer
1190  * @param[in] context Pointer to the HTTP client context
1191  * @return Error code
1192  **/
1193 
1195 {
1196  error_t error;
1197  size_t n;
1198 
1199  //Make sure the HTTP client context is valid
1200  if(context == NULL)
1201  return ERROR_INVALID_PARAMETER;
1202 
1203  //Check HTTP connection state
1204  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1205  return ERROR_WRONG_STATE;
1206 
1207  //Initialize status code
1208  error = NO_ERROR;
1209 
1210  //Send HTTP request header
1211  while(!error)
1212  {
1213  //Check HTTP request state
1214  if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER)
1215  {
1216  //Initiate the sending process
1218  }
1219  else if(context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1220  {
1221  //Any remaining data to be sent?
1222  if(context->bufferPos < context->bufferLen)
1223  {
1224  //Send more data
1225  error = httpClientSendData(context,
1226  context->buffer + context->bufferPos,
1227  context->bufferLen - context->bufferPos, &n, 0);
1228 
1229  //Check status code
1230  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1231  {
1232  //Advance data pointer
1233  context->bufferPos += n;
1234  }
1235  }
1236  else
1237  {
1238  //The request trailer has been successfully transmitted
1241  }
1242  }
1243  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1244  {
1245  //We are done
1246  break;
1247  }
1248  else
1249  {
1250  //Invalid state
1251  error = ERROR_WRONG_STATE;
1252  }
1253  }
1254 
1255  //Check status code
1256  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1257  {
1258  //Check whether the timeout has elapsed
1259  error = httpClientCheckTimeout(context);
1260  }
1261 
1262  //Return status code
1263  return error;
1264 }
1265 
1266 
1267 /**
1268  * @brief Read HTTP response header
1269  * @param[in] context Pointer to the HTTP client context
1270  * @return Error code
1271  **/
1272 
1274 {
1275  error_t error;
1276  size_t n;
1277 
1278  //Make sure the HTTP client context is valid
1279  if(context == NULL)
1280  return ERROR_INVALID_PARAMETER;
1281 
1282  //Check HTTP connection state
1283  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1284  return ERROR_WRONG_STATE;
1285 
1286  //Initialize status code
1287  error = NO_ERROR;
1288 
1289  //Send HTTP request header
1290  while(!error)
1291  {
1292  //Check HTTP request state
1293  if(context->requestState == HTTP_REQ_STATE_SEND_BODY ||
1294  context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1295  {
1296  //Close HTTP request body
1297  error = httpClientCloseBody(context);
1298  }
1299  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
1300  context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1301  {
1302  //The last chunk is followed by an optional trailer
1303  error = httpClientWriteTrailer(context);
1304  }
1305  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE ||
1306  context->requestState == HTTP_REQ_STATE_RECEIVE_HEADER)
1307  {
1308  //The CRLF sequence is always used to terminate a line
1309  if(context->bufferLen >= (context->bufferPos + 2) &&
1310  context->buffer[context->bufferLen - 2] == '\r' &&
1311  context->buffer[context->bufferLen - 1] == '\n')
1312  {
1313  //Strip the CRLF at the end of the line
1314  context->bufferLen -= 2;
1315 
1316  //The first line of a response message is the Status-Line
1317  if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1318  {
1319  //Thee Status-Line consists of the protocol version followed by
1320  //a numeric status code and its associated textual phrase
1321  error = httpClientParseStatusLine(context, context->buffer +
1322  context->bufferPos, context->bufferLen - context->bufferPos);
1323 
1324  //Valid syntax?
1325  if(!error)
1326  {
1327  //Receive response header fields
1329  }
1330  }
1331  else
1332  {
1333  //An empty line indicates the end of the header fields
1334  if(context->bufferLen == context->bufferPos)
1335  {
1336  //Save TLS session
1337  error = httpClientSaveSession(context);
1338 
1339  //Check status code
1340  if(!error)
1341  {
1342  //The HTTP response header has been successfully received
1344  }
1345  }
1346  else
1347  {
1348  //The response header fields allow the server to pass additional
1349  //information about the response which cannot be placed in the
1350  //Status-Line
1351  error = httpClientParseHeaderField(context, context->buffer +
1352  context->bufferPos, context->bufferLen - context->bufferPos);
1353  }
1354  }
1355  }
1356  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1357  {
1358  //Receive more data
1359  error = httpClientReceiveData(context, context->buffer +
1360  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1362 
1363  //Check status code
1364  if(!error)
1365  {
1366  //Adjust the length of the buffer
1367  context->bufferLen += n;
1368  //Save current time
1369  context->timestamp = osGetSystemTime();
1370  }
1371  }
1372  else
1373  {
1374  //The client implementation limits the size of headers it accepts
1375  error = ERROR_BUFFER_OVERFLOW;
1376  }
1377  }
1378  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1379  {
1380  //Rewind to the beginning of the buffer
1381  context->bufferPos = 0;
1382  //We are done
1383  break;
1384  }
1385  else
1386  {
1387  //Invalid state
1388  error = ERROR_WRONG_STATE;
1389  }
1390  }
1391 
1392  //Check status code
1393  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1394  {
1395  //Check whether the timeout has elapsed
1396  error = httpClientCheckTimeout(context);
1397  }
1398 
1399  //Return status code
1400  return error;
1401 }
1402 
1403 
1404 /**
1405  * @brief Retrieve the HTTP status code of the response
1406  * @param[in] context Pointer to the HTTP client context
1407  * @return HTTP status code
1408  **/
1409 
1411 {
1412  uint_t status;
1413 
1414  //Initialize status code
1415  status = 0;
1416 
1417  //Make sure the HTTP client context is valid
1418  if(context != NULL)
1419  {
1420  //The status code is a three-digit integer code giving the result
1421  //of the attempt to understand and satisfy the request
1422  status = context->statusCode;
1423  }
1424 
1425  //Return HTTP status code
1426  return status;
1427 }
1428 
1429 
1430 /**
1431  * @brief Retrieve the value of the specified header field name
1432  * @param[in] context Pointer to the HTTP client context
1433  * @param[in] name NULL-terminated string that specifies the header field name
1434  * @return Value of the header field
1435  **/
1436 
1438  const char_t *name)
1439 {
1440  size_t i;
1441  size_t nameLen;
1442  size_t valueLen;
1443  const char_t *value;
1444 
1445  //Initialize field value
1446  value = NULL;
1447 
1448  //Check parameters
1449  if(context != NULL && name != NULL)
1450  {
1451  //Check HTTP request state
1452  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
1453  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER)
1454  {
1455  //Point to the first header field of the response
1456  i = 0;
1457 
1458  //Parse HTTP response header
1459  while(i < context->bufferLen)
1460  {
1461  //Calculate the length of the field name
1462  nameLen = strlen(context->buffer + i);
1463  //Calculate the length of the field value
1464  valueLen = strlen(context->buffer + i + nameLen + 1);
1465 
1466  //Check whether the current header field matches the specified name
1467  if(!strcasecmp(context->buffer + i, name))
1468  {
1469  //Retrieve the value of the header field
1470  value = context->buffer + i + nameLen + 1;
1471  //Exit immediately
1472  break;
1473  }
1474 
1475  //Point to next header field
1476  i += nameLen + valueLen + 2;
1477  }
1478  }
1479  }
1480 
1481  //Return the value of the header field
1482  return value;
1483 }
1484 
1485 
1486 /**
1487  * @brief Iterate through the HTTP response header
1488  * @param[in] context Pointer to the HTTP client context
1489  * @param[out] name NULL-terminated string that contains the name of the next
1490  * header field
1491  * @param[out] value NULL-terminated string that contains the value of the next
1492  * header field
1493  * @return Error code
1494  **/
1495 
1497  const char_t **name, const char_t **value)
1498 {
1499  size_t nameLen;
1500  size_t valueLen;
1501 
1502  //Check parameters
1503  if(context == NULL || name == NULL || value == NULL)
1504  return ERROR_INVALID_PARAMETER;
1505 
1506  //Check HTTP request state
1507  if(context->requestState != HTTP_REQ_STATE_PARSE_HEADER &&
1508  context->requestState != HTTP_REQ_STATE_PARSE_TRAILER)
1509  {
1510  return ERROR_WRONG_STATE;
1511  }
1512 
1513  //Check whether the end of the HTTP response header has been reached
1514  if(context->bufferPos >= context->bufferLen)
1515  return ERROR_END_OF_STREAM;
1516 
1517  //Calculate the length of the field name
1518  nameLen = strlen(context->buffer + context->bufferPos);
1519  //Calculate the length of the field value
1520  valueLen = strlen(context->buffer + context->bufferPos + nameLen + 1);
1521 
1522  //Return the name and the value of the header field
1523  *name = context->buffer + context->bufferPos;
1524  *value = context->buffer + context->bufferPos + nameLen + 1;
1525 
1526  //Point to the next header field
1527  context->bufferPos += nameLen + valueLen + 2;
1528 
1529  //Successful processing
1530  return NO_ERROR;
1531 }
1532 
1533 
1534 /**
1535  * @brief Read HTTP response body
1536  * @param[in] context Pointer to the HTTP client context
1537  * @param[out] data Buffer where to store the incoming data
1538  * @param[in] size Maximum number of bytes that can be received
1539  * @param[out] received Number of bytes that have been received
1540  * @param[in] flags Set of flags that influences the behavior of this function
1541  * @return Error code
1542  **/
1543 
1545  size_t size, size_t *received, uint_t flags)
1546 {
1547  error_t error;
1548  size_t n;
1549  char_t *p;
1550 
1551  //Check parameters
1552  if(context == NULL || data == NULL || received == NULL)
1553  return ERROR_INVALID_PARAMETER;
1554 
1555  //Check HTTP connection state
1556  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1557  return ERROR_WRONG_STATE;
1558 
1559  //Initialize status code
1560  error = NO_ERROR;
1561 
1562  //Point to the output buffer
1563  p = data;
1564  //No data has been read yet
1565  *received = 0;
1566 
1567  //Read as much data as possible
1568  while(*received < size && !error)
1569  {
1570  //Check HTTP request state
1571  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1572  {
1573  //Flush receive buffer
1574  context->bufferLen = 0;
1575  context->bufferPos = 0;
1576 
1577  //Any response to a HEAD request and any response with a 1xx, 204 or
1578  //304 status code is always terminated by the first empty line after
1579  //the header fields, regardless of the header fields present in the
1580  //message, and thus cannot contain a message body
1581  if(!strcasecmp(context->method, "HEAD") ||
1582  HTTP_STATUS_CODE_1YZ(context->statusCode) ||
1583  context->statusCode == 204 ||
1584  context->statusCode == 304)
1585  {
1586  //The HTTP response does not contain a body
1588  }
1589  else
1590  {
1591  //The HTTP response contains a body
1593  }
1594  }
1595  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_BODY)
1596  {
1597  //Chunked transfer encoding?
1598  if(context->chunkedEncoding)
1599  {
1600  //The chunked encoding modifies the body in order to transfer it
1601  //as a series of chunks, each with its own size indicator
1603  }
1604  else
1605  {
1606  //The length of the body is determined by the Content-Length field
1607  if(context->bodyPos < context->bodyLen)
1608  {
1609  //Limit the number of bytes to read
1610  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1611 
1612  //Read response body
1613  error = httpClientReceiveData(context, p, n, &n, flags);
1614 
1615  //Check status code
1616  if(!error)
1617  {
1618  //Advance data pointer
1619  p += n;
1620  *received += n;
1621  context->bodyPos += n;
1622 
1623  //Save current time
1624  context->timestamp = osGetSystemTime();
1625 
1626  //We are done
1627  break;
1628  }
1629  else
1630  {
1631  //In practice, many widely deployed HTTPS servers close connections
1632  //abruptly, without any prior notice, and in particular without
1633  //sending the close_notify alert
1634  if(!context->keepAlive && context->bodyLen == UINT_MAX)
1635  {
1636  //The HTTP transaction is complete
1638  //Close the HTTP connection
1640  //The end of the response body has been reached
1641  error = ERROR_END_OF_STREAM;
1642  }
1643  }
1644  }
1645  else
1646  {
1647  //The HTTP transaction is complete
1649  //The end of the response body has been reached
1650  error = ERROR_END_OF_STREAM;
1651  }
1652  }
1653  }
1654  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE)
1655  {
1656  //The chunked encoding modifies the body in order to transfer it as
1657  //a series of chunks, each with its own size indicator
1658  if(context->bufferLen >= 3 &&
1659  context->buffer[context->bufferLen - 2] == '\r' &&
1660  context->buffer[context->bufferLen - 1] == '\n')
1661  {
1662  //Parse chunk-size field
1663  error = httpClientParseChunkSize(context, context->buffer,
1664  context->bufferLen);
1665  }
1666  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1667  {
1668  //Receive more data
1669  error = httpClientReceiveData(context, context->buffer +
1670  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1672 
1673  //Check status code
1674  if(!error)
1675  {
1676  //Adjust the length of the buffer
1677  context->bufferLen += n;
1678  //Save current time
1679  context->timestamp = osGetSystemTime();
1680  }
1681  }
1682  else
1683  {
1684  //The client implementation limits the length of the chunk-size field
1685  error = ERROR_BUFFER_OVERFLOW;
1686  }
1687  }
1688  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
1689  {
1690  //The data stream is divided into a series of chunks
1691  if(context->bodyPos < context->bodyLen)
1692  {
1693  //The length of the data chunk is determined by the chunk-size field
1694  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1695 
1696  //Read chunk data
1697  error = httpClientReceiveData(context, p, n, &n, flags);
1698 
1699  //Check status code
1700  if(!error)
1701  {
1702  //Total number of data that have been read
1703  *received += n;
1704  //Number of bytes left to process in the current chunk
1705  context->bodyPos += n;
1706 
1707  //Save current time
1708  context->timestamp = osGetSystemTime();
1709 
1710  //Check flags
1712  {
1713  //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop
1714  //reading data as soon as the specified break character is
1715  //encountered
1716  if(p[n - 1] == LSB(flags))
1717  break;
1718  }
1719  else if(!(flags & HTTP_FLAG_WAIT_ALL))
1720  {
1721  //The HTTP_FLAG_WAIT_ALL flag causes the function to return
1722  //only when the requested number of bytes have been read
1723  break;
1724  }
1725  else
1726  {
1727  //Just for sanity
1728  }
1729 
1730  //Advance data pointer
1731  p += n;
1732  }
1733  }
1734  else
1735  {
1736  //The chunked encoding modifies the body in order to transfer it
1737  //as a series of chunks, each with its own size indicator
1739  }
1740  }
1741  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER ||
1742  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1743  context->requestState == HTTP_REQ_STATE_COMPLETE)
1744  {
1745  //The user must be satisfied with data already on hand
1746  if(*received > 0)
1747  {
1748  //Some data are pending in the receive buffer
1749  break;
1750  }
1751  else
1752  {
1753  //The end of the response body has been reached
1754  error = ERROR_END_OF_STREAM;
1755  }
1756  }
1757  else
1758  {
1759  //Invalid state
1760  error = ERROR_WRONG_STATE;
1761  }
1762  }
1763 
1764  //Check status code
1765  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1766  {
1767  //Check whether the timeout has elapsed
1768  error = httpClientCheckTimeout(context);
1769  }
1770 
1771  //Return status code
1772  return error;
1773 }
1774 
1775 
1776 /**
1777  * @brief Read HTTP trailer
1778  * @param[in] context Pointer to the HTTP client context
1779  * @return Error code
1780  **/
1781 
1783 {
1784  error_t error;
1785  size_t n;
1786 
1787  //Make sure the HTTP client context is valid
1788  if(context == NULL)
1789  return ERROR_INVALID_PARAMETER;
1790 
1791  //Initialize status code
1792  error = NO_ERROR;
1793 
1794  //Send HTTP request header
1795  while(!error)
1796  {
1797  //Check HTTP connection state
1798  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
1799  {
1800  //Check HTTP request state
1801  if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
1802  {
1803  //The CRLF sequence is always used to terminate a line
1804  if(context->bufferLen >= (context->bufferPos + 2) &&
1805  context->buffer[context->bufferLen - 2] == '\r' &&
1806  context->buffer[context->bufferLen - 1] == '\n')
1807  {
1808  //Strip the CRLF at the end of the line
1809  context->bufferLen -= 2;
1810 
1811  //An empty line indicates the end of the header fields
1812  if(context->bufferLen == context->bufferPos)
1813  {
1814  //The HTTP transaction is complete
1816  }
1817  else
1818  {
1819  //The response header fields allow the server to pass additional
1820  //information about the response which cannot be placed in the
1821  //Status-Line
1822  error = httpClientParseHeaderField(context, context->buffer +
1823  context->bufferPos, context->bufferLen - context->bufferPos);
1824  }
1825  }
1826  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1827  {
1828  //Receive more data
1829  error = httpClientReceiveData(context, context->buffer +
1830  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1832 
1833  //Check status code
1834  if(!error)
1835  {
1836  //Adjust the length of the buffer
1837  context->bufferLen += n;
1838  //Save current time
1839  context->timestamp = osGetSystemTime();
1840  }
1841  }
1842  else
1843  {
1844  //The client implementation limits the size of headers it accepts
1845  error = ERROR_BUFFER_OVERFLOW;
1846  }
1847  }
1848  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1849  context->requestState == HTTP_REQ_STATE_COMPLETE)
1850  {
1851  //Rewind to the beginning of the buffer
1852  context->bufferPos = 0;
1853 
1854  //Persistent HTTP connection?
1855  if(context->keepAlive)
1856  {
1857  //Persistent connections stay open across transactions, until
1858  //either the client or the server decides to close them
1859  break;
1860  }
1861  else
1862  {
1863  //The connection will be closed after completion of the response
1865  }
1866  }
1867  else
1868  {
1869  //Invalid HTTP request state
1870  error = ERROR_WRONG_STATE;
1871  }
1872  }
1873  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
1874  {
1875  //Shutdown connection
1876  error = httpClientDisconnect(context);
1877  }
1878  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
1879  {
1880  //Rewind to the beginning of the buffer
1881  context->bufferPos = 0;
1882  //We are done
1883  break;
1884  }
1885  else
1886  {
1887  //Invalid HTTP connection state
1888  error = ERROR_WRONG_STATE;
1889  }
1890  }
1891 
1892  //Check status code
1893  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1894  {
1895  //Check whether the timeout has elapsed
1896  error = httpClientCheckTimeout(context);
1897  }
1898 
1899  //Return status code
1900  return error;
1901 }
1902 
1903 
1904 /**
1905  * @brief Close HTTP request or response body
1906  * @param[in] context Pointer to the HTTP client context
1907  * @return Error code
1908  **/
1909 
1911 {
1912  error_t error;
1913  size_t n;
1914 
1915  //Make sure the HTTP client context is valid
1916  if(context == NULL)
1917  return ERROR_INVALID_PARAMETER;
1918 
1919  //Initialize status code
1920  error = NO_ERROR;
1921 
1922  //Close HTTP request or response body
1923  while(!error)
1924  {
1925  //Check HTTP connection state
1926  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
1927  {
1928  //Check HTTP request state
1929  if(context->requestState == HTTP_REQ_STATE_SEND_BODY)
1930  {
1931  //Chunked transfer encoding?
1932  if(context->chunkedEncoding)
1933  {
1934  //The chunked encoding is ended by any chunk whose size is zero
1935  error = httpClientFormatChunkSize(context, 0);
1936  }
1937  else
1938  {
1939  //Ensure the HTTP request body is complete
1940  if(context->bodyPos == context->bodyLen)
1941  {
1942  //Flush receive buffer
1943  context->bufferLen = 0;
1944  context->bufferPos = 0;
1945 
1946  //Receive the HTTP response header
1949  }
1950  else
1951  {
1952  //Incomplete request body
1953  error = ERROR_WRONG_STATE;
1954  }
1955  }
1956  }
1957  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1958  {
1959  //Ensure the chunk data is complete
1960  if(context->bodyPos == context->bodyLen)
1961  {
1962  //The chunked encoding is ended by any chunk whose size is zero
1963  error = httpClientFormatChunkSize(context, 0);
1964  }
1965  else
1966  {
1967  //Incomplete chunk data
1968  error = ERROR_WRONG_STATE;
1969  }
1970  }
1971  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
1972  context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1973  {
1974  //The HTTP request body is closed
1975  break;
1976  }
1977  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
1978  context->requestState == HTTP_REQ_STATE_RECEIVE_BODY ||
1979  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE ||
1980  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
1981  {
1982  //Consume HTTP response body
1983  error = httpClientReadBody(context, context->buffer,
1984  HTTP_CLIENT_BUFFER_SIZE, &n, 0);
1985 
1986  //Check whether the end of the response body has been reached
1987  if(error == ERROR_END_OF_STREAM)
1988  {
1989  //Continue reading the optional trailer
1990  error = NO_ERROR;
1991  }
1992  }
1993  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
1994  {
1995  //Consume HTTP trailer
1996  error = httpClientReadTrailer(context);
1997  }
1998  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1999  context->requestState == HTTP_REQ_STATE_COMPLETE)
2000  {
2001  //The HTTP response body is closed
2002  break;
2003  }
2004  else
2005  {
2006  //Invalid HTTP request state
2007  error = ERROR_WRONG_STATE;
2008  }
2009  }
2010  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
2011  {
2012  //Shutdown connection
2013  error = httpClientDisconnect(context);
2014  }
2015  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
2016  {
2017  //Rewind to the beginning of the buffer
2018  context->bufferPos = 0;
2019  //We are done
2020  break;
2021  }
2022  else
2023  {
2024  //Invalid HTTP connection state
2025  error = ERROR_WRONG_STATE;
2026  }
2027  }
2028 
2029  //Check status code
2030  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2031  {
2032  //Check whether the timeout has elapsed
2033  error = httpClientCheckTimeout(context);
2034  }
2035 
2036  //Return status code
2037  return error;
2038 }
2039 
2040 
2041 /**
2042  * @brief Gracefully disconnect from the HTTP server
2043  * @param[in] context Pointer to the HTTP client context
2044  * @return Error code
2045  **/
2046 
2048 {
2049  error_t error;
2050 
2051  //Make sure the HTTP client context is valid
2052  if(context == NULL)
2053  return ERROR_INVALID_PARAMETER;
2054 
2055  //Initialize status code
2056  error = NO_ERROR;
2057 
2058  //Execute HTTP command sequence
2059  while(!error)
2060  {
2061  //Check HTTP connection state
2062  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
2063  {
2064  //Gracefully disconnect from the HTTP server
2066  }
2067  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
2068  {
2069  //Shutdown connection
2070  error = httpClientShutdownConnection(context);
2071 
2072  //Check status code
2073  if(error == NO_ERROR)
2074  {
2075  //Close connection
2076  httpClientCloseConnection(context);
2077  //Update HTTP connection state
2079  }
2080  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2081  {
2082  //Check whether the timeout has elapsed
2083  error = httpClientCheckTimeout(context);
2084  }
2085  else
2086  {
2087  //A communication error has occured
2088  }
2089  }
2090  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
2091  {
2092  //We are done
2093  break;
2094  }
2095  else
2096  {
2097  //Invalid state
2098  error = ERROR_WRONG_STATE;
2099  }
2100  }
2101 
2102  //Failed to gracefully disconnect from the HTTP server?
2103  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
2104  {
2105  //Close connection
2106  httpClientCloseConnection(context);
2107  //Update HTTP connection state
2109  }
2110 
2111  //Return status code
2112  return error;
2113 }
2114 
2115 
2116 /**
2117  * @brief Close the connection with the HTTP server
2118  * @param[in] context Pointer to the HTTP client context
2119  * @return Error code
2120  **/
2121 
2123 {
2124  //Make sure the HTTP client context is valid
2125  if(context == NULL)
2126  return ERROR_INVALID_PARAMETER;
2127 
2128  //Close connection
2129  httpClientCloseConnection(context);
2130  //Update HTTP connection state
2132 
2133  //Successful processing
2134  return NO_ERROR;
2135 }
2136 
2137 
2138 /**
2139  * @brief Release HTTP client context
2140  * @param[in] context Pointer to the HTTP client context
2141  **/
2142 
2144 {
2145  //Make sure the HTTP client context is valid
2146  if(context != NULL)
2147  {
2148  //Close connection
2149  httpClientCloseConnection(context);
2150 
2151 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
2152  //Release TLS session state
2153  tlsFreeSessionState(&context->tlsSession);
2154 #endif
2155 
2156  //Clear HTTP client context
2157  memset(context, 0, sizeof(HttpClientContext));
2158  }
2159 }
2160 
2161 #endif
error_t httpClientWriteHeader(HttpClientContext *context)
Write HTTP request header.
Definition: http_client.c:915
uint16_t version
Definition: dtls_misc.h:165
uint32_t systime_t
Definition: compiler_port.h:46
void httpClientChangeRequestState(HttpClientContext *context, HttpRequestState newState)
Update HTTP request state.
error_t httpClientWriteBody(HttpClientContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Write HTTP request body.
Definition: http_client.c:1038
char char_t
Definition: compiler_port.h:43
uint8_t flags
Definition: tcp.h:314
error_t httpClientRegisterTlsInitCallback(HttpClientContext *context, HttpClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: http_client.c:110
#define HTTP_CLIENT_BUFFER_SIZE
Definition: http_client.h:96
systime_t osGetSystemTime(void)
Retrieve system time.
TCP/IP stack core.
#define HttpClientContext
Definition: http_client.h:205
void httpClientCloseConnection(HttpClientContext *context)
Close network connection.
Transport protocol abstraction layer.
Debugging facilities.
uint8_t p
Definition: ndp.h:298
error_t httpClientFormatChunkSize(HttpClientContext *context, size_t length)
Format chunk-size field.
Invalid parameter.
Definition: error.h:47
uint_t httpClientGetStatus(HttpClientContext *context)
Retrieve the HTTP status code of the response.
Definition: http_client.c:1410
IP network address.
Definition: ip.h:71
#define HTTP_STATUS_CODE_1YZ(code)
Definition: http_common.h:43
Helper functions for HTTP client.
String manipulation helper functions.
error_t tlsInitSessionState(TlsSessionState *session)
Initialize session state.
Definition: tls.c:2394
#define LSB(x)
Definition: os_port.h:54
uint8_t m
Definition: ndp.h:302
error_t httpClientDisconnect(HttpClientContext *context)
Gracefully disconnect from the HTTP server.
Definition: http_client.c:2047
error_t httpClientEstablishConnection(HttpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish network connection.
#define strcasecmp
error_t(* HttpClientRandCallback)(uint8_t *data, size_t length)
Random data generation callback function.
Definition: http_client.h:243
error_t httpClientShutdownConnection(HttpClientContext *context)
Shutdown network connection.
error_t httpClientReadBody(HttpClientContext *context, void *data, size_t size, size_t *received, uint_t flags)
Read HTTP response body.
Definition: http_client.c:1544
#define HTTP_PORT
Definition: http_common.h:38
error_t httpClientParseContentLengthField(HttpClientContext *context, const char_t *value)
Parse Content-Length header field.
#define HTTP_CLIENT_MAX_METHOD_LEN
Definition: http_client.h:117
error_t httpClientSendData(HttpClientContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Send data using the relevant transport protocol.
#define HTTP_CLIENT_MAX_USERNAME_LEN
Definition: http_client.h:124
char_t name[]
error_t httpClientParseStatusLine(HttpClientContext *context, char_t *line, size_t length)
Parse HTTP status line.
error_t httpClientAddQueryParam(HttpClientContext *context, const char_t *name, const char_t *value)
Add a key/value pair to the query string.
Definition: http_client.c:691
void httpClientChangeState(HttpClientContext *context, HttpClientState newState)
Update HTTP client state.
error_t(* HttpClientTlsInitCallback)(HttpClientContext *context, TlsContext *tlsContext)
TLS initialization callback function.
Definition: http_client.h:233
error_t httpClientSetMethod(HttpClientContext *context, const char_t *method)
Set HTTP request method.
Definition: http_client.c:402
void tlsFreeSessionState(TlsSessionState *session)
Properly dispose a session state.
Definition: tls.c:2699
error_t httpClientCreateRequest(HttpClientContext *context)
Create a new HTTP request.
Definition: http_client.c:365
error_t httpClientOpenConnection(HttpClientContext *context)
Open network connection.
error_t httpClientSetHost(HttpClientContext *context, const char_t *host, uint16_t port)
Set the hostname and port number of the resource being requested.
Definition: http_client.c:533
#define MIN(a, b)
Definition: os_port.h:62
error_t httpClientRegisterRandCallback(HttpClientContext *context, HttpClientRandCallback callback)
Register random data generation callback function.
Definition: http_client.c:134
error_t httpClientSetContentLength(HttpClientContext *context, size_t length)
Set the length of the HTTP request body.
Definition: http_client.c:888
void httpClientInitAuthParams(HttpClientAuthParams *authParams)
Initialize HTTP authentication parameters.
error_t httpClientParseHeaderField(HttpClientContext *context, char_t *line, size_t length)
Parse HTTP response header field.
error_t httpClientGetNextHeaderField(HttpClientContext *context, const char_t **name, const char_t **value)
Iterate through the HTTP response header.
Definition: http_client.c:1496
error_t httpClientSetVersion(HttpClientContext *context, HttpVersion version)
Set the HTTP protocol version to be used.
Definition: http_client.c:161
Success.
Definition: error.h:44
void httpClientDeinit(HttpClientContext *context)
Release HTTP client context.
Definition: http_client.c:2143
error_t
Error codes.
Definition: error.h:42
error_t httpClientReceiveData(HttpClientContext *context, void *data, size_t size, size_t *received, uint_t flags)
Receive data using the relevant transport protocol.
error_t httpClientCloseBody(HttpClientContext *context)
Close HTTP request or response body.
Definition: http_client.c:1910
error_t httpClientParseChunkSize(HttpClientContext *context, char_t *line, size_t length)
Parse chunk-size field.
const char_t * httpClientGetHeaderField(HttpClientContext *context, const char_t *name)
Retrieve the value of the specified header field name.
Definition: http_client.c:1437
unsigned int uint_t
Definition: compiler_port.h:45
error_t httpClientBindToInterface(HttpClientContext *context, NetInterface *interface)
Bind the HTTP client to a particular network interface.
Definition: http_client.c:246
error_t httpClientCheckTimeout(HttpClientContext *context)
Determine whether a timeout error has occurred.
error_t httpClientFormatAuthorizationField(HttpClientContext *context)
Format Authorization header field.
uint8_t data[]
Definition: dtls_misc.h:169
#define PRIuSIZE
Definition: compiler_port.h:78
error_t httpClientClose(HttpClientContext *context)
Close the connection with the HTTP server.
Definition: http_client.c:2122
#define NetInterface
Definition: net.h:36
HttpVersion
HTTP version numbers.
Definition: http_common.h:59
#define HTTP_CLIENT_MAX_PASSWORD_LEN
Definition: http_client.h:131
error_t httpClientSetQueryString(HttpClientContext *context, const char_t *queryString)
Set query string.
Definition: http_client.c:594
uint8_t value[]
Definition: dtls_misc.h:143
#define HTTP_CLIENT_DEFAULT_TIMEOUT
Definition: http_client.h:89
uint16_t port
Definition: dns_common.h:223
error_t httpClientSetUri(HttpClientContext *context, const char_t *uri)
Set request URI.
Definition: http_client.c:462
error_t httpClientInit(HttpClientContext *context)
Initialize HTTP client context.
Definition: http_client.c:65
error_t httpClientSetTimeout(HttpClientContext *context, systime_t timeout)
Set communication timeout.
Definition: http_client.c:186
error_t httpClientReadTrailer(HttpClientContext *context)
Read HTTP trailer.
Definition: http_client.c:1782
error_t httpClientSaveSession(HttpClientContext *context)
Save TLS session.
error_t httpClientSetAuthInfo(HttpClientContext *context, const char_t *username, const char_t *password)
Set authentication information.
Definition: http_client.c:208
error_t httpClientFormatRequestHeader(HttpClientContext *context)
Format default HTTP request header.
uint8_t length
Definition: dtls_misc.h:142
uint8_t n
error_t httpClientConnect(HttpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish a connection with the specified HTTP server.
Definition: http_client.c:269
HTTP client (HyperText Transfer Protocol)
error_t httpClientWriteTrailer(HttpClientContext *context)
Write HTTP trailer.
Definition: http_client.c:1194
HTTP authentication.
error_t httpClientReadHeader(HttpClientContext *context)
Read HTTP response header.
Definition: http_client.c:1273
error_t httpClientParseTransferEncodingField(HttpClientContext *context, const char_t *value)
Parse Transfer-Encoding header field.
error_t httpClientParseConnectionField(HttpClientContext *context, const char_t *value)
Parse Connection header field.
#define TRACE_DEBUG(...)
Definition: debug.h:106
error_t httpClientAddHeaderField(HttpClientContext *context, const char_t *name, const char_t *value)
Add a header field to the HTTP request.
Definition: http_client.c:808