Skip to content
File

Blob: firmware/platform/http.c

c119 lines
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include "radio_bridge.h"
5#include "esp_crt_bundle.h"
6#include "esp_heap_caps.h"
7#include "esp_http_client.h"
8#include "signaling_private.h"
9 
10#define RESPONSE_LIMIT 24576
11static const char empty_post_body[] = "";
12 
13struct radio_http {
14 esp_http_client_handle_t client;
15 esp_http_client_config_t config;
16 char *response;
17 size_t used;
18 bool overflow;
19};
20 
21static esp_err_t on_http(esp_http_client_event_t *event)
22{
23 radio_http *h = event->user_data;
24 if (event->event_id == HTTP_EVENT_ON_DATA && event->data_len > 0) {
25 size_t length = event->data_len;
26 if (length > RESPONSE_LIMIT - h->used)
27 h->overflow = true;
28 else if (!h->overflow) {
29 memcpy(h->response + h->used, event->data, length);
30 h->used += length;
31 }
32 }
33 return ESP_OK;
34}
35 
36radio_http *radio_http_open(void)
37{
38 radio_http *h = calloc(1, sizeof(*h));
39 if (!h)
40 return NULL;
41 h->response = heap_caps_malloc(RESPONSE_LIMIT, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
42 if (!h->response) {
43 free(h);
44 return NULL;
45 }
46 h->config = (esp_http_client_config_t){.url = SIGNALING_URL,
47 .crt_bundle_attach = esp_crt_bundle_attach,
48 .timeout_ms = 12000,
49 .event_handler = on_http,
50 .user_data = h,
51 .buffer_size = 4096,
52 .buffer_size_tx = 4096,
53 .disable_auto_redirect = true,
54 .keep_alive_enable = true};
55 h->client = esp_http_client_init(&h->config);
56 if (!h->client) {
57 radio_http_free(h);
58 return NULL;
59 }
60 int err = esp_http_client_set_method(h->client, HTTP_METHOD_POST);
61 if (!err)
62 err = esp_http_client_set_header(h->client, "Content-Type", "application/json");
63 if (!err)
64 err = esp_http_client_set_header(h->client, "Authorization",
65 "Bearer " SIGNALING_DEVICE_TOKEN);
66 if (!err)
67 err = esp_http_client_set_header(h->client, "User-Agent", "PocketRadio-S3-Rust/1.0");
68 if (err) {
69 radio_http_free(h);
70 return NULL;
71 }
72 return h;
73}
74 
75int32_t radio_http_post(radio_http *h, const char *path, const uint8_t *body, size_t length,
76 const uint8_t **response, size_t *used)
77{
78 *response = NULL;
79 *used = 0;
80 h->used = 0;
81 h->overflow = false;
82 if (!radio_network_ready() || length > 20000)
83 return -1;
84 char url[256];
85 int count = snprintf(url, sizeof(url), "%s/api/device/%s", SIGNALING_URL, path);
86 if (count < 0 || (size_t)count >= sizeof(url))
87 return -1;
88 int err = esp_http_client_set_url(h->client, url);
89 if (!err)
90 err = esp_http_client_set_post_field(h->client, (const char *)body, length);
91 if (!err)
92 err = esp_http_client_perform(h->client);
93 int status = esp_http_client_get_status_code(h->client);
94 /* perform is blocking, so callbacks have finished. Replace the Rust borrow
95 * with C-static storage; NULL would also remove Content-Type in ESP-IDF.
96 * The pinned setter stores this pointer before a header lookup can fail. */
97 int release_error = esp_http_client_set_post_field(h->client, empty_post_body, 0);
98 if (!err)
99 err = release_error;
100 if (err || h->overflow) {
101 esp_http_client_close(h->client);
102 h->used = 0;
103 return -1;
104 }
105 /* The caller borrows only these initialized bytes until the next post/free. */
106 *response = (const uint8_t *)h->response;
107 *used = h->used;
108 return status;
109}
110void radio_http_free(radio_http *h)
111{
112 if (!h)
113 return;
114 if (h->client)
115 esp_http_client_cleanup(h->client);
116 free(h->response);
117 free(h);
118}