1 // Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 
16 #include <string.h>
17 
18 #include "esp_system.h"
19 #include "esp_log.h"
20 
21 #include "http_header.h"
22 #include "esp_transport.h"
23 #include "esp_transport_tcp.h"
24 #include "http_utils.h"
25 #include "http_auth.h"
26 #include "sdkconfig.h"
27 #include "esp_http_client.h"
28 #include "errno.h"
29 
30 #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS
31 #include "esp_transport_ssl.h"
32 #endif
33 
34 static const char *TAG = "HTTP_CLIENT";
35 
36 /**
37  * HTTP Buffer
38  */
39 typedef struct {
40     char *data;         /*!< The HTTP data received from the server */
41     int len;            /*!< The HTTP data len received from the server */
42     char *raw_data;     /*!< The HTTP data after decoding */
43     int raw_len;        /*!< The HTTP data len after decoding */
44     char *output_ptr;   /*!< The destination address of the data to be copied to after decoding */
45 } esp_http_buffer_t;
46 
47 /**
48  * private HTTP Data structure
49  */
50 typedef struct {
51     http_header_handle_t headers;       /*!< http header */
52     esp_http_buffer_t   *buffer;        /*!< data buffer as linked list */
53     int                 status_code;    /*!< status code (integer) */
54     int                 content_length; /*!< data length */
55     int                 chunk_length;   /*!< chunk length */
56     int                 data_offset;    /*!< offset to http data (Skip header) */
57     int                 data_process;   /*!< data processed */
58     int                 method;         /*!< http method */
59     bool                is_chunked;
60 } esp_http_data_t;
61 
62 typedef struct {
63     char                         *url;
64     char                         *scheme;
65     char                         *host;
66     int                          port;
67     char                         *username;
68     char                         *password;
69     char                         *path;
70     char                         *query;
71     char                         *cert_pem;
72     esp_http_client_method_t     method;
73     esp_http_client_auth_type_t  auth_type;
74     esp_http_client_transport_t  transport_type;
75     int                          max_store_header_size;
76 } connection_info_t;
77 
78 typedef enum {
79     HTTP_STATE_UNINIT = 0,
80     HTTP_STATE_INIT,
81     HTTP_STATE_CONNECTED,
82     HTTP_STATE_REQ_COMPLETE_HEADER,
83     HTTP_STATE_REQ_COMPLETE_DATA,
84     HTTP_STATE_RES_COMPLETE_HEADER,
85     HTTP_STATE_RES_COMPLETE_DATA,
86     HTTP_STATE_CLOSE
87 } esp_http_state_t;
88 /**
89  * HTTP client class
90  */
91 struct esp_http_client {
92     int                         redirect_counter;
93     int                         max_redirection_count;
94     int                         max_authorization_retries;
95     int                         process_again;
96     struct http_parser          *parser;
97     struct http_parser_settings *parser_settings;
98     esp_transport_list_handle_t     transport_list;
99     esp_transport_handle_t          transport;
100     esp_http_data_t                 *request;
101     esp_http_data_t                 *response;
102     void                        *user_data;
103     esp_http_auth_data_t        *auth_data;
104     char                        *post_data;
105     char                        *location;
106     char                        *auth_header;
107     char                        *current_header_key;
108     char                        *current_header_value;
109     int                         post_len;
110     connection_info_t           connection_info;
111     bool                        is_chunk_complete;
112     esp_http_state_t            state;
113     http_event_handle_cb        event_handler;
114     int                         timeout_ms;
115     int                         buffer_size_rx;
116     int                         buffer_size_tx;
117     bool                        disable_auto_redirect;
118     esp_http_client_event_t     event;
119     int                         data_written_index;
120     int                         data_write_left;
121     bool                        first_line_prepared;
122     int                         header_index;
123     bool                        is_async;
124     esp_transport_keep_alive_t  keep_alive_cfg;
125 };
126 
127 typedef struct esp_http_client esp_http_client_t;
128 
129 static esp_err_t _clear_connection_info(esp_http_client_handle_t client);
130 /**
131  * Default settings
132  */
133 #define DEFAULT_HTTP_PORT (80)
134 #define DEFAULT_HTTPS_PORT (443)
135 
136 #define ASYNC_TRANS_CONNECT_FAIL -1
137 #define ASYNC_TRANS_CONNECTING 0
138 #define ASYNC_TRANS_CONNECT_PASS 1
139 
140 static const char *DEFAULT_HTTP_USER_AGENT = "ESP32 HTTP Client/1.0";
141 static const char *DEFAULT_HTTP_PROTOCOL = "HTTP/1.1";
142 static const char *DEFAULT_HTTP_PATH = "/";
143 static const int DEFAULT_MAX_REDIRECT = 10;
144 static const int DEFAULT_MAX_AUTH_RETRIES = 10;
145 static const int DEFAULT_TIMEOUT_MS = 5000;
146 static const int DEFAULT_KEEP_ALIVE_IDLE = 5;
147 static const int DEFAULT_KEEP_ALIVE_INTERVAL= 5;
148 static const int DEFAULT_KEEP_ALIVE_COUNT= 3;
149 
150 static const char *HTTP_METHOD_MAPPING[] = {
151     "GET",
152     "POST",
153     "PUT",
154     "PATCH",
155     "DELETE",
156     "HEAD",
157     "NOTIFY",
158     "SUBSCRIBE",
159     "UNSUBSCRIBE",
160     "OPTIONS",
161     "COPY",
162     "MOVE",
163     "LOCK",
164     "UNLOCK",
165     "PROPFIND",
166     "PROPPATCH",
167     "MKCOL"
168 };
169 
170 static esp_err_t esp_http_client_request_send(esp_http_client_handle_t client, int write_len);
171 static esp_err_t esp_http_client_connect(esp_http_client_handle_t client);
172 static esp_err_t esp_http_client_send_post_data(esp_http_client_handle_t client);
173 
http_dispatch_event(esp_http_client_t * client,esp_http_client_event_id_t event_id,void * data,int len)174 static esp_err_t http_dispatch_event(esp_http_client_t *client, esp_http_client_event_id_t event_id, void *data, int len)
175 {
176     esp_http_client_event_t *event = &client->event;
177 
178     if (client->event_handler) {
179         event->event_id = event_id;
180         event->user_data = client->user_data;
181         event->data = data;
182         event->data_len = len;
183         return client->event_handler(event);
184     }
185     return ESP_OK;
186 }
187 
http_on_message_begin(http_parser * parser)188 static int http_on_message_begin(http_parser *parser)
189 {
190     esp_http_client_t *client = parser->data;
191     ESP_LOGD(TAG, "on_message_begin");
192 
193     client->response->is_chunked = false;
194     client->is_chunk_complete = false;
195     return 0;
196 }
197 
http_on_url(http_parser * parser,const char * at,size_t length)198 static int http_on_url(http_parser *parser, const char *at, size_t length)
199 {
200     ESP_LOGD(TAG, "http_on_url");
201     return 0;
202 }
203 
http_on_status(http_parser * parser,const char * at,size_t length)204 static int http_on_status(http_parser *parser, const char *at, size_t length)
205 {
206     return 0;
207 }
208 
http_on_header_field(http_parser * parser,const char * at,size_t length)209 static int http_on_header_field(http_parser *parser, const char *at, size_t length)
210 {
211     esp_http_client_t *client = parser->data;
212     http_utils_assign_string(&client->current_header_key, at, length);
213 
214     return 0;
215 }
216 
http_on_header_value(http_parser * parser,const char * at,size_t length)217 static int http_on_header_value(http_parser *parser, const char *at, size_t length)
218 {
219     esp_http_client_handle_t client = parser->data;
220     if (client->current_header_key == NULL) {
221         return 0;
222     }
223     if (strcasecmp(client->current_header_key, "Location") == 0) {
224         http_utils_assign_string(&client->location, at, length);
225     } else if (strcasecmp(client->current_header_key, "Transfer-Encoding") == 0
226                && memcmp(at, "chunked", length) == 0) {
227         client->response->is_chunked = true;
228     } else if (strcasecmp(client->current_header_key, "WWW-Authenticate") == 0) {
229         http_utils_assign_string(&client->auth_header, at, length);
230     }
231     http_utils_assign_string(&client->current_header_value, at, length);
232 
233     ESP_LOGD(TAG, "HEADER=%s:%s", client->current_header_key, client->current_header_value);
234     client->event.header_key = client->current_header_key;
235     client->event.header_value = client->current_header_value;
236     http_dispatch_event(client, HTTP_EVENT_ON_HEADER, NULL, 0);
237     free(client->current_header_key);
238     free(client->current_header_value);
239     client->current_header_key = NULL;
240     client->current_header_value = NULL;
241     return 0;
242 }
243 
http_on_headers_complete(http_parser * parser)244 static int http_on_headers_complete(http_parser *parser)
245 {
246     esp_http_client_handle_t client = parser->data;
247     client->response->status_code = parser->status_code;
248     client->response->data_offset = parser->nread;
249     client->response->content_length = parser->content_length;
250     client->response->data_process = 0;
251     ESP_LOGD(TAG, "http_on_headers_complete, status=%d, offset=%d, nread=%d", parser->status_code, client->response->data_offset, parser->nread);
252     client->state = HTTP_STATE_RES_COMPLETE_HEADER;
253     return 0;
254 }
255 
http_on_body(http_parser * parser,const char * at,size_t length)256 static int http_on_body(http_parser *parser, const char *at, size_t length)
257 {
258     esp_http_client_t *client = parser->data;
259     ESP_LOGD(TAG, "http_on_body %d", length);
260     client->response->buffer->raw_data = (char *)at;
261     if (client->response->buffer->output_ptr) {
262         memcpy(client->response->buffer->output_ptr, (char *)at, length);
263         client->response->buffer->output_ptr += length;
264     }
265 
266     client->response->data_process += length;
267     client->response->buffer->raw_len += length;
268     http_dispatch_event(client, HTTP_EVENT_ON_DATA, (void *)at, length);
269     return 0;
270 }
271 
http_on_message_complete(http_parser * parser)272 static int http_on_message_complete(http_parser *parser)
273 {
274     ESP_LOGD(TAG, "http_on_message_complete, parser=%x", (int)parser);
275     esp_http_client_handle_t client = parser->data;
276     client->is_chunk_complete = true;
277     return 0;
278 }
279 
http_on_chunk_complete(http_parser * parser)280 static int http_on_chunk_complete(http_parser *parser)
281 {
282     ESP_LOGD(TAG, "http_on_chunk_complete");
283     return 0;
284 }
285 
http_on_chunk_header(http_parser * parser)286 static int http_on_chunk_header(http_parser *parser)
287 {
288     esp_http_client_handle_t client = parser->data;
289     client->response->chunk_length = parser->content_length;
290     ESP_LOGD(TAG, "http_on_chunk_header, chunk_length");
291     return 0;
292 }
293 
esp_http_client_set_header(esp_http_client_handle_t client,const char * key,const char * value)294 esp_err_t esp_http_client_set_header(esp_http_client_handle_t client, const char *key, const char *value)
295 {
296     return http_header_set(client->request->headers, key, value);
297 }
298 
esp_http_client_get_header(esp_http_client_handle_t client,const char * key,char ** value)299 esp_err_t esp_http_client_get_header(esp_http_client_handle_t client, const char *key, char **value)
300 {
301     return http_header_get(client->request->headers, key, value);
302 }
303 
esp_http_client_delete_header(esp_http_client_handle_t client,const char * key)304 esp_err_t esp_http_client_delete_header(esp_http_client_handle_t client, const char *key)
305 {
306     return http_header_delete(client->request->headers, key);
307 }
308 
esp_http_client_get_username(esp_http_client_handle_t client,char ** value)309 esp_err_t esp_http_client_get_username(esp_http_client_handle_t client, char **value)
310 {
311     if (client == NULL || value == NULL) {
312         ESP_LOGE(TAG, "client or value must not be NULL");
313         return ESP_ERR_INVALID_ARG;
314     }
315     *value = client->connection_info.username;
316     return ESP_OK;
317 }
318 
esp_http_client_set_username(esp_http_client_handle_t client,const char * username)319 esp_err_t esp_http_client_set_username(esp_http_client_handle_t client, const char *username)
320 {
321     if (client == NULL) {
322         ESP_LOGE(TAG, "client must not be NULL");
323         return ESP_ERR_INVALID_ARG;
324     }
325     if (client->connection_info.username != NULL) {
326         free(client->connection_info.username);
327     }
328     client->connection_info.username = username ? strdup(username) : NULL;
329     return ESP_OK;
330 }
331 
esp_http_client_get_password(esp_http_client_handle_t client,char ** value)332 esp_err_t esp_http_client_get_password(esp_http_client_handle_t client, char **value)
333 {
334     if (client == NULL || value == NULL) {
335         ESP_LOGE(TAG, "client or value must not be NULL");
336         return ESP_ERR_INVALID_ARG;
337     }
338     *value = client->connection_info.password;
339     return ESP_OK;
340 }
341 
esp_http_client_set_password(esp_http_client_handle_t client,char * password)342 esp_err_t esp_http_client_set_password(esp_http_client_handle_t client, char *password)
343 {
344     if (client == NULL) {
345         ESP_LOGE(TAG, "client must not be NULL");
346         return ESP_ERR_INVALID_ARG;
347     }
348     if (client->connection_info.password != NULL) {
349         memset(client->connection_info.password, 0, strlen(client->connection_info.password));
350         free(client->connection_info.password);
351     }
352     client->connection_info.password = password ? strdup(password) : NULL;
353     return ESP_OK;
354 }
355 
esp_http_client_set_authtype(esp_http_client_handle_t client,esp_http_client_auth_type_t auth_type)356 esp_err_t esp_http_client_set_authtype(esp_http_client_handle_t client, esp_http_client_auth_type_t auth_type)
357 {
358     if (client == NULL) {
359         ESP_LOGE(TAG, "client must not be NULL");
360         return ESP_ERR_INVALID_ARG;
361     }
362     client->connection_info.auth_type = auth_type;
363     return ESP_OK;
364 }
365 
_set_config(esp_http_client_handle_t client,const esp_http_client_config_t * config)366 static esp_err_t _set_config(esp_http_client_handle_t client, const esp_http_client_config_t *config)
367 {
368     client->connection_info.method = config->method;
369     client->connection_info.port = config->port;
370     client->connection_info.auth_type = config->auth_type;
371     client->event_handler = config->event_handler;
372     client->timeout_ms = config->timeout_ms;
373     client->max_redirection_count = config->max_redirection_count;
374     client->max_authorization_retries = config->max_authorization_retries;
375     client->user_data = config->user_data;
376     client->buffer_size_rx = config->buffer_size;
377     client->buffer_size_tx = config->buffer_size_tx;
378     client->disable_auto_redirect = config->disable_auto_redirect;
379 
380     if (config->buffer_size == 0) {
381         client->buffer_size_rx = DEFAULT_HTTP_BUF_SIZE;
382     }
383 
384     if (config->buffer_size_tx == 0) {
385         client->buffer_size_tx = DEFAULT_HTTP_BUF_SIZE;
386     }
387 
388     if (client->max_redirection_count == 0) {
389         client->max_redirection_count = DEFAULT_MAX_REDIRECT;
390     }
391 
392     if (client->max_authorization_retries == 0) {
393         client->max_authorization_retries = DEFAULT_MAX_AUTH_RETRIES;
394     } else if (client->max_authorization_retries == -1) {
395         client->max_authorization_retries = 0;
396     }
397 
398     if (config->path) {
399         client->connection_info.path = strdup(config->path);
400     } else {
401         client->connection_info.path = strdup(DEFAULT_HTTP_PATH);
402     }
403     HTTP_MEM_CHECK(TAG, client->connection_info.path, {
404         return ESP_ERR_NO_MEM;
405     });
406 
407     if (config->host) {
408         client->connection_info.host = strdup(config->host);
409 
410         HTTP_MEM_CHECK(TAG, client->connection_info.host, {
411             _clear_connection_info(client);
412             return ESP_ERR_NO_MEM;
413         });
414     }
415 
416     if (config->query) {
417         client->connection_info.query = strdup(config->query);
418         HTTP_MEM_CHECK(TAG, client->connection_info.query, {
419             _clear_connection_info(client);
420             return ESP_ERR_NO_MEM;
421         });
422     }
423 
424     if (config->username) {
425         client->connection_info.username = strdup(config->username);
426         HTTP_MEM_CHECK(TAG, client->connection_info.username, {
427             _clear_connection_info(client);
428             return ESP_ERR_NO_MEM;
429         });
430     }
431 
432     if (config->password) {
433         client->connection_info.password = strdup(config->password);
434         HTTP_MEM_CHECK(TAG, client->connection_info.password, {
435             _clear_connection_info(client);
436             return ESP_ERR_NO_MEM;
437         });
438     }
439 
440     if (config->transport_type == HTTP_TRANSPORT_OVER_SSL) {
441         http_utils_assign_string(&client->connection_info.scheme, "https", -1);
442         if (client->connection_info.port == 0) {
443             client->connection_info.port = DEFAULT_HTTPS_PORT;
444         }
445     } else {
446         http_utils_assign_string(&client->connection_info.scheme, "http", -1);
447         if (client->connection_info.port == 0) {
448             client->connection_info.port = DEFAULT_HTTP_PORT;
449         }
450     }
451     if (client->timeout_ms == 0) {
452         client->timeout_ms = DEFAULT_TIMEOUT_MS;
453     }
454     if (config->is_async) {
455         client->is_async = true;
456     }
457 
458     return ESP_OK;
459 }
460 
_clear_connection_info(esp_http_client_handle_t client)461 static esp_err_t _clear_connection_info(esp_http_client_handle_t client)
462 {
463     free(client->connection_info.path);
464     free(client->connection_info.host);
465     free(client->connection_info.query);
466     free(client->connection_info.username);
467     if (client->connection_info.password) {
468         memset(client->connection_info.password, 0, strlen(client->connection_info.password));
469         free(client->connection_info.password);
470     }
471     free(client->connection_info.scheme);
472     free(client->connection_info.url);
473     memset(&client->connection_info, 0, sizeof(connection_info_t));
474     return ESP_OK;
475 }
476 
_clear_auth_data(esp_http_client_handle_t client)477 static esp_err_t _clear_auth_data(esp_http_client_handle_t client)
478 {
479     if (client->auth_data == NULL) {
480         return ESP_FAIL;
481     }
482 
483     free(client->auth_data->method);
484     free(client->auth_data->realm);
485     free(client->auth_data->algorithm);
486     free(client->auth_data->qop);
487     free(client->auth_data->nonce);
488     free(client->auth_data->opaque);
489     memset(client->auth_data, 0, sizeof(esp_http_auth_data_t));
490     return ESP_OK;
491 }
492 
esp_http_client_prepare(esp_http_client_handle_t client)493 static esp_err_t esp_http_client_prepare(esp_http_client_handle_t client)
494 {
495     client->process_again = 0;
496     client->response->data_process = 0;
497     client->first_line_prepared = false;
498     http_parser_init(client->parser, HTTP_RESPONSE);
499     if (client->connection_info.username) {
500         char *auth_response = NULL;
501 
502         if (client->connection_info.auth_type == HTTP_AUTH_TYPE_BASIC) {
503             auth_response = http_auth_basic(client->connection_info.username, client->connection_info.password);
504         } else if (client->connection_info.auth_type == HTTP_AUTH_TYPE_DIGEST && client->auth_data) {
505             client->auth_data->uri = client->connection_info.path;
506             client->auth_data->cnonce = ((uint64_t)esp_random() << 32) + esp_random();
507             auth_response = http_auth_digest(client->connection_info.username, client->connection_info.password, client->auth_data);
508             client->auth_data->nc ++;
509         }
510 
511         if (auth_response) {
512             ESP_LOGD(TAG, "auth_response=%s", auth_response);
513             esp_http_client_set_header(client, "Authorization", auth_response);
514             free(auth_response);
515         }
516     }
517     return ESP_OK;
518 }
519 
_get_host_header(char * host,int port)520 static char *_get_host_header(char *host, int port)
521 {
522     int err = 0;
523     char *host_name;
524     if (port != DEFAULT_HTTP_PORT && port != DEFAULT_HTTPS_PORT) {
525         err = asprintf(&host_name, "%s:%d", host, port);
526     } else {
527         err = asprintf(&host_name, "%s", host);
528     }
529     if (err == -1) {
530         return NULL;
531     }
532     return host_name;
533 }
534 
esp_http_client_init(const esp_http_client_config_t * config)535 esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *config)
536 {
537 
538     esp_http_client_handle_t client;
539     esp_transport_handle_t tcp = NULL;
540     char *host_name;
541     bool _success;
542 
543     _success = (
544                    (client                         = calloc(1, sizeof(esp_http_client_t)))           &&
545                    (client->parser                 = calloc(1, sizeof(struct http_parser)))          &&
546                    (client->parser_settings        = calloc(1, sizeof(struct http_parser_settings))) &&
547                    (client->auth_data              = calloc(1, sizeof(esp_http_auth_data_t)))        &&
548                    (client->request                = calloc(1, sizeof(esp_http_data_t)))             &&
549                    (client->request->headers       = http_header_init())                             &&
550                    (client->request->buffer        = calloc(1, sizeof(esp_http_buffer_t)))           &&
551                    (client->response               = calloc(1, sizeof(esp_http_data_t)))             &&
552                    (client->response->headers      = http_header_init())                             &&
553                    (client->response->buffer       = calloc(1, sizeof(esp_http_buffer_t)))
554                );
555 
556     if (!_success) {
557         ESP_LOGE(TAG, "Error allocate memory");
558         goto error;
559     }
560 
561     _success = (
562                    (client->transport_list = esp_transport_list_init()) &&
563                    (tcp = esp_transport_tcp_init()) &&
564                    (esp_transport_set_default_port(tcp, DEFAULT_HTTP_PORT) == ESP_OK) &&
565                    (esp_transport_list_add(client->transport_list, tcp, "http") == ESP_OK)
566                );
567     if (!_success) {
568         ESP_LOGE(TAG, "Error initialize transport");
569         goto error;
570     }
571     if (config->keep_alive_enable == true) {
572         client->keep_alive_cfg.keep_alive_enable = true;
573         client->keep_alive_cfg.keep_alive_idle = (config->keep_alive_idle == 0) ? DEFAULT_KEEP_ALIVE_IDLE : config->keep_alive_idle;
574         client->keep_alive_cfg.keep_alive_interval = (config->keep_alive_interval == 0) ? DEFAULT_KEEP_ALIVE_INTERVAL : config->keep_alive_interval;
575         client->keep_alive_cfg.keep_alive_count =  (config->keep_alive_count == 0) ? DEFAULT_KEEP_ALIVE_COUNT : config->keep_alive_count;
576         esp_transport_tcp_set_keep_alive(tcp, &client->keep_alive_cfg);
577     }
578 #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS
579     esp_transport_handle_t ssl = NULL;
580     _success = (
581                    (ssl = esp_transport_ssl_init()) &&
582                    (esp_transport_set_default_port(ssl, DEFAULT_HTTPS_PORT) == ESP_OK) &&
583                    (esp_transport_list_add(client->transport_list, ssl, "https") == ESP_OK)
584                );
585 
586     if (!_success) {
587         ESP_LOGE(TAG, "Error initialize SSL Transport");
588         goto error;
589     }
590 
591     if (config->use_global_ca_store == true) {
592         esp_transport_ssl_enable_global_ca_store(ssl);
593     } else if (config->cert_pem) {
594         esp_transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem));
595     }
596 
597     if (config->client_cert_pem) {
598         esp_transport_ssl_set_client_cert_data(ssl, config->client_cert_pem, strlen(config->client_cert_pem));
599     }
600 
601     if (config->client_key_pem) {
602         esp_transport_ssl_set_client_key_data(ssl, config->client_key_pem, strlen(config->client_key_pem));
603     }
604 
605     if (config->skip_cert_common_name_check) {
606         esp_transport_ssl_skip_common_name_check(ssl);
607     }
608 
609     if (config->keep_alive_enable == true) {
610         esp_transport_ssl_set_keep_alive(ssl, &client->keep_alive_cfg);
611     }
612 #endif
613 
614     if (_set_config(client, config) != ESP_OK) {
615         ESP_LOGE(TAG, "Error set configurations");
616         goto error;
617     }
618     _success = (
619                    (client->request->buffer->data  = malloc(client->buffer_size_tx))  &&
620                    (client->response->buffer->data = malloc(client->buffer_size_rx))
621                );
622 
623     if (!_success) {
624         ESP_LOGE(TAG, "Allocation failed");
625         goto error;
626     }
627 
628     const char *user_agent = config->user_agent == NULL ? DEFAULT_HTTP_USER_AGENT : config->user_agent;
629 
630     if (config->host != NULL && config->path != NULL) {
631         host_name = _get_host_header(client->connection_info.host, client->connection_info.port);
632         if (host_name == NULL) {
633             ESP_LOGE(TAG, "Failed to allocate memory for host header");
634             goto error;
635         }
636         _success = (
637             (esp_http_client_set_header(client, "User-Agent", user_agent) == ESP_OK) &&
638             (esp_http_client_set_header(client, "Host", host_name) == ESP_OK)
639         );
640         free(host_name);
641         if (!_success) {
642             ESP_LOGE(TAG, "Error while setting default configurations");
643             goto error;
644         }
645     } else if (config->url != NULL) {
646         if (esp_http_client_set_url(client, config->url) != ESP_OK) {
647             ESP_LOGE(TAG, "Failed to set URL");
648             goto error;
649         }
650         host_name = _get_host_header(client->connection_info.host, client->connection_info.port);
651         if (host_name == NULL) {
652             ESP_LOGE(TAG, "Failed to allocate memory for host header");
653             goto error;
654         }
655 
656         _success = (
657                     (esp_http_client_set_header(client, "User-Agent", user_agent) == ESP_OK) &&
658                     (esp_http_client_set_header(client, "Host", host_name) == ESP_OK)
659                 );
660 
661         free(host_name);
662         if (!_success) {
663             ESP_LOGE(TAG, "Error while setting default configurations");
664             goto error;
665         }
666     } else {
667         ESP_LOGE(TAG, "config should have either URL or host & path");
668         goto error;
669     }
670 
671     client->parser_settings->on_message_begin = http_on_message_begin;
672     client->parser_settings->on_url = http_on_url;
673     client->parser_settings->on_status = http_on_status;
674     client->parser_settings->on_header_field = http_on_header_field;
675     client->parser_settings->on_header_value = http_on_header_value;
676     client->parser_settings->on_headers_complete = http_on_headers_complete;
677     client->parser_settings->on_body = http_on_body;
678     client->parser_settings->on_message_complete = http_on_message_complete;
679     client->parser_settings->on_chunk_complete = http_on_chunk_complete;
680     client->parser_settings->on_chunk_header = http_on_chunk_header;
681     client->parser->data = client;
682     client->event.client = client;
683 
684     client->state = HTTP_STATE_INIT;
685     return client;
686 error:
687     esp_http_client_cleanup(client);
688     return NULL;
689 }
690 
esp_http_client_cleanup(esp_http_client_handle_t client)691 esp_err_t esp_http_client_cleanup(esp_http_client_handle_t client)
692 {
693     if (client == NULL) {
694         return ESP_FAIL;
695     }
696     esp_http_client_close(client);
697     esp_transport_list_destroy(client->transport_list);
698     if (client->request) {
699         http_header_destroy(client->request->headers);
700         if (client->request->buffer) {
701             free(client->request->buffer->data);
702         }
703         free(client->request->buffer);
704         free(client->request);
705     }
706     if (client->response) {
707         http_header_destroy(client->response->headers);
708         if (client->response->buffer) {
709             free(client->response->buffer->data);
710         }
711         free(client->response->buffer);
712         free(client->response);
713     }
714 
715     free(client->parser);
716     free(client->parser_settings);
717     _clear_connection_info(client);
718     _clear_auth_data(client);
719     free(client->auth_data);
720     free(client->current_header_key);
721     free(client->location);
722     free(client->auth_header);
723     free(client);
724     return ESP_OK;
725 }
726 
esp_http_client_set_redirection(esp_http_client_handle_t client)727 esp_err_t esp_http_client_set_redirection(esp_http_client_handle_t client)
728 {
729     if (client == NULL) {
730         return ESP_ERR_INVALID_ARG;
731     }
732     if (client->location == NULL) {
733         return ESP_ERR_INVALID_ARG;
734     }
735     ESP_LOGD(TAG, "Redirect to %s", client->location);
736     return esp_http_client_set_url(client, client->location);
737 }
738 
esp_http_check_response(esp_http_client_handle_t client)739 static esp_err_t esp_http_check_response(esp_http_client_handle_t client)
740 {
741     if (client->response->status_code >= HttpStatus_Ok && client->response->status_code < HttpStatus_MultipleChoices) {
742         return ESP_OK;
743     }
744     if (client->redirect_counter >= client->max_redirection_count || client->disable_auto_redirect) {
745         ESP_LOGE(TAG, "Error, reach max_redirection_count count=%d", client->redirect_counter);
746         return ESP_ERR_HTTP_MAX_REDIRECT;
747     }
748     switch (client->response->status_code) {
749         case HttpStatus_MovedPermanently:
750         case HttpStatus_Found:
751         case HttpStatus_TemporaryRedirect:
752             esp_http_client_set_redirection(client);
753             client->redirect_counter ++;
754             client->process_again = 1;
755             break;
756         case HttpStatus_Unauthorized:
757             esp_http_client_add_auth(client);
758     }
759     return ESP_OK;
760 }
761 
esp_http_client_set_url(esp_http_client_handle_t client,const char * url)762 esp_err_t esp_http_client_set_url(esp_http_client_handle_t client, const char *url)
763 {
764     char *old_host = NULL;
765     struct http_parser_url purl;
766     int old_port;
767 
768     if (client == NULL || url == NULL) {
769         ESP_LOGE(TAG, "client or url must not NULL");
770         return ESP_ERR_INVALID_ARG;
771     }
772 
773     http_parser_url_init(&purl);
774 
775     int parser_status = http_parser_parse_url(url, strlen(url), 0, &purl);
776 
777     if (parser_status != 0) {
778         ESP_LOGE(TAG, "Error parse url %s", url);
779         return ESP_ERR_INVALID_ARG;
780     }
781     if (client->connection_info.host) {
782         old_host = strdup(client->connection_info.host);
783     }
784     old_port = client->connection_info.port;
785 
786     if (purl.field_data[UF_HOST].len) {
787         http_utils_assign_string(&client->connection_info.host, url + purl.field_data[UF_HOST].off, purl.field_data[UF_HOST].len);
788         HTTP_MEM_CHECK(TAG, client->connection_info.host, {
789             free(old_host);
790             return ESP_ERR_NO_MEM;
791         });
792     }
793     // Close the connection if host was changed
794     if (old_host && client->connection_info.host
795             && strcasecmp(old_host, (const void *)client->connection_info.host) != 0) {
796         ESP_LOGD(TAG, "New host assign = %s", client->connection_info.host);
797         if (esp_http_client_set_header(client, "Host", client->connection_info.host) != ESP_OK) {
798             free(old_host);
799             return ESP_ERR_NO_MEM;
800         }
801         esp_http_client_close(client);
802     }
803 
804     if (old_host) {
805         free(old_host);
806         old_host = NULL;
807     }
808 
809     if (purl.field_data[UF_SCHEMA].len) {
810         http_utils_assign_string(&client->connection_info.scheme, url + purl.field_data[UF_SCHEMA].off, purl.field_data[UF_SCHEMA].len);
811         HTTP_MEM_CHECK(TAG, client->connection_info.scheme, return ESP_ERR_NO_MEM);
812 
813         if (strcasecmp(client->connection_info.scheme, "http") == 0) {
814             client->connection_info.port = DEFAULT_HTTP_PORT;
815         } else if (strcasecmp(client->connection_info.scheme, "https") == 0) {
816             client->connection_info.port = DEFAULT_HTTPS_PORT;
817         }
818     }
819 
820     if (purl.field_data[UF_PORT].len) {
821         client->connection_info.port = strtol((const char*)(url + purl.field_data[UF_PORT].off), NULL, 10);
822     }
823 
824     if (old_port != client->connection_info.port) {
825         esp_http_client_close(client);
826     }
827 
828     if (purl.field_data[UF_USERINFO].len) {
829         char *user_info = NULL;
830         http_utils_assign_string(&user_info, url + purl.field_data[UF_USERINFO].off, purl.field_data[UF_USERINFO].len);
831         if (user_info) {
832             char *username = user_info;
833             char *password = strchr(user_info, ':');
834             if (password) {
835                 *password = 0;
836                 password ++;
837                 http_utils_assign_string(&client->connection_info.password, password, -1);
838                 HTTP_MEM_CHECK(TAG, client->connection_info.password, return ESP_ERR_NO_MEM);
839             }
840             http_utils_assign_string(&client->connection_info.username, username, -1);
841             HTTP_MEM_CHECK(TAG, client->connection_info.username, return ESP_ERR_NO_MEM);
842             free(user_info);
843         } else {
844             return ESP_ERR_NO_MEM;
845         }
846     }
847 
848     //Reset path and query if there are no information
849     if (purl.field_data[UF_PATH].len) {
850         http_utils_assign_string(&client->connection_info.path, url + purl.field_data[UF_PATH].off, purl.field_data[UF_PATH].len);
851     } else {
852         http_utils_assign_string(&client->connection_info.path, "/", -1);
853     }
854     HTTP_MEM_CHECK(TAG, client->connection_info.path, return ESP_ERR_NO_MEM);
855 
856     if (purl.field_data[UF_QUERY].len) {
857         http_utils_assign_string(&client->connection_info.query, url + purl.field_data[UF_QUERY].off, purl.field_data[UF_QUERY].len);
858         HTTP_MEM_CHECK(TAG, client->connection_info.query, return ESP_ERR_NO_MEM);
859     } else if (client->connection_info.query) {
860         free(client->connection_info.query);
861         client->connection_info.query = NULL;
862     }
863 
864     return ESP_OK;
865 }
866 
esp_http_client_set_method(esp_http_client_handle_t client,esp_http_client_method_t method)867 esp_err_t esp_http_client_set_method(esp_http_client_handle_t client, esp_http_client_method_t method)
868 {
869     client->connection_info.method = method;
870     return ESP_OK;
871 }
872 
esp_http_client_get_data(esp_http_client_handle_t client)873 static int esp_http_client_get_data(esp_http_client_handle_t client)
874 {
875     if (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
876         return ESP_FAIL;
877     }
878 
879     if (client->connection_info.method == HTTP_METHOD_HEAD) {
880         return 0;
881     }
882 
883     esp_http_buffer_t *res_buffer = client->response->buffer;
884 
885     ESP_LOGD(TAG, "data_process=%d, content_length=%d", client->response->data_process, client->response->content_length);
886 
887     int rlen = esp_transport_read(client->transport, res_buffer->data, client->buffer_size_rx, client->timeout_ms);
888     if (rlen >= 0) {
889         http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen);
890     }
891     return rlen;
892 }
893 
esp_http_client_is_complete_data_received(esp_http_client_handle_t client)894 bool esp_http_client_is_complete_data_received(esp_http_client_handle_t client)
895 {
896     if (client->response->is_chunked) {
897         if (!client->is_chunk_complete) {
898             ESP_LOGD(TAG, "Chunks were not completely read");
899             return false;
900         }
901     } else {
902         if (client->response->data_process != client->response->content_length) {
903             ESP_LOGD(TAG, "Data processed %d != Data specified in content length %d", client->response->data_process, client->response->content_length);
904             return false;
905         }
906     }
907     return true;
908 }
909 
esp_http_client_read(esp_http_client_handle_t client,char * buffer,int len)910 int esp_http_client_read(esp_http_client_handle_t client, char *buffer, int len)
911 {
912     esp_http_buffer_t *res_buffer = client->response->buffer;
913 
914     int rlen = ESP_FAIL, ridx = 0;
915     if (res_buffer->raw_len) {
916         int remain_len = client->response->buffer->raw_len;
917         if (remain_len > len) {
918             remain_len = len;
919         }
920         memcpy(buffer, res_buffer->raw_data, remain_len);
921         res_buffer->raw_len -= remain_len;
922         res_buffer->raw_data += remain_len;
923         ridx = remain_len;
924     }
925     int need_read = len - ridx;
926     bool is_data_remain = true;
927     while (need_read > 0 && is_data_remain) {
928         if (client->response->is_chunked) {
929             is_data_remain = !client->is_chunk_complete;
930         } else {
931             is_data_remain = client->response->data_process < client->response->content_length;
932         }
933         ESP_LOGD(TAG, "is_data_remain=%d, is_chunked=%d, content_length=%d", is_data_remain, client->response->is_chunked, client->response->content_length);
934         if (!is_data_remain) {
935             break;
936         }
937         int byte_to_read = need_read;
938         if (byte_to_read > client->buffer_size_rx) {
939             byte_to_read = client->buffer_size_rx;
940         }
941         errno = 0;
942         rlen = esp_transport_read(client->transport, res_buffer->data, byte_to_read, client->timeout_ms);
943         ESP_LOGD(TAG, "need_read=%d, byte_to_read=%d, rlen=%d, ridx=%d", need_read, byte_to_read, rlen, ridx);
944 
945         if (rlen <= 0) {
946             if (errno != 0) {
947                 esp_log_level_t sev = ESP_LOG_WARN;
948                 /* On connection close from server, recv should ideally return 0 but we have error conversion
949                  * in `tcp_transport` SSL layer which translates it `-1` and hence below additional checks */
950                 if (rlen == -1 && errno == ENOTCONN && client->response->is_chunked) {
951                     /* Explicit call to parser for invoking `message_complete` callback */
952                     http_parser_execute(client->parser, client->parser_settings, res_buffer->data, 0);
953                     /* ...and lowering the message severity, as closed connection from server side is expected in chunked transport */
954                     sev = ESP_LOG_DEBUG;
955                 }
956                 ESP_LOG_LEVEL(sev, TAG, "esp_transport_read returned:%d and errno:%d ", rlen, errno);
957             }
958             if (rlen < 0 && ridx == 0 && !esp_http_client_is_complete_data_received(client)) {
959                 return ESP_FAIL;
960             } else {
961                 return ridx;
962             }
963         }
964         res_buffer->output_ptr = buffer + ridx;
965         http_parser_execute(client->parser, client->parser_settings, res_buffer->data, rlen);
966         ridx += res_buffer->raw_len;
967         need_read -= res_buffer->raw_len;
968 
969         res_buffer->raw_len = 0; //clear
970         res_buffer->output_ptr = NULL;
971     }
972 
973     return ridx;
974 }
975 
esp_http_client_perform(esp_http_client_handle_t client)976 esp_err_t esp_http_client_perform(esp_http_client_handle_t client)
977 {
978     esp_err_t err;
979     do {
980         if (client->process_again) {
981             esp_http_client_prepare(client);
982         }
983         switch (client->state) {
984         /* In case of blocking esp_http_client_perform(), the following states will fall through one after the after;
985            in case of non-blocking esp_http_client_perform(), if there is an error condition, like EINPROGRESS or EAGAIN,
986            then the esp_http_client_perform() API will return ESP_ERR_HTTP_EAGAIN error. The user may call
987            esp_http_client_perform API again, and for this reason, we maintain the states */
988             case HTTP_STATE_INIT:
989                 if ((err = esp_http_client_connect(client)) != ESP_OK) {
990                     if (client->is_async && err == ESP_ERR_HTTP_CONNECTING) {
991                         return ESP_ERR_HTTP_EAGAIN;
992                     }
993                     return err;
994                 }
995                 /* falls through */
996             case HTTP_STATE_CONNECTED:
997                 if ((err = esp_http_client_request_send(client, client->post_len)) != ESP_OK) {
998                     if (client->is_async && errno == EAGAIN) {
999                         return ESP_ERR_HTTP_EAGAIN;
1000                     }
1001                     return err;
1002                 }
1003                 /* falls through */
1004             case HTTP_STATE_REQ_COMPLETE_HEADER:
1005                 if ((err = esp_http_client_send_post_data(client)) != ESP_OK) {
1006                     if (client->is_async && errno == EAGAIN) {
1007                         return ESP_ERR_HTTP_EAGAIN;
1008                     }
1009                     return err;
1010                 }
1011                 /* falls through */
1012             case HTTP_STATE_REQ_COMPLETE_DATA:
1013                 if (esp_http_client_fetch_headers(client) < 0) {
1014                     if (client->is_async && errno == EAGAIN) {
1015                         return ESP_ERR_HTTP_EAGAIN;
1016                     }
1017                     return ESP_ERR_HTTP_FETCH_HEADER;
1018                 }
1019                 /* falls through */
1020             case HTTP_STATE_RES_COMPLETE_HEADER:
1021                 if ((err = esp_http_check_response(client)) != ESP_OK) {
1022                     ESP_LOGE(TAG, "Error response");
1023                     return err;
1024                 }
1025                 while (client->response->is_chunked && !client->is_chunk_complete) {
1026                     if (esp_http_client_get_data(client) <= 0) {
1027                         if (client->is_async && errno == EAGAIN) {
1028                             return ESP_ERR_HTTP_EAGAIN;
1029                         }
1030                         ESP_LOGD(TAG, "Read finish or server requests close");
1031                         break;
1032                     }
1033                 }
1034                 while (client->response->data_process < client->response->content_length) {
1035                     if (esp_http_client_get_data(client) <= 0) {
1036                         if (client->is_async && errno == EAGAIN) {
1037                             return ESP_ERR_HTTP_EAGAIN;
1038                         }
1039                         ESP_LOGD(TAG, "Read finish or server requests close");
1040                         break;
1041                     }
1042                 }
1043                 http_dispatch_event(client, HTTP_EVENT_ON_FINISH, NULL, 0);
1044 
1045                 client->response->buffer->raw_len = 0;
1046                 if (!http_should_keep_alive(client->parser)) {
1047                     ESP_LOGD(TAG, "Close connection");
1048                     esp_http_client_close(client);
1049                 } else {
1050                     if (client->state > HTTP_STATE_CONNECTED) {
1051                         client->state = HTTP_STATE_CONNECTED;
1052                         client->first_line_prepared = false;
1053                     }
1054                 }
1055                 break;
1056                 default:
1057                 break;
1058         }
1059     } while (client->process_again);
1060     return ESP_OK;
1061 }
1062 
esp_http_client_fetch_headers(esp_http_client_handle_t client)1063 int esp_http_client_fetch_headers(esp_http_client_handle_t client)
1064 {
1065     if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
1066         return ESP_FAIL;
1067     }
1068 
1069     client->state = HTTP_STATE_REQ_COMPLETE_DATA;
1070     esp_http_buffer_t *buffer = client->response->buffer;
1071     client->response->status_code = -1;
1072 
1073     while (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
1074         buffer->len = esp_transport_read(client->transport, buffer->data, client->buffer_size_rx, client->timeout_ms);
1075         if (buffer->len <= 0) {
1076             return ESP_FAIL;
1077         }
1078         http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len);
1079     }
1080     ESP_LOGD(TAG, "content_length = %d", client->response->content_length);
1081     if (client->response->content_length <= 0) {
1082         client->response->is_chunked = true;
1083         return 0;
1084     }
1085     return client->response->content_length;
1086 }
1087 
esp_http_client_connect(esp_http_client_handle_t client)1088 static esp_err_t esp_http_client_connect(esp_http_client_handle_t client)
1089 {
1090     esp_err_t err;
1091 
1092     if (client->state == HTTP_STATE_UNINIT) {
1093         ESP_LOGE(TAG, "Client has not been initialized");
1094         return ESP_ERR_INVALID_STATE;
1095     }
1096 
1097     if ((err = esp_http_client_prepare(client)) != ESP_OK) {
1098         ESP_LOGE(TAG, "Failed to initialize request data");
1099         esp_http_client_close(client);
1100         return err;
1101     }
1102 
1103     if (client->state < HTTP_STATE_CONNECTED) {
1104         ESP_LOGD(TAG, "Begin connect to: %s://%s:%d", client->connection_info.scheme, client->connection_info.host, client->connection_info.port);
1105         client->transport = esp_transport_list_get_transport(client->transport_list, client->connection_info.scheme);
1106         if (client->transport == NULL) {
1107             ESP_LOGE(TAG, "No transport found");
1108 #ifndef CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS
1109             if (strcasecmp(client->connection_info.scheme, "https") == 0) {
1110                 ESP_LOGE(TAG, "Please enable HTTPS at menuconfig to allow requesting via https");
1111             }
1112 #endif
1113             return ESP_ERR_HTTP_INVALID_TRANSPORT;
1114         }
1115         if (!client->is_async) {
1116             if (esp_transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) {
1117                 ESP_LOGE(TAG, "Connection failed, sock < 0");
1118                 return ESP_ERR_HTTP_CONNECT;
1119             }
1120         } else {
1121             int ret = esp_transport_connect_async(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms);
1122             if (ret == ASYNC_TRANS_CONNECT_FAIL) {
1123                 ESP_LOGE(TAG, "Connection failed");
1124                 if (strcasecmp(client->connection_info.scheme, "http") == 0) {
1125                     ESP_LOGE(TAG, "Asynchronous mode doesn't work for HTTP based connection");
1126                     return ESP_ERR_INVALID_ARG;
1127                 }
1128                 return ESP_ERR_HTTP_CONNECT;
1129             } else if (ret == ASYNC_TRANS_CONNECTING) {
1130                 ESP_LOGD(TAG, "Connection not yet established");
1131                 return ESP_ERR_HTTP_CONNECTING;
1132             }
1133         }
1134         client->state = HTTP_STATE_CONNECTED;
1135         http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0);
1136     }
1137     return ESP_OK;
1138 }
1139 
http_client_prepare_first_line(esp_http_client_handle_t client,int write_len)1140 static int http_client_prepare_first_line(esp_http_client_handle_t client, int write_len)
1141 {
1142     if (write_len >= 0) {
1143         http_header_set_format(client->request->headers, "Content-Length", "%d", write_len);
1144     } else {
1145         esp_http_client_set_header(client, "Transfer-Encoding", "chunked");
1146         esp_http_client_set_method(client, HTTP_METHOD_POST);
1147     }
1148 
1149     const char *method = HTTP_METHOD_MAPPING[client->connection_info.method];
1150 
1151     int first_line_len = snprintf(client->request->buffer->data,
1152                                   client->buffer_size_tx, "%s %s",
1153                                   method,
1154                                   client->connection_info.path);
1155     if (first_line_len >= client->buffer_size_tx) {
1156         ESP_LOGE(TAG, "Out of buffer");
1157         return -1;
1158     }
1159 
1160     if (client->connection_info.query) {
1161         first_line_len += snprintf(client->request->buffer->data + first_line_len,
1162                                    client->buffer_size_tx - first_line_len, "?%s", client->connection_info.query);
1163         if (first_line_len >= client->buffer_size_tx) {
1164             ESP_LOGE(TAG, "Out of buffer");
1165             return -1;
1166 
1167         }
1168     }
1169     first_line_len += snprintf(client->request->buffer->data + first_line_len,
1170                                client->buffer_size_tx - first_line_len, " %s\r\n", DEFAULT_HTTP_PROTOCOL);
1171     if (first_line_len >= client->buffer_size_tx) {
1172         ESP_LOGE(TAG, "Out of buffer");
1173         return -1;
1174     }
1175     return first_line_len;
1176 }
1177 
esp_http_client_request_send(esp_http_client_handle_t client,int write_len)1178 static esp_err_t esp_http_client_request_send(esp_http_client_handle_t client, int write_len)
1179 {
1180     int first_line_len = 0;
1181     if (!client->first_line_prepared) {
1182         if ((first_line_len = http_client_prepare_first_line(client, write_len)) < 0) {
1183             return first_line_len;
1184         }
1185         client->first_line_prepared = true;
1186         client->header_index = 0;
1187         client->data_written_index = 0;
1188         client->data_write_left = 0;
1189     }
1190 
1191     if (client->data_write_left > 0) {
1192         /* sending leftover data from previous call to esp_http_client_request_send() API */
1193         int wret = 0;
1194         if (((wret = esp_http_client_write(client, client->request->buffer->data + client->data_written_index, client->data_write_left)) < 0)) {
1195             ESP_LOGE(TAG, "Error write request");
1196             return ESP_ERR_HTTP_WRITE_DATA;
1197         }
1198         client->data_write_left -= wret;
1199         client->data_written_index += wret;
1200         if (client->is_async && client->data_write_left > 0) {
1201             return ESP_ERR_HTTP_WRITE_DATA;      /* In case of EAGAIN error, we return ESP_ERR_HTTP_WRITE_DATA,
1202                                                  and the handling of EAGAIN should be done in the higher level APIs. */
1203         }
1204     }
1205 
1206     int wlen = client->buffer_size_tx - first_line_len;
1207     while ((client->header_index = http_header_generate_string(client->request->headers, client->header_index, client->request->buffer->data + first_line_len, &wlen))) {
1208         if (wlen <= 0) {
1209             break;
1210         }
1211         if (first_line_len) {
1212             wlen += first_line_len;
1213             first_line_len = 0;
1214         }
1215         client->request->buffer->data[wlen] = 0;
1216         ESP_LOGD(TAG, "Write header[%d]: %s", client->header_index, client->request->buffer->data);
1217 
1218         client->data_write_left = wlen;
1219         client->data_written_index = 0;
1220         while (client->data_write_left > 0) {
1221             int wret = esp_transport_write(client->transport, client->request->buffer->data + client->data_written_index, client->data_write_left, client->timeout_ms);
1222             if (wret <= 0) {
1223                 ESP_LOGE(TAG, "Error write request");
1224                 esp_http_client_close(client);
1225                 return ESP_ERR_HTTP_WRITE_DATA;
1226             }
1227             client->data_write_left -= wret;
1228             client->data_written_index += wret;
1229         }
1230         wlen = client->buffer_size_tx;
1231     }
1232 
1233     client->data_written_index = 0;
1234     client->data_write_left = client->post_len;
1235     http_dispatch_event(client, HTTP_EVENT_HEADERS_SENT, NULL, 0);
1236     client->state = HTTP_STATE_REQ_COMPLETE_HEADER;
1237     return ESP_OK;
1238 }
1239 
esp_http_client_send_post_data(esp_http_client_handle_t client)1240 static esp_err_t esp_http_client_send_post_data(esp_http_client_handle_t client)
1241 {
1242     if (client->state != HTTP_STATE_REQ_COMPLETE_HEADER) {
1243         ESP_LOGE(TAG, "Invalid state");
1244         return ESP_ERR_INVALID_STATE;
1245     }
1246     if (!(client->post_data && client->post_len)) {
1247         goto success;
1248     }
1249 
1250     int wret = esp_http_client_write(client, client->post_data + client->data_written_index, client->data_write_left);
1251     if (wret < 0) {
1252         return wret;
1253     }
1254     client->data_write_left -= wret;
1255     client->data_written_index += wret;
1256 
1257     if (client->data_write_left <= 0) {
1258         goto success;
1259     } else {
1260         return ESP_ERR_HTTP_WRITE_DATA;
1261     }
1262 
1263 success:
1264     client->state = HTTP_STATE_REQ_COMPLETE_DATA;
1265     return ESP_OK;
1266 }
1267 
esp_http_client_open(esp_http_client_handle_t client,int write_len)1268 esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len)
1269 {
1270     client->post_len = write_len;
1271     esp_err_t err;
1272     if ((err = esp_http_client_connect(client)) != ESP_OK) {
1273         return err;
1274     }
1275     if ((err = esp_http_client_request_send(client, write_len)) != ESP_OK) {
1276         return err;
1277     }
1278     return ESP_OK;
1279 }
1280 
esp_http_client_write(esp_http_client_handle_t client,const char * buffer,int len)1281 int esp_http_client_write(esp_http_client_handle_t client, const char *buffer, int len)
1282 {
1283     if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
1284         return ESP_FAIL;
1285     }
1286 
1287     int wlen = 0, widx = 0;
1288     while (len > 0) {
1289         wlen = esp_transport_write(client->transport, buffer + widx, len, client->timeout_ms);
1290         /* client->async_block is initialised in case of non-blocking IO, and in this case we return how
1291            much ever data was written by the esp_transport_write() API. */
1292         if (client->is_async || wlen <= 0) {
1293             return wlen;
1294         }
1295         widx += wlen;
1296         len -= wlen;
1297     }
1298     return widx;
1299 }
1300 
esp_http_client_close(esp_http_client_handle_t client)1301 esp_err_t esp_http_client_close(esp_http_client_handle_t client)
1302 {
1303     if (client->state >= HTTP_STATE_INIT) {
1304         http_dispatch_event(client, HTTP_EVENT_DISCONNECTED, esp_transport_get_error_handle(client->transport), 0);
1305         client->state = HTTP_STATE_INIT;
1306         return esp_transport_close(client->transport);
1307     }
1308     return ESP_OK;
1309 }
1310 
esp_http_client_set_post_field(esp_http_client_handle_t client,const char * data,int len)1311 esp_err_t esp_http_client_set_post_field(esp_http_client_handle_t client, const char *data, int len)
1312 {
1313     esp_err_t err = ESP_OK;
1314     client->post_data = (char *)data;
1315     client->post_len = len;
1316     ESP_LOGD(TAG, "set post file length = %d", len);
1317     if (client->post_data) {
1318         char *value = NULL;
1319         if ((err = esp_http_client_get_header(client, "Content-Type", &value)) != ESP_OK) {
1320             return err;
1321         }
1322         if (value == NULL) {
1323             err = esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
1324         }
1325     } else {
1326         client->post_len = 0;
1327         err = esp_http_client_set_header(client, "Content-Type", NULL);
1328     }
1329     return err;
1330 }
1331 
esp_http_client_get_post_field(esp_http_client_handle_t client,char ** data)1332 int esp_http_client_get_post_field(esp_http_client_handle_t client, char **data)
1333 {
1334     if (client->post_data) {
1335         *data = client->post_data;
1336         return client->post_len;
1337     }
1338     return 0;
1339 }
1340 
esp_http_client_get_status_code(esp_http_client_handle_t client)1341 int esp_http_client_get_status_code(esp_http_client_handle_t client)
1342 {
1343     return client->response->status_code;
1344 }
1345 
esp_http_client_get_content_length(esp_http_client_handle_t client)1346 int esp_http_client_get_content_length(esp_http_client_handle_t client)
1347 {
1348     return client->response->content_length;
1349 }
1350 
esp_http_client_is_chunked_response(esp_http_client_handle_t client)1351 bool esp_http_client_is_chunked_response(esp_http_client_handle_t client)
1352 {
1353     return client->response->is_chunked;
1354 }
1355 
esp_http_client_get_transport_type(esp_http_client_handle_t client)1356 esp_http_client_transport_t esp_http_client_get_transport_type(esp_http_client_handle_t client)
1357 {
1358     if (!strcasecmp(client->connection_info.scheme, "https") ) {
1359         return HTTP_TRANSPORT_OVER_SSL;
1360     } else if (!strcasecmp(client->connection_info.scheme, "http")) {
1361         return HTTP_TRANSPORT_OVER_TCP;
1362     } else {
1363         return HTTP_TRANSPORT_UNKNOWN;
1364     }
1365 }
1366 
esp_http_client_add_auth(esp_http_client_handle_t client)1367 void esp_http_client_add_auth(esp_http_client_handle_t client)
1368 {
1369     if (client == NULL) {
1370         return;
1371     }
1372     if (client->state != HTTP_STATE_RES_COMPLETE_HEADER) {
1373         return;
1374     }
1375     if (client->redirect_counter >= client->max_authorization_retries) {
1376         ESP_LOGE(TAG, "Error, reached max_authorization_retries count=%d", client->redirect_counter);
1377         return;
1378     }
1379 
1380     char *auth_header = client->auth_header;
1381     if (auth_header) {
1382         http_utils_trim_whitespace(&auth_header);
1383         ESP_LOGD(TAG, "UNAUTHORIZED: %s", auth_header);
1384         client->redirect_counter++;
1385         if (http_utils_str_starts_with(auth_header, "Digest") == 0) {
1386             ESP_LOGD(TAG, "type = Digest");
1387             client->connection_info.auth_type = HTTP_AUTH_TYPE_DIGEST;
1388 #ifdef CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH
1389         } else if (http_utils_str_starts_with(auth_header, "Basic") == 0) {
1390             ESP_LOGD(TAG, "type = Basic");
1391             client->connection_info.auth_type = HTTP_AUTH_TYPE_BASIC;
1392 #endif
1393         } else {
1394             client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE;
1395             ESP_LOGE(TAG, "This authentication method is not supported: %s", auth_header);
1396             return;
1397         }
1398 
1399         _clear_auth_data(client);
1400 
1401         client->auth_data->method = strdup(HTTP_METHOD_MAPPING[client->connection_info.method]);
1402 
1403         client->auth_data->nc = 1;
1404         client->auth_data->realm = http_utils_get_string_between(auth_header, "realm=\"", "\"");
1405         client->auth_data->algorithm = http_utils_get_string_between(auth_header, "algorithm=", ",");
1406         if (client->auth_data->algorithm == NULL) {
1407             client->auth_data->algorithm = strdup("MD5");
1408         }
1409         client->auth_data->qop = http_utils_get_string_between(auth_header, "qop=\"", "\"");
1410         client->auth_data->nonce = http_utils_get_string_between(auth_header, "nonce=\"", "\"");
1411         client->auth_data->opaque = http_utils_get_string_between(auth_header, "opaque=\"", "\"");
1412         client->process_again = 1;
1413     } else {
1414         client->connection_info.auth_type = HTTP_AUTH_TYPE_NONE;
1415         ESP_LOGW(TAG, "This request requires authentication, but does not provide header information for that");
1416     }
1417 }
1418 
esp_http_client_read_response(esp_http_client_handle_t client,char * buffer,int len)1419 int esp_http_client_read_response(esp_http_client_handle_t client, char *buffer, int len)
1420 {
1421     int read_len = 0;
1422     while (read_len < len) {
1423         int data_read = esp_http_client_read(client, buffer + read_len, len - read_len);
1424         if (data_read <= 0) {
1425             return read_len;
1426         }
1427         read_len += data_read;
1428     }
1429     return read_len;
1430 }
1431 
esp_http_client_flush_response(esp_http_client_handle_t client,int * len)1432 esp_err_t esp_http_client_flush_response(esp_http_client_handle_t client, int *len)
1433 {
1434     if (client == NULL) {
1435         ESP_LOGE(TAG, "client must not be NULL");
1436         return ESP_ERR_INVALID_ARG;
1437     }
1438     int read_len = 0;
1439     while (!esp_http_client_is_complete_data_received(client)) {
1440         int data_read = esp_http_client_get_data(client);
1441         if (data_read < 0) {
1442             return ESP_FAIL;
1443         }
1444         read_len += data_read;
1445     }
1446     if (len) {
1447         *len = read_len;
1448     }
1449     return ESP_OK;
1450 }
1451 
esp_http_client_get_url(esp_http_client_handle_t client,char * url,const int len)1452 esp_err_t esp_http_client_get_url(esp_http_client_handle_t client, char *url, const int len)
1453 {
1454     if (client == NULL || url == NULL) {
1455         return ESP_ERR_INVALID_ARG;
1456     }
1457     if (client->connection_info.host && client->connection_info.scheme && client->connection_info.path) {
1458         snprintf(url, len, "%s://%s%s", client->connection_info.scheme, client->connection_info.host, client->connection_info.path);
1459         return ESP_OK;
1460     } else {
1461         ESP_LOGE(TAG, "Failed to get URL");
1462     }
1463     return ESP_FAIL;
1464 }
1465 
esp_http_client_get_chunk_length(esp_http_client_handle_t client,int * len)1466 esp_err_t esp_http_client_get_chunk_length(esp_http_client_handle_t client, int *len)
1467 {
1468     if (client == NULL || len == NULL) {
1469         return ESP_ERR_INVALID_ARG;
1470     }
1471     if (esp_http_client_is_chunked_response(client)) {
1472         *len = client->response->chunk_length;
1473     } else {
1474         ESP_LOGE(TAG, "Response is not chunked");
1475         return ESP_FAIL;
1476     }
1477     return ESP_OK;
1478 }
1479