484 lines
14 KiB
Markdown
484 lines
14 KiB
Markdown
# LISA HTTP 组件
|
||
|
||
基于 HTTP 协议的客户端组件,为 ARCS 平台提供完整的 HTTP 通信功能,支持多种请求方法、同步和异步传输、文件下载、分块传输等功能。
|
||
|
||
## 功能特性
|
||
|
||
- **HTTP 方法支持**: 支持 GET、POST、PUT、PATCH、DELETE 等标准 HTTP 方法
|
||
- **传输模式**: 支持同步传输、分块传输和文件下载多种传输模式
|
||
- **HTTPS 兼容**: 自动将 HTTPS 转换为 HTTP 处理,简化安全连接使用
|
||
- **灵活配置**: 支持自定义头部、请求体、超时时间等配置选项
|
||
- **回调机制**: 提供灵活的数据接收回调函数,支持流式数据处理
|
||
- **内存管理**: 自动内存管理,支持动态分配和释放 HTTP 实例
|
||
- **错误处理**: 完善的错误代码体系,便于问题定位和处理
|
||
- **大文件支持**: 分块传输模式支持大文件下载,单个数据块最大 4096 字节
|
||
|
||
## 配置选项
|
||
|
||
在 `prj.conf` 中启用组件:
|
||
|
||
```kconfig
|
||
CONFIG_LISA_HTTP=y # 启用 HTTP 组件
|
||
```
|
||
|
||
## API 接口
|
||
|
||
### 生命周期管理
|
||
|
||
```c
|
||
lisa_http_t *lisa_http_init(lisa_http_request_t *req);
|
||
lisa_http_err_e lisa_http_cleanup(lisa_http_t *ins);
|
||
```
|
||
|
||
### HTTP 请求接口
|
||
|
||
```c
|
||
lisa_http_err_e lisa_http_perform(lisa_http_t *ins);
|
||
lisa_http_err_e lisa_http_download(lisa_http_t *ins);
|
||
```
|
||
|
||
### 分块传输接口
|
||
|
||
```c
|
||
lisa_http_err_e lisa_http_perform_chunked(lisa_http_t *ins);
|
||
lisa_http_err_e lisa_http_perform_chunked_with_cb(lisa_http_t *ins,
|
||
int (*on_chunk)(lisa_http_data_t *));
|
||
```
|
||
|
||
## 使用示例
|
||
|
||
### 基础 HTTP GET 请求示例
|
||
|
||
```c
|
||
#include "lisa_http.h"
|
||
#include "lisa_log.h"
|
||
|
||
// HTTP 响应数据回调函数
|
||
static void http_response_callback(lisa_http_data_t *data)
|
||
{
|
||
if (data && data->buf && data->len > 0) {
|
||
LISA_INFO("收到 HTTP 响应数据,长度: %d", data->len);
|
||
|
||
// 将数据转换为字符串并输出(假设是文本数据)
|
||
char *response = lisa_mem_alloc(data->len + 1);
|
||
if (response) {
|
||
memcpy(response, data->buf, data->len);
|
||
response[data->len] = '\0';
|
||
LISA_INFO("响应内容: %s", response);
|
||
lisa_mem_free(response);
|
||
}
|
||
}
|
||
}
|
||
|
||
int basic_http_get_example(void)
|
||
{
|
||
// 1. 配置 HTTP 请求
|
||
lisa_http_request_t request = {
|
||
.method = LISA_HTTP_GET,
|
||
.url = (uint8_t *)"http://httpbin.org/get",
|
||
.headers = NULL, // 使用默认头部
|
||
.body = NULL,
|
||
.body_len = 0,
|
||
.timeout = 10000, // 10秒超时
|
||
.user = NULL,
|
||
.on_data = http_response_callback,
|
||
};
|
||
|
||
// 2. 创建 HTTP 实例
|
||
lisa_http_t *http = lisa_http_init(&request);
|
||
if (!http) {
|
||
LISA_ERR("创建 HTTP 实例失败");
|
||
return -1;
|
||
}
|
||
|
||
// 3. 执行 HTTP 请求
|
||
lisa_http_err_e result = lisa_http_perform(http);
|
||
if (result == LISA_HTTP_OK) {
|
||
LISA_INFO("HTTP 请求执行成功");
|
||
} else {
|
||
LISA_ERR("HTTP 请求失败,错误代码: %d", result);
|
||
}
|
||
|
||
// 4. 清理资源
|
||
lisa_http_cleanup(http);
|
||
return (result == LISA_HTTP_OK) ? 0 : -1;
|
||
}
|
||
```
|
||
|
||
### HTTP POST 请求示例
|
||
|
||
```c
|
||
#include "lisa_http.h"
|
||
#include "lisa_log.h"
|
||
#include "cJSON.h"
|
||
|
||
// POST 请求响应回调函数
|
||
static void post_response_callback(lisa_http_data_t *data)
|
||
{
|
||
if (data && data->buf && data->len > 0) {
|
||
char *response = lisa_mem_alloc(data->len + 1);
|
||
if (response) {
|
||
memcpy(response, data->buf, data->len);
|
||
response[data->len] = '\0';
|
||
|
||
LISA_INFO("POST 响应: %s", response);
|
||
|
||
// 解析 JSON 响应(如果需要)
|
||
cJSON *json = cJSON_Parse(response);
|
||
if (json) {
|
||
// 处理 JSON 数据
|
||
LISA_INFO("JSON 解析成功");
|
||
cJSON_Delete(json);
|
||
}
|
||
|
||
lisa_mem_free(response);
|
||
}
|
||
}
|
||
}
|
||
|
||
int http_post_example(void)
|
||
{
|
||
// 1. 准备 POST 数据(JSON 格式)
|
||
cJSON *json_data = cJSON_CreateObject();
|
||
if (!json_data) {
|
||
LISA_ERR("创建 JSON 对象失败");
|
||
return -1;
|
||
}
|
||
|
||
cJSON_AddStringToObject(json_data, "name", "LISA HTTP Client");
|
||
cJSON_AddNumberToObject(json_data, "version", 1.0);
|
||
cJSON_AddStringToObject(json_data, "message", "Hello from LISA");
|
||
|
||
char *json_string = cJSON_PrintUnformatted(json_data);
|
||
cJSON_Delete(json_data);
|
||
|
||
if (!json_string) {
|
||
LISA_ERR("JSON 序列化失败");
|
||
return -1;
|
||
}
|
||
|
||
// 2. 配置 HTTP POST 请求
|
||
lisa_http_request_t request = {
|
||
.method = LISA_HTTP_POST,
|
||
.url = (uint8_t *)"http://httpbin.org/post",
|
||
.headers = (uint8_t *)"Content-Type: application/json\r\n", // 设置 JSON 头部
|
||
.body = json_string,
|
||
.body_len = strlen(json_string),
|
||
.timeout = 15000, // 15秒超时
|
||
.user = NULL,
|
||
.on_data = post_response_callback,
|
||
};
|
||
|
||
// 3. 创建并执行 HTTP 请求
|
||
lisa_http_t *http = lisa_http_init(&request);
|
||
if (!http) {
|
||
LISA_ERR("创建 HTTP 实例失败");
|
||
free(json_string);
|
||
return -1;
|
||
}
|
||
|
||
lisa_http_err_e result = lisa_http_perform(http);
|
||
|
||
// 4. 清理资源
|
||
lisa_http_cleanup(http);
|
||
free(json_string);
|
||
|
||
return (result == LISA_HTTP_OK) ? 0 : -1;
|
||
}
|
||
```
|
||
|
||
### 文件下载示例
|
||
|
||
```c
|
||
#include "lisa_http.h"
|
||
#include "lisa_log.h"
|
||
#include "lisa_fs.h" // 假设有文件系统接口
|
||
|
||
// 文件下载上下文
|
||
typedef struct {
|
||
FILE *file;
|
||
uint32_t total_size;
|
||
uint32_t downloaded_size;
|
||
} download_context_t;
|
||
|
||
// 文件下载回调函数
|
||
static void download_callback(lisa_http_data_t *data)
|
||
{
|
||
download_context_t *ctx = (download_context_t *)data->user;
|
||
|
||
if (ctx && ctx->file && data && data->buf && data->len > 0) {
|
||
// 写入文件
|
||
size_t written = fwrite(data->buf, 1, data->len, ctx->file);
|
||
if (written == data->len) {
|
||
ctx->downloaded_size += written;
|
||
LISA_INFO("下载进度: %d/%d 字节 (%.1f%%)",
|
||
ctx->downloaded_size, ctx->total_size,
|
||
(float)ctx->downloaded_size * 100.0f / ctx->total_size);
|
||
} else {
|
||
LISA_ERR("文件写入失败");
|
||
}
|
||
}
|
||
}
|
||
|
||
int file_download_example(void)
|
||
{
|
||
// 1. 准备下载上下文
|
||
download_context_t download_ctx = {0};
|
||
download_ctx.file = fopen("/tmp/downloaded_file.bin", "wb");
|
||
if (!download_ctx.file) {
|
||
LISA_ERR("无法创建下载文件");
|
||
return -1;
|
||
}
|
||
|
||
download_ctx.total_size = 0; // 初始不知道文件大小
|
||
|
||
// 2. 配置下载请求
|
||
lisa_http_request_t request = {
|
||
.method = LISA_HTTP_GET,
|
||
.url = (uint8_t *)"http://httpbin.org/bytes/1024", // 下载 1KB 数据
|
||
.headers = NULL,
|
||
.body = NULL,
|
||
.body_len = 0,
|
||
.timeout = 30000, // 30秒超时
|
||
.user = &download_ctx,
|
||
.on_data = download_callback,
|
||
};
|
||
|
||
// 3. 执行下载
|
||
lisa_http_t *http = lisa_http_init(&request);
|
||
if (!http) {
|
||
LISA_ERR("创建 HTTP 实例失败");
|
||
fclose(download_ctx.file);
|
||
return -1;
|
||
}
|
||
|
||
lisa_http_err_e result = lisa_http_download(http);
|
||
|
||
// 4. 清理资源
|
||
fclose(download_ctx.file);
|
||
lisa_http_cleanup(http);
|
||
|
||
if (result == LISA_HTTP_OK) {
|
||
LISA_INFO("文件下载完成,总大小: %d 字节", download_ctx.downloaded_size);
|
||
} else {
|
||
LISA_ERR("文件下载失败,错误代码: %d", result);
|
||
}
|
||
|
||
return (result == LISA_HTTP_OK) ? 0 : -1;
|
||
}
|
||
```
|
||
|
||
### 分块传输示例
|
||
|
||
```c
|
||
#include "lisa_http.h"
|
||
#include "lisa_log.h"
|
||
|
||
// 分块数据处理回调函数
|
||
static int chunk_callback(lisa_http_data_t *data)
|
||
{
|
||
if (data && data->buf && data->len > 0) {
|
||
LISA_INFO("收到数据块,大小: %d 字节", data->len);
|
||
|
||
// 处理数据块(这里简单计算校验和)
|
||
uint8_t checksum = 0;
|
||
uint8_t *chunk_data = (uint8_t *)data->buf;
|
||
for (int i = 0; i < data->len; i++) {
|
||
checksum ^= chunk_data[i];
|
||
}
|
||
|
||
LISA_INFO("数据块校验和: 0x%02X", checksum);
|
||
|
||
// 检查是否需要继续接收数据
|
||
// 返回 0 继续,返回非 0 停止传输
|
||
static int chunk_count = 0;
|
||
chunk_count++;
|
||
|
||
if (chunk_count >= 5) {
|
||
LISA_INFO("接收到 5 个数据块,停止传输");
|
||
return -1; // 停止传输
|
||
}
|
||
}
|
||
|
||
return 0; // 继续接收数据
|
||
}
|
||
|
||
int chunked_transfer_example(void)
|
||
{
|
||
// 1. 配置分块传输请求
|
||
lisa_http_request_t request = {
|
||
.method = LISA_HTTP_GET,
|
||
.url = (uint8_t *)"http://httpbin.org/stream/20", // 流式数据接口
|
||
.headers = NULL,
|
||
.body = NULL,
|
||
.body_len = 0,
|
||
.timeout = 20000, // 20秒超时
|
||
.user = NULL,
|
||
.on_data = NULL, // 使用自定义回调,不需要这个
|
||
};
|
||
|
||
// 2. 创建 HTTP 实例
|
||
lisa_http_t *http = lisa_http_init(&request);
|
||
if (!http) {
|
||
LISA_ERR("创建 HTTP 实例失败");
|
||
return -1;
|
||
}
|
||
|
||
// 3. 执行分块传输(使用自定义回调)
|
||
lisa_http_err_e result = lisa_http_perform_chunked_with_cb(http, chunk_callback);
|
||
|
||
// 4. 清理资源
|
||
lisa_http_cleanup(http);
|
||
|
||
if (result == LISA_HTTP_OK) {
|
||
LISA_INFO("分块传输完成");
|
||
} else {
|
||
LISA_ERR("分块传输失败,错误代码: %d", result);
|
||
}
|
||
|
||
return (result == LISA_HTTP_OK) ? 0 : -1;
|
||
}
|
||
```
|
||
|
||
### 多请求管理示例
|
||
|
||
```c
|
||
#include "lisa_http.h"
|
||
#include "lisa_log.h"
|
||
|
||
// 请求管理结构
|
||
typedef struct {
|
||
lisa_http_t *http;
|
||
char *name;
|
||
bool completed;
|
||
lisa_http_err_e result;
|
||
} http_request_t;
|
||
|
||
// 统一的响应处理函数
|
||
static void unified_response_handler(lisa_http_data_t *data)
|
||
{
|
||
// 从 user_data 中获取请求管理结构
|
||
http_request_t *req_info = (http_request_t *)data->user;
|
||
|
||
if (req_info) {
|
||
req_info->completed = true;
|
||
|
||
if (data && data->buf && data->len > 0) {
|
||
LISA_INFO("请求 '%s' 收到响应,长度: %d", req_info->name, data->len);
|
||
} else {
|
||
LISA_INFO("请求 '%s' 完成,无响应数据", req_info->name);
|
||
}
|
||
}
|
||
}
|
||
|
||
int multi_request_example(void)
|
||
{
|
||
// 1. 定义多个请求
|
||
http_request_t requests[3];
|
||
const char *urls[] = {
|
||
"http://httpbin.org/get",
|
||
"http://httpbin.org/uuid",
|
||
"http://httpbin.org/ip"
|
||
};
|
||
const char *names[] = {"GET请求", "UUID请求", "IP请求"};
|
||
|
||
// 2. 创建并配置多个 HTTP 实例
|
||
for (int i = 0; i < 3; i++) {
|
||
lisa_http_request_t request = {
|
||
.method = LISA_HTTP_GET,
|
||
.url = (uint8_t *)urls[i],
|
||
.headers = NULL,
|
||
.body = NULL,
|
||
.body_len = 0,
|
||
.timeout = 10000,
|
||
.user = &requests[i],
|
||
.on_data = unified_response_handler,
|
||
};
|
||
|
||
requests[i].http = lisa_http_init(&request);
|
||
requests[i].name = (char *)names[i];
|
||
requests[i].completed = false;
|
||
requests[i].result = LISA_HTTP_COMMON_ERR;
|
||
|
||
if (!requests[i].http) {
|
||
LISA_ERR("创建请求 '%s' 失败", names[i]);
|
||
}
|
||
}
|
||
|
||
// 3. 执行所有请求
|
||
for (int i = 0; i < 3; i++) {
|
||
if (requests[i].http) {
|
||
requests[i].result = lisa_http_perform(requests[i].http);
|
||
LISA_INFO("请求 '%s' 执行结果: %d", names[i], requests[i].result);
|
||
}
|
||
}
|
||
|
||
// 4. 等待所有请求完成(在实际应用中可能需要更复杂的同步机制)
|
||
for (int i = 0; i < 3; i++) {
|
||
if (requests[i].completed) {
|
||
LISA_INFO("请求 '%s' 已完成", names[i]);
|
||
}
|
||
}
|
||
|
||
// 5. 清理所有资源
|
||
for (int i = 0; i < 3; i++) {
|
||
if (requests[i].http) {
|
||
lisa_http_cleanup(requests[i].http);
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
## HTTP 方法说明
|
||
|
||
### 支持的 HTTP 方法
|
||
|
||
| 方法 | 用途 | 请求体 | 适用场景 |
|
||
|------|------|--------|---------|
|
||
| `LISA_HTTP_GET` | 获取资源 | 不支持 | 数据查询、信息获取 |
|
||
| `LISA_HTTP_POST` | 提交数据 | 支持 | 数据创建、表单提交 |
|
||
| `LISA_HTTP_PUT` | 更新资源 | 支持 | 完整资源更新 |
|
||
| `LISA_HTTP_PATCH` | 部分更新 | 支持 | 部分资源更新 |
|
||
| `LISA_HTTP_DELETE` | 删除资源 | 不支持 | 资源删除 |
|
||
|
||
### 传输模式对比
|
||
|
||
| 模式 | 同步性 | 适用场景 | 特点 |
|
||
|------|--------|---------|------|
|
||
| `lisa_http_perform()` | 同步 | 小数据量、简单请求 | 简单易用,等待完整响应 |
|
||
| `lisa_http_download()` | 同步 | 文件下载 | 优化的文件传输 |
|
||
| `lisa_http_perform_chunked()` | 异步 | 大数据流处理 | 内部回调,分块处理 |
|
||
| `lisa_http_perform_chunked_with_cb()` | 异步 | 自定义数据处理 | 用户控制数据处理流程 |
|
||
|
||
## 错误代码说明
|
||
|
||
| 错误代码 | 说明 | 可能原因 | 解决方法 |
|
||
|---------|------|---------|---------|
|
||
| `LISA_HTTP_OK` | 操作成功 | - | - |
|
||
| `LISA_HTTP_COMMON_ERR` | 一般错误 | 参数错误、内存不足 | 检查参数和内存状态 |
|
||
| `LISA_HTTP_PARAM_ERROR` | 参数错误 | URL 为空、回调函数为 NULL | 检查请求配置 |
|
||
| `LISA_HTTP_NET_ERR` | 网络错误 | 连接失败、超时 | 检查网络连接和超时设置 |
|
||
|
||
## 使用注意事项
|
||
|
||
1. **参数验证**: 创建 HTTP 实例前必须验证 URL 和回调函数不为 NULL
|
||
2. **内存管理**: 请求体数据在请求执行期间必须保持有效
|
||
3. **HTTPS 处理**: 组件会自动将 HTTPS URL 转换为 HTTP 处理
|
||
4. **超时设置**: 根据网络环境和数据大小合理设置超时时间
|
||
5. **回调函数**: 回调函数应尽量简短,避免阻塞网络处理线程
|
||
6. **分块大小**: 分块传输时单个数据块最大支持 4096 字节
|
||
7. **资源清理**: 请求完成后必须调用 `lisa_http_cleanup()` 释放资源
|
||
8. **错误处理**: 所有 API 调用都应检查返回值并处理错误情况
|
||
9. **URL 长度**: URL 最大长度为 1023 字符(包含结束符)
|
||
10. **请求体大小**: 请求体最大长度为 1024 字节
|
||
11. **线程安全**: 每个 HTTP 实例独立工作,但多线程使用时需要注意资源同步
|
||
12. **文件操作**: 文件下载时需要确保文件系统可用且有写入权限
|
||
|
||
## 文件说明
|
||
|
||
- `lisa_http.h` - HTTP 组件头文件,包含 API 接口和数据结构定义
|
||
- `lisa_http.c` - HTTP 组件实现文件,基于 HTTPCUsr_api 实现
|
||
- `Kconfig` - 组件配置选项定义文件
|
||
- `CMakeLists.txt` - 组件构建配置文件 |