26
26
#include <string.h>
27
27
#include "esp_log.h"
28
28
#include "https_client.h"
29
+ #include "https_client_utils.h"
29
30
#include "esp_tls.h"
30
31
#include <sdkconfig.h>
31
32
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
36
37
static const char * TAG = "HTTPS_CLIENT" ;
37
38
38
39
typedef 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 ;
45
53
} http_info_t ;
46
54
47
55
esp_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)
61
69
break ;
62
70
case HTTP_EVENT_ON_HEADER :
63
71
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
+ }
64
79
info -> header (evt -> header_key , evt -> header_value , info -> ctx );
65
80
}
66
81
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)
102
117
ESP_LOGD (TAG , "Last mbedtls failure: 0x%x" , mbedtls_err );
103
118
}
104
119
break ;
105
- case HTTP_EVENT_REDIRECT :
106
- esp_http_client_set_redirection (evt -> client );
107
- break ;
108
120
}
109
121
return ESP_OK ;
110
122
}
111
123
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 )
113
185
{
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
+ }
114
199
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 ,
118
203
};
119
- esp_http_client_config_t config = {
120
- .url = url ,
204
+ esp_http_client_config_t http_config = {
205
+ .url = req -> url ,
121
206
.event_handler = _http_event_handler ,
122
207
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
123
208
.crt_bundle_attach = esp_crt_bundle_attach ,
124
209
#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 ,
125
218
.user_data = & info ,
126
- .timeout_ms = 10000 , // Change default timeout to be 10s
127
219
};
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 );
129
221
if (client == NULL ) {
130
222
ESP_LOGE (TAG , "Fail to init client" );
131
223
return -1 ;
132
224
}
133
225
// POST
134
226
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 ) {
137
230
esp_http_client_set_method (client , HTTP_METHOD_POST );
138
- } else if (strcmp (method , "DELETE" ) == 0 ) {
231
+ } else if (strcmp (req -> method , "DELETE" ) == 0 ) {
139
232
esp_http_client_set_method (client , HTTP_METHOD_DELETE );
140
- } else if (strcmp (method , "PATCH" ) == 0 ) {
233
+ } else if (strcmp (req -> method , "PATCH" ) == 0 ) {
141
234
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 );
142
237
} else {
143
238
err = -1 ;
144
239
goto _exit ;
145
240
}
241
+
146
242
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 ) {
148
247
int i = 0 ;
149
248
// 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 , ':' );
152
255
if (dot ) {
153
256
* dot = 0 ;
154
- if (strcmp (headers [ i ] , "Content-Type" ) == 0 ) {
257
+ if (strcmp (h , "Content-Type" ) == 0 ) {
155
258
has_content_type = true;
156
259
}
157
260
char * cont = dot + 2 ;
158
- esp_http_client_set_header (client , headers [ i ] , cont );
261
+ esp_http_client_set_header (client , h , cont );
159
262
* dot = ':' ;
160
263
}
264
+ free (h );
161
265
i ++ ;
162
266
}
163
267
}
164
- if (data != NULL ) {
268
+ if (post_data != NULL ) {
165
269
if (has_content_type == false) {
166
270
esp_http_client_set_header (client , "Content-Type" , "text/plain;charset=UTF-8" );
167
271
}
168
- esp_http_client_set_post_field (client , data , strlen (data ));
272
+ esp_http_client_set_post_field (client , req -> data , strlen (req -> data ));
169
273
}
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
+
170
282
err = esp_http_client_perform (client );
171
283
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" ,
173
285
esp_http_client_get_status_code (client ),
174
286
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
+ }
175
304
} 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 ));
177
306
}
178
307
_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
180
313
if (info .data ) {
181
314
free (info .data );
182
315
}
316
+ esp_http_client_cleanup (client );
183
317
return err ;
184
318
}
185
319
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
+
186
334
int https_post (const char * url , char * * headers , char * data , http_header_t header_cb , http_body_t body , void * ctx )
187
335
{
188
336
return https_send_request ("POST" , headers , url , data , header_cb , body , ctx );
0 commit comments