Files
xiaozhi-esp32/managed_components/78__esp-ml307/include/http_client.h
2025-09-05 13:25:11 +08:00

156 lines
4.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef HTTP_CLIENT_H
#define HTTP_CLIENT_H
#include "http.h"
#include "tcp.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <map>
#include <string>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <optional>
#include <memory>
#include <deque>
#include <cstring>
#define EC801E_HTTP_EVENT_HEADERS_RECEIVED (1 << 0)
#define EC801E_HTTP_EVENT_BODY_RECEIVED (1 << 1)
#define EC801E_HTTP_EVENT_ERROR (1 << 2)
#define EC801E_HTTP_EVENT_COMPLETE (1 << 3)
class NetworkInterface;
class HttpClient : public Http {
public:
HttpClient(NetworkInterface* network, int connect_id = 0);
~HttpClient();
void SetTimeout(int timeout_ms) override;
void SetHeader(const std::string& key, const std::string& value) override;
void SetContent(std::string&& content) override;
bool Open(const std::string& method, const std::string& url) override;
void Close() override;
int Read(char* buffer, size_t buffer_size) override;
int Write(const char* buffer, size_t buffer_size) override;
int GetStatusCode() override;
std::string GetResponseHeader(const std::string& key) const override;
size_t GetBodyLength() override;
std::string ReadAll() override;
private:
// 数据块结构,用于队列缓冲
struct DataChunk {
std::string data;
size_t offset = 0; // 当前读取偏移
// 使用移动构造函数避免拷贝
DataChunk(std::string&& d) : data(std::move(d)), offset(0) {}
DataChunk(const std::string& d) : data(d), offset(0) {}
size_t available() const {
return data.size() - offset;
}
size_t read(char* buffer, size_t size) {
size_t bytes_to_read = std::min(size, available());
if (bytes_to_read > 0) {
memcpy(buffer, data.data() + offset, bytes_to_read);
offset += bytes_to_read;
}
return bytes_to_read;
}
bool empty() const {
return offset >= data.size();
}
};
// 头部条目结构体,用于高效存储和查找
struct HeaderEntry {
std::string original_key; // 保留原始大小写的key用于输出HTTP头部
std::string value; // 头部值
HeaderEntry() = default;
HeaderEntry(const std::string& key, const std::string& val)
: original_key(key), value(val) {}
};
NetworkInterface* network_;
int connect_id_;
std::unique_ptr<Tcp> tcp_;
EventGroupHandle_t event_group_handle_;
std::mutex mutex_;
std::condition_variable cv_;
// 用于读取操作的专门锁和缓冲区队列
std::mutex read_mutex_;
std::deque<DataChunk> body_chunks_;
std::condition_variable write_cv_;
const size_t MAX_BODY_CHUNKS_SIZE = 8192;
int status_code_ = -1;
int timeout_ms_ = 30000;
std::string rx_buffer_;
std::map<std::string, HeaderEntry> headers_; // key为小写用于快速查找
std::string url_;
std::string method_;
std::string protocol_;
std::string host_;
std::string path_;
int port_ = 80;
std::optional<std::string> content_ = std::nullopt;
std::map<std::string, HeaderEntry> response_headers_; // key为小写用于快速查找
// 移除原来的 body_ 变量,现在使用 body_chunks_ 队列
size_t body_offset_ = 0;
size_t content_length_ = 0;
size_t total_body_received_ = 0; // 总共接收的响应体字节数
bool eof_ = false;
bool connected_ = false;
bool headers_received_ = false;
bool request_chunked_ = false;
bool response_chunked_ = false;
bool connection_error_ = false; // 新增:标记连接是否异常断开
// HTTP 协议解析状态
enum class ParseState {
STATUS_LINE,
HEADERS,
BODY,
CHUNK_SIZE,
CHUNK_DATA,
CHUNK_TRAILER,
COMPLETE
};
ParseState parse_state_ = ParseState::STATUS_LINE;
size_t chunk_size_ = 0;
size_t chunk_received_ = 0;
// 私有方法
bool ParseUrl(const std::string& url);
std::string BuildHttpRequest();
void OnTcpData(const std::string& data);
void OnTcpDisconnected();
void ProcessReceivedData();
bool ParseStatusLine(const std::string& line);
bool ParseHeaderLine(const std::string& line);
void ParseChunkedBody(const std::string& data);
void ParseRegularBody(const std::string& data);
size_t ParseChunkSize(const std::string& chunk_size_line);
std::string GetNextLine(std::string& buffer);
bool HasCompleteLine(const std::string& buffer);
void SetError();
// 新增:向读取队列添加数据的方法
void AddBodyData(const std::string& data);
void AddBodyData(std::string&& data); // 移动版本
// 新增:检查数据是否完整接收
bool IsDataComplete() const;
};
#endif