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.2
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)
974  {
975  //Advance data pointer
976  context->bufferPos += n;
977  //Save current time
978  context->timestamp = osGetSystemTime();
979  }
980  }
981  else
982  {
983  //The request header has been successfully transmitted
984  if(!strcasecmp(context->method, "POST") ||
985  !strcasecmp(context->method, "PUT") ||
986  !strcasecmp(context->method, "PATCH"))
987  {
988  //POST, PUT and PATCH requests have a body
990  }
991  else
992  {
993  //GET, HEAD and DELETE requests do not have a body
996  }
997  }
998  }
999  else if(context->requestState == HTTP_REQ_STATE_SEND_BODY ||
1000  context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1001  {
1002  //We are done
1003  break;
1004  }
1005  else
1006  {
1007  //Invalid HTTP request state
1008  error = ERROR_WRONG_STATE;
1009  }
1010  }
1011  else
1012  {
1013  //Invalid HTTP connection state
1014  error = ERROR_WRONG_STATE;
1015  }
1016  }
1017 
1018  //Check status code
1019  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1020  {
1021  //Check whether the timeout has elapsed
1022  error = httpClientCheckTimeout(context);
1023  }
1024 
1025  //Return status code
1026  return error;
1027 }
1028 
1029 
1030 /**
1031  * @brief Write HTTP request body
1032  * @param[in] context Pointer to the HTTP client context
1033  * @param[in] data Pointer to the buffer containing the data to be transmitted
1034  * @param[in] length Number of data bytes to send
1035  * @param[out] written Actual number of bytes written (optional parameter)
1036  * @param[in] flags Set of flags that influences the behavior of this function
1037  * @return Error code
1038  **/
1039 
1041  size_t length, size_t *written, uint_t flags)
1042 {
1043  error_t error;
1044  size_t n;
1045  size_t totalLength;
1046 
1047  //Make sure the HTTP client context is valid
1048  if(context == NULL)
1049  return ERROR_INVALID_PARAMETER;
1050 
1051  //Check HTTP connection state
1052  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1053  return ERROR_WRONG_STATE;
1054 
1055  //Initialize status code
1056  error = NO_ERROR;
1057 
1058  //Actual number of bytes written
1059  totalLength = 0;
1060 
1061  //Send as much data as possible
1062  while(totalLength < length && !error)
1063  {
1064  //Check HTTP request state
1065  if(context->requestState == HTTP_REQ_STATE_SEND_BODY)
1066  {
1067  //Chunked transfer encoding?
1068  if(context->chunkedEncoding)
1069  {
1070  //The chunked encoding modifies the body in order to transfer it
1071  //as a series of chunks, each with its own size indicator
1072  error = httpClientFormatChunkSize(context, length - totalLength);
1073  }
1074  else
1075  {
1076  //Number of bytes left to send
1077  n = length - totalLength;
1078 
1079  //The length of the body shall not exceed the value specified in
1080  //the Content-Length field
1081  if(n <= (context->bodyLen - context->bodyPos))
1082  {
1083  //Send request body
1084  error = httpClientSendData(context, data, n, &n, flags);
1085 
1086  //Check status code
1087  if(!error)
1088  {
1089  //Advance data pointer
1090  data = (uint8_t *) data + n;
1091  totalLength += n;
1092  context->bodyPos += n;
1093 
1094  //Save current time
1095  context->timestamp = osGetSystemTime();
1096  }
1097  }
1098  else
1099  {
1100  //Report an error
1101  error = ERROR_INVALID_LENGTH;
1102  }
1103  }
1104  }
1105  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_SIZE)
1106  {
1107  //Send the chunk-size field
1108  if(context->bufferPos < context->bufferLen)
1109  {
1110  //Send more data
1111  error = httpClientSendData(context,
1112  context->buffer + context->bufferPos,
1113  context->bufferLen - context->bufferPos, &n, 0);
1114 
1115  //Check status code
1116  if(!error)
1117  {
1118  //Advance data pointer
1119  context->bufferPos += n;
1120  //Save current time
1121  context->timestamp = osGetSystemTime();
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)
1144  {
1145  //Advance data pointer
1146  data = (uint8_t *) data + n;
1147  totalLength += n;
1148  context->bodyPos += n;
1149 
1150  //Save current time
1151  context->timestamp = osGetSystemTime();
1152  }
1153  }
1154  else
1155  {
1156  //The chunked encoding modifies the body in order to transfer it
1157  //as a series of chunks, each with its own size indicator
1158  error = httpClientFormatChunkSize(context, length - totalLength);
1159  }
1160  }
1161  else
1162  {
1163  //Invalid state
1164  error = ERROR_WRONG_STATE;
1165  }
1166  }
1167 
1168  //Check status code
1169  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1170  {
1171  //Check whether the timeout has elapsed
1172  error = httpClientCheckTimeout(context);
1173  }
1174 
1175  //Total number of data that have been written
1176  if(written != NULL)
1177  *written = totalLength;
1178 
1179  //Return status code
1180  return error;
1181 }
1182 
1183 
1184 /**
1185  * @brief Write HTTP trailer
1186  * @param[in] context Pointer to the HTTP client context
1187  * @return Error code
1188  **/
1189 
1191 {
1192  error_t error;
1193  size_t n;
1194 
1195  //Make sure the HTTP client context is valid
1196  if(context == NULL)
1197  return ERROR_INVALID_PARAMETER;
1198 
1199  //Check HTTP connection state
1200  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1201  return ERROR_WRONG_STATE;
1202 
1203  //Initialize status code
1204  error = NO_ERROR;
1205 
1206  //Send HTTP request header
1207  while(!error)
1208  {
1209  //Check HTTP request state
1210  if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER)
1211  {
1212  //Initiate the sending process
1214  }
1215  else if(context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1216  {
1217  //Any remaining data to be sent?
1218  if(context->bufferPos < context->bufferLen)
1219  {
1220  //Send more data
1221  error = httpClientSendData(context,
1222  context->buffer + context->bufferPos,
1223  context->bufferLen - context->bufferPos, &n, 0);
1224 
1225  //Check status code
1226  if(!error)
1227  {
1228  //Advance data pointer
1229  context->bufferPos += n;
1230  //Save current time
1231  context->timestamp = osGetSystemTime();
1232  }
1233  }
1234  else
1235  {
1236  //The request trailer has been successfully transmitted
1239  }
1240  }
1241  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1242  {
1243  //We are done
1244  break;
1245  }
1246  else
1247  {
1248  //Invalid state
1249  error = ERROR_WRONG_STATE;
1250  }
1251  }
1252 
1253  //Check status code
1254  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1255  {
1256  //Check whether the timeout has elapsed
1257  error = httpClientCheckTimeout(context);
1258  }
1259 
1260  //Return status code
1261  return error;
1262 }
1263 
1264 
1265 /**
1266  * @brief Read HTTP response header
1267  * @param[in] context Pointer to the HTTP client context
1268  * @return Error code
1269  **/
1270 
1272 {
1273  error_t error;
1274  size_t n;
1275 
1276  //Make sure the HTTP client context is valid
1277  if(context == NULL)
1278  return ERROR_INVALID_PARAMETER;
1279 
1280  //Check HTTP connection state
1281  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1282  return ERROR_WRONG_STATE;
1283 
1284  //Initialize status code
1285  error = NO_ERROR;
1286 
1287  //Send HTTP request header
1288  while(!error)
1289  {
1290  //Check HTTP request state
1291  if(context->requestState == HTTP_REQ_STATE_SEND_BODY ||
1292  context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1293  {
1294  //Close HTTP request body
1295  error = httpClientCloseBody(context);
1296  }
1297  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
1298  context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1299  {
1300  //The last chunk is followed by an optional trailer
1301  error = httpClientWriteTrailer(context);
1302  }
1303  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE ||
1304  context->requestState == HTTP_REQ_STATE_RECEIVE_HEADER)
1305  {
1306  //The CRLF sequence is always used to terminate a line
1307  if(context->bufferLen >= (context->bufferPos + 2) &&
1308  context->buffer[context->bufferLen - 2] == '\r' &&
1309  context->buffer[context->bufferLen - 1] == '\n')
1310  {
1311  //Strip the CRLF at the end of the line
1312  context->bufferLen -= 2;
1313 
1314  //The first line of a response message is the Status-Line
1315  if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1316  {
1317  //Thee Status-Line consists of the protocol version followed by
1318  //a numeric status code and its associated textual phrase
1319  error = httpClientParseStatusLine(context, context->buffer +
1320  context->bufferPos, context->bufferLen - context->bufferPos);
1321 
1322  //Valid syntax?
1323  if(!error)
1324  {
1325  //Receive response header fields
1327  }
1328  }
1329  else
1330  {
1331  //An empty line indicates the end of the header fields
1332  if(context->bufferLen == context->bufferPos)
1333  {
1334  //The HTTP response header has been successfully received
1336  }
1337  else
1338  {
1339  //The response header fields allow the server to pass additional
1340  //information about the response which cannot be placed in the
1341  //Status-Line
1342  error = httpClientParseHeaderField(context, context->buffer +
1343  context->bufferPos, context->bufferLen - context->bufferPos);
1344  }
1345  }
1346  }
1347  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1348  {
1349  //Receive more data
1350  error = httpClientReceiveData(context, context->buffer +
1351  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1353 
1354  //Check status code
1355  if(!error)
1356  {
1357  //Advance data pointer
1358  context->bufferLen += n;
1359  //Save current time
1360  context->timestamp = osGetSystemTime();
1361  }
1362  }
1363  else
1364  {
1365  //The client implementation limits the size of headers it accepts
1366  error = ERROR_BUFFER_OVERFLOW;
1367  }
1368  }
1369  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1370  {
1371  //Rewind to the beginning of the buffer
1372  context->bufferPos = 0;
1373  //We are done
1374  break;
1375  }
1376  else
1377  {
1378  //Invalid state
1379  error = ERROR_WRONG_STATE;
1380  }
1381  }
1382 
1383  //Check status code
1384  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1385  {
1386  //Check whether the timeout has elapsed
1387  error = httpClientCheckTimeout(context);
1388  }
1389 
1390  //Return status code
1391  return error;
1392 }
1393 
1394 
1395 /**
1396  * @brief Retrieve the HTTP status code of the response
1397  * @param[in] context Pointer to the HTTP client context
1398  * @return HTTP status code
1399  **/
1400 
1402 {
1403  uint_t status;
1404 
1405  //Initialize status code
1406  status = 0;
1407 
1408  //Make sure the HTTP client context is valid
1409  if(context != NULL)
1410  {
1411  //The status code is a three-digit integer code giving the result
1412  //of the attempt to understand and satisfy the request
1413  status = context->statusCode;
1414  }
1415 
1416  //Return HTTP status code
1417  return status;
1418 }
1419 
1420 
1421 /**
1422  * @brief Retrieve the value of the specified header field name
1423  * @param[in] context Pointer to the HTTP client context
1424  * @param[in] name NULL-terminated string that specifies the header field name
1425  * @return Value of the header field
1426  **/
1427 
1429  const char_t *name)
1430 {
1431  size_t i;
1432  size_t nameLen;
1433  size_t valueLen;
1434  const char_t *value;
1435 
1436  //Initialize field value
1437  value = NULL;
1438 
1439  //Check parameters
1440  if(context != NULL && name != NULL)
1441  {
1442  //Check HTTP request state
1443  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
1444  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER)
1445  {
1446  //Point to the first header field of the response
1447  i = 0;
1448 
1449  //Parse HTTP response header
1450  while(i < context->bufferLen)
1451  {
1452  //Calculate the length of the field name
1453  nameLen = strlen(context->buffer + i);
1454  //Calculate the length of the field value
1455  valueLen = strlen(context->buffer + i + nameLen + 1);
1456 
1457  //Check whether the current header field matches the specified name
1458  if(!strcasecmp(context->buffer + i, name))
1459  {
1460  //Retrieve the value of the header field
1461  value = context->buffer + i + nameLen + 1;
1462  //Exit immediately
1463  break;
1464  }
1465 
1466  //Point to next header field
1467  i += nameLen + valueLen + 2;
1468  }
1469  }
1470  }
1471 
1472  //Return the value of the header field
1473  return value;
1474 }
1475 
1476 
1477 /**
1478  * @brief Iterate through the HTTP response header
1479  * @param[in] context Pointer to the HTTP client context
1480  * @param[out] name NULL-terminated string that contains the name of the next
1481  * header field
1482  * @param[out] value NULL-terminated string that contains the value of the next
1483  * header field
1484  * @return Error code
1485  **/
1486 
1488  const char_t **name, const char_t **value)
1489 {
1490  size_t nameLen;
1491  size_t valueLen;
1492 
1493  //Check parameters
1494  if(context == NULL || name == NULL || value == NULL)
1495  return ERROR_INVALID_PARAMETER;
1496 
1497  //Check HTTP request state
1498  if(context->requestState != HTTP_REQ_STATE_PARSE_HEADER &&
1499  context->requestState != HTTP_REQ_STATE_PARSE_TRAILER)
1500  {
1501  return ERROR_WRONG_STATE;
1502  }
1503 
1504  //Check whether the end of the HTTP response header has been reached
1505  if(context->bufferPos >= context->bufferLen)
1506  return ERROR_END_OF_STREAM;
1507 
1508  //Calculate the length of the field name
1509  nameLen = strlen(context->buffer + context->bufferPos);
1510  //Calculate the length of the field value
1511  valueLen = strlen(context->buffer + context->bufferPos + nameLen + 1);
1512 
1513  //Return the name and the value of the header field
1514  *name = context->buffer + context->bufferPos;
1515  *value = context->buffer + context->bufferPos + nameLen + 1;
1516 
1517  //Point to the next header field
1518  context->bufferPos += nameLen + valueLen + 2;
1519 
1520  //Successful processing
1521  return NO_ERROR;
1522 }
1523 
1524 
1525 /**
1526  * @brief Read HTTP response body
1527  * @param[in] context Pointer to the HTTP client context
1528  * @param[out] data Buffer where to store the incoming data
1529  * @param[in] size Maximum number of bytes that can be received
1530  * @param[out] received Number of bytes that have been received
1531  * @param[in] flags Set of flags that influences the behavior of this function
1532  * @return Error code
1533  **/
1534 
1536  size_t size, size_t *received, uint_t flags)
1537 {
1538  error_t error;
1539  size_t n;
1540  char_t *p;
1541 
1542  //Check parameters
1543  if(context == NULL || data == NULL || received == NULL)
1544  return ERROR_INVALID_PARAMETER;
1545 
1546  //Check HTTP connection state
1547  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1548  return ERROR_WRONG_STATE;
1549 
1550  //Initialize status code
1551  error = NO_ERROR;
1552 
1553  //Point to the output buffer
1554  p = data;
1555  //No data has been read yet
1556  *received = 0;
1557 
1558  //Read as much data as possible
1559  while(*received < size && !error)
1560  {
1561  //Check HTTP request state
1562  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1563  {
1564  //Flush receive buffer
1565  context->bufferLen = 0;
1566  context->bufferPos = 0;
1567 
1568  //Any response to a HEAD request and any response with a 1xx, 204 or
1569  //304 status code is always terminated by the first empty line after
1570  //the header fields, regardless of the header fields present in the
1571  //message, and thus cannot contain a message body
1572  if(!strcasecmp(context->method, "HEAD") ||
1573  HTTP_STATUS_CODE_1YZ(context->statusCode) ||
1574  context->statusCode == 204 ||
1575  context->statusCode == 304)
1576  {
1577  //The HTTP response does not contain a body
1579  }
1580  else
1581  {
1582  //The HTTP response contains a body
1584  }
1585  }
1586  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_BODY)
1587  {
1588  //Chunked transfer encoding?
1589  if(context->chunkedEncoding)
1590  {
1591  //The chunked encoding modifies the body in order to transfer it
1592  //as a series of chunks, each with its own size indicator
1594  }
1595  else
1596  {
1597  //The length of the body is determined by the Content-Length field
1598  if(context->bodyPos < context->bodyLen)
1599  {
1600  //Limit the number of bytes to read
1601  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1602 
1603  //Read response body
1604  error = httpClientReceiveData(context, p, n, &n, flags);
1605 
1606  //Check status code
1607  if(!error)
1608  {
1609  //Advance data pointer
1610  p += n;
1611  *received += n;
1612  context->bodyPos += n;
1613 
1614  //Save current time
1615  context->timestamp = osGetSystemTime();
1616 
1617  //We are done
1618  break;
1619  }
1620  else
1621  {
1622  //In practice, many widely deployed HTTPS servers close connections
1623  //abruptly, without any prior notice, and in particular without
1624  //sending the close_notify alert
1625  if(!context->keepAlive && context->bodyLen == UINT_MAX)
1626  {
1627  //The HTTP transaction is complete
1629  //The end of the response body has been reached
1630  error = ERROR_END_OF_STREAM;
1631  }
1632  }
1633  }
1634  else
1635  {
1636  //The HTTP transaction is complete
1638  //The end of the response body has been reached
1639  error = ERROR_END_OF_STREAM;
1640  }
1641  }
1642  }
1643  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE)
1644  {
1645  //The chunked encoding modifies the body in order to transfer it as
1646  //a series of chunks, each with its own size indicator
1647  if(context->bufferLen >= 3 &&
1648  context->buffer[context->bufferLen - 2] == '\r' &&
1649  context->buffer[context->bufferLen - 1] == '\n')
1650  {
1651  //Parse chunk-size field
1652  error = httpClientParseChunkSize(context, context->buffer,
1653  context->bufferLen);
1654  }
1655  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1656  {
1657  //Receive more data
1658  error = httpClientReceiveData(context, context->buffer +
1659  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1661 
1662  //Check status code
1663  if(!error)
1664  {
1665  //Advance data pointer
1666  context->bufferLen += n;
1667  //Save current time
1668  context->timestamp = osGetSystemTime();
1669  }
1670  }
1671  else
1672  {
1673  //The client implementation limits the length of the chunk-size field
1674  error = ERROR_BUFFER_OVERFLOW;
1675  }
1676  }
1677  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
1678  {
1679  //The data stream is divided into a series of chunks
1680  if(context->bodyPos < context->bodyLen)
1681  {
1682  //The length of the data chunk is determined by the chunk-size field
1683  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1684 
1685  //Read chunk data
1686  error = httpClientReceiveData(context, p, n, &n, flags);
1687 
1688  //Check status code
1689  if(!error)
1690  {
1691  //Total number of data that have been read
1692  *received += n;
1693  //Number of bytes left to process in the current chunk
1694  context->bodyPos += n;
1695 
1696  //Save current time
1697  context->timestamp = osGetSystemTime();
1698 
1699  //Check flags
1701  {
1702  //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop
1703  //reading data as soon as the specified break character is
1704  //encountered
1705  if(p[n - 1] == LSB(flags))
1706  break;
1707  }
1708  else if(!(flags & HTTP_FLAG_WAIT_ALL))
1709  {
1710  //The HTTP_FLAG_WAIT_ALL flag causes the function to return
1711  //only when the requested number of bytes have been read
1712  break;
1713  }
1714  else
1715  {
1716  //Just for sanity
1717  }
1718 
1719  //Advance data pointer
1720  p += n;
1721  }
1722  }
1723  else
1724  {
1725  //The chunked encoding modifies the body in order to transfer it
1726  //as a series of chunks, each with its own size indicator
1728  }
1729  }
1730  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER ||
1731  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1732  context->requestState == HTTP_REQ_STATE_COMPLETE)
1733  {
1734  //The user must be satisfied with data already on hand
1735  if(*received > 0)
1736  {
1737  //Some data are pending in the receive buffer
1738  break;
1739  }
1740  else
1741  {
1742  //The end of the response body has been reached
1743  error = ERROR_END_OF_STREAM;
1744  }
1745  }
1746  else
1747  {
1748  //Invalid state
1749  error = ERROR_WRONG_STATE;
1750  }
1751  }
1752 
1753  //Check status code
1754  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1755  {
1756  //Check whether the timeout has elapsed
1757  error = httpClientCheckTimeout(context);
1758  }
1759 
1760  //Return status code
1761  return error;
1762 }
1763 
1764 
1765 /**
1766  * @brief Read HTTP trailer
1767  * @param[in] context Pointer to the HTTP client context
1768  * @return Error code
1769  **/
1770 
1772 {
1773  error_t error;
1774  size_t n;
1775 
1776  //Make sure the HTTP client context is valid
1777  if(context == NULL)
1778  return ERROR_INVALID_PARAMETER;
1779 
1780  //Initialize status code
1781  error = NO_ERROR;
1782 
1783  //Send HTTP request header
1784  while(!error)
1785  {
1786  //Check HTTP connection state
1787  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
1788  {
1789  //Check HTTP request state
1790  if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
1791  {
1792  //The CRLF sequence is always used to terminate a line
1793  if(context->bufferLen >= (context->bufferPos + 2) &&
1794  context->buffer[context->bufferLen - 2] == '\r' &&
1795  context->buffer[context->bufferLen - 1] == '\n')
1796  {
1797  //Strip the CRLF at the end of the line
1798  context->bufferLen -= 2;
1799 
1800  //An empty line indicates the end of the header fields
1801  if(context->bufferLen == context->bufferPos)
1802  {
1803  //The HTTP transaction is complete
1805  }
1806  else
1807  {
1808  //The response header fields allow the server to pass additional
1809  //information about the response which cannot be placed in the
1810  //Status-Line
1811  error = httpClientParseHeaderField(context, context->buffer +
1812  context->bufferPos, context->bufferLen - context->bufferPos);
1813  }
1814  }
1815  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1816  {
1817  //Receive more data
1818  error = httpClientReceiveData(context, context->buffer +
1819  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1821 
1822  //Check status code
1823  if(!error)
1824  {
1825  //Advance data pointer
1826  context->bufferLen += n;
1827  //Save current time
1828  context->timestamp = osGetSystemTime();
1829  }
1830  }
1831  else
1832  {
1833  //The client implementation limits the size of headers it accepts
1834  error = ERROR_BUFFER_OVERFLOW;
1835  }
1836  }
1837  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1838  context->requestState == HTTP_REQ_STATE_COMPLETE)
1839  {
1840  //Rewind to the beginning of the buffer
1841  context->bufferPos = 0;
1842 
1843  //Persistent HTTP connection?
1844  if(context->keepAlive)
1845  {
1846  //Persistent connections stay open across transactions, until
1847  //either the client or the server decides to close them
1848  break;
1849  }
1850  else
1851  {
1852  //The connection will be closed after completion of the response
1854  }
1855  }
1856  else
1857  {
1858  //Invalid HTTP request state
1859  error = ERROR_WRONG_STATE;
1860  }
1861  }
1862  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
1863  {
1864  //Shutdown connection
1865  error = httpClientDisconnect(context);
1866  }
1867  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
1868  {
1869  //Rewind to the beginning of the buffer
1870  context->bufferPos = 0;
1871  //We are done
1872  break;
1873  }
1874  else
1875  {
1876  //Invalid HTTP connection state
1877  error = ERROR_WRONG_STATE;
1878  }
1879  }
1880 
1881  //Check status code
1882  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1883  {
1884  //Check whether the timeout has elapsed
1885  error = httpClientCheckTimeout(context);
1886  }
1887 
1888  //Return status code
1889  return error;
1890 }
1891 
1892 
1893 /**
1894  * @brief Close HTTP request or response body
1895  * @param[in] context Pointer to the HTTP client context
1896  * @return Error code
1897  **/
1898 
1900 {
1901  error_t error;
1902  size_t n;
1903 
1904  //Make sure the HTTP client context is valid
1905  if(context == NULL)
1906  return ERROR_INVALID_PARAMETER;
1907 
1908  //Check HTTP connection state
1909  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1910  return ERROR_WRONG_STATE;
1911 
1912  //Initialize status code
1913  error = NO_ERROR;
1914 
1915  //Close HTTP request or response body
1916  while(!error)
1917  {
1918  //Check HTTP request state
1919  if(context->requestState == HTTP_REQ_STATE_SEND_BODY)
1920  {
1921  //Chunked transfer encoding?
1922  if(context->chunkedEncoding)
1923  {
1924  //The chunked encoding is ended by any chunk whose size is zero
1925  error = httpClientFormatChunkSize(context, 0);
1926  }
1927  else
1928  {
1929  //Ensure the HTTP request body is complete
1930  if(context->bodyPos == context->bodyLen)
1931  {
1932  //Flush receive buffer
1933  context->bufferLen = 0;
1934  context->bufferPos = 0;
1935 
1936  //Receive the HTTP response header
1939  }
1940  else
1941  {
1942  //Incomplete request body
1943  error = ERROR_WRONG_STATE;
1944  }
1945  }
1946  }
1947  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1948  {
1949  //Ensure the chunk data is complete
1950  if(context->bodyPos == context->bodyLen)
1951  {
1952  //The chunked encoding is ended by any chunk whose size is zero
1953  error = httpClientFormatChunkSize(context, 0);
1954  }
1955  else
1956  {
1957  //Incomplete chunk data
1958  error = ERROR_WRONG_STATE;
1959  }
1960  }
1961  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
1962  context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1963  {
1964  //The HTTP request body is closed
1965  break;
1966  }
1967  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
1968  context->requestState == HTTP_REQ_STATE_RECEIVE_BODY ||
1969  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE ||
1970  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
1971  {
1972  //Consume HTTP response body
1973  error = httpClientReadBody(context, context->buffer,
1974  HTTP_CLIENT_BUFFER_SIZE, &n, 0);
1975 
1976  //Check whether the end of the response body has been reached
1977  if(error == ERROR_END_OF_STREAM)
1978  {
1979  //Continue reading the optional trailer
1980  error = NO_ERROR;
1981  }
1982  }
1983  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
1984  {
1985  //Consume HTTP trailer
1986  error = httpClientReadTrailer(context);
1987  }
1988  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1989  context->requestState == HTTP_REQ_STATE_COMPLETE)
1990  {
1991  //The HTTP response body is closed
1992  break;
1993  }
1994  else
1995  {
1996  //Invalid HTTP request state
1997  error = ERROR_WRONG_STATE;
1998  }
1999  }
2000 
2001  //Check status code
2002  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2003  {
2004  //Check whether the timeout has elapsed
2005  error = httpClientCheckTimeout(context);
2006  }
2007 
2008  //Return status code
2009  return error;
2010 }
2011 
2012 
2013 /**
2014  * @brief Gracefully disconnect from the HTTP server
2015  * @param[in] context Pointer to the HTTP client context
2016  * @return Error code
2017  **/
2018 
2020 {
2021  error_t error;
2022 
2023  //Make sure the HTTP client context is valid
2024  if(context == NULL)
2025  return ERROR_INVALID_PARAMETER;
2026 
2027  //Initialize status code
2028  error = NO_ERROR;
2029 
2030  //Execute HTTP command sequence
2031  while(!error)
2032  {
2033  //Check HTTP connection state
2034  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
2035  {
2036  //Gracefully disconnect from the HTTP server
2038  }
2039  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
2040  {
2041  //Shutdown connection
2042  error = httpClientShutdownConnection(context);
2043 
2044  //Check status code
2045  if(error == NO_ERROR)
2046  {
2047  //Close connection
2048  httpClientCloseConnection(context);
2049  //Update HTTP connection state
2051  }
2052  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2053  {
2054  //Check whether the timeout has elapsed
2055  error = httpClientCheckTimeout(context);
2056  }
2057  else
2058  {
2059  //A communication error has occured
2060  }
2061  }
2062  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
2063  {
2064  //We are done
2065  break;
2066  }
2067  else
2068  {
2069  //Invalid state
2070  error = ERROR_WRONG_STATE;
2071  }
2072  }
2073 
2074  //Failed to gracefully disconnect from the HTTP server?
2075  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
2076  {
2077  //Close connection
2078  httpClientCloseConnection(context);
2079  //Update HTTP connection state
2081  }
2082 
2083  //Return status code
2084  return error;
2085 }
2086 
2087 
2088 /**
2089  * @brief Close the connection with the HTTP server
2090  * @param[in] context Pointer to the HTTP client context
2091  * @return Error code
2092  **/
2093 
2095 {
2096  //Make sure the HTTP client context is valid
2097  if(context == NULL)
2098  return ERROR_INVALID_PARAMETER;
2099 
2100  //Close connection
2101  httpClientCloseConnection(context);
2102  //Update HTTP connection state
2104 
2105  //Successful processing
2106  return NO_ERROR;
2107 }
2108 
2109 
2110 /**
2111  * @brief Release HTTP client context
2112  * @param[in] context Pointer to the HTTP client context
2113  **/
2114 
2116 {
2117  //Make sure the HTTP client context is valid
2118  if(context != NULL)
2119  {
2120  //Close connection
2121  httpClientCloseConnection(context);
2122 
2123 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
2124  //Release TLS session state
2125  tlsFreeSessionState(&context->tlsSession);
2126 #endif
2127 
2128  //Clear HTTP client context
2129  memset(context, 0, sizeof(HttpClientContext));
2130  }
2131 }
2132 
2133 #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:1040
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:297
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:1401
IP network address.
Definition: ip.h:59
#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:301
error_t httpClientDisconnect(HttpClientContext *context)
Gracefully disconnect from the HTTP server.
Definition: http_client.c:2019
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:1535
#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:1487
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:2115
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:1899
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:1428
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:2094
#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:1771
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:1190
HTTP authentication.
error_t httpClientReadHeader(HttpClientContext *context)
Read HTTP response header.
Definition: http_client.c:1271
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