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