LISA HTTP 组件

基于 HTTP 协议的客户端组件,为 ARCS 平台提供完整的 HTTP 通信功能,支持多种请求方法、同步和异步传输、文件下载、分块传输等功能。

功能特性

  • HTTP 方法支持: 支持 GET、POST、PUT、PATCH、DELETE 等标准 HTTP 方法

  • 传输模式: 支持同步传输、分块传输和文件下载多种传输模式

  • HTTPS 兼容: 自动将 HTTPS 转换为 HTTP 处理,简化安全连接使用

  • 灵活配置: 支持自定义头部、请求体、超时时间等配置选项

  • 回调机制: 提供灵活的数据接收回调函数,支持流式数据处理

  • 内存管理: 自动内存管理,支持动态分配和释放 HTTP 实例

  • 错误处理: 完善的错误代码体系,便于问题定位和处理

  • 大文件支持: 分块传输模式支持大文件下载,单个数据块最大 4096 字节

配置选项

prj.conf 中启用组件:

CONFIG_LISA_HTTP=y     # 启用 HTTP 组件

API 接口

生命周期管理

lisa_http_t *lisa_http_init(lisa_http_request_t *req);
lisa_http_err_e lisa_http_cleanup(lisa_http_t *ins);

HTTP 请求接口

lisa_http_err_e lisa_http_perform(lisa_http_t *ins);
lisa_http_err_e lisa_http_download(lisa_http_t *ins);

分块传输接口

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 请求示例

#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 请求示例

#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;
}

文件下载示例

#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;
}

分块传输示例

#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;
}

多请求管理示例

#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 - 组件构建配置文件