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-2025 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 2.5.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 <stdarg.h>
48 #include "core/net.h"
49 #include "http/http_client.h"
50 #include "http/http_client_auth.h"
52 #include "http/http_client_misc.h"
53 #include "str.h"
54 #include "debug.h"
55 
56 //Check TCP/IP stack configuration
57 #if (HTTP_CLIENT_SUPPORT == ENABLED)
58 
59 
60 /**
61  * @brief Initialize HTTP client context
62  * @param[in] context Pointer to the HTTP client context
63  * @return Error code
64  **/
65 
67 {
68 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
69  error_t error;
70 #endif
71 
72  //Make sure the HTTP client context is valid
73  if(context == NULL)
75 
76  //Clear HTTP client context
77  osMemset(context, 0, sizeof(HttpClientContext));
78 
79 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
80  //Initialize TLS session state
81  error = tlsInitSessionState(&context->tlsSession);
82  //Any error to report?
83  if(error)
84  return error;
85 #endif
86 
87  //Initialize HTTP connection state
88  context->state = HTTP_CLIENT_STATE_DISCONNECTED;
89  //Initialize HTTP request state
90  context->requestState = HTTP_REQ_STATE_INIT;
91 
92  //Default protocol version
93  context->version = HTTP_VERSION_1_1;
94  //Default timeout
95  context->timeout = HTTP_CLIENT_DEFAULT_TIMEOUT;
96 
97  //Successful initialization
98  return NO_ERROR;
99 }
100 
101 
102 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
103 
104 /**
105  * @brief Register TLS initialization callback function
106  * @param[in] context Pointer to the HTTP client context
107  * @param[in] callback TLS initialization callback function
108  * @return Error code
109  **/
110 
112  HttpClientTlsInitCallback callback)
113 {
114  //Make sure the HTTP client context is valid
115  if(context == NULL)
117 
118  //Save callback function
119  context->tlsInitCallback = callback;
120 
121  //Successful processing
122  return NO_ERROR;
123 }
124 
125 #endif
126 
127 
128 /**
129  * @brief Register random data generation callback function
130  * @param[in] context Pointer to the HTTP client context
131  * @param[in] callback Random data generation callback function
132  * @return Error code
133  **/
134 
136  HttpClientRandCallback callback)
137 {
138 #if (HTTP_CLIENT_DIGEST_AUTH_SUPPORT == ENABLED)
139  //Make sure the HTTP client context is valid
140  if(context == NULL)
142 
143  //Save callback function
144  context->randCallback = callback;
145 
146  //Successful processing
147  return NO_ERROR;
148 #else
149  //Digest authentication is not implemented
150  return ERROR_NOT_IMPLEMENTED;
151 #endif
152 }
153 
154 
155 /**
156  * @brief Set the HTTP protocol version to be used
157  * @param[in] context Pointer to the HTTP client context
158  * @param[in] version HTTP protocol version (1.0 or 1.1)
159  * @return Error code
160  **/
161 
163 {
164  //Make sure the HTTP client context is valid
165  if(context == NULL)
167 
168  //Check HTTP version
170  return ERROR_INVALID_VERSION;
171 
172  //Save the protocol version to be used
173  context->version = version;
174 
175  //Successful processing
176  return NO_ERROR;
177 }
178 
179 
180 /**
181  * @brief Set communication timeout
182  * @param[in] context Pointer to the HTTP client context
183  * @param[in] timeout Timeout value, in milliseconds
184  * @return Error code
185  **/
186 
188 {
189  //Make sure the HTTP client context is valid
190  if(context == NULL)
192 
193  //Save timeout value
194  context->timeout = timeout;
195 
196  //Successful processing
197  return NO_ERROR;
198 }
199 
200 
201 /**
202  * @brief Set authentication information
203  * @param[in] context Pointer to the HTTP client context
204  * @param[in] username NULL-terminated string containing the user name to be used
205  * @param[in] password NULL-terminated string containing the password to be used
206  * @return Error code
207  **/
208 
210  const char_t *username, const char_t *password)
211 {
212 #if (HTTP_CLIENT_AUTH_SUPPORT == ENABLED)
213  //Check parameters
214  if(context == NULL || username == NULL || password == NULL)
216 
217  //Make sure the length of the user name is acceptable
218  if(osStrlen(username) > HTTP_CLIENT_MAX_USERNAME_LEN)
219  return ERROR_INVALID_LENGTH;
220 
221  //Make sure the length of the password is acceptable
222  if(osStrlen(password) > HTTP_CLIENT_MAX_PASSWORD_LEN)
223  return ERROR_INVALID_LENGTH;
224 
225  //Save user name
226  osStrcpy(context->authParams.username, username);
227  //Save password
228  osStrcpy(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 occurred
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 = osStrlen(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 = osStrchr(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  osMemmove(context->buffer + n, p, context->bufferLen + 1 - m);
441  //Copy the new method token
442  osMemcpy(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  osStrcpy(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 = osStrchr(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 = osStrlen(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  osMemmove(p + n, q, context->buffer + context->bufferLen + 1 - q);
514  //Copy the new resource name
515  osMemcpy(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  osStrcpy(temp, "");
558  }
559  else
560  {
561  //Format port number information
562  osSprintf(temp, ":%" PRIu16, port);
563  }
564 
565  //Compute the length of the hostname
566  n = osStrlen(host);
567  //Compute the length of the trailing port information
568  m = osStrlen(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  osSprintf(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 += 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 = osStrchr(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 = osStrchr(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 = osStrlen(queryString);
650 
651  //Empty query string?
652  if(n == 0)
653  {
654  //Remove the query string
655  osMemmove(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  osMemmove(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  osMemcpy(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 = osStrchr(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 = osStrchr(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 = osStrlen(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  osMemmove(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  osMemcpy(p + 1, name, nameLen);
765 
766  //Adjust the length of the request header
767  context->bufferLen += nameLen + 1;
768  }
769  else
770  {
771  //Compute the length of the parameter value
772  valueLen = osStrlen(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  osMemmove(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  osMemcpy(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  osMemcpy(p + nameLen + 2, value, valueLen);
790 
791  //Adjust the length of the request header
792  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 = osStrlen(name);
837  valueLen = osStrlen(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  osSprintf(context->buffer + context->bufferLen - 2, "%s: %s\r\n\r\n",
849  name, value);
850 
851  //Check header field name
852  if(osStrcasecmp(name, "Connection") == 0)
853  {
854  //Parse Connection header field
855  error = httpClientParseConnectionField(context, value);
856  }
857  else if(osStrcasecmp(name, "Transfer-Encoding") == 0)
858  {
859  //Parse Transfer-Encoding header field
861  }
862  else if(osStrcasecmp(name, "Content-Length") == 0)
863  {
864  //Parse Content-Length header field
865  error = httpClientParseContentLengthField(context, value);
866  }
867  else
868  {
869  //Discard unknown header fields
870  error = NO_ERROR;
871  }
872 
873  //Adjust the length of the request header
874  context->bufferLen += n;
875 
876  //Return status code
877  return error;
878 }
879 
880 
881 /**
882  * @brief Format an HTTP header field
883  * @param[in] context Pointer to the HTTP client context
884  * @param[in] name NULL-terminated string that holds the header field name
885  * @param[in] format NULL-terminated string that that contains a format string
886  * @param[in] ... Optional arguments
887  * @return Error code
888  **/
889 
891  const char_t *name, const char_t *format, ...)
892 {
893  error_t error;
894  size_t n;
895  size_t size;
896  size_t nameLen;
897  char_t *value;
898  va_list args;
899 
900  //Check parameters
901  if(context == NULL || name == NULL || format == NULL)
903 
904  //The field name must not be empty
905  if(name[0] == '\0')
907 
908  //Check HTTP request state
909  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER &&
910  context->requestState != HTTP_REQ_STATE_FORMAT_TRAILER)
911  {
912  return ERROR_WRONG_STATE;
913  }
914 
915  //Make sure the buffer contains a valid HTTP request
916  if(context->bufferLen < 2 || context->bufferLen > HTTP_CLIENT_BUFFER_SIZE)
917  return ERROR_INVALID_SYNTAX;
918 
919  //Retrieve the length of the field name
920  nameLen = osStrlen(name);
921 
922  //Make sure the buffer is large enough to hold the new header field
923  if((context->bufferLen + nameLen + 4) > HTTP_CLIENT_BUFFER_SIZE)
924  return ERROR_BUFFER_OVERFLOW;
925 
926  //Point to the buffer where to format the field value
927  value = context->buffer + context->bufferLen + nameLen;
928  //Calculate the maximum size of the formatted string
929  size = HTTP_CLIENT_BUFFER_SIZE - context->bufferLen - nameLen - 4;
930 
931  //Initialize processing of a varying-length argument list
932  va_start(args, format);
933  //Format field value
934  n = osVsnprintf(value, size, format, args);
935  //End varying-length argument list processing
936  va_end(args);
937 
938  //A return value of size or more means that the output was truncated
939  if(n >= size)
940  return ERROR_BUFFER_OVERFLOW;
941 
942  //Each header field consists of a case-insensitive field name followed
943  //by a colon, optional leading whitespace and the field value
944  osMemcpy(context->buffer + context->bufferLen - 2, name, nameLen);
945  osMemcpy(context->buffer + context->bufferLen + nameLen - 2, ": ", 2);
946 
947  //Check header field name
948  if(osStrcasecmp(name, "Connection") == 0)
949  {
950  //Parse Connection header field
951  error = httpClientParseConnectionField(context, value);
952  }
953  else if(osStrcasecmp(name, "Transfer-Encoding") == 0)
954  {
955  //Parse Transfer-Encoding header field
957  }
958  else if(osStrcasecmp(name, "Content-Length") == 0)
959  {
960  //Parse Content-Length header field
961  error = httpClientParseContentLengthField(context, value);
962  }
963  else
964  {
965  //Discard unknown header fields
966  error = NO_ERROR;
967  }
968 
969  //Terminate the header field with a CRLF sequence
970  osStrcpy(context->buffer + context->bufferLen + nameLen + n, "\r\n\r\n");
971 
972  //Adjust the length of the request header
973  context->bufferLen += nameLen + n + 4;
974 
975  //Return status code
976  return error;
977 }
978 
979 
980 /**
981  * @brief Set the length of the HTTP request body
982  * @param[in] context Pointer to the HTTP client context
983  * @param[in] length Length of the HTTP request body, in bytes
984  * @return Error code
985  **/
986 
988 {
989  char_t temp[11];
990 
991  //Make sure the HTTP client context is valid
992  if(context == NULL)
994 
995  //Check HTTP request state
996  if(context->requestState != HTTP_REQ_STATE_FORMAT_HEADER)
997  return ERROR_WRONG_STATE;
998 
999  //The Content-Length header field indicates the size of the body, in
1000  //decimal number of octets
1001  osSprintf(temp, "%" PRIuSIZE, length);
1002 
1003  //Add the Content-Length header field
1004  return httpClientAddHeaderField(context, "Content-Length", temp);
1005 }
1006 
1007 
1008 /**
1009  * @brief Write HTTP request header
1010  * @param[in] context Pointer to the HTTP client context
1011  * @return Error code
1012  **/
1013 
1015 {
1016  error_t error;
1017  size_t n;
1018 
1019  //Make sure the HTTP client context is valid
1020  if(context == NULL)
1021  return ERROR_INVALID_PARAMETER;
1022 
1023  //Initialize status code
1024  error = NO_ERROR;
1025 
1026  //Send HTTP request header
1027  while(!error)
1028  {
1029  //Check HTTP connection state
1030  if(context->state == HTTP_CLIENT_STATE_DISCONNECTED ||
1031  context->state == HTTP_CLIENT_STATE_CONNECTING)
1032  {
1033  //If the HTTP connection is not persistent, then a new connection
1034  //must be established
1035  error = httpClientConnect(context, NULL, 0);
1036  }
1037  else if(context->state == HTTP_CLIENT_STATE_CONNECTED)
1038  {
1039  //Check HTTP request state
1040  if(context->requestState == HTTP_REQ_STATE_FORMAT_HEADER)
1041  {
1042 #if (HTTP_CLIENT_AUTH_SUPPORT == ENABLED)
1043  //HTTP authentication requested by the server?
1044  if(context->authParams.mode != HTTP_AUTH_MODE_NONE &&
1045  context->authParams.username[0] != '\0')
1046  {
1047  //Format Authorization header field
1048  error = httpClientFormatAuthorizationField(context);
1049  }
1050 #endif
1051  //Check status code
1052  if(!error)
1053  {
1054  //Dump HTTP request header
1055  TRACE_DEBUG("HTTP request header:\r\n%s", context->buffer);
1056 
1057  //Initiate the sending process
1059  }
1060  }
1061  else if(context->requestState == HTTP_REQ_STATE_SEND_HEADER)
1062  {
1063  //Any remaining data to be sent?
1064  if(context->bufferPos < context->bufferLen)
1065  {
1066  //Send more data
1067  error = httpClientSendData(context,
1068  context->buffer + context->bufferPos,
1069  context->bufferLen - context->bufferPos, &n, 0);
1070 
1071  //Check status code
1072  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1073  {
1074  //Advance data pointer
1075  context->bufferPos += n;
1076  }
1077  }
1078  else
1079  {
1080  //The request header has been successfully transmitted
1081  if(osStrcasecmp(context->method, "POST") == 0 ||
1082  osStrcasecmp(context->method, "PUT") == 0 ||
1083  osStrcasecmp(context->method, "PATCH") == 0)
1084  {
1085  //POST, PUT and PATCH requests have a body
1087  }
1088  else
1089  {
1090  //GET, HEAD and DELETE requests do not have a body
1093  }
1094  }
1095  }
1096  else if(context->requestState == HTTP_REQ_STATE_SEND_BODY ||
1097  context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1098  {
1099  //We are done
1100  break;
1101  }
1102  else
1103  {
1104  //Invalid HTTP request state
1105  error = ERROR_WRONG_STATE;
1106  }
1107  }
1108  else
1109  {
1110  //Invalid HTTP connection state
1111  error = ERROR_WRONG_STATE;
1112  }
1113  }
1114 
1115  //Check status code
1116  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1117  {
1118  //Check whether the timeout has elapsed
1119  error = httpClientCheckTimeout(context);
1120  }
1121 
1122  //Return status code
1123  return error;
1124 }
1125 
1126 
1127 /**
1128  * @brief Write HTTP request body
1129  * @param[in] context Pointer to the HTTP client context
1130  * @param[in] data Pointer to the buffer containing the data to be transmitted
1131  * @param[in] length Number of data bytes to send
1132  * @param[out] written Actual number of bytes written (optional parameter)
1133  * @param[in] flags Set of flags that influences the behavior of this function
1134  * @return Error code
1135  **/
1136 
1138  size_t length, size_t *written, uint_t flags)
1139 {
1140  error_t error;
1141  size_t n;
1142  size_t totalLength;
1143 
1144  //Make sure the HTTP client context is valid
1145  if(context == NULL)
1146  return ERROR_INVALID_PARAMETER;
1147 
1148  //Check HTTP connection state
1149  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1150  return ERROR_WRONG_STATE;
1151 
1152  //Initialize status code
1153  error = NO_ERROR;
1154 
1155  //Actual number of bytes written
1156  totalLength = 0;
1157 
1158  //Send as much data as possible
1159  while(totalLength < length && !error)
1160  {
1161  //Check HTTP request state
1162  if(context->requestState == HTTP_REQ_STATE_SEND_BODY)
1163  {
1164  //Chunked transfer encoding?
1165  if(context->chunkedEncoding)
1166  {
1167  //The chunked encoding modifies the body in order to transfer it
1168  //as a series of chunks, each with its own size indicator
1169  error = httpClientFormatChunkSize(context, length - totalLength);
1170  }
1171  else
1172  {
1173  //Number of bytes left to send
1174  n = length - totalLength;
1175 
1176  //The length of the body shall not exceed the value specified in
1177  //the Content-Length field
1178  if(n <= (context->bodyLen - context->bodyPos))
1179  {
1180  //Send request body
1181  error = httpClientSendData(context, data, n, &n, flags);
1182 
1183  //Check status code
1184  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1185  {
1186  //Any data transmitted?
1187  if(n > 0)
1188  {
1189  //Advance data pointer
1190  data = (uint8_t *) data + n;
1191  totalLength += n;
1192  context->bodyPos += n;
1193 
1194  //Save current time
1195  context->timestamp = osGetSystemTime();
1196  }
1197  }
1198  }
1199  else
1200  {
1201  //Report an error
1202  error = ERROR_INVALID_LENGTH;
1203  }
1204  }
1205  }
1206  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_SIZE)
1207  {
1208  //Send the chunk-size field
1209  if(context->bufferPos < context->bufferLen)
1210  {
1211  //Send more data
1212  error = httpClientSendData(context,
1213  context->buffer + context->bufferPos,
1214  context->bufferLen - context->bufferPos, &n, 0);
1215 
1216  //Check status code
1217  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1218  {
1219  //Advance data pointer
1220  context->bufferPos += n;
1221  }
1222  }
1223  else
1224  {
1225  //Send chunk data
1227  }
1228  }
1229  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1230  {
1231  //The data stream is divided into a series of chunks
1232  if(context->bodyPos < context->bodyLen)
1233  {
1234  //The length of the chunk shall not exceed the value specified in
1235  //the chunk-size field
1236  n = MIN(length - totalLength, context->bodyLen - context->bodyPos);
1237 
1238  //Send chunk data
1239  error = httpClientSendData(context, data, n, &n, flags);
1240 
1241  //Check status code
1242  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1243  {
1244  //Any data transmitted?
1245  if(n > 0)
1246  {
1247  //Advance data pointer
1248  data = (uint8_t *) data + n;
1249  totalLength += n;
1250  context->bodyPos += n;
1251 
1252  //Save current time
1253  context->timestamp = osGetSystemTime();
1254  }
1255  }
1256  }
1257  else
1258  {
1259  //The chunked encoding modifies the body in order to transfer it
1260  //as a series of chunks, each with its own size indicator
1261  error = httpClientFormatChunkSize(context, length - totalLength);
1262  }
1263  }
1264  else
1265  {
1266  //Invalid state
1267  error = ERROR_WRONG_STATE;
1268  }
1269  }
1270 
1271  //Check status code
1272  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1273  {
1274  //Check whether the timeout has elapsed
1275  error = httpClientCheckTimeout(context);
1276  }
1277 
1278  //Total number of data that have been written
1279  if(written != NULL)
1280  {
1281  *written = totalLength;
1282  }
1283 
1284  //Return status code
1285  return error;
1286 }
1287 
1288 
1289 /**
1290  * @brief Write HTTP trailer
1291  * @param[in] context Pointer to the HTTP client context
1292  * @return Error code
1293  **/
1294 
1296 {
1297  error_t error;
1298  size_t n;
1299 
1300  //Make sure the HTTP client context is valid
1301  if(context == NULL)
1302  return ERROR_INVALID_PARAMETER;
1303 
1304  //Check HTTP connection state
1305  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1306  return ERROR_WRONG_STATE;
1307 
1308  //Initialize status code
1309  error = NO_ERROR;
1310 
1311  //Send HTTP request header
1312  while(!error)
1313  {
1314  //Check HTTP request state
1315  if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER)
1316  {
1317  //Initiate the sending process
1319  }
1320  else if(context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1321  {
1322  //Any remaining data to be sent?
1323  if(context->bufferPos < context->bufferLen)
1324  {
1325  //Send more data
1326  error = httpClientSendData(context,
1327  context->buffer + context->bufferPos,
1328  context->bufferLen - context->bufferPos, &n, 0);
1329 
1330  //Check status code
1331  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1332  {
1333  //Advance data pointer
1334  context->bufferPos += n;
1335  }
1336  }
1337  else
1338  {
1339  //The request trailer has been successfully transmitted
1342  }
1343  }
1344  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1345  {
1346  //We are done
1347  break;
1348  }
1349  else
1350  {
1351  //Invalid state
1352  error = ERROR_WRONG_STATE;
1353  }
1354  }
1355 
1356  //Check status code
1357  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1358  {
1359  //Check whether the timeout has elapsed
1360  error = httpClientCheckTimeout(context);
1361  }
1362 
1363  //Return status code
1364  return error;
1365 }
1366 
1367 
1368 /**
1369  * @brief Read HTTP response header
1370  * @param[in] context Pointer to the HTTP client context
1371  * @return Error code
1372  **/
1373 
1375 {
1376  error_t error;
1377  size_t n;
1378 
1379  //Make sure the HTTP client context is valid
1380  if(context == NULL)
1381  return ERROR_INVALID_PARAMETER;
1382 
1383  //Check HTTP connection state
1384  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1385  return ERROR_WRONG_STATE;
1386 
1387  //Initialize status code
1388  error = NO_ERROR;
1389 
1390  //Send HTTP request header
1391  while(!error)
1392  {
1393  //Check HTTP request state
1394  if(context->requestState == HTTP_REQ_STATE_SEND_BODY ||
1395  context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1396  {
1397  //Close HTTP request body
1398  error = httpClientCloseBody(context);
1399  }
1400  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
1401  context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1402  {
1403  //The last chunk is followed by an optional trailer
1404  error = httpClientWriteTrailer(context);
1405  }
1406  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE ||
1407  context->requestState == HTTP_REQ_STATE_RECEIVE_HEADER)
1408  {
1409  //The CRLF sequence is always used to terminate a line
1410  if(context->bufferLen >= (context->bufferPos + 2) &&
1411  context->buffer[context->bufferLen - 2] == '\r' &&
1412  context->buffer[context->bufferLen - 1] == '\n')
1413  {
1414  //Strip the CRLF at the end of the line
1415  context->bufferLen -= 2;
1416 
1417  //The first line of a response message is the Status-Line
1418  if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1419  {
1420  //Thee Status-Line consists of the protocol version followed by
1421  //a numeric status code and its associated textual phrase
1422  error = httpClientParseStatusLine(context, context->buffer +
1423  context->bufferPos, context->bufferLen - context->bufferPos);
1424 
1425  //Valid syntax?
1426  if(!error)
1427  {
1428  //Receive response header fields
1430  }
1431  }
1432  else
1433  {
1434  //An empty line indicates the end of the header fields
1435  if(context->bufferLen == context->bufferPos)
1436  {
1437  //Save TLS session
1438  error = httpClientSaveSession(context);
1439 
1440  //Check status code
1441  if(!error)
1442  {
1443  //Debug message
1444  TRACE_DEBUG("\r\n");
1445 
1446  //The HTTP response header has been successfully received
1448  }
1449  }
1450  else
1451  {
1452  //The response header fields allow the server to pass additional
1453  //information about the response which cannot be placed in the
1454  //Status-Line
1455  error = httpClientParseHeaderField(context, context->buffer +
1456  context->bufferPos, context->bufferLen - context->bufferPos);
1457  }
1458  }
1459  }
1460  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1461  {
1462  //Receive more data
1463  error = httpClientReceiveData(context, context->buffer +
1464  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1466 
1467  //Check status code
1468  if(!error)
1469  {
1470  //Adjust the length of the buffer
1471  context->bufferLen += n;
1472  //Save current time
1473  context->timestamp = osGetSystemTime();
1474  }
1475  }
1476  else
1477  {
1478  //The client implementation limits the size of headers it accepts
1479  error = ERROR_BUFFER_OVERFLOW;
1480  }
1481  }
1482  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1483  {
1484  //Rewind to the beginning of the buffer
1485  context->bufferPos = 0;
1486  //We are done
1487  break;
1488  }
1489  else
1490  {
1491  //Invalid state
1492  error = ERROR_WRONG_STATE;
1493  }
1494  }
1495 
1496  //Check status code
1497  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1498  {
1499  //Check whether the timeout has elapsed
1500  error = httpClientCheckTimeout(context);
1501  }
1502 
1503  //Return status code
1504  return error;
1505 }
1506 
1507 
1508 /**
1509  * @brief Retrieve the HTTP status code of the response
1510  * @param[in] context Pointer to the HTTP client context
1511  * @return HTTP status code
1512  **/
1513 
1515 {
1516  uint_t status;
1517 
1518  //Initialize status code
1519  status = 0;
1520 
1521  //Make sure the HTTP client context is valid
1522  if(context != NULL)
1523  {
1524  //The status code is a three-digit integer code giving the result
1525  //of the attempt to understand and satisfy the request
1526  status = context->statusCode;
1527  }
1528 
1529  //Return HTTP status code
1530  return status;
1531 }
1532 
1533 
1534 /**
1535  * @brief Retrieve the value of the specified header field name
1536  * @param[in] context Pointer to the HTTP client context
1537  * @param[in] name NULL-terminated string that specifies the header field name
1538  * @return Value of the header field
1539  **/
1540 
1542  const char_t *name)
1543 {
1544  size_t i;
1545  size_t nameLen;
1546  size_t valueLen;
1547  const char_t *value;
1548 
1549  //Initialize field value
1550  value = NULL;
1551 
1552  //Check parameters
1553  if(context != NULL && name != NULL)
1554  {
1555  //Check HTTP request state
1556  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
1557  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER)
1558  {
1559  //Point to the first header field of the response
1560  i = 0;
1561 
1562  //Parse HTTP response header
1563  while(i < context->bufferLen)
1564  {
1565  //Calculate the length of the field name
1566  nameLen = osStrlen(context->buffer + i);
1567  //Calculate the length of the field value
1568  valueLen = osStrlen(context->buffer + i + nameLen + 1);
1569 
1570  //Check whether the current header field matches the specified name
1571  if(osStrcasecmp(context->buffer + i, name) == 0)
1572  {
1573  //Retrieve the value of the header field
1574  value = context->buffer + i + nameLen + 1;
1575  //Exit immediately
1576  break;
1577  }
1578 
1579  //Point to next header field
1580  i += nameLen + valueLen + 2;
1581  }
1582  }
1583  }
1584 
1585  //Return the value of the header field
1586  return value;
1587 }
1588 
1589 
1590 /**
1591  * @brief Iterate through the HTTP response header
1592  * @param[in] context Pointer to the HTTP client context
1593  * @param[out] name NULL-terminated string that contains the name of the next
1594  * header field
1595  * @param[out] value NULL-terminated string that contains the value of the next
1596  * header field
1597  * @return Error code
1598  **/
1599 
1601  const char_t **name, const char_t **value)
1602 {
1603  size_t nameLen;
1604  size_t valueLen;
1605 
1606  //Check parameters
1607  if(context == NULL || name == NULL || value == NULL)
1608  return ERROR_INVALID_PARAMETER;
1609 
1610  //Check HTTP request state
1611  if(context->requestState != HTTP_REQ_STATE_PARSE_HEADER &&
1612  context->requestState != HTTP_REQ_STATE_PARSE_TRAILER)
1613  {
1614  return ERROR_WRONG_STATE;
1615  }
1616 
1617  //Check whether the end of the HTTP response header has been reached
1618  if(context->bufferPos >= context->bufferLen)
1619  return ERROR_END_OF_STREAM;
1620 
1621  //Calculate the length of the field name
1622  nameLen = osStrlen(context->buffer + context->bufferPos);
1623  //Calculate the length of the field value
1624  valueLen = osStrlen(context->buffer + context->bufferPos + nameLen + 1);
1625 
1626  //Return the name and the value of the header field
1627  *name = context->buffer + context->bufferPos;
1628  *value = context->buffer + context->bufferPos + nameLen + 1;
1629 
1630  //Point to the next header field
1631  context->bufferPos += nameLen + valueLen + 2;
1632 
1633  //Successful processing
1634  return NO_ERROR;
1635 }
1636 
1637 
1638 /**
1639  * @brief Read HTTP response body
1640  * @param[in] context Pointer to the HTTP client context
1641  * @param[out] data Buffer where to store the incoming data
1642  * @param[in] size Maximum number of bytes that can be received
1643  * @param[out] received Number of bytes that have been received
1644  * @param[in] flags Set of flags that influences the behavior of this function
1645  * @return Error code
1646  **/
1647 
1649  size_t size, size_t *received, uint_t flags)
1650 {
1651  error_t error;
1652  size_t n;
1653  char_t *p;
1654 
1655  //Check parameters
1656  if(context == NULL || data == NULL || received == NULL)
1657  return ERROR_INVALID_PARAMETER;
1658 
1659  //Check HTTP connection state
1660  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1661  return ERROR_WRONG_STATE;
1662 
1663  //Initialize status code
1664  error = NO_ERROR;
1665 
1666  //Point to the output buffer
1667  p = data;
1668  //No data has been read yet
1669  *received = 0;
1670 
1671  //Read as much data as possible
1672  while(*received < size && !error)
1673  {
1674  //Check HTTP request state
1675  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1676  {
1677  //Flush receive buffer
1678  context->bufferLen = 0;
1679  context->bufferPos = 0;
1680 
1681  //Any response to a HEAD request and any response with a 1xx, 204 or
1682  //304 status code is always terminated by the first empty line after
1683  //the header fields, regardless of the header fields present in the
1684  //message, and thus cannot contain a message body
1685  if(osStrcasecmp(context->method, "HEAD") == 0 ||
1686  HTTP_STATUS_CODE_1YZ(context->statusCode) ||
1687  context->statusCode == 204 ||
1688  context->statusCode == 304)
1689  {
1690  //The HTTP response does not contain a body
1692  }
1693  else
1694  {
1695  //The HTTP response contains a body
1697  }
1698  }
1699  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_BODY)
1700  {
1701  //Chunked transfer encoding?
1702  if(context->chunkedEncoding)
1703  {
1704  //The chunked encoding modifies the body in order to transfer it
1705  //as a series of chunks, each with its own size indicator
1707  }
1708  else
1709  {
1710  //The length of the body is determined by the Content-Length field
1711  if(context->bodyPos < context->bodyLen)
1712  {
1713  //Limit the number of bytes to read
1714  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1715 
1716  //Read response body
1717  error = httpClientReceiveData(context, p, n, &n, flags);
1718 
1719  //Check status code
1720  if(!error)
1721  {
1722  //Advance data pointer
1723  p += n;
1724  *received += n;
1725  context->bodyPos += n;
1726 
1727  //Save current time
1728  context->timestamp = osGetSystemTime();
1729 
1730  //We are done
1731  break;
1732  }
1733  else
1734  {
1735  //In practice, many widely deployed HTTPS servers close connections
1736  //abruptly, without any prior notice, and in particular without
1737  //sending the close_notify alert
1738  if(!context->keepAlive && context->bodyLen == UINT_MAX)
1739  {
1740  //The HTTP transaction is complete
1742  //Close the HTTP connection
1744  //The end of the response body has been reached
1745  error = ERROR_END_OF_STREAM;
1746  }
1747  }
1748  }
1749  else
1750  {
1751  //The HTTP transaction is complete
1753  //The end of the response body has been reached
1754  error = ERROR_END_OF_STREAM;
1755  }
1756  }
1757  }
1758  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE)
1759  {
1760  //The chunked encoding modifies the body in order to transfer it as
1761  //a series of chunks, each with its own size indicator
1762  if(context->bufferLen >= 3 &&
1763  context->buffer[context->bufferLen - 2] == '\r' &&
1764  context->buffer[context->bufferLen - 1] == '\n')
1765  {
1766  //Parse chunk-size field
1767  error = httpClientParseChunkSize(context, context->buffer,
1768  context->bufferLen);
1769  }
1770  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1771  {
1772  //Receive more data
1773  error = httpClientReceiveData(context, context->buffer +
1774  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1776 
1777  //Check status code
1778  if(!error)
1779  {
1780  //Adjust the length of the buffer
1781  context->bufferLen += n;
1782  //Save current time
1783  context->timestamp = osGetSystemTime();
1784  }
1785  }
1786  else
1787  {
1788  //The client implementation limits the length of the chunk-size field
1789  error = ERROR_BUFFER_OVERFLOW;
1790  }
1791  }
1792  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
1793  {
1794  //The data stream is divided into a series of chunks
1795  if(context->bodyPos < context->bodyLen)
1796  {
1797  //The length of the data chunk is determined by the chunk-size field
1798  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1799 
1800  //Read chunk data
1801  error = httpClientReceiveData(context, p, n, &n, flags);
1802 
1803  //Check status code
1804  if(!error)
1805  {
1806  //Total number of data that have been read
1807  *received += n;
1808  //Number of bytes left to process in the current chunk
1809  context->bodyPos += n;
1810 
1811  //Save current time
1812  context->timestamp = osGetSystemTime();
1813 
1814  //Check flags
1815  if((flags & HTTP_FLAG_BREAK_CHAR) != 0)
1816  {
1817  //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop
1818  //reading data as soon as the specified break character is
1819  //encountered
1820  if(p[n - 1] == LSB(flags))
1821  break;
1822  }
1823  else if((flags & HTTP_FLAG_WAIT_ALL) == 0)
1824  {
1825  //The HTTP_FLAG_WAIT_ALL flag causes the function to return
1826  //only when the requested number of bytes have been read
1827  break;
1828  }
1829  else
1830  {
1831  //Just for sanity
1832  }
1833 
1834  //Advance data pointer
1835  p += n;
1836  }
1837  }
1838  else
1839  {
1840  //The chunked encoding modifies the body in order to transfer it
1841  //as a series of chunks, each with its own size indicator
1843  }
1844  }
1845  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER ||
1846  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1847  context->requestState == HTTP_REQ_STATE_COMPLETE)
1848  {
1849  //The user must be satisfied with data already on hand
1850  if(*received > 0)
1851  {
1852  //Some data are pending in the receive buffer
1853  break;
1854  }
1855  else
1856  {
1857  //The end of the response body has been reached
1858  error = ERROR_END_OF_STREAM;
1859  }
1860  }
1861  else
1862  {
1863  //Invalid state
1864  error = ERROR_WRONG_STATE;
1865  }
1866  }
1867 
1868  //Check status code
1869  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1870  {
1871  //Check whether the timeout has elapsed
1872  error = httpClientCheckTimeout(context);
1873  }
1874 
1875  //Return status code
1876  return error;
1877 }
1878 
1879 
1880 /**
1881  * @brief Read HTTP trailer
1882  * @param[in] context Pointer to the HTTP client context
1883  * @return Error code
1884  **/
1885 
1887 {
1888  error_t error;
1889  size_t n;
1890 
1891  //Make sure the HTTP client context is valid
1892  if(context == NULL)
1893  return ERROR_INVALID_PARAMETER;
1894 
1895  //Initialize status code
1896  error = NO_ERROR;
1897 
1898  //Send HTTP request header
1899  while(!error)
1900  {
1901  //Check HTTP connection state
1902  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
1903  {
1904  //Check HTTP request state
1905  if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
1906  {
1907  //The CRLF sequence is always used to terminate a line
1908  if(context->bufferLen >= (context->bufferPos + 2) &&
1909  context->buffer[context->bufferLen - 2] == '\r' &&
1910  context->buffer[context->bufferLen - 1] == '\n')
1911  {
1912  //Strip the CRLF at the end of the line
1913  context->bufferLen -= 2;
1914 
1915  //An empty line indicates the end of the header fields
1916  if(context->bufferLen == context->bufferPos)
1917  {
1918  //The HTTP transaction is complete
1920  }
1921  else
1922  {
1923  //The response header fields allow the server to pass additional
1924  //information about the response which cannot be placed in the
1925  //Status-Line
1926  error = httpClientParseHeaderField(context, context->buffer +
1927  context->bufferPos, context->bufferLen - context->bufferPos);
1928  }
1929  }
1930  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1931  {
1932  //Receive more data
1933  error = httpClientReceiveData(context, context->buffer +
1934  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1936 
1937  //Check status code
1938  if(!error)
1939  {
1940  //Adjust the length of the buffer
1941  context->bufferLen += n;
1942  //Save current time
1943  context->timestamp = osGetSystemTime();
1944  }
1945  }
1946  else
1947  {
1948  //The client implementation limits the size of headers it accepts
1949  error = ERROR_BUFFER_OVERFLOW;
1950  }
1951  }
1952  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1953  context->requestState == HTTP_REQ_STATE_COMPLETE)
1954  {
1955  //Rewind to the beginning of the buffer
1956  context->bufferPos = 0;
1957 
1958  //Persistent HTTP connection?
1959  if(context->keepAlive)
1960  {
1961  //Persistent connections stay open across transactions, until
1962  //either the client or the server decides to close them
1963  break;
1964  }
1965  else
1966  {
1967  //The connection will be closed after completion of the response
1969  }
1970  }
1971  else
1972  {
1973  //Invalid HTTP request state
1974  error = ERROR_WRONG_STATE;
1975  }
1976  }
1977  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
1978  {
1979  //Shutdown connection
1980  error = httpClientDisconnect(context);
1981  }
1982  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
1983  {
1984  //Rewind to the beginning of the buffer
1985  context->bufferPos = 0;
1986  //We are done
1987  break;
1988  }
1989  else
1990  {
1991  //Invalid HTTP connection state
1992  error = ERROR_WRONG_STATE;
1993  }
1994  }
1995 
1996  //Check status code
1997  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1998  {
1999  //Check whether the timeout has elapsed
2000  error = httpClientCheckTimeout(context);
2001  }
2002 
2003  //Return status code
2004  return error;
2005 }
2006 
2007 
2008 /**
2009  * @brief Close HTTP request or response body
2010  * @param[in] context Pointer to the HTTP client context
2011  * @return Error code
2012  **/
2013 
2015 {
2016  error_t error;
2017  size_t n;
2018 
2019  //Make sure the HTTP client context is valid
2020  if(context == NULL)
2021  return ERROR_INVALID_PARAMETER;
2022 
2023  //Initialize status code
2024  error = NO_ERROR;
2025 
2026  //Close HTTP request or response body
2027  while(!error)
2028  {
2029  //Check HTTP connection state
2030  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
2031  {
2032  //Check HTTP request state
2033  if(context->requestState == HTTP_REQ_STATE_SEND_BODY)
2034  {
2035  //Chunked transfer encoding?
2036  if(context->chunkedEncoding)
2037  {
2038  //The chunked encoding is ended by any chunk whose size is zero
2039  error = httpClientFormatChunkSize(context, 0);
2040  }
2041  else
2042  {
2043  //Ensure the HTTP request body is complete
2044  if(context->bodyPos == context->bodyLen)
2045  {
2046  //Flush receive buffer
2047  context->bufferLen = 0;
2048  context->bufferPos = 0;
2049 
2050  //Receive the HTTP response header
2053  }
2054  else
2055  {
2056  //Incomplete request body
2057  error = ERROR_WRONG_STATE;
2058  }
2059  }
2060  }
2061  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
2062  {
2063  //Ensure the chunk data is complete
2064  if(context->bodyPos == context->bodyLen)
2065  {
2066  //The chunked encoding is ended by any chunk whose size is zero
2067  error = httpClientFormatChunkSize(context, 0);
2068  }
2069  else
2070  {
2071  //Incomplete chunk data
2072  error = ERROR_WRONG_STATE;
2073  }
2074  }
2075  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
2076  context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
2077  {
2078  //The HTTP request body is closed
2079  break;
2080  }
2081  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
2082  context->requestState == HTTP_REQ_STATE_RECEIVE_BODY ||
2083  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE ||
2084  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
2085  {
2086  //Consume HTTP response body
2087  error = httpClientReadBody(context, context->buffer,
2088  HTTP_CLIENT_BUFFER_SIZE, &n, 0);
2089 
2090  //Check whether the end of the response body has been reached
2091  if(error == ERROR_END_OF_STREAM)
2092  {
2093  //Continue reading the optional trailer
2094  error = NO_ERROR;
2095  }
2096  }
2097  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
2098  {
2099  //Consume HTTP trailer
2100  error = httpClientReadTrailer(context);
2101  }
2102  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
2103  context->requestState == HTTP_REQ_STATE_COMPLETE)
2104  {
2105  //The HTTP response body is closed
2106  break;
2107  }
2108  else
2109  {
2110  //Invalid HTTP request state
2111  error = ERROR_WRONG_STATE;
2112  }
2113  }
2114  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
2115  {
2116  //Shutdown connection
2117  error = httpClientDisconnect(context);
2118  }
2119  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
2120  {
2121  //Rewind to the beginning of the buffer
2122  context->bufferPos = 0;
2123  //We are done
2124  break;
2125  }
2126  else
2127  {
2128  //Invalid HTTP connection state
2129  error = ERROR_WRONG_STATE;
2130  }
2131  }
2132 
2133  //Check status code
2134  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2135  {
2136  //Check whether the timeout has elapsed
2137  error = httpClientCheckTimeout(context);
2138  }
2139 
2140  //Return status code
2141  return error;
2142 }
2143 
2144 
2145 /**
2146  * @brief Gracefully disconnect from the HTTP server
2147  * @param[in] context Pointer to the HTTP client context
2148  * @return Error code
2149  **/
2150 
2152 {
2153  error_t error;
2154 
2155  //Make sure the HTTP client context is valid
2156  if(context == NULL)
2157  return ERROR_INVALID_PARAMETER;
2158 
2159  //Initialize status code
2160  error = NO_ERROR;
2161 
2162  //Execute HTTP command sequence
2163  while(!error)
2164  {
2165  //Check HTTP connection state
2166  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
2167  {
2168  //Gracefully disconnect from the HTTP server
2170  }
2171  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
2172  {
2173  //Shutdown connection
2174  error = httpClientShutdownConnection(context);
2175 
2176  //Check status code
2177  if(error == NO_ERROR)
2178  {
2179  //Close connection
2180  httpClientCloseConnection(context);
2181  //Update HTTP connection state
2183  }
2184  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2185  {
2186  //Check whether the timeout has elapsed
2187  error = httpClientCheckTimeout(context);
2188  }
2189  else
2190  {
2191  //A communication error has occurred
2192  }
2193  }
2194  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
2195  {
2196  //We are done
2197  break;
2198  }
2199  else
2200  {
2201  //Invalid state
2202  error = ERROR_WRONG_STATE;
2203  }
2204  }
2205 
2206  //Failed to gracefully disconnect from the HTTP server?
2207  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
2208  {
2209  //Close connection
2210  httpClientCloseConnection(context);
2211  //Update HTTP connection state
2213  }
2214 
2215  //Return status code
2216  return error;
2217 }
2218 
2219 
2220 /**
2221  * @brief Close the connection with the HTTP server
2222  * @param[in] context Pointer to the HTTP client context
2223  * @return Error code
2224  **/
2225 
2227 {
2228  //Make sure the HTTP client context is valid
2229  if(context == NULL)
2230  return ERROR_INVALID_PARAMETER;
2231 
2232  //Close connection
2233  httpClientCloseConnection(context);
2234  //Update HTTP connection state
2236 
2237  //Successful processing
2238  return NO_ERROR;
2239 }
2240 
2241 
2242 /**
2243  * @brief Release HTTP client context
2244  * @param[in] context Pointer to the HTTP client context
2245  **/
2246 
2248 {
2249  //Make sure the HTTP client context is valid
2250  if(context != NULL)
2251  {
2252  //Close connection
2253  httpClientCloseConnection(context);
2254 
2255 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
2256  //Release TLS session state
2257  tlsFreeSessionState(&context->tlsSession);
2258 #endif
2259 
2260  //Clear HTTP client context
2261  osMemset(context, 0, sizeof(HttpClientContext));
2262  }
2263 }
2264 
2265 #endif
@ HTTP_REQ_STATE_SEND_TRAILER
Definition: http_common.h:119
#define osStrchr(s, c)
Definition: os_port.h:198
#define HTTP_CLIENT_MAX_PASSWORD_LEN
Definition: http_client.h:131
error_t httpClientDisconnect(HttpClientContext *context)
Gracefully disconnect from the HTTP server.
Definition: http_client.c:2151
String manipulation helper functions.
error_t httpClientCloseBody(HttpClientContext *context)
Close HTTP request or response body.
Definition: http_client.c:2014
Transport protocol abstraction layer.
@ HTTP_REQ_STATE_SEND_BODY
Definition: http_common.h:115
error_t httpClientBindToInterface(HttpClientContext *context, NetInterface *interface)
Bind the HTTP client to a particular network interface.
Definition: http_client.c:246
error_t httpClientOpenConnection(HttpClientContext *context)
Open network connection.
error_t httpClientSetUri(HttpClientContext *context, const char_t *uri)
Set request URI.
Definition: http_client.c:462
@ ERROR_WOULD_BLOCK
Definition: error.h:96
IP network address.
Definition: ip.h:90
@ ERROR_BUFFER_OVERFLOW
Definition: error.h:143
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
error_t httpClientReadBody(HttpClientContext *context, void *data, size_t size, size_t *received, uint_t flags)
Read HTTP response body.
Definition: http_client.c:1648
HttpVersion
HTTP version numbers.
Definition: http_common.h:60
uint8_t p
Definition: ndp.h:300
#define HTTP_CLIENT_DEFAULT_TIMEOUT
Definition: http_client.h:89
error_t(* HttpClientTlsInitCallback)(HttpClientContext *context, TlsContext *tlsContext)
TLS initialization callback function.
Definition: http_client.h:226
@ HTTP_REQ_STATE_FORMAT_HEADER
Definition: http_common.h:112
error_t httpClientSetAuthInfo(HttpClientContext *context, const char_t *username, const char_t *password)
Set authentication information.
Definition: http_client.c:209
uint8_t data[]
Definition: ethernet.h:224
error_t httpClientSendData(HttpClientContext *context, const void *data, size_t length, size_t *written, uint_t flags)
Send data using the relevant transport protocol.
error_t httpClientFormatChunkSize(HttpClientContext *context, size_t length)
Format chunk-size field.
error_t httpClientParseTransferEncodingField(HttpClientContext *context, const char_t *value)
Parse Transfer-Encoding header field.
#define HTTP_CLIENT_MAX_METHOD_LEN
Definition: http_client.h:117
error_t httpClientParseConnectionField(HttpClientContext *context, const char_t *value)
Parse Connection header field.
char_t name[]
uint16_t totalLength
Definition: ipv4.h:323
#define osStrlen(s)
Definition: os_port.h:168
uint8_t version
Definition: coap_common.h:177
@ ERROR_END_OF_STREAM
Definition: error.h:211
error_t httpClientSetContentLength(HttpClientContext *context, size_t length)
Set the length of the HTTP request body.
Definition: http_client.c:987
@ ERROR_INVALID_VERSION
Definition: error.h:118
error_t httpClientCreateRequest(HttpClientContext *context)
Create a new HTTP request.
Definition: http_client.c:365
void tlsFreeSessionState(TlsSessionState *session)
Properly dispose a session state.
Definition: tls.c:2883
error_t httpClientParseHeaderField(HttpClientContext *context, char_t *line, size_t length)
Parse HTTP response header field.
error_t httpClientWriteTrailer(HttpClientContext *context)
Write HTTP trailer.
Definition: http_client.c:1295
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
error_t httpClientSetVersion(HttpClientContext *context, HttpVersion version)
Set the HTTP protocol version to be used.
Definition: http_client.c:162
@ ERROR_WRONG_STATE
Definition: error.h:210
void httpClientDeinit(HttpClientContext *context)
Release HTTP client context.
Definition: http_client.c:2247
error_t httpClientRegisterRandCallback(HttpClientContext *context, HttpClientRandCallback callback)
Register random data generation callback function.
Definition: http_client.c:135
@ HTTP_REQ_STATE_PARSE_TRAILER
Definition: http_common.h:128
@ HTTP_VERSION_1_0
Definition: http_common.h:62
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
#define HttpClientContext
Definition: http_client.h:198
error_t httpClientWriteHeader(HttpClientContext *context)
Write HTTP request header.
Definition: http_client.c:1014
@ HTTP_FLAG_BREAK_CHAR
Definition: http_common.h:98
error_t
Error codes.
Definition: error.h:43
error_t httpClientFormatHeaderField(HttpClientContext *context, const char_t *name, const char_t *format,...)
Format an HTTP header field.
Definition: http_client.c:890
#define osSprintf(dest,...)
Definition: os_port.h:234
void httpClientChangeRequestState(HttpClientContext *context, HttpRequestState newState)
Update HTTP request state.
error_t httpClientSetQueryString(HttpClientContext *context, const char_t *queryString)
Set query string.
Definition: http_client.c:594
void httpClientCloseConnection(HttpClientContext *context)
Close network connection.
error_t httpClientRegisterTlsInitCallback(HttpClientContext *context, HttpClientTlsInitCallback callback)
Register TLS initialization callback function.
Definition: http_client.c:111
#define osVsnprintf(dest, size, format, ap)
Definition: os_port.h:246
const char_t * httpClientGetHeaderField(HttpClientContext *context, const char_t *name)
Retrieve the value of the specified header field name.
Definition: http_client.c:1541
error_t httpClientParseContentLengthField(HttpClientContext *context, const char_t *value)
Parse Content-Length header field.
@ HTTP_REQ_STATE_RECEIVE_BODY
Definition: http_common.h:123
#define NetInterface
Definition: net.h:36
error_t httpClientSetTimeout(HttpClientContext *context, systime_t timeout)
Set communication timeout.
Definition: http_client.c:187
error_t httpClientClose(HttpClientContext *context)
Close the connection with the HTTP server.
Definition: http_client.c:2226
@ HTTP_REQ_STATE_SEND_CHUNK_DATA
Definition: http_common.h:117
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ HTTP_REQ_STATE_RECEIVE_CHUNK_DATA
Definition: http_common.h:125
#define osStrcasecmp(s1, s2)
Definition: os_port.h:186
#define HTTP_STATUS_CODE_1YZ(code)
Definition: http_common.h:43
error_t httpClientSaveSession(HttpClientContext *context)
Save TLS session.
error_t httpClientReadTrailer(HttpClientContext *context)
Read HTTP trailer.
Definition: http_client.c:1886
@ HTTP_CLIENT_STATE_DISCONNECTING
Definition: http_client.h:215
@ HTTP_REQ_STATE_SEND_HEADER
Definition: http_common.h:113
error_t httpClientParseStatusLine(HttpClientContext *context, char_t *line, size_t length)
Parse HTTP status line.
uint8_t length
Definition: tcp.h:375
#define LSB(x)
Definition: os_port.h:55
error_t httpClientInit(HttpClientContext *context)
Initialize HTTP client context.
Definition: http_client.c:66
uint_t httpClientGetStatus(HttpClientContext *context)
Retrieve the HTTP status code of the response.
Definition: http_client.c:1514
error_t httpClientCheckTimeout(HttpClientContext *context)
Determine whether a timeout error has occurred.
#define MIN(a, b)
Definition: os_port.h:63
error_t httpClientShutdownConnection(HttpClientContext *context)
Shutdown network connection.
@ HTTP_REQ_STATE_RECEIVE_HEADER
Definition: http_common.h:121
error_t httpClientParseChunkSize(HttpClientContext *context, char_t *line, size_t length)
Parse chunk-size field.
@ HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE
Definition: http_common.h:124
HTTP client (HyperText Transfer Protocol)
error_t httpClientReadHeader(HttpClientContext *context)
Read HTTP response header.
Definition: http_client.c:1374
@ HTTP_REQ_STATE_PARSE_HEADER
Definition: http_common.h:122
@ HTTP_REQ_STATE_RECEIVE_TRAILER
Definition: http_common.h:127
error_t(* HttpClientRandCallback)(uint8_t *data, size_t length)
Random data generation callback function.
Definition: http_client.h:236
uint32_t systime_t
System time.
uint16_t port
Definition: dns_common.h:269
uint8_t flags
Definition: tcp.h:358
#define TRACE_DEBUG(...)
Definition: debug.h:119
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:55
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
#define HTTP_PORT
Definition: http_common.h:38
@ HTTP_FLAG_BREAK_CRLF
Definition: http_common.h:99
error_t httpClientConnect(HttpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish a connection with the specified HTTP server.
Definition: http_client.c:269
uint8_t m
Definition: ndp.h:304
uint8_t n
@ HTTP_VERSION_1_1
Definition: http_common.h:63
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:1137
#define HTTP_CLIENT_MAX_USERNAME_LEN
Definition: http_client.h:124
error_t httpClientFormatRequestHeader(HttpClientContext *context)
Format default HTTP request header.
@ HTTP_CLIENT_STATE_DISCONNECTED
Definition: http_client.h:212
uint8_t value[]
Definition: tcp.h:376
@ HTTP_CLIENT_STATE_CONNECTING
Definition: http_client.h:213
void httpClientInitAuthParams(HttpClientAuthParams *authParams)
Initialize HTTP authentication parameters.
@ HTTP_REQ_STATE_INIT
Definition: http_common.h:111
@ HTTP_FLAG_WAIT_ALL
Definition: http_common.h:97
@ ERROR_INVALID_SYNTAX
Definition: error.h:68
Helper functions for HTTP client.
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
@ HTTP_REQ_STATE_FORMAT_TRAILER
Definition: http_common.h:118
#define HTTP_CLIENT_BUFFER_SIZE
Definition: http_client.h:96
error_t httpClientEstablishConnection(HttpClientContext *context, const IpAddr *serverIpAddr, uint16_t serverPort)
Establish network connection.
error_t httpClientSetMethod(HttpClientContext *context, const char_t *method)
Set HTTP request method.
Definition: http_client.c:402
error_t httpClientFormatAuthorizationField(HttpClientContext *context)
Format Authorization header field.
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:57
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 httpClientGetNextHeaderField(HttpClientContext *context, const char_t **name, const char_t **value)
Iterate through the HTTP response header.
Definition: http_client.c:1600
#define osMemset(p, value, length)
Definition: os_port.h:138
TCP/IP stack core.
@ HTTP_REQ_STATE_RECEIVE_STATUS_LINE
Definition: http_common.h:120
@ HTTP_CLIENT_STATE_CONNECTED
Definition: http_client.h:214
@ HTTP_AUTH_MODE_NONE
Definition: http_common.h:73
error_t tlsInitSessionState(TlsSessionState *session)
Initialize session state.
Definition: tls.c:2740
#define osStrcpy(s1, s2)
Definition: os_port.h:210
void httpClientChangeState(HttpClientContext *context, HttpClientState newState)
Update HTTP client state.
@ HTTP_REQ_STATE_SEND_CHUNK_SIZE
Definition: http_common.h:116
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define osMemmove(dest, src, length)
Definition: os_port.h:150
HTTP authentication.
systime_t osGetSystemTime(void)
Retrieve system time.
@ HTTP_REQ_STATE_COMPLETE
Definition: http_common.h:129