chore: initial commit

This commit is contained in:
CSBOOK\44744
2026-06-25 00:34:34 +08:00
commit 3cce1be162
295 changed files with 30971 additions and 0 deletions
@@ -0,0 +1,293 @@
/*
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "platform.h"
#include <limits.h>
#include <netdb.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include "esp_crt_bundle.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define PLATFORM_HTTPS_DEFAULT_TIMEOUT_MS 10000
#define PLATFORM_HTTPS_DNS_RETRY_COUNT 2
#define PLATFORM_HTTPS_DNS_RETRY_DELAY_MS 500
#define PLATFORM_HTTPS_HOST_MAX_LEN 256
static const char *TAG = "platform_https";
static int effective_positive_or_zero(int value)
{
return value > 0 ? value : 0;
}
static const char *dns_error_name(int err)
{
switch (err) {
#ifdef EAI_NONAME
case EAI_NONAME:
return "EAI_NONAME";
#endif
#ifdef EAI_SERVICE
case EAI_SERVICE:
return "EAI_SERVICE";
#endif
#ifdef EAI_FAIL
case EAI_FAIL:
return "EAI_FAIL";
#endif
#ifdef EAI_MEMORY
case EAI_MEMORY:
return "EAI_MEMORY";
#endif
#ifdef EAI_FAMILY
case EAI_FAMILY:
return "EAI_FAMILY";
#endif
default:
return "EAI_UNKNOWN";
}
}
static esp_err_t parse_url_host(const char *url, char *host, size_t host_capacity)
{
if (url == NULL || host == NULL || host_capacity == 0U) {
return ESP_ERR_INVALID_ARG;
}
const char *host_begin = strstr(url, "://");
host_begin = host_begin != NULL ? host_begin + 3 : url;
if (host_begin[0] == '\0') {
return ESP_ERR_INVALID_ARG;
}
const char *host_end = NULL;
if (host_begin[0] == '[') {
host_begin++;
host_end = strchr(host_begin, ']');
if (host_end == NULL) {
return ESP_ERR_INVALID_ARG;
}
} else {
host_end = host_begin;
while (host_end[0] != '\0' &&
host_end[0] != '/' &&
host_end[0] != '?' &&
host_end[0] != '#' &&
host_end[0] != ':') {
host_end++;
}
}
size_t host_len = (size_t)(host_end - host_begin);
if (host_len == 0U || host_len >= host_capacity) {
return ESP_ERR_INVALID_ARG;
}
memcpy(host, host_begin, host_len);
host[host_len] = '\0';
return ESP_OK;
}
static esp_err_t resolve_host_before_open(const char *url)
{
char host[PLATFORM_HTTPS_HOST_MAX_LEN];
esp_err_t err = parse_url_host(url, host, sizeof(host));
if (err != ESP_OK) {
return err;
}
for (int attempt = 1; attempt <= PLATFORM_HTTPS_DNS_RETRY_COUNT; ++attempt) {
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *addr = NULL;
int dns_err = getaddrinfo(host, NULL, &hints, &addr);
if (dns_err == 0 && addr != NULL) {
freeaddrinfo(addr);
return ESP_OK;
}
if (addr != NULL) {
freeaddrinfo(addr);
}
if (dns_err == EAI_MEMORY) {
ESP_LOGW(TAG, "DNS resolve failed host=%s err=%s(%d)", host, dns_error_name(dns_err), dns_err);
return ESP_ERR_NO_MEM;
}
if (attempt < PLATFORM_HTTPS_DNS_RETRY_COUNT) {
ESP_LOGW(TAG,
"DNS resolve failed host=%s err=%s(%d), retrying",
host,
dns_error_name(dns_err),
dns_err);
vTaskDelay(pdMS_TO_TICKS(PLATFORM_HTTPS_DNS_RETRY_DELAY_MS));
} else {
ESP_LOGW(TAG,
"DNS resolve failed host=%s err=%s(%d) after %d attempts",
host,
dns_error_name(dns_err),
dns_err,
PLATFORM_HTTPS_DNS_RETRY_COUNT);
}
}
return ESP_ERR_HTTP_CONNECT;
}
static esp_err_t write_all(esp_http_client_handle_t client, const char *payload)
{
size_t payload_len = strlen(payload);
if (payload_len > (size_t)INT_MAX) {
return ESP_ERR_INVALID_SIZE;
}
size_t total_written = 0U;
while (total_written < payload_len) {
int written = esp_http_client_write(client,
payload + total_written,
(int)(payload_len - total_written));
if (written <= 0) {
return ESP_FAIL;
}
total_written += (size_t)written;
}
return ESP_OK;
}
esp_err_t platform_https_post_json(const platform_https_post_json_request_t *request,
platform_https_response_t *response)
{
if (response != NULL) {
response->body_len = 0U;
response->status_code = -1;
if (response->body != NULL && response->body_capacity > 0U) {
response->body[0] = '\0';
}
}
if (request == NULL ||
response == NULL ||
request->url == NULL ||
request->url[0] == '\0' ||
request->payload == NULL ||
response->body == NULL ||
response->body_capacity < 2U ||
(request->header_count > 0U && request->headers == NULL)) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = resolve_host_before_open(request->url);
if (err != ESP_OK) {
return err;
}
esp_http_client_config_t cfg = {
.url = request->url,
.method = HTTP_METHOD_POST,
.timeout_ms = request->timeout_ms > 0 ? request->timeout_ms
: PLATFORM_HTTPS_DEFAULT_TIMEOUT_MS,
.crt_bundle_attach = esp_crt_bundle_attach,
.buffer_size_tx = effective_positive_or_zero(request->tx_buffer_size),
.buffer_size = effective_positive_or_zero(request->rx_buffer_size),
};
esp_http_client_handle_t client = esp_http_client_init(&cfg);
if (client == NULL) {
return ESP_ERR_NO_MEM;
}
esp_http_client_set_header(client, "Content-Type", "application/json");
for (size_t i = 0; i < request->header_count; ++i) {
const platform_https_header_t *header = &request->headers[i];
if (header->name == NULL || header->name[0] == '\0' ||
header->value == NULL) {
esp_http_client_cleanup(client);
return ESP_ERR_INVALID_ARG;
}
esp_http_client_set_header(client, header->name, header->value);
}
size_t payload_len = strlen(request->payload);
if (payload_len > (size_t)INT_MAX) {
esp_http_client_cleanup(client);
return ESP_ERR_INVALID_SIZE;
}
err = esp_http_client_open(client, (int)payload_len);
if (err != ESP_OK) {
ESP_LOGW(TAG, "HTTPS open failed: %s", esp_err_to_name(err));
esp_http_client_cleanup(client);
return err;
}
err = write_all(client, request->payload);
if (err != ESP_OK) {
ESP_LOGW(TAG, "HTTPS write failed: %s", esp_err_to_name(err));
esp_http_client_close(client);
esp_http_client_cleanup(client);
return err;
}
int64_t content_length = esp_http_client_fetch_headers(client);
int status = esp_http_client_get_status_code(client);
response->status_code = status;
size_t max_read = response->body_capacity - 1U;
if (content_length >= 0 && (uint64_t)content_length > (uint64_t)max_read) {
esp_http_client_close(client);
esp_http_client_cleanup(client);
return ESP_ERR_INVALID_SIZE;
}
size_t total_read = 0U;
while (true) {
if (total_read >= max_read) {
uint8_t overflow = 0U;
int overflow_len = esp_http_client_read(client, (char *)&overflow, 1);
if (overflow_len > 0) {
esp_http_client_close(client);
esp_http_client_cleanup(client);
return ESP_ERR_INVALID_SIZE;
}
if (overflow_len < 0) {
esp_http_client_close(client);
esp_http_client_cleanup(client);
return ESP_ERR_INVALID_RESPONSE;
}
break;
}
int read_len = esp_http_client_read(client,
response->body + total_read,
(int)(max_read - total_read));
if (read_len < 0) {
esp_http_client_close(client);
esp_http_client_cleanup(client);
return ESP_ERR_INVALID_RESPONSE;
}
if (read_len == 0) {
break;
}
total_read += (size_t)read_len;
}
esp_http_client_close(client);
esp_http_client_cleanup(client);
response->body[total_read] = '\0';
response->body_len = total_read;
return ESP_OK;
}