2626#include <string.h>
2727#include "esp_log.h"
2828#include "https_client.h"
29+ #include "https_client_utils.h"
2930#include "esp_tls.h"
3031#include <sdkconfig.h>
3132#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
3637static const char * TAG = "HTTPS_CLIENT" ;
3738
3839typedef struct {
39- http_header_t header ;
40- http_body_t body ;
41- uint8_t * data ;
42- int fill_size ;
43- int size ;
44- void * ctx ;
40+ const char * location ;
41+ const char * auth ;
42+ const char * cookie ;
43+ } http_resp_keep_t ;
44+
45+ typedef struct {
46+ http_header_t header ;
47+ http_body_t body ;
48+ http_resp_keep_t resp_keep ;
49+ uint8_t * data ;
50+ int fill_size ;
51+ int size ;
52+ void * ctx ;
4553} http_info_t ;
4654
4755esp_err_t _http_event_handler (esp_http_client_event_t * evt )
@@ -61,6 +69,13 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
6169 break ;
6270 case HTTP_EVENT_ON_HEADER :
6371 if (info -> header ) {
72+ if (strcmp (evt -> header_key , "Location" ) == 0 ) {
73+ info -> resp_keep .location = strdup (evt -> header_value );
74+ } else if (strcmp (evt -> header_key , "Authorization" ) == 0 ) {
75+ info -> resp_keep .auth = strdup (evt -> header_value );
76+ } else if (strcmp (evt -> header_key , "Set-Cookie" ) == 0 ) {
77+ info -> resp_keep .cookie = strdup (evt -> header_value );
78+ }
6479 info -> header (evt -> header_key , evt -> header_value , info -> ctx );
6580 }
6681 ESP_LOGD (TAG , "HTTP_EVENT_ON_HEADER, key=%s, value=%s" , evt -> header_key ,
@@ -102,87 +117,220 @@ esp_err_t _http_event_handler(esp_http_client_event_t *evt)
102117 ESP_LOGD (TAG , "Last mbedtls failure: 0x%x" , mbedtls_err );
103118 }
104119 break ;
105- case HTTP_EVENT_REDIRECT :
106- esp_http_client_set_redirection (evt -> client );
107- break ;
108120 }
109121 return ESP_OK ;
110122}
111123
112- int https_send_request (const char * method , char * * headers , const char * url , char * data , http_header_t header_cb , http_body_t body , void * ctx )
124+ static int https_handle_redirect (esp_http_client_handle_t client , https_request_t * req , http_resp_keep_t * resp , char * * last_url )
125+ {
126+ if (resp -> location == NULL ) {
127+ ESP_LOGE (TAG , "Redirect location not found" );
128+ return -1 ;
129+ }
130+ int status_code = esp_http_client_get_status_code (client );
131+ if (!(status_code == 307 || status_code == 308 )) {
132+ esp_http_client_set_post_field (client , NULL , 0 );
133+ esp_http_client_set_method (client , HTTP_METHOD_GET );
134+ }
135+ char * full_url = strdup (resp -> location );
136+ if (full_url == NULL ) {
137+ return -1 ;
138+ }
139+ if (strncmp (resp -> location , "http" , 4 ) != 0 ) {
140+ full_url = join_url (* last_url , resp -> location );
141+ if (full_url == NULL ) {
142+ return -1 ;
143+ }
144+ }
145+ // Handle auth
146+ if (resp -> auth ) {
147+ // Only valid when origin same
148+ if (is_same_origin (* last_url , full_url )) {
149+ esp_http_client_set_header (client , "Authorization" , resp -> auth );
150+ } else {
151+ esp_http_client_set_header (client , "Authorization" , NULL );
152+ }
153+ }
154+ // Handle cookie
155+ if (resp -> cookie != NULL ) {
156+ esp_http_client_set_header (client , "Cookie" , resp -> cookie );
157+ }
158+ // Store to last url
159+ if (* last_url != req -> url ) {
160+ free (* last_url );
161+ }
162+ * last_url = full_url ;
163+ ESP_LOGI (TAG , "Redirect to: %s %p\n" , full_url , full_url );
164+ esp_http_client_set_url (client , full_url );
165+ return 0 ;
166+ }
167+
168+ static void free_resp_header (http_resp_keep_t * resp )
169+ {
170+ if (resp -> location ) {
171+ free ((void * )resp -> location );
172+ resp -> location = NULL ;
173+ }
174+ if (resp -> auth ) {
175+ free ((void * )resp -> auth );
176+ resp -> auth = NULL ;
177+ }
178+ if (resp -> cookie ) {
179+ free ((void * )resp -> cookie );
180+ resp -> cookie = NULL ;
181+ }
182+ }
183+
184+ int https_request_advance (https_request_cfg_t * cfg , https_request_t * req )
113185{
186+ https_request_cfg_t default_cfg = {
187+ .timeout_ms = DEFAULT_HTTPS_TIMEOUT ,
188+ .max_redirects = DEFAULT_HTTPS_MAX_REDIRECTS ,
189+ .buffer_size = DEFAULT_HTTPS_HEAD_SIZE ,
190+ .buffer_size_tx = DEFAULT_HTTPS_TX_SIZE ,
191+ .keep_alive_enable = DEFAULT_HTTPS_KEEP_ALIVE_ENABLE ,
192+ .keep_alive_count = DEFAULT_HTTPS_KEEP_ALIVE_COUNT ,
193+ .keep_alive_idle = DEFAULT_HTTPS_KEEP_ALIVE_IDLE ,
194+ .keep_alive_interval = DEFAULT_HTTPS_KEEP_ALIVE_INTERVAL ,
195+ };
196+ if (cfg == NULL ) {
197+ cfg = & default_cfg ;
198+ }
114199 http_info_t info = {
115- .body = body ,
116- .header = header_cb ,
117- .ctx = ctx ,
200+ .body = req -> body_cb ,
201+ .header = req -> header_cb ,
202+ .ctx = req -> ctx ,
118203 };
119- esp_http_client_config_t config = {
120- .url = url ,
204+ esp_http_client_config_t http_config = {
205+ .url = req -> url ,
121206 .event_handler = _http_event_handler ,
122207#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
123208 .crt_bundle_attach = esp_crt_bundle_attach ,
124209#endif
210+ .timeout_ms = cfg -> timeout_ms ? cfg -> timeout_ms : default_cfg .timeout_ms ,
211+ .buffer_size = cfg -> buffer_size ? cfg -> buffer_size : default_cfg .buffer_size ,
212+ .buffer_size_tx = cfg -> buffer_size_tx ? cfg -> buffer_size_tx : default_cfg .buffer_size_tx ,
213+ .disable_auto_redirect = true, // Handle redirects manually
214+ .keep_alive_enable = cfg -> keep_alive_enable ,
215+ .keep_alive_idle = cfg -> keep_alive_idle ,
216+ .keep_alive_interval = cfg -> keep_alive_interval ,
217+ .keep_alive_count = cfg -> keep_alive_count ,
125218 .user_data = & info ,
126- .timeout_ms = 10000 , // Change default timeout to be 10s
127219 };
128- esp_http_client_handle_t client = esp_http_client_init (& config );
220+ esp_http_client_handle_t client = esp_http_client_init (& http_config );
129221 if (client == NULL ) {
130222 ESP_LOGE (TAG , "Fail to init client" );
131223 return -1 ;
132224 }
133225 // POST
134226 int err = 0 ;
135- esp_http_client_set_url (client , url );
136- if (strcmp (method , "POST" ) == 0 ) {
227+ char * last_url = (char * )req -> url ;
228+ esp_http_client_set_url (client , req -> url );
229+ if (strcmp (req -> method , "POST" ) == 0 ) {
137230 esp_http_client_set_method (client , HTTP_METHOD_POST );
138- } else if (strcmp (method , "DELETE" ) == 0 ) {
231+ } else if (strcmp (req -> method , "DELETE" ) == 0 ) {
139232 esp_http_client_set_method (client , HTTP_METHOD_DELETE );
140- } else if (strcmp (method , "PATCH" ) == 0 ) {
233+ } else if (strcmp (req -> method , "PATCH" ) == 0 ) {
141234 esp_http_client_set_method (client , HTTP_METHOD_PATCH );
235+ } else if (strcmp (req -> method , "GET" ) == 0 ) {
236+ esp_http_client_set_method (client , HTTP_METHOD_GET );
142237 } else {
143238 err = -1 ;
144239 goto _exit ;
145240 }
241+
146242 bool has_content_type = false;
147- if (headers ) {
243+ uint8_t redirect_limit = cfg -> max_redirects ? cfg -> max_redirects : DEFAULT_HTTPS_MAX_REDIRECTS ;
244+ uint8_t redirect_count = 0 ;
245+ const char * post_data = req -> data ;
246+ if (req -> headers ) {
148247 int i = 0 ;
149248 // TODO suppose header writable
150- while (headers [i ]) {
151- char * dot = strchr (headers [i ], ':' );
249+ while (req -> headers [i ]) {
250+ char * h = strdup (req -> headers [i ]);
251+ if (h == NULL ) {
252+ continue ;
253+ }
254+ char * dot = strchr (h , ':' );
152255 if (dot ) {
153256 * dot = 0 ;
154- if (strcmp (headers [ i ] , "Content-Type" ) == 0 ) {
257+ if (strcmp (h , "Content-Type" ) == 0 ) {
155258 has_content_type = true;
156259 }
157260 char * cont = dot + 2 ;
158- esp_http_client_set_header (client , headers [ i ] , cont );
261+ esp_http_client_set_header (client , h , cont );
159262 * dot = ':' ;
160263 }
264+ free (h );
161265 i ++ ;
162266 }
163267 }
164- if (data != NULL ) {
268+ if (post_data != NULL ) {
165269 if (has_content_type == false) {
166270 esp_http_client_set_header (client , "Content-Type" , "text/plain;charset=UTF-8" );
167271 }
168- esp_http_client_set_post_field (client , data , strlen (data ));
272+ esp_http_client_set_post_field (client , req -> data , strlen (req -> data ));
169273 }
274+ RETRY_PERFORM :
275+ if (info .data ) {
276+ free (info .data );
277+ info .data = NULL ;
278+ }
279+ info .fill_size = 0 ;
280+ free_resp_header (& info .resp_keep );
281+
170282 err = esp_http_client_perform (client );
171283 if (err == ESP_OK ) {
172- ESP_LOGI (TAG , "HTTP POST Status = %d, content_length = %lld" ,
284+ ESP_LOGI (TAG , "HTTP Status = %d, content_length = %lld" ,
173285 esp_http_client_get_status_code (client ),
174286 esp_http_client_get_content_length (client ));
287+ int status_code = esp_http_client_get_status_code (client );
288+ if (status_code >= 301 && status_code < 400 ) {
289+ // Handle redirection
290+ if (redirect_count >= redirect_limit ) {
291+ ESP_LOGE (TAG , "Redirect limit exceeded: %d" , redirect_count );
292+ err = -1 ;
293+ } else {
294+ err = https_handle_redirect (client , req , & info .resp_keep , & last_url );
295+ if (!(status_code == 307 || status_code == 308 )) {
296+ post_data = NULL ;
297+ }
298+ redirect_count ++ ;
299+ if (err == 0 ) {
300+ goto RETRY_PERFORM ;
301+ }
302+ }
303+ }
175304 } else {
176- ESP_LOGE (TAG , "HTTP POST request failed: %s" , esp_err_to_name (err ));
305+ ESP_LOGE (TAG , "HTTP %s request failed: %s" , req -> method , esp_err_to_name (err ));
177306 }
178307_exit :
179- esp_http_client_cleanup (client );
308+ if (last_url != req -> url ) {
309+ free (last_url );
310+ }
311+ free_resp_header (& info .resp_keep );
312+ // Free Body
180313 if (info .data ) {
181314 free (info .data );
182315 }
316+ esp_http_client_cleanup (client );
183317 return err ;
184318}
185319
320+ int https_send_request (const char * method , char * * headers , const char * url , char * data , http_header_t header_cb , http_body_t body , void * ctx )
321+ {
322+ https_request_t req = {
323+ .method = method ,
324+ .url = url ,
325+ .headers = headers ,
326+ .data = data ,
327+ .header_cb = header_cb ,
328+ .body_cb = body ,
329+ .ctx = ctx ,
330+ };
331+ return https_request_advance (NULL , & req );
332+ }
333+
186334int https_post (const char * url , char * * headers , char * data , http_header_t header_cb , http_body_t body , void * ctx )
187335{
188336 return https_send_request ("POST" , headers , url , data , header_cb , body , ctx );
0 commit comments