// Copyright (2025) Beijing Volcano Engine Technology Ltd. // SPDX-License-Identifier: Apache-2.0 #include "volc_http.h" #include #include #include #include "volc_platform.h" #include "esp_crt_bundle.h" static const char* TAG = "VOLC_HTTP"; // Response data structure for event handler typedef struct { char* buffer; size_t buffer_size; size_t total_read; size_t max_size; } response_data_t; // HTTP client event handler to capture response data static esp_err_t http_event_handler(esp_http_client_event_t *evt) { response_data_t *response_data = (response_data_t *)evt->user_data; switch (evt->event_id) { case HTTP_EVENT_ON_DATA: if (response_data && evt->data && evt->data_len > 0) { // Calculate available space in buffer size_t available = response_data->max_size - response_data->total_read; if (available > 0) { // Copy data to buffer (don't exceed max size) size_t copy_len = (evt->data_len < available) ? evt->data_len : available; memcpy(response_data->buffer + response_data->total_read, evt->data, copy_len); response_data->total_read += copy_len; ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA: read %d bytes, total: %d", evt->data_len, response_data->total_read); } } break; default: break; } return ESP_OK; } char* volc_http_post(const char* uri, const char* post_data, int data_len) { char* response_buffer = NULL; esp_http_client_handle_t client = NULL; int response_status = 0; esp_err_t err = ESP_OK; int attempts = 0; const int max_attempts = 3; if (!uri || !post_data || data_len <= 0) { ESP_LOGE(TAG, "Invalid parameters: uri=%p, post_data=%p, data_len=%d", uri, post_data, data_len); return NULL; } ESP_LOGD(TAG, "HTTP POST: uri=%s, post_data=%s, data_len=%d", uri, post_data, data_len); /* Perform HTTP request with retries */ while (attempts < max_attempts) { ESP_LOGI(TAG, "HTTP POST attempt %d/%d", attempts + 1, max_attempts); /* Allocate buffer for response */ const size_t max_response_size = 16384; // 增加缓冲区大小到16KB,解决响应被截断的问题 char *buffer = (char*)hal_malloc(max_response_size + 1); if (!buffer) { ESP_LOGE(TAG, "Failed to allocate buffer for response reading"); goto cleanup_attempt; } memset(buffer, 0, max_response_size + 1); /* Initialize response data structure */ response_data_t response_data = { .buffer = buffer, .buffer_size = max_response_size + 1, .total_read = 0, .max_size = max_response_size }; /* Configure HTTP client with event handler */ esp_http_client_config_t config = { .url = uri, .timeout_ms = 15000, .transport_type = HTTP_TRANSPORT_OVER_SSL, .crt_bundle_attach = esp_crt_bundle_attach, .skip_cert_common_name_check = true, .method = HTTP_METHOD_POST, .event_handler = http_event_handler, .user_data = &response_data, // Pass response data to event handler .disable_auto_redirect = false, // Enable redirects .max_redirection_count = 3, // Allow up to 3 redirects }; /* Initialize HTTP client */ client = esp_http_client_init(&config); if (client == NULL) { ESP_LOGE(TAG, "Failed to initialize HTTP client"); hal_free(buffer); goto cleanup_attempt; } /* Set content type to application/json */ err = esp_http_client_set_header(client, "Content-Type", "application/json"); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to set Content-Type header: %s", esp_err_to_name(err)); goto cleanup_attempt; } /* Set POST data */ err = esp_http_client_set_post_field(client, post_data, data_len); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to set POST field: %s", esp_err_to_name(err)); goto cleanup_attempt; } /* Perform HTTP request */ /* The event handler will capture response data */ ESP_LOGI(TAG, "Performing HTTP request..."); err = esp_http_client_perform(client); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to perform HTTP request: %s", esp_err_to_name(err)); goto cleanup_attempt; } /* Get response status */ response_status = esp_http_client_get_status_code(client); ESP_LOGI(TAG, "HTTP response status: %d", response_status); if (response_status == 200) { /* Get content length from response header */ int64_t content_length = esp_http_client_get_content_length(client); ESP_LOGI(TAG, "Content-Length: %lld, actually read: %d", content_length, response_data.total_read); if (response_data.total_read > 0) { /* Ensure null termination */ buffer[response_data.total_read] = '\0'; ESP_LOGI(TAG, "Successfully read response: %d bytes", response_data.total_read); ESP_LOGD(TAG, "Response data: %s", buffer); /* Copy to final buffer */ response_buffer = (char*)hal_malloc(response_data.total_read + 1); if (response_buffer) { memcpy(response_buffer, buffer, response_data.total_read + 1); } else { ESP_LOGE(TAG, "Failed to allocate final response buffer"); } } else { ESP_LOGE(TAG, "Failed to read any response data via event handler"); /* Try a fallback method - read directly after perform */ ESP_LOGI(TAG, "Trying fallback: direct read after perform..."); int fallback_read = esp_http_client_read_response(client, buffer, max_response_size); if (fallback_read > 0) { ESP_LOGI(TAG, "Fallback successful: read %d bytes", fallback_read); buffer[fallback_read] = '\0'; ESP_LOGD(TAG, "Response data: %s", buffer); response_buffer = (char*)hal_malloc(fallback_read + 1); if (response_buffer) { memcpy(response_buffer, buffer, fallback_read + 1); } } else { ESP_LOGE(TAG, "Fallback also failed: %d", fallback_read); } } } else { ESP_LOGW(TAG, "Non-200 status: %d", response_status); } cleanup_attempt: /* Clean up client and buffer */ if (client != NULL) { esp_http_client_cleanup(client); client = NULL; } if (buffer != NULL) { hal_free(buffer); buffer = NULL; } /* If we got a response, break the retry loop */ if (response_buffer) { break; } attempts++; if (attempts < max_attempts) { int backoff_ms = 500 * attempts; ESP_LOGI(TAG, "Retrying in %d ms...", backoff_ms); hal_thread_sleep(backoff_ms); } } if (attempts == max_attempts) { ESP_LOGE(TAG, "HTTP POST failed after %d attempts", max_attempts); } ESP_LOGI(TAG, "HTTP POST request completed, response_buffer=%p", response_buffer); return response_buffer; }