#include "rest_server_internal.h" #include #include #include #include "esp_heap_caps.h" #include "mbedtls/base64.h" #include "domain.h" #define REST_SERVER_MAX_BODY_BYTES (4 * 1024 * 1024) static void *alloc_prefer_psram(size_t size) { void *ptr = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (ptr == NULL) { ptr = malloc(size); } return ptr; } static void *calloc_prefer_psram(size_t n, size_t size) { void *ptr = heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); if (ptr == NULL) { ptr = calloc(n, size); } return ptr; } bool rest_server_auth_ok(httpd_req_t *req) { if (strlen(CONFIG_TQ_API_KEY) == 0) { return true; } char buf[96] = {0}; if (httpd_req_get_hdr_value_str(req, "X-API-Key", buf, sizeof(buf)) != ESP_OK) { return false; } return strcmp(buf, CONFIG_TQ_API_KEY) == 0; } esp_err_t rest_server_send_json(httpd_req_t *req, const char *status, cJSON *root) { char *text = cJSON_PrintUnformatted(root); if (text == NULL) { return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "json encode failed"); } httpd_resp_set_type(req, "application/json"); httpd_resp_set_status(req, status); esp_err_t err = httpd_resp_sendstr(req, text); cJSON_free(text); domain_diag_counter_add(DOMAIN_DIAG_COUNTER_REST_RESPONSES_TOTAL, 1); return err; } esp_err_t rest_server_send_error(httpd_req_t *req, const char *status, const char *message) { cJSON *root = cJSON_CreateObject(); cJSON_AddBoolToObject(root, "ok", false); cJSON_AddStringToObject(root, "error", message != NULL ? message : "unknown"); esp_err_t err = rest_server_send_json(req, status, root); cJSON_Delete(root); domain_diag_counter_add(DOMAIN_DIAG_COUNTER_REST_ERRORS_TOTAL, 1); domain_diag_record_error("rest_api", ESP_FAIL, message != NULL ? message : "unknown"); return err; } esp_err_t rest_server_read_body(httpd_req_t *req, char **out_body) { if (out_body == NULL) { return ESP_ERR_INVALID_ARG; } *out_body = NULL; if (req->content_len <= 0 || req->content_len > REST_SERVER_MAX_BODY_BYTES) { return ESP_ERR_INVALID_SIZE; } char *buf = (char *)calloc_prefer_psram(1, (size_t)req->content_len + 1); if (buf == NULL) { return ESP_ERR_NO_MEM; } int received = 0; while (received < req->content_len) { int ret = httpd_req_recv(req, buf + received, req->content_len - received); if (ret <= 0) { free(buf); return ESP_FAIL; } received += ret; } *out_body = buf; return ESP_OK; } bool rest_server_parse_uri_u32_tail(const char *uri, uint32_t *out_value) { if (uri == NULL || out_value == NULL) { return false; } const char *slash = strrchr(uri, '/'); if (slash == NULL || *(slash + 1) == '\0') { return false; } errno = 0; char *endptr = NULL; unsigned long val = strtoul(slash + 1, &endptr, 10); if (errno != 0 || endptr == slash + 1 || *endptr != '\0' || val > UINT32_MAX) { return false; } *out_value = (uint32_t)val; return true; } esp_err_t rest_server_base64_decode_alloc(const char *b64, uint8_t **out_raw, size_t *out_len) { if (b64 == NULL || out_raw == NULL || out_len == NULL) { return ESP_ERR_INVALID_ARG; } *out_raw = NULL; *out_len = 0; size_t decoded_len = 0; int rc = mbedtls_base64_decode(NULL, 0, &decoded_len, (const unsigned char *)b64, strlen(b64)); if (!(rc == 0 || rc == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)) { return ESP_ERR_INVALID_ARG; } uint8_t *buf = (uint8_t *)alloc_prefer_psram(decoded_len); if (buf == NULL) { return ESP_ERR_NO_MEM; } rc = mbedtls_base64_decode(buf, decoded_len, &decoded_len, (const unsigned char *)b64, strlen(b64)); if (rc != 0) { free(buf); return ESP_ERR_INVALID_ARG; } *out_raw = buf; *out_len = decoded_len; return ESP_OK; } bool rest_server_json_bool_with_default(cJSON *item, bool default_value) { if (item == NULL) { return default_value; } if (cJSON_IsBool(item)) { return cJSON_IsTrue(item); } return default_value; }