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