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-2024 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.4.4
40  **/
41 
42 //Switch to the appropriate trace level
43 #define TRACE_LEVEL HTTP_TRACE_LEVEL
44 
45 //Dependencies
46 #include <limits.h>
47 #include <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  *written = totalLength;
1281 
1282  //Return status code
1283  return error;
1284 }
1285 
1286 
1287 /**
1288  * @brief Write HTTP trailer
1289  * @param[in] context Pointer to the HTTP client context
1290  * @return Error code
1291  **/
1292 
1294 {
1295  error_t error;
1296  size_t n;
1297 
1298  //Make sure the HTTP client context is valid
1299  if(context == NULL)
1300  return ERROR_INVALID_PARAMETER;
1301 
1302  //Check HTTP connection state
1303  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1304  return ERROR_WRONG_STATE;
1305 
1306  //Initialize status code
1307  error = NO_ERROR;
1308 
1309  //Send HTTP request header
1310  while(!error)
1311  {
1312  //Check HTTP request state
1313  if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER)
1314  {
1315  //Initiate the sending process
1317  }
1318  else if(context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1319  {
1320  //Any remaining data to be sent?
1321  if(context->bufferPos < context->bufferLen)
1322  {
1323  //Send more data
1324  error = httpClientSendData(context,
1325  context->buffer + context->bufferPos,
1326  context->bufferLen - context->bufferPos, &n, 0);
1327 
1328  //Check status code
1329  if(error == NO_ERROR || error == ERROR_TIMEOUT)
1330  {
1331  //Advance data pointer
1332  context->bufferPos += n;
1333  }
1334  }
1335  else
1336  {
1337  //The request trailer has been successfully transmitted
1340  }
1341  }
1342  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1343  {
1344  //We are done
1345  break;
1346  }
1347  else
1348  {
1349  //Invalid state
1350  error = ERROR_WRONG_STATE;
1351  }
1352  }
1353 
1354  //Check status code
1355  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1356  {
1357  //Check whether the timeout has elapsed
1358  error = httpClientCheckTimeout(context);
1359  }
1360 
1361  //Return status code
1362  return error;
1363 }
1364 
1365 
1366 /**
1367  * @brief Read HTTP response header
1368  * @param[in] context Pointer to the HTTP client context
1369  * @return Error code
1370  **/
1371 
1373 {
1374  error_t error;
1375  size_t n;
1376 
1377  //Make sure the HTTP client context is valid
1378  if(context == NULL)
1379  return ERROR_INVALID_PARAMETER;
1380 
1381  //Check HTTP connection state
1382  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1383  return ERROR_WRONG_STATE;
1384 
1385  //Initialize status code
1386  error = NO_ERROR;
1387 
1388  //Send HTTP request header
1389  while(!error)
1390  {
1391  //Check HTTP request state
1392  if(context->requestState == HTTP_REQ_STATE_SEND_BODY ||
1393  context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
1394  {
1395  //Close HTTP request body
1396  error = httpClientCloseBody(context);
1397  }
1398  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
1399  context->requestState == HTTP_REQ_STATE_SEND_TRAILER)
1400  {
1401  //The last chunk is followed by an optional trailer
1402  error = httpClientWriteTrailer(context);
1403  }
1404  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE ||
1405  context->requestState == HTTP_REQ_STATE_RECEIVE_HEADER)
1406  {
1407  //The CRLF sequence is always used to terminate a line
1408  if(context->bufferLen >= (context->bufferPos + 2) &&
1409  context->buffer[context->bufferLen - 2] == '\r' &&
1410  context->buffer[context->bufferLen - 1] == '\n')
1411  {
1412  //Strip the CRLF at the end of the line
1413  context->bufferLen -= 2;
1414 
1415  //The first line of a response message is the Status-Line
1416  if(context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
1417  {
1418  //Thee Status-Line consists of the protocol version followed by
1419  //a numeric status code and its associated textual phrase
1420  error = httpClientParseStatusLine(context, context->buffer +
1421  context->bufferPos, context->bufferLen - context->bufferPos);
1422 
1423  //Valid syntax?
1424  if(!error)
1425  {
1426  //Receive response header fields
1428  }
1429  }
1430  else
1431  {
1432  //An empty line indicates the end of the header fields
1433  if(context->bufferLen == context->bufferPos)
1434  {
1435  //Save TLS session
1436  error = httpClientSaveSession(context);
1437 
1438  //Check status code
1439  if(!error)
1440  {
1441  //Debug message
1442  TRACE_DEBUG("\r\n");
1443 
1444  //The HTTP response header has been successfully received
1446  }
1447  }
1448  else
1449  {
1450  //The response header fields allow the server to pass additional
1451  //information about the response which cannot be placed in the
1452  //Status-Line
1453  error = httpClientParseHeaderField(context, context->buffer +
1454  context->bufferPos, context->bufferLen - context->bufferPos);
1455  }
1456  }
1457  }
1458  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1459  {
1460  //Receive more data
1461  error = httpClientReceiveData(context, context->buffer +
1462  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1464 
1465  //Check status code
1466  if(!error)
1467  {
1468  //Adjust the length of the buffer
1469  context->bufferLen += n;
1470  //Save current time
1471  context->timestamp = osGetSystemTime();
1472  }
1473  }
1474  else
1475  {
1476  //The client implementation limits the size of headers it accepts
1477  error = ERROR_BUFFER_OVERFLOW;
1478  }
1479  }
1480  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1481  {
1482  //Rewind to the beginning of the buffer
1483  context->bufferPos = 0;
1484  //We are done
1485  break;
1486  }
1487  else
1488  {
1489  //Invalid state
1490  error = ERROR_WRONG_STATE;
1491  }
1492  }
1493 
1494  //Check status code
1495  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1496  {
1497  //Check whether the timeout has elapsed
1498  error = httpClientCheckTimeout(context);
1499  }
1500 
1501  //Return status code
1502  return error;
1503 }
1504 
1505 
1506 /**
1507  * @brief Retrieve the HTTP status code of the response
1508  * @param[in] context Pointer to the HTTP client context
1509  * @return HTTP status code
1510  **/
1511 
1513 {
1514  uint_t status;
1515 
1516  //Initialize status code
1517  status = 0;
1518 
1519  //Make sure the HTTP client context is valid
1520  if(context != NULL)
1521  {
1522  //The status code is a three-digit integer code giving the result
1523  //of the attempt to understand and satisfy the request
1524  status = context->statusCode;
1525  }
1526 
1527  //Return HTTP status code
1528  return status;
1529 }
1530 
1531 
1532 /**
1533  * @brief Retrieve the value of the specified header field name
1534  * @param[in] context Pointer to the HTTP client context
1535  * @param[in] name NULL-terminated string that specifies the header field name
1536  * @return Value of the header field
1537  **/
1538 
1540  const char_t *name)
1541 {
1542  size_t i;
1543  size_t nameLen;
1544  size_t valueLen;
1545  const char_t *value;
1546 
1547  //Initialize field value
1548  value = NULL;
1549 
1550  //Check parameters
1551  if(context != NULL && name != NULL)
1552  {
1553  //Check HTTP request state
1554  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
1555  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER)
1556  {
1557  //Point to the first header field of the response
1558  i = 0;
1559 
1560  //Parse HTTP response header
1561  while(i < context->bufferLen)
1562  {
1563  //Calculate the length of the field name
1564  nameLen = osStrlen(context->buffer + i);
1565  //Calculate the length of the field value
1566  valueLen = osStrlen(context->buffer + i + nameLen + 1);
1567 
1568  //Check whether the current header field matches the specified name
1569  if(osStrcasecmp(context->buffer + i, name) == 0)
1570  {
1571  //Retrieve the value of the header field
1572  value = context->buffer + i + nameLen + 1;
1573  //Exit immediately
1574  break;
1575  }
1576 
1577  //Point to next header field
1578  i += nameLen + valueLen + 2;
1579  }
1580  }
1581  }
1582 
1583  //Return the value of the header field
1584  return value;
1585 }
1586 
1587 
1588 /**
1589  * @brief Iterate through the HTTP response header
1590  * @param[in] context Pointer to the HTTP client context
1591  * @param[out] name NULL-terminated string that contains the name of the next
1592  * header field
1593  * @param[out] value NULL-terminated string that contains the value of the next
1594  * header field
1595  * @return Error code
1596  **/
1597 
1599  const char_t **name, const char_t **value)
1600 {
1601  size_t nameLen;
1602  size_t valueLen;
1603 
1604  //Check parameters
1605  if(context == NULL || name == NULL || value == NULL)
1606  return ERROR_INVALID_PARAMETER;
1607 
1608  //Check HTTP request state
1609  if(context->requestState != HTTP_REQ_STATE_PARSE_HEADER &&
1610  context->requestState != HTTP_REQ_STATE_PARSE_TRAILER)
1611  {
1612  return ERROR_WRONG_STATE;
1613  }
1614 
1615  //Check whether the end of the HTTP response header has been reached
1616  if(context->bufferPos >= context->bufferLen)
1617  return ERROR_END_OF_STREAM;
1618 
1619  //Calculate the length of the field name
1620  nameLen = osStrlen(context->buffer + context->bufferPos);
1621  //Calculate the length of the field value
1622  valueLen = osStrlen(context->buffer + context->bufferPos + nameLen + 1);
1623 
1624  //Return the name and the value of the header field
1625  *name = context->buffer + context->bufferPos;
1626  *value = context->buffer + context->bufferPos + nameLen + 1;
1627 
1628  //Point to the next header field
1629  context->bufferPos += nameLen + valueLen + 2;
1630 
1631  //Successful processing
1632  return NO_ERROR;
1633 }
1634 
1635 
1636 /**
1637  * @brief Read HTTP response body
1638  * @param[in] context Pointer to the HTTP client context
1639  * @param[out] data Buffer where to store the incoming data
1640  * @param[in] size Maximum number of bytes that can be received
1641  * @param[out] received Number of bytes that have been received
1642  * @param[in] flags Set of flags that influences the behavior of this function
1643  * @return Error code
1644  **/
1645 
1647  size_t size, size_t *received, uint_t flags)
1648 {
1649  error_t error;
1650  size_t n;
1651  char_t *p;
1652 
1653  //Check parameters
1654  if(context == NULL || data == NULL || received == NULL)
1655  return ERROR_INVALID_PARAMETER;
1656 
1657  //Check HTTP connection state
1658  if(context->state != HTTP_CLIENT_STATE_CONNECTED)
1659  return ERROR_WRONG_STATE;
1660 
1661  //Initialize status code
1662  error = NO_ERROR;
1663 
1664  //Point to the output buffer
1665  p = data;
1666  //No data has been read yet
1667  *received = 0;
1668 
1669  //Read as much data as possible
1670  while(*received < size && !error)
1671  {
1672  //Check HTTP request state
1673  if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER)
1674  {
1675  //Flush receive buffer
1676  context->bufferLen = 0;
1677  context->bufferPos = 0;
1678 
1679  //Any response to a HEAD request and any response with a 1xx, 204 or
1680  //304 status code is always terminated by the first empty line after
1681  //the header fields, regardless of the header fields present in the
1682  //message, and thus cannot contain a message body
1683  if(osStrcasecmp(context->method, "HEAD") == 0 ||
1684  HTTP_STATUS_CODE_1YZ(context->statusCode) ||
1685  context->statusCode == 204 ||
1686  context->statusCode == 304)
1687  {
1688  //The HTTP response does not contain a body
1690  }
1691  else
1692  {
1693  //The HTTP response contains a body
1695  }
1696  }
1697  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_BODY)
1698  {
1699  //Chunked transfer encoding?
1700  if(context->chunkedEncoding)
1701  {
1702  //The chunked encoding modifies the body in order to transfer it
1703  //as a series of chunks, each with its own size indicator
1705  }
1706  else
1707  {
1708  //The length of the body is determined by the Content-Length field
1709  if(context->bodyPos < context->bodyLen)
1710  {
1711  //Limit the number of bytes to read
1712  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1713 
1714  //Read response body
1715  error = httpClientReceiveData(context, p, n, &n, flags);
1716 
1717  //Check status code
1718  if(!error)
1719  {
1720  //Advance data pointer
1721  p += n;
1722  *received += n;
1723  context->bodyPos += n;
1724 
1725  //Save current time
1726  context->timestamp = osGetSystemTime();
1727 
1728  //We are done
1729  break;
1730  }
1731  else
1732  {
1733  //In practice, many widely deployed HTTPS servers close connections
1734  //abruptly, without any prior notice, and in particular without
1735  //sending the close_notify alert
1736  if(!context->keepAlive && context->bodyLen == UINT_MAX)
1737  {
1738  //The HTTP transaction is complete
1740  //Close the HTTP connection
1742  //The end of the response body has been reached
1743  error = ERROR_END_OF_STREAM;
1744  }
1745  }
1746  }
1747  else
1748  {
1749  //The HTTP transaction is complete
1751  //The end of the response body has been reached
1752  error = ERROR_END_OF_STREAM;
1753  }
1754  }
1755  }
1756  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE)
1757  {
1758  //The chunked encoding modifies the body in order to transfer it as
1759  //a series of chunks, each with its own size indicator
1760  if(context->bufferLen >= 3 &&
1761  context->buffer[context->bufferLen - 2] == '\r' &&
1762  context->buffer[context->bufferLen - 1] == '\n')
1763  {
1764  //Parse chunk-size field
1765  error = httpClientParseChunkSize(context, context->buffer,
1766  context->bufferLen);
1767  }
1768  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1769  {
1770  //Receive more data
1771  error = httpClientReceiveData(context, context->buffer +
1772  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1774 
1775  //Check status code
1776  if(!error)
1777  {
1778  //Adjust the length of the buffer
1779  context->bufferLen += n;
1780  //Save current time
1781  context->timestamp = osGetSystemTime();
1782  }
1783  }
1784  else
1785  {
1786  //The client implementation limits the length of the chunk-size field
1787  error = ERROR_BUFFER_OVERFLOW;
1788  }
1789  }
1790  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
1791  {
1792  //The data stream is divided into a series of chunks
1793  if(context->bodyPos < context->bodyLen)
1794  {
1795  //The length of the data chunk is determined by the chunk-size field
1796  n = MIN(size - *received, context->bodyLen - context->bodyPos);
1797 
1798  //Read chunk data
1799  error = httpClientReceiveData(context, p, n, &n, flags);
1800 
1801  //Check status code
1802  if(!error)
1803  {
1804  //Total number of data that have been read
1805  *received += n;
1806  //Number of bytes left to process in the current chunk
1807  context->bodyPos += n;
1808 
1809  //Save current time
1810  context->timestamp = osGetSystemTime();
1811 
1812  //Check flags
1813  if((flags & HTTP_FLAG_BREAK_CHAR) != 0)
1814  {
1815  //The HTTP_FLAG_BREAK_CHAR flag causes the function to stop
1816  //reading data as soon as the specified break character is
1817  //encountered
1818  if(p[n - 1] == LSB(flags))
1819  break;
1820  }
1821  else if((flags & HTTP_FLAG_WAIT_ALL) == 0)
1822  {
1823  //The HTTP_FLAG_WAIT_ALL flag causes the function to return
1824  //only when the requested number of bytes have been read
1825  break;
1826  }
1827  else
1828  {
1829  //Just for sanity
1830  }
1831 
1832  //Advance data pointer
1833  p += n;
1834  }
1835  }
1836  else
1837  {
1838  //The chunked encoding modifies the body in order to transfer it
1839  //as a series of chunks, each with its own size indicator
1841  }
1842  }
1843  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER ||
1844  context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1845  context->requestState == HTTP_REQ_STATE_COMPLETE)
1846  {
1847  //The user must be satisfied with data already on hand
1848  if(*received > 0)
1849  {
1850  //Some data are pending in the receive buffer
1851  break;
1852  }
1853  else
1854  {
1855  //The end of the response body has been reached
1856  error = ERROR_END_OF_STREAM;
1857  }
1858  }
1859  else
1860  {
1861  //Invalid state
1862  error = ERROR_WRONG_STATE;
1863  }
1864  }
1865 
1866  //Check status code
1867  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1868  {
1869  //Check whether the timeout has elapsed
1870  error = httpClientCheckTimeout(context);
1871  }
1872 
1873  //Return status code
1874  return error;
1875 }
1876 
1877 
1878 /**
1879  * @brief Read HTTP trailer
1880  * @param[in] context Pointer to the HTTP client context
1881  * @return Error code
1882  **/
1883 
1885 {
1886  error_t error;
1887  size_t n;
1888 
1889  //Make sure the HTTP client context is valid
1890  if(context == NULL)
1891  return ERROR_INVALID_PARAMETER;
1892 
1893  //Initialize status code
1894  error = NO_ERROR;
1895 
1896  //Send HTTP request header
1897  while(!error)
1898  {
1899  //Check HTTP connection state
1900  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
1901  {
1902  //Check HTTP request state
1903  if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
1904  {
1905  //The CRLF sequence is always used to terminate a line
1906  if(context->bufferLen >= (context->bufferPos + 2) &&
1907  context->buffer[context->bufferLen - 2] == '\r' &&
1908  context->buffer[context->bufferLen - 1] == '\n')
1909  {
1910  //Strip the CRLF at the end of the line
1911  context->bufferLen -= 2;
1912 
1913  //An empty line indicates the end of the header fields
1914  if(context->bufferLen == context->bufferPos)
1915  {
1916  //The HTTP transaction is complete
1918  }
1919  else
1920  {
1921  //The response header fields allow the server to pass additional
1922  //information about the response which cannot be placed in the
1923  //Status-Line
1924  error = httpClientParseHeaderField(context, context->buffer +
1925  context->bufferPos, context->bufferLen - context->bufferPos);
1926  }
1927  }
1928  else if(context->bufferLen < HTTP_CLIENT_BUFFER_SIZE)
1929  {
1930  //Receive more data
1931  error = httpClientReceiveData(context, context->buffer +
1932  context->bufferLen, HTTP_CLIENT_BUFFER_SIZE - context->bufferLen,
1934 
1935  //Check status code
1936  if(!error)
1937  {
1938  //Adjust the length of the buffer
1939  context->bufferLen += n;
1940  //Save current time
1941  context->timestamp = osGetSystemTime();
1942  }
1943  }
1944  else
1945  {
1946  //The client implementation limits the size of headers it accepts
1947  error = ERROR_BUFFER_OVERFLOW;
1948  }
1949  }
1950  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
1951  context->requestState == HTTP_REQ_STATE_COMPLETE)
1952  {
1953  //Rewind to the beginning of the buffer
1954  context->bufferPos = 0;
1955 
1956  //Persistent HTTP connection?
1957  if(context->keepAlive)
1958  {
1959  //Persistent connections stay open across transactions, until
1960  //either the client or the server decides to close them
1961  break;
1962  }
1963  else
1964  {
1965  //The connection will be closed after completion of the response
1967  }
1968  }
1969  else
1970  {
1971  //Invalid HTTP request state
1972  error = ERROR_WRONG_STATE;
1973  }
1974  }
1975  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
1976  {
1977  //Shutdown connection
1978  error = httpClientDisconnect(context);
1979  }
1980  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
1981  {
1982  //Rewind to the beginning of the buffer
1983  context->bufferPos = 0;
1984  //We are done
1985  break;
1986  }
1987  else
1988  {
1989  //Invalid HTTP connection state
1990  error = ERROR_WRONG_STATE;
1991  }
1992  }
1993 
1994  //Check status code
1995  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
1996  {
1997  //Check whether the timeout has elapsed
1998  error = httpClientCheckTimeout(context);
1999  }
2000 
2001  //Return status code
2002  return error;
2003 }
2004 
2005 
2006 /**
2007  * @brief Close HTTP request or response body
2008  * @param[in] context Pointer to the HTTP client context
2009  * @return Error code
2010  **/
2011 
2013 {
2014  error_t error;
2015  size_t n;
2016 
2017  //Make sure the HTTP client context is valid
2018  if(context == NULL)
2019  return ERROR_INVALID_PARAMETER;
2020 
2021  //Initialize status code
2022  error = NO_ERROR;
2023 
2024  //Close HTTP request or response body
2025  while(!error)
2026  {
2027  //Check HTTP connection state
2028  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
2029  {
2030  //Check HTTP request state
2031  if(context->requestState == HTTP_REQ_STATE_SEND_BODY)
2032  {
2033  //Chunked transfer encoding?
2034  if(context->chunkedEncoding)
2035  {
2036  //The chunked encoding is ended by any chunk whose size is zero
2037  error = httpClientFormatChunkSize(context, 0);
2038  }
2039  else
2040  {
2041  //Ensure the HTTP request body is complete
2042  if(context->bodyPos == context->bodyLen)
2043  {
2044  //Flush receive buffer
2045  context->bufferLen = 0;
2046  context->bufferPos = 0;
2047 
2048  //Receive the HTTP response header
2051  }
2052  else
2053  {
2054  //Incomplete request body
2055  error = ERROR_WRONG_STATE;
2056  }
2057  }
2058  }
2059  else if(context->requestState == HTTP_REQ_STATE_SEND_CHUNK_DATA)
2060  {
2061  //Ensure the chunk data is complete
2062  if(context->bodyPos == context->bodyLen)
2063  {
2064  //The chunked encoding is ended by any chunk whose size is zero
2065  error = httpClientFormatChunkSize(context, 0);
2066  }
2067  else
2068  {
2069  //Incomplete chunk data
2070  error = ERROR_WRONG_STATE;
2071  }
2072  }
2073  else if(context->requestState == HTTP_REQ_STATE_FORMAT_TRAILER ||
2074  context->requestState == HTTP_REQ_STATE_RECEIVE_STATUS_LINE)
2075  {
2076  //The HTTP request body is closed
2077  break;
2078  }
2079  else if(context->requestState == HTTP_REQ_STATE_PARSE_HEADER ||
2080  context->requestState == HTTP_REQ_STATE_RECEIVE_BODY ||
2081  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_SIZE ||
2082  context->requestState == HTTP_REQ_STATE_RECEIVE_CHUNK_DATA)
2083  {
2084  //Consume HTTP response body
2085  error = httpClientReadBody(context, context->buffer,
2086  HTTP_CLIENT_BUFFER_SIZE, &n, 0);
2087 
2088  //Check whether the end of the response body has been reached
2089  if(error == ERROR_END_OF_STREAM)
2090  {
2091  //Continue reading the optional trailer
2092  error = NO_ERROR;
2093  }
2094  }
2095  else if(context->requestState == HTTP_REQ_STATE_RECEIVE_TRAILER)
2096  {
2097  //Consume HTTP trailer
2098  error = httpClientReadTrailer(context);
2099  }
2100  else if(context->requestState == HTTP_REQ_STATE_PARSE_TRAILER ||
2101  context->requestState == HTTP_REQ_STATE_COMPLETE)
2102  {
2103  //The HTTP response body is closed
2104  break;
2105  }
2106  else
2107  {
2108  //Invalid HTTP request state
2109  error = ERROR_WRONG_STATE;
2110  }
2111  }
2112  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
2113  {
2114  //Shutdown connection
2115  error = httpClientDisconnect(context);
2116  }
2117  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
2118  {
2119  //Rewind to the beginning of the buffer
2120  context->bufferPos = 0;
2121  //We are done
2122  break;
2123  }
2124  else
2125  {
2126  //Invalid HTTP connection state
2127  error = ERROR_WRONG_STATE;
2128  }
2129  }
2130 
2131  //Check status code
2132  if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2133  {
2134  //Check whether the timeout has elapsed
2135  error = httpClientCheckTimeout(context);
2136  }
2137 
2138  //Return status code
2139  return error;
2140 }
2141 
2142 
2143 /**
2144  * @brief Gracefully disconnect from the HTTP server
2145  * @param[in] context Pointer to the HTTP client context
2146  * @return Error code
2147  **/
2148 
2150 {
2151  error_t error;
2152 
2153  //Make sure the HTTP client context is valid
2154  if(context == NULL)
2155  return ERROR_INVALID_PARAMETER;
2156 
2157  //Initialize status code
2158  error = NO_ERROR;
2159 
2160  //Execute HTTP command sequence
2161  while(!error)
2162  {
2163  //Check HTTP connection state
2164  if(context->state == HTTP_CLIENT_STATE_CONNECTED)
2165  {
2166  //Gracefully disconnect from the HTTP server
2168  }
2169  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTING)
2170  {
2171  //Shutdown connection
2172  error = httpClientShutdownConnection(context);
2173 
2174  //Check status code
2175  if(error == NO_ERROR)
2176  {
2177  //Close connection
2178  httpClientCloseConnection(context);
2179  //Update HTTP connection state
2181  }
2182  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
2183  {
2184  //Check whether the timeout has elapsed
2185  error = httpClientCheckTimeout(context);
2186  }
2187  else
2188  {
2189  //A communication error has occurred
2190  }
2191  }
2192  else if(context->state == HTTP_CLIENT_STATE_DISCONNECTED)
2193  {
2194  //We are done
2195  break;
2196  }
2197  else
2198  {
2199  //Invalid state
2200  error = ERROR_WRONG_STATE;
2201  }
2202  }
2203 
2204  //Failed to gracefully disconnect from the HTTP server?
2205  if(error != NO_ERROR && error != ERROR_WOULD_BLOCK)
2206  {
2207  //Close connection
2208  httpClientCloseConnection(context);
2209  //Update HTTP connection state
2211  }
2212 
2213  //Return status code
2214  return error;
2215 }
2216 
2217 
2218 /**
2219  * @brief Close the connection with the HTTP server
2220  * @param[in] context Pointer to the HTTP client context
2221  * @return Error code
2222  **/
2223 
2225 {
2226  //Make sure the HTTP client context is valid
2227  if(context == NULL)
2228  return ERROR_INVALID_PARAMETER;
2229 
2230  //Close connection
2231  httpClientCloseConnection(context);
2232  //Update HTTP connection state
2234 
2235  //Successful processing
2236  return NO_ERROR;
2237 }
2238 
2239 
2240 /**
2241  * @brief Release HTTP client context
2242  * @param[in] context Pointer to the HTTP client context
2243  **/
2244 
2246 {
2247  //Make sure the HTTP client context is valid
2248  if(context != NULL)
2249  {
2250  //Close connection
2251  httpClientCloseConnection(context);
2252 
2253 #if (HTTP_CLIENT_TLS_SUPPORT == ENABLED)
2254  //Release TLS session state
2255  tlsFreeSessionState(&context->tlsSession);
2256 #endif
2257 
2258  //Clear HTTP client context
2259  osMemset(context, 0, sizeof(HttpClientContext));
2260  }
2261 }
2262 
2263 #endif
@ HTTP_REQ_STATE_SEND_TRAILER
Definition: http_common.h:119
#define osStrchr(s, c)
Definition: os_port.h:195
#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:2149
String manipulation helper functions.
error_t httpClientCloseBody(HttpClientContext *context)
Close HTTP request or response body.
Definition: http_client.c:2012
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:142
@ 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:1646
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:222
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:322
#define osStrlen(s)
Definition: os_port.h:165
uint8_t version
Definition: coap_common.h:177
@ ERROR_END_OF_STREAM
Definition: error.h:210
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:2753
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:1293
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:209
void httpClientDeinit(HttpClientContext *context)
Release HTTP client context.
Definition: http_client.c:2245
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:141
#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:231
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:243
const char_t * httpClientGetHeaderField(HttpClientContext *context, const char_t *name)
Retrieve the value of the specified header field name.
Definition: http_client.c:1539
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:2224
@ 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:183
#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:1884
@ 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:368
#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:1512
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:1372
@ 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:267
uint8_t flags
Definition: tcp.h:351
#define TRACE_DEBUG(...)
Definition: debug.h:107
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:48
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:369
@ 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:50
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:1598
#define osMemset(p, value, length)
Definition: os_port.h:135
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:2610
#define osStrcpy(s1, s2)
Definition: os_port.h:207
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:147
HTTP authentication.
systime_t osGetSystemTime(void)
Retrieve system time.
@ HTTP_REQ_STATE_COMPLETE
Definition: http_common.h:129