Skip to content

Commit 86d0c8f

Browse files
committed
Merge branch 'feature/support_redirect' into 'main'
Refine HTTP request to support redirection and options See merge request adf/esp-webrtc-solution!46
2 parents 65d1342 + d339fd9 commit 86d0c8f

File tree

3 files changed

+411
-32
lines changed

3 files changed

+411
-32
lines changed

components/esp_webrtc/impl/apprtc_signal/https_client.c

Lines changed: 179 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
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
@@ -36,12 +37,19 @@
3637
static const char *TAG = "HTTPS_CLIENT";
3738

3839
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;
4553
} http_info_t;
4654

4755
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)
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+
186334
int 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);

components/esp_webrtc/impl/apprtc_signal/https_client.h

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,21 @@
2424

2525
#pragma once
2626

27+
#include <stdbool.h>
28+
2729
#ifdef __cplusplus
2830
extern "C" {
2931
#endif
3032

33+
#define DEFAULT_HTTPS_TIMEOUT 10000
34+
#define DEFAULT_HTTPS_MAX_REDIRECTS 3
35+
#define DEFAULT_HTTPS_HEAD_SIZE 4096
36+
#define DEFAULT_HTTPS_TX_SIZE 4096
37+
#define DEFAULT_HTTPS_KEEP_ALIVE_ENABLE true
38+
#define DEFAULT_HTTPS_KEEP_ALIVE_COUNT 3
39+
#define DEFAULT_HTTPS_KEEP_ALIVE_IDLE 5
40+
#define DEFAULT_HTTPS_KEEP_ALIVE_INTERVAL 3
41+
3142
/**
3243
* @brief Https response data
3344
*/
@@ -53,6 +64,34 @@ typedef void (*http_body_t)(http_resp_t *resp, void *ctx);
5364
*/
5465
typedef void (*http_header_t)(const char *key, const char *value, void *ctx);
5566

67+
/**
68+
* @brief HTTP request configuration
69+
* These settings will bypass to `esp_http_client` or handled by this module internally
70+
*/
71+
typedef struct {
72+
uint16_t timeout_ms; /*!< Connection timeout (unit milliseconds) */
73+
uint8_t max_redirects; /*!< Maximum redirection limit */
74+
uint16_t buffer_size; /*!< HTTP header buffer size */
75+
uint16_t buffer_size_tx; /*!< HTTP send buffer size */
76+
bool keep_alive_enable; /*!< Keep alive enable or not */
77+
uint8_t keep_alive_count; /*!< Keep alive count */
78+
uint16_t keep_alive_idle; /*!< Keep alive idle */
79+
uint16_t keep_alive_interval; /*!< Keep alive send interval */
80+
} https_request_cfg_t;
81+
82+
/**
83+
* @brief HTTP request information
84+
*/
85+
typedef struct {
86+
const char *method; /*!< HTTP method POST, GET, DELETE, PATCH etc */
87+
const char *url; /*!< Request URL */
88+
char **headers; /*!< HTTP headers Arrays of "Type: Info" */
89+
char *data; /*!< Content data to send (special for post) */
90+
http_header_t header_cb; /*!< Response header callback */
91+
http_body_t body_cb; /*!< Response body callback */
92+
void *ctx; /*!< Callback context */
93+
} https_request_t;
94+
5695
/**
5796
* @brief Send https requests
5897
*
@@ -75,7 +114,7 @@ int https_send_request(const char *method, char **headers, const char *url, char
75114
/**
76115
* @brief Do post https request
77116
*
78-
* @note This API will internally call `https_send_request`
117+
* @note This API will internally call `https_request_advance`
79118
*
80119
* @param[in] url HTTPS URL to post
81120
* @param[in] headers HTTP headers, headers are array of "Type: Info", last one need set to NULL
@@ -90,6 +129,20 @@ int https_send_request(const char *method, char **headers, const char *url, char
90129
*/
91130
int https_post(const char *url, char **headers, char *data, http_header_t header_cb, http_body_t body, void *ctx);
92131

132+
/**
133+
* @brief Set https request with advanced options
134+
*
135+
* @note This API is run in synchronized until response or error returns
136+
*
137+
* @param[in] cfg Request HTTP configurations
138+
* @param[in] req Request settings
139+
*
140+
* @return
141+
* - 0 On success
142+
* - Others Fail to do https request
143+
*/
144+
int https_request_advance(https_request_cfg_t *cfg, https_request_t *req);
145+
93146
#ifdef __cplusplus
94147
}
95148
#endif

0 commit comments

Comments
 (0)