From 8b4d226c4df1080078dd354fabf46f95a3618cac Mon Sep 17 00:00:00 2001 From: Caisking Date: Mon, 19 May 2025 18:00:32 +0800 Subject: [PATCH] =?utf8?q?=E5=A2=9E=E5=8A=A0https=E3=80=81websocket?= =?utf8?q?=E7=9B=B8=E5=85=B3=E6=9C=8D=E5=8A=A1=EF=BC=8C=E5=BE=85=E6=B5=8B?= =?utf8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../lwip/src/apps/http/https_client.c | 883 ++++++++++++++++++ .../lwip/src/apps/websocket/websocket.c | 527 +++++++++++ .../lwip/src/apps/websocket/websocket.h | 128 +++ .../lwip/src/include/lwip/apps/https_client.h | 201 ++++ 4 files changed, 1739 insertions(+) create mode 100644 code/sdk/zhihui/4g/cat1Service/lwip/src/apps/http/https_client.c create mode 100644 code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.c create mode 100644 code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.h create mode 100644 code/sdk/zhihui/4g/cat1Service/lwip/src/include/lwip/apps/https_client.h diff --git a/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/http/https_client.c b/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/http/https_client.c new file mode 100644 index 000000000..66372b8c2 --- /dev/null +++ b/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/http/https_client.c @@ -0,0 +1,883 @@ +/** + * @file + * HTTPS client implementation + */ + +/* + * Copyright (c) 2023 DoubaoAssistant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/apps/https_client.h" +#include "lwip/altcp_tcp.h" +#include "lwip/altcp_tls.h" +#include "lwip/debug.h" +#include "lwip/dns.h" +#include "lwip/init.h" +#include "lwip/mem.h" +#include +#include +#include + +#if LWIP_TCP && LWIP_CALLBACK_API && LWIP_SSL + +/** + * HTTPSC_DEBUG: Enable debugging for HTTPS client. + */ +#ifndef HTTPSC_DEBUG +#define HTTPSC_DEBUG LWIP_DBG_OFF +#endif + +/** Set this to 1 to keep server name and uri in request state */ +#ifndef HTTPSC_DEBUG_REQUEST +#define HTTPSC_DEBUG_REQUEST 0 +#endif + +/** This string is passed in the HTTP header as "User-Agent: " */ +#ifndef HTTPSC_CLIENT_AGENT +#define HTTPSC_CLIENT_AGENT \ + "lwIP/" LWIP_VERSION_STRING " (https://savannah.nongnu.org/projects/lwip)" +#endif + +/* the various debug levels for this file */ +#define HTTPSC_DEBUG_TRACE (HTTPSC_DEBUG | LWIP_DBG_TRACE) +#define HTTPSC_DEBUG_STATE (HTTPSC_DEBUG | LWIP_DBG_STATE) +#define HTTPSC_DEBUG_WARN (HTTPSC_DEBUG | LWIP_DBG_LEVEL_WARNING) +#define HTTPSC_DEBUG_WARN_STATE \ + (HTTPSC_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE) +#define HTTPSC_DEBUG_SERIOUS (HTTPSC_DEBUG | LWIP_DBG_LEVEL_SERIOUS) + +#define HTTPSC_POLL_INTERVAL 1 +#define HTTPSC_POLL_TIMEOUT 80 /* 15 seconds */ + +#define HTTPSC_CONTENT_LEN_INVALID 0xFFFFFFFF + +/* 内部状态定义 */ +typedef enum httpsc_conn_state { + HTTPSC_STATE_INIT = 0, + HTTPSC_STATE_CONNECTING, + HTTPSC_STATE_SSL_HANDSHAKING, + HTTPSC_STATE_SENDING, + HTTPSC_STATE_RECEIVING, + HTTPSC_STATE_CLOSING, + HTTPSC_STATE_ERROR +} httpsc_conn_state_t; + +/* 构建HTTP请求字符串 */ +static int httpsc_create_request_string(const httpsc_connection_t *settings, + const char *server_name, + int server_port, const char *uri, + int use_host, char *buffer, + size_t buffer_size) { + if (settings->use_proxy) { + LWIP_ASSERT("server_name != NULL", server_name != NULL); + if (server_port != HTTPS_DEFAULT_PORT) { + return snprintf(buffer, buffer_size, + "GET https://%s:%d%s HTTP/1.1\r\n" + "User-Agent: %s\r\n" + "Accept: */*\r\n" + "Host: %s\r\n" + "Connection: Close\r\n" + "\r\n", + server_name, server_port, uri, HTTPSC_CLIENT_AGENT, + server_name); + } else { + return snprintf(buffer, buffer_size, + "GET https://%s%s HTTP/1.1\r\n" + "User-Agent: %s\r\n" + "Accept: */*\r\n" + "Host: %s\r\n" + "Connection: Close\r\n" + "\r\n", + server_name, uri, HTTPSC_CLIENT_AGENT, server_name); + } + } else if (use_host) { + LWIP_ASSERT("server_name != NULL", server_name != NULL); + return snprintf(buffer, buffer_size, + "GET %s HTTP/1.1\r\n" + "User-Agent: %s\r\n" + "Accept: */*\r\n" + "Host: %s\r\n" + "Connection: Close\r\n" + "\r\n", + uri, HTTPSC_CLIENT_AGENT, server_name); + } else { + return snprintf(buffer, buffer_size, + "GET %s HTTP/1.1\r\n" + "User-Agent: %s\r\n" + "Accept: */*\r\n" + "Connection: Close\r\n" + "\r\n", + uri, HTTPSC_CLIENT_AGENT); + } +} + +/** 初始化SSL/TLS上下文 */ +static err_t httpsc_init_ssl_context(httpsc_state_t *req, + const char *server_name) { + err_t err = ERR_OK; + const httpsc_ssl_config_t *ssl_config = req->conn_settings->ssl_config; + + /* 初始化SSL上下文 */ + err = altcp_tls_init(&req->ssl_ctx, ssl_config->altcp_allocator); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPSC_DEBUG_SERIOUS, + ("httpsc_init_ssl_context: altcp_tls_init failed: %d\n", err)); + return err; + } + + /* 设置SSL/TLS版本 */ + if (ssl_config->tls_versions) { + altcp_tls_set_versions(&req->ssl_ctx, ssl_config->tls_versions); + } + + /* 设置密码套件 */ + if (ssl_config->ciphers) { + altcp_tls_set_ciphers(&req->ssl_ctx, ssl_config->ciphers); + } + + /* 设置证书验证回调 */ + if (ssl_config->verify_cert) { + altcp_tls_set_verify(&req->ssl_ctx, ssl_config->verify_cert, req); + } + + /* 设置信任的CA证书 */ + if (ssl_config->trust_anchor && ssl_config->trust_anchor_len > 0) { + err = altcp_tls_set_trust_anchor(&req->ssl_ctx, ssl_config->trust_anchor, + ssl_config->trust_anchor_len); + if (err != ERR_OK) { + LWIP_DEBUGF( + HTTPSC_DEBUG_SERIOUS, + ("httpsc_init_ssl_context: set trust anchor failed: %d\n", err)); + altcp_tls_free(&req->ssl_ctx); + return err; + } + } + + /* 设置是否验证证书 */ + altcp_tls_set_verify_mode( + &req->ssl_ctx, ssl_config->verify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE); + + /* 设置SNI */ + if (ssl_config->sni_hostname) { + altcp_tls_set_sni(&req->ssl_ctx, ssl_config->sni_hostname); + } else if (server_name) { + altcp_tls_set_sni(&req->ssl_ctx, server_name); + } + + return ERR_OK; +} + +/** 释放HTTPS客户端状态和资源 */ +static err_t httpsc_free_state(httpsc_state_t *req) { + if (req == NULL) { + return ERR_OK; + } + + /* 释放SSL上下文 */ + if (req->ssl_ctx.tcp_pcb != NULL) { + altcp_tls_free(&req->ssl_ctx); + } + + /* 释放请求缓冲区 */ + if (req->request != NULL) { + pbuf_free(req->request); + req->request = NULL; + } + + /* 释放接收的头部 */ + if (req->rx_hdrs != NULL) { + pbuf_free(req->rx_hdrs); + req->rx_hdrs = NULL; + } + + /* 关闭TCP连接 */ + if (req->pcb != NULL) { + err_t err; + altcp_arg(req->pcb, NULL); + altcp_recv(req->pcb, NULL); + altcp_err(req->pcb, NULL); + altcp_poll(req->pcb, NULL, 0); + altcp_sent(req->pcb, NULL); + + err = altcp_close(req->pcb); + if (err != ERR_OK) { + altcp_abort(req->pcb); + err = ERR_ABRT; + } + req->pcb = NULL; + return err; + } + + /* 释放状态内存 */ + mem_free(req); + return ERR_OK; +} + +/** 关闭连接并调用回调 */ +static err_t httpsc_close(httpsc_state_t *req, httpsc_result_t result, + u32_t server_response, err_t err) { + if (req != NULL) { + /* 调用结果回调 */ + if (req->conn_settings != NULL && req->conn_settings->result_fn != NULL) { + req->conn_settings->result_fn(req->callback_arg, result, + req->rx_content_len, server_response, err); + } + + /* 释放资源 */ + return httpsc_free_state(req); + } + return ERR_OK; +} + +/** 解析HTTP响应状态行 */ +static err_t httpsc_parse_response_status(struct pbuf *p, u16_t *http_version, + u16_t *http_status, + u16_t *status_str_offset) { + u16_t end1 = pbuf_memfind(p, "\r\n", 2, 0); + if (end1 == 0xFFFF) { + return ERR_VAL; + } + + /* 查找第一个空格 */ + u16_t space1 = pbuf_memfind(p, " ", 1, 0); + if (space1 == 0xFFFF) { + return ERR_VAL; + } + + /* 验证是否是HTTP响应 */ + if (pbuf_memcmp(p, 0, "HTTP/", 5) != 0 || pbuf_get_at(p, 6) != '.') { + return ERR_VAL; + } + + /* 解析HTTP版本 */ + *http_version = ((pbuf_get_at(p, 5) - '0') << 8) | (pbuf_get_at(p, 7) - '0'); + + /* 查找第二个空格 */ + u16_t space2 = pbuf_memfind(p, " ", 1, space1 + 1); + if (space2 == 0xFFFF) { + return ERR_VAL; + } + + /* 解析状态码 */ + char status_code[4] = {0}; + u16_t len = space2 - space1 - 1; + if (len > 3) { + len = 3; + } + + if (pbuf_copy_partial(p, status_code, len, space1 + 1) != len) { + return ERR_VAL; + } + + *http_status = atoi(status_code); + *status_str_offset = space2 + 1; + + return ERR_OK; +} + +/** 等待所有HTTP头部接收完成 */ +static err_t httpsc_wait_headers(struct pbuf *p, u32_t *content_length, + u16_t *total_header_len) { + u16_t end = pbuf_memfind(p, "\r\n\r\n", 4, 0); + if (end == 0xFFFF) { + return ERR_VAL; + } + + *total_header_len = end + 4; + *content_length = HTTPSC_CONTENT_LEN_INVALID; + + /* 查找Content-Length头部 */ + u16_t cl_pos = pbuf_memfind(p, "Content-Length: ", 16, 0); + if (cl_pos != 0xFFFF) { + u16_t cl_end = pbuf_memfind(p, "\r\n", 2, cl_pos); + if (cl_end != 0xFFFF) { + char cl_str[20] = {0}; + u16_t len = cl_end - cl_pos - 16; + if (len < sizeof(cl_str) - 1) { + if (pbuf_copy_partial(p, cl_str, len, cl_pos + 16) == len) { + *content_length = atoi(cl_str); + } + } + } + } + + return ERR_OK; +} + +/** SSL/TLS握手完成回调 */ +static void httpsc_ssl_handshake_done(void *arg, err_t err) { + httpsc_state_t *req = (httpsc_state_t *)arg; + + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPSC_DEBUG_WARN, + ("httpsc_ssl_handshake_done: failed: %d\n", err)); + httpsc_close(req, HTTPSC_RESULT_ERR_SSL_HANDSHAKE, 0, err); + return; + } + + LWIP_DEBUGF(HTTPSC_DEBUG_TRACE, ("httpsc_ssl_handshake_done: success\n")); + + /* 发送HTTP请求 */ + err = altcp_tls_write(&req->ssl_ctx, req->request->payload, + req->request->len - 1, 0); + if (err != ERR_OK) { + LWIP_DEBUGF(HTTPSC_DEBUG_WARN, + ("httpsc_ssl_handshake_done: write failed: %d\n", err)); + httpsc_close(req, HTTPSC_RESULT_ERR_CONNECT, 0, err); + return; + } + + /* 释放请求缓冲区 */ + pbuf_free(req->request); + req->request = NULL; + + /* 更新状态 */ + req->parse_state = HTTPSC_PARSE_WAIT_FIRST_LINE; +} + +/** TCP接收回调 */ +static err_t httpsc_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, + err_t err) { + httpsc_state_t *req = (httpsc_state_t *)arg; + + if (p == NULL) { + /* 连接关闭 */ + httpsc_result_t result; + + if (req->parse_state != HTTPSC_PARSE_RX_DATA) { + result = HTTPSC_RESULT_ERR_CLOSED; + } else if (req->hdr_content_len != HTTPSC_CONTENT_LEN_INVALID && + req->hdr_content_len != req->rx_content_len) { + result = HTTPSC_RESULT_ERR_CONTENT_LEN; + } else { + result = HTTPSC_RESULT_OK; + } + + return httpsc_close(req, result, req->rx_status, err); + } + + /* 重置超时计数器 */ + req->timeout_ticks = HTTPSC_POLL_TIMEOUT; + + /* 处理接收到的数据 */ + if (req->parse_state != HTTPSC_PARSE_RX_DATA) { + /* 处理头部 */ + if (req->rx_hdrs == NULL) { + req->rx_hdrs = p; + } else { + pbuf_cat(req->rx_hdrs, p); + } + + if (req->parse_state == HTTPSC_PARSE_WAIT_FIRST_LINE) { + /* 解析状态行 */ + u16_t status_str_offset; + err_t res = + httpsc_parse_response_status(req->rx_hdrs, &req->rx_http_version, + &req->rx_status, &status_str_offset); + if (res == ERR_OK) { + req->parse_state = HTTPSC_PARSE_WAIT_HEADERS; + } + } + + if (req->parse_state == HTTPSC_PARSE_WAIT_HEADERS) { + /* 等待所有头部接收完成 */ + u16_t header_len; + err_t res = + httpsc_wait_headers(req->rx_hdrs, &req->hdr_content_len, &header_len); + if (res == ERR_OK) { + /* 调用头部处理回调 */ + if (req->conn_settings != NULL && + req->conn_settings->headers_done_fn != NULL) { + res = req->conn_settings->headers_done_fn(req, req->callback_arg, + req->rx_hdrs, header_len, + req->hdr_content_len); + if (res != ERR_OK) { + return httpsc_close(req, HTTPSC_RESULT_LOCAL_ABORT, req->rx_status, + res); + } + } + + /* 从pbuf中移除头部,只保留数据部分 */ + struct pbuf *data = pbuf_free_header(req->rx_hdrs, header_len); + pbuf_free(req->rx_hdrs); + req->rx_hdrs = NULL; + + /* 更新状态并处理数据 */ + req->parse_state = HTTPSC_PARSE_RX_DATA; + p = data; + } + } + } + + /* 处理数据部分 */ + if (p != NULL && req->parse_state == HTTPSC_PARSE_RX_DATA) { + req->rx_content_len += p->tot_len; + + /* 调用数据接收回调 */ + if (req->recv_fn != NULL) { + err = req->recv_fn(req->callback_arg, pcb, p, err); + } else { + altcp_recved(pcb, p->tot_len); + pbuf_free(p); + err = ERR_OK; + } + } + + return err; +} + +/** TCP错误回调 */ +static void httpsc_tcp_err(void *arg, err_t err) { + httpsc_state_t *req = (httpsc_state_t *)arg; + + LWIP_DEBUGF(HTTPSC_DEBUG_WARN, ("httpsc_tcp_err: error %d\n", err)); + + if (req != NULL) { + httpsc_close(req, HTTPSC_RESULT_ERR_CONNECT, 0, err); + } +} + +/** TCP轮询回调 */ +static err_t httpsc_tcp_poll(void *arg, struct altcp_pcb *pcb) { + httpsc_state_t *req = (httpsc_state_t *)arg; + + LWIP_UNUSED_ARG(pcb); + + if (req != NULL) { + if (req->timeout_ticks > 0) { + req->timeout_ticks--; + } + + if (req->timeout_ticks == 0) { + LWIP_DEBUGF(HTTPSC_DEBUG_WARN, ("httpsc_tcp_poll: timeout\n")); + return httpsc_close(req, HTTPSC_RESULT_ERR_TIMEOUT, 0, ERR_OK); + } + } + + return ERR_OK; +} + +/** 初始化HTTPS连接 */ +static err_t httpsc_init_connection_common(httpsc_state_t **connection, + const httpsc_connection_t *settings, + const char *server_name, + u16_t server_port, const char *uri, + altcp_recv_fn recv_fn, + void *callback_arg, int use_host) { + err_t err; + httpsc_state_t *req; + char request_buf[1024]; + int request_len; + + LWIP_ASSERT("uri != NULL", uri != NULL); + + /* 构建HTTP请求字符串 */ + request_len = + httpsc_create_request_string(settings, server_name, server_port, uri, + use_host, request_buf, sizeof(request_buf)); + if (request_len <= 0 || request_len >= sizeof(request_buf)) { + return ERR_VAL; + } + + /* 分配状态结构 */ + req = (httpsc_state_t *)mem_malloc(sizeof(httpsc_state_t)); + if (req == NULL) { + return ERR_MEM; + } + memset(req, 0, sizeof(httpsc_state_t)); + + /* 初始化状态 */ + req->remote_port = settings->use_proxy ? settings->proxy_port : server_port; + req->timeout_ticks = HTTPSC_POLL_TIMEOUT; + req->parse_state = HTTPSC_PARSE_WAIT_FIRST_LINE; + req->conn_settings = settings; + req->recv_fn = recv_fn; + req->callback_arg = callback_arg; + + /* 分配请求缓冲区 */ + req->request = pbuf_alloc(PBUF_RAW, request_len + 1, PBUF_RAM); + if (req->request == NULL) { + mem_free(req); + return ERR_MEM; + } + + /* 复制请求数据 */ + memcpy(req->request->payload, request_buf, request_len); + ((char *)req->request->payload)[request_len] = + '\0'; /* 确保字符串以NULL结尾 */ + + /* 创建TCP控制块 */ + req->pcb = altcp_new(settings->altcp_allocator); + if (req->pcb == NULL) { + pbuf_free(req->request); + mem_free(req); + return ERR_MEM; + } + + /* 设置TCP回调 */ + altcp_arg(req->pcb, req); + altcp_recv(req->pcb, httpsc_tcp_recv); + altcp_err(req->pcb, httpsc_tcp_err); + altcp_poll(req->pcb, httpsc_tcp_poll, HTTPSC_POLL_INTERVAL); + + /* 初始化SSL上下文 */ + err = httpsc_init_ssl_context(req, server_name); + if (err != ERR_OK) { + altcp_abort(req->pcb); + pbuf_free(req->request); + mem_free(req); + return err; + } + + /* 绑定SSL上下文到TCP控制块 */ + err = altcp_tls_bind(req->pcb, &req->ssl_ctx, httpsc_ssl_handshake_done); + if (err != ERR_OK) { + altcp_abort(req->pcb); + pbuf_free(req->request); + mem_free(req); + return err; + } + + *connection = req; + return ERR_OK; +} + +/** 通过IP地址获取文件 */ +err_t httpsc_get_file(const ip_addr_t *server_addr, u16_t port, const char *uri, + const httpsc_connection_t *settings, + altcp_recv_fn recv_fn, void *callback_arg, + httpsc_state_t **connection) { + err_t err; + httpsc_state_t *req; + char *server_name_str = NULL; + + LWIP_ASSERT("server_addr != NULL", server_addr != NULL); + LWIP_ASSERT("uri != NULL", uri != NULL); + + /* 将IP地址转换为字符串 */ + server_name_str = ipaddr_ntoa(server_addr); + if (server_name_str == NULL) { + return ERR_VAL; + } + + /* 初始化连接 */ + err = httpsc_init_connection_common(&req, settings, server_name_str, port, + uri, recv_fn, callback_arg, 1); + if (err != ERR_OK) { + return err; + } + + /* 连接到服务器 */ + if (settings->use_proxy) { + err = altcp_connect(req->pcb, &settings->proxy_addr, settings->proxy_port, + NULL); + } else { + err = altcp_connect(req->pcb, server_addr, port, NULL); + } + + if (err != ERR_OK) { + httpsc_free_state(req); + return err; + } + + if (connection != NULL) { + *connection = req; + } + + return ERR_OK; +} + +/** 通过域名获取文件 */ +err_t httpsc_get_file_dns(const char *server_name, u16_t port, const char *uri, + const httpsc_connection_t *settings, + altcp_recv_fn recv_fn, void *callback_arg, + httpsc_state_t **connection) { + err_t err; + httpsc_state_t *req; + + LWIP_ASSERT("server_name != NULL", server_name != NULL); + LWIP_ASSERT("uri != NULL", uri != NULL); + + /* 初始化连接 */ + err = httpsc_init_connection_common(&req, settings, server_name, port, uri, + recv_fn, callback_arg, 1); + if (err != ERR_OK) { + return err; + } + + /* 解析域名 */ + if (settings->use_proxy) { + err = altcp_connect(req->pcb, &settings->proxy_addr, settings->proxy_port, + NULL); + } else { + err = dns_gethostbyname(server_name, &req->remote_addr, + (dns_found_callback)httpsc_get_file, req); + if (err == ERR_INPROGRESS) { + /* 域名解析正在进行中 */ + if (connection != NULL) { + *connection = req; + } + return ERR_OK; + } else if (err != ERR_OK) { + /* 域名解析失败 */ + httpsc_free_state(req); + return err; + } + + /* 域名已缓存或直接是IP地址字符串 */ + err = altcp_connect(req->pcb, &req->remote_addr, port, NULL); + } + + if (err != ERR_OK) { + httpsc_free_state(req); + return err; + } + + if (connection != NULL) { + *connection = req; + } + + return ERR_OK; +} + +#if LWIP_HTTPSC_HAVE_FILE_IO +/* 文件下载功能实现 */ + +typedef struct _httpsc_filestate { + const char *local_file_name; + FILE *file; + httpsc_connection_t settings; + const httpsc_connection_t *client_settings; + void *callback_arg; +} httpsc_filestate_t; + +/** 文件接收回调 */ +static err_t httpsc_fs_tcp_recv(void *arg, struct altcp_pcb *pcb, + struct pbuf *p, err_t err) { + httpsc_filestate_t *fs = (httpsc_filestate_t *)arg; + struct pbuf *q; + + if (p == NULL) { + /* 连接关闭 */ + return err; + } + + /* 将数据写入文件 */ + for (q = p; q != NULL; q = q->next) { + if (fs->file) { + fwrite(q->payload, 1, q->len, fs->file); + } + } + + altcp_recved(pcb, p->tot_len); + pbuf_free(p); + + return ERR_OK; +} + +/** 文件下载完成回调 */ +static void httpsc_fs_result(void *arg, httpsc_result_t result, + u32_t rx_content_len, u32_t srv_res, err_t err) { + httpsc_filestate_t *fs = (httpsc_filestate_t *)arg; + + /* 关闭文件 */ + if (fs->file) { + fclose(fs->file); + fs->file = NULL; + } + + /* 调用用户回调 */ + if (fs->client_settings->result_fn) { + fs->client_settings->result_fn(fs->callback_arg, result, rx_content_len, + srv_res, err); + } + + /* 释放内存 */ + mem_free(fs); +} + +/** 初始化文件下载状态 */ +static err_t httpsc_fs_init(httpsc_filestate_t **filestate_out, + const char *local_file_name, + const httpsc_connection_t *settings, + void *callback_arg) { + httpsc_filestate_t *fs; + size_t name_len; + + name_len = strlen(local_file_name); + fs = (httpsc_filestate_t *)mem_malloc(sizeof(httpsc_filestate_t) + name_len + + 1); + if (fs == NULL) { + return ERR_MEM; + } + + memset(fs, 0, sizeof(httpsc_filestate_t)); + fs->local_file_name = (const char *)(fs + 1); + memcpy((char *)fs->local_file_name, local_file_name, name_len + 1); + fs->client_settings = settings; + fs->callback_arg = callback_arg; + + /* 复制设置并替换回调函数 */ + memcpy(&fs->settings, settings, sizeof(httpsc_connection_t)); + fs->settings.result_fn = httpsc_fs_result; + + /* 打开文件 */ + fs->file = fopen(local_file_name, "wb"); + if (fs->file == NULL) { + mem_free(fs); + return ERR_VAL; + } + + *filestate_out = fs; + return ERR_OK; +} + +/** 通过IP地址下载文件到磁盘 */ +err_t httpsc_get_file_to_disk(const ip_addr_t *server_addr, u16_t port, + const char *uri, + const httpsc_connection_t *settings, + void *callback_arg, const char *local_file_name, + httpsc_state_t **connection) { + err_t err; + httpsc_filestate_t *fs; + httpsc_state_t *req; + + LWIP_ASSERT("server_addr != NULL", server_addr != NULL); + LWIP_ASSERT("uri != NULL", uri != NULL); + LWIP_ASSERT("local_file_name != NULL", local_file_name != NULL); + + /* 初始化文件状态 */ + err = httpsc_fs_init(&fs, local_file_name, settings, callback_arg); + if (err != ERR_OK) { + return err; + } + + /* 初始化连接 */ + char *server_name_str = ipaddr_ntoa(server_addr); + if (server_name_str == NULL) { + mem_free(fs); + return ERR_VAL; + } + + err = httpsc_init_connection_common(&req, &fs->settings, server_name_str, + port, uri, httpsc_fs_tcp_recv, fs, 1); + if (err != ERR_OK) { + mem_free(fs); + return err; + } + + /* 连接到服务器 */ + if (settings->use_proxy) { + err = altcp_connect(req->pcb, &settings->proxy_addr, settings->proxy_port, + NULL); + } else { + err = altcp_connect(req->pcb, server_addr, port, NULL); + } + + if (err != ERR_OK) { + httpsc_free_state(req); + mem_free(fs); + return err; + } + + if (connection != NULL) { + *connection = req; + } + + return ERR_OK; +} + +/** 通过域名下载文件到磁盘 */ +err_t httpsc_get_file_dns_to_disk(const char *server_name, u16_t port, + const char *uri, + const httpsc_connection_t *settings, + void *callback_arg, + const char *local_file_name, + httpsc_state_t **connection) { + err_t err; + httpsc_filestate_t *fs; + httpsc_state_t *req; + + LWIP_ASSERT("server_name != NULL", server_name != NULL); + LWIP_ASSERT("uri != NULL", uri != NULL); + LWIP_ASSERT("local_file_name != NULL", local_file_name != NULL); + + /* 初始化文件状态 */ + err = httpsc_fs_init(&fs, local_file_name, settings, callback_arg); + if (err != ERR_OK) { + return err; + } + + /* 初始化连接 */ + err = httpsc_init_connection_common(&req, &fs->settings, server_name, port, + uri, httpsc_fs_tcp_recv, fs, 1); + if (err != ERR_OK) { + mem_free(fs); + return err; + } + + /* 解析域名 */ + if (settings->use_proxy) { + err = altcp_connect(req->pcb, &settings->proxy_addr, settings->proxy_port, + NULL); + } else { + err = dns_gethostbyname(server_name, &req->remote_addr, + (dns_found_callback)httpsc_get_file, req); + if (err == ERR_INPROGRESS) { + /* 域名解析正在进行中 */ + if (connection != NULL) { + *connection = req; + } + return ERR_OK; + } else if (err != ERR_OK) { + /* 域名解析失败 */ + httpsc_free_state(req); + mem_free(fs); + return err; + } + + /* 域名已缓存或直接是IP地址字符串 */ + err = altcp_connect(req->pcb, &req->remote_addr, port, NULL); + } + + if (err != ERR_OK) { + httpsc_free_state(req); + mem_free(fs); + return err; + } + + if (connection != NULL) { + *connection = req; + } + + return ERR_OK; +} +#endif /* LWIP_HTTPSC_HAVE_FILE_IO */ + +#endif /* LWIP_TCP && LWIP_CALLBACK_API && LWIP_SSL */ \ No newline at end of file diff --git a/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.c b/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.c new file mode 100644 index 000000000..c59845726 --- /dev/null +++ b/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.c @@ -0,0 +1,527 @@ +/** + * @file + * WebSocket 服务器实现(支持 HTTP/HTTPS 双协议) + */ + +#include "lwip/apps/websocket.h" +#include "lwip/altcp.h" +#include "lwip/altcp_tcp.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/sys.h" +#include +#include +#include "lwip/apps/https_client.h" // 引入 HTTPS 相关头文件 + +#if LWIP_TCP && LWIP_CALLBACK_API + +#define WS_MAGIC_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" // WebSocket 魔法密钥 +#define WS_HANDSHAKE_RESPONSE_HTTP \ // HTTP 握手响应(非加密) + "HTTP/1.1 101 Switching Protocols\r\n" \ + "Upgrade: websocket\r\n" \ + "Connection: Upgrade\r\n" \ + "Sec-WebSocket-Accept: %s\r\n\r\n" +#define WS_HANDSHAKE_RESPONSE_HTTPS WS_HANDSHAKE_RESPONSE_HTTP // HTTPS 握手响应(加密通道发送) + +/* WebSocket 连接状态(扩展:区分 HTTP/HTTPS 连接) */ +struct _websocket_conn { + struct altcp_pcb *pcb; // TCP 控制块指针 + websocket_settings_t settings; // 连接配置参数 + struct pbuf *rx_buf; // 接收缓冲区 + size_t rx_offset; // 接收偏移量 + u8_t is_websocket:1; // 是否已完成 WebSocket 握手 + u8_t is_secure:1; // 是否为 HTTPS/WSS 连接 + u8_t is_closing:1; // 连接关闭标志 + struct altcp_tls_context ssl_ctx; // SSL/TLS 上下文(仅 HTTPS 连接使用) +}; + +static err_t websocket_handshake(websocket_conn_t *conn, struct pbuf *p); +static err_t websocket_parse_frame(websocket_conn_t *conn, struct pbuf *p); +static err_t websocket_send_frame(websocket_conn_t *conn, u8_t opcode, + const void *data, size_t len, u8_t is_fin); +static void websocket_close_conn(websocket_conn_t *conn, u16_t reason, + const char *reason_msg, size_t reason_msg_len); + +/* 检查连接是否为 HTTPS/WSS */ +u8_t websocket_is_secure(websocket_conn_t *conn) { + return conn ? conn->is_secure : 0; +} + +/* TCP 连接接受回调(区分 HTTP/HTTPS 连接) */ +static err_t websocket_accept(void *arg, struct altcp_pcb *pcb, err_t err) { + websocket_settings_t *settings = (websocket_settings_t *)arg; + websocket_conn_t *conn; + LWIP_UNUSED_ARG(err); + + if (pcb == NULL || settings == NULL) { + return ERR_ARG; + } + + conn = (websocket_conn_t *)mem_calloc(1, sizeof(websocket_conn_t)); + if (conn == NULL) { + altcp_abort(pcb); + return ERR_MEM; + } + + conn->pcb = pcb; + memcpy(&conn->settings, settings, sizeof(websocket_settings_t)); + conn->rx_buf = NULL; + conn->rx_offset = 0; + conn->is_websocket = 0; + conn->is_closing = 0; + + // 判断是否为 HTTPS 连接(根据端口或 SSL 配置) + conn->is_secure = (settings->ssl_config != NULL); + if (conn->is_secure) { + // 初始化 SSL/TLS 上下文 + if (altcp_tls_init(&conn->ssl_ctx, settings->altcp_allocator) != ERR_OK) { + mem_free(conn); + altcp_abort(pcb); + return ERR_MEM; + } + + // 配置 SSL/TLS 参数 + if (settings->ssl_config) { + altcp_tls_set_config(&conn->ssl_ctx, settings->ssl_config); + } + } + + altcp_arg(pcb, conn); + altcp_recv(pcb, websocket_tcp_recv); + altcp_err(pcb, websocket_tcp_err); + altcp_poll(pcb, websocket_tcp_poll, 2); + + return ERR_OK; +} + +/* TCP 接收回调(处理 HTTP/HTTPS 数据) */ +static err_t websocket_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) { + websocket_conn_t *conn = (websocket_conn_t *)arg; + err_t ret = ERR_OK; + + if (err != ERR_OK || p == NULL) { + // 连接关闭或错误 + if (conn != NULL) { + if (conn->settings.close_fn) { + conn->settings.close_fn(conn, conn->settings.arg); + } + if (conn->is_secure) { + altcp_tls_free(&conn->ssl_ctx); + } + if (conn->rx_buf != NULL) { + pbuf_free(conn->rx_buf); + } + mem_free(conn); + } + return ERR_OK; + } + + if (conn == NULL || conn->is_closing) { + pbuf_free(p); + return ERR_ARG; + } + + // 未完成 WebSocket 握手,处理 HTTP/HTTPS 请求 + if (!conn->is_websocket) { + if (conn->is_secure) { + // HTTPS 连接:先进行 TLS 解密(简化处理,实际需调用 altcp_tls_read) + struct pbuf *decrypted = NULL; + err = altcp_tls_read(&conn->ssl_ctx, &decrypted, 0); + if (err == ERR_OK && decrypted) { + ret = websocket_handshake(conn, decrypted); + pbuf_free(decrypted); + } else { + ret = err; + } + } else { + // HTTP 连接:直接处理明文 + ret = websocket_handshake(conn, p); + } + pbuf_free(p); + return ret; + } + + // 已完成握手,处理 WebSocket 帧 + ret = websocket_parse_frame(conn, p); + pbuf_free(p); + return ret; +} + +/* TCP 错误回调 */ +static void websocket_tcp_err(void *arg, err_t err) { + websocket_conn_t *conn = (websocket_conn_t *)arg; + LWIP_UNUSED_ARG(err); + + if (conn != NULL) { + if (conn->settings.close_fn) { + conn->settings.close_fn(conn, conn->settings.arg); + } + if (conn->is_secure) { + altcp_tls_free(&conn->ssl_ctx); + } + if (conn->rx_buf != NULL) { + pbuf_free(conn->rx_buf); + } + mem_free(conn); + } +} + +/* TCP 轮询回调 */ +static err_t websocket_tcp_poll(void *arg, struct altcp_pcb *pcb) { + websocket_conn_t *conn = (websocket_conn_t *)arg; + LWIP_UNUSED_ARG(pcb); + + if (conn == NULL) { + return ERR_ARG; + } + + // 启用心跳机制,定期发送 PING 帧 + if (conn->settings.enable_ping_pong && conn->is_websocket && !conn->is_closing) { + return websocket_send_frame(conn, WS_OPCODE_PING, NULL, 0, 1); + } + + return ERR_OK; +} + +/* 初始化 WebSocket 服务器 */ +err_t websocket_server_init(u16_t port, const websocket_settings_t *settings) { + struct altcp_pcb *pcb; + err_t err; + + if (settings == NULL || settings->recv_fn == NULL) { + return ERR_ARG; + } + + // 创建 TCP 控制块 + pcb = altcp_new(settings->altcp_allocator ? settings->altcp_allocator() : altcp_tcp_alloc()); + if (pcb == NULL) { + return ERR_MEM; + } + + // 绑定端口 + err = altcp_bind(pcb, IP_ADDR_ANY, port); + if (err != ERR_OK) { + altcp_close(pcb); + return err; + } + + // 开始监听 + pcb = altcp_listen(pcb); + if (pcb == NULL) { + return ERR_MEM; + } + + // 设置回调函数 + altcp_arg(pcb, (void *)settings); + altcp_accept(pcb, websocket_accept); + + return ERR_OK; +} + +/* 通过 WebSocket 发送数据 */ +err_t websocket_send(websocket_conn_t *conn, u8_t opcode, + const void *data, size_t len, u8_t is_fin) { + if (conn == NULL || !conn->is_websocket || conn->is_closing) { + return ERR_ARG; + } + + return websocket_send_frame(conn, opcode, data, len, is_fin); +} + +/* 关闭 WebSocket 连接 */ +err_t websocket_close(websocket_conn_t *conn, u16_t reason, + const char *reason_msg, size_t reason_msg_len) { + if (conn == NULL || !conn->is_websocket || conn->is_closing) { + return ERR_ARG; + } + + websocket_close_conn(conn, reason, reason_msg, reason_msg_len); + return ERR_OK; +} + +/* 处理 WebSocket 握手(HTTP 升级请求) */ +static err_t websocket_handshake(websocket_conn_t *conn, struct pbuf *p) { + char *data, *key, *response; + char accept_key[29]; + size_t data_len, response_len; + err_t err; + + // 检查是否接收到完整的 HTTP 请求(包含 \r\n\r\n) + if (pbuf_memfind(p, "\r\n\r\n", 4, 0) == 0xFFFF) { + return ERR_INPROGRESS; // 数据不完整 + } + + // 分配内存并复制请求数据 + data_len = p->tot_len + 1; + data = (char *)mem_malloc(data_len); + if (data == NULL) { + return ERR_MEM; + } + + if (pbuf_copy_partial(p, data, p->tot_len, 0) != p->tot_len) { + mem_free(data); + return ERR_VAL; + } + data[p->tot_len] = '\0'; + + // 验证是否为 WebSocket 升级请求 + if (strstr(data, "Upgrade: websocket") == NULL || + strstr(data, "Connection: Upgrade") == NULL) { + mem_free(data); + return ERR_VAL; // 非 WebSocket 请求 + } + + // 提取 Sec-WebSocket-Key + key = strstr(data, "Sec-WebSocket-Key: "); + if (key == NULL) { + mem_free(data); + return ERR_VAL; + } + key += 19; // 跳过 "Sec-WebSocket-Key: " 前缀 + + // 计算 Sec-WebSocket-Accept + { + unsigned char sha1[20]; + char combined[60]; + size_t key_len = strcspn(key, "\r\n"); + + if (key_len != 24) { + mem_free(data); + return ERR_VAL; + } + + snprintf(combined, sizeof(combined), "%.*s%s", (int)key_len, key, WS_MAGIC_KEY); + mbedtls_sha1((unsigned char *)combined, strlen(combined), sha1); + base64_encode(sha1, sizeof(sha1), accept_key, sizeof(accept_key)); + } + + // 生成握手响应 + response_len = strlen(WS_HANDSHAKE_RESPONSE_HTTP) + strlen(accept_key); + response = (char *)mem_malloc(response_len); + if (response == NULL) { + mem_free(data); + return ERR_MEM; + } + + snprintf(response, response_len, WS_HANDSHAKE_RESPONSE_HTTP, accept_key); + + // 发送握手响应(根据连接类型选择普通或加密通道) + if (conn->is_secure) { + err = altcp_tls_write(&conn->ssl_ctx, response, strlen(response), 0); + } else { + err = altcp_write(conn->pcb, response, strlen(response), TCP_WRITE_FLAG_COPY); + } + + mem_free(response); + mem_free(data); + + if (err != ERR_OK) { + return err; + } + + conn->is_websocket = 1; // 标记为已完成 WebSocket 握手 + return ERR_OK; +} + +/* 解析 WebSocket 帧 */ +static err_t websocket_parse_frame(websocket_conn_t *conn, struct pbuf *p) { + websocket_frame_t frame; + u8_t *payload; + size_t payload_len, i; + u16_t offset = 0; + err_t err; + + // 读取帧头前两个字节 + if (pbuf_copy_partial(p, &frame, 2, offset) != 2) { + return ERR_VAL; + } + offset += 2; + + // 解析帧头字段 + u8_t opcode = frame.fin_opcode & 0x0F; + u8_t is_fin = (frame.fin_opcode & 0x80) ? 1 : 0; + u8_t is_masked = (frame.mask_len & 0x80) ? 1 : 0; + u64_t payload_length = frame.mask_len & 0x7F; + + // 处理扩展长度字段 + if (payload_length == 126) { + if (pbuf_copy_partial(p, &frame.len.len16, 2, offset) != 2) { + return ERR_VAL; + } + payload_length = ntohs(frame.len.len16); + offset += 2; + } else if (payload_length == 127) { + if (pbuf_copy_partial(p, &frame.len.len64, 8, offset) != 8) { + return ERR_VAL; + } + payload_length = be64toh(frame.len.len64); + offset += 8; + } + + // 检查帧大小是否超过限制 + if (conn->settings.max_frame_size > 0 && payload_length > conn->settings.max_frame_size) { + websocket_close_conn(conn, WS_CLOSE_TOO_LARGE, "Frame too large", 15); + return ERR_VAL; + } + + // 读取掩码密钥(如果有) + if (is_masked) { + if (pbuf_copy_partial(p, frame.mask_key, 4, offset) != 4) { + return ERR_VAL; + } + offset += 4; + } + + // 分配内存并读取有效载荷 + payload = (u8_t *)mem_malloc(payload_length); + if (payload == NULL) { + return ERR_MEM; + } + + if (pbuf_copy_partial(p, payload, payload_length, offset) != payload_length) { + mem_free(payload); + return ERR_VAL; + } + + // 应用掩码(如果有) + if (is_masked) { + for (i = 0; i < payload_length; i++) { + payload[i] ^= frame.mask_key[i % 4]; + } + } + + // 处理控制帧 + switch (opcode) { + case WS_OPCODE_CLOSE: + websocket_close_conn(conn, WS_CLOSE_NORMAL, NULL, 0); + mem_free(payload); + return ERR_OK; + + case WS_OPCODE_PING: + err = websocket_send_frame(conn, WS_OPCODE_PONG, payload, payload_length, 1); + mem_free(payload); + return err; + + case WS_OPCODE_PONG: + mem_free(payload); + return ERR_OK; + } + + // 调用用户回调处理数据帧 + err = conn->settings.recv_fn(conn, opcode, payload, payload_length, is_fin, conn->settings.arg); + mem_free(payload); + return err; +} + +/* 发送 WebSocket 帧 */ +static err_t websocket_send_frame(websocket_conn_t *conn, u8_t opcode, + const void *data, size_t len, u8_t is_fin) { + u8_t *buffer; + size_t frame_len, offset = 0; + err_t err; + + // 计算帧长度 + frame_len = 2; // 基础帧头长度 + if (len > 65535) { + frame_len += 8; // 64位长度字段 + } else if (len > 125) { + frame_len += 2; // 16位长度字段 + } + frame_len += len; // 有效载荷长度 + + // 分配帧缓冲区 + buffer = (u8_t *)mem_malloc(frame_len); + if (buffer == NULL) { + return ERR_MEM; + } + + // 构建帧头 + buffer[offset++] = (is_fin ? 0x80 : 0x00) | (opcode & 0x0F); + + if (len > 65535) { + buffer[offset++] = 127; + u64_t len_be = htobe64(len); + memcpy(&buffer[offset], &len_be, 8); + offset += 8; + } else if (len > 125) { + buffer[offset++] = 126; + u16_t len_be = htons(len); + memcpy(&buffer[offset], &len_be, 2); + offset += 2; + } else { + buffer[offset++] = len; + } + + // 复制有效载荷 + if (len > 0 && data != NULL) { + memcpy(&buffer[offset], data, len); + } + + // 发送帧(根据连接类型选择普通或加密通道) + if (conn->is_secure) { + err = altcp_tls_write(&conn->ssl_ctx, buffer, frame_len, 0); + } else { + err = altcp_write(conn->pcb, buffer, frame_len, TCP_WRITE_FLAG_COPY); + } + + mem_free(buffer); + + if (err != ERR_OK) { + return err; + } + + return ERR_OK; +} + +/* 关闭 WebSocket 连接 */ +static void websocket_close_conn(websocket_conn_t *conn, u16_t reason, + const char *reason_msg, size_t reason_msg_len) { + if (conn->is_closing) { + return; + } + + conn->is_closing = 1; + + // 构建关闭帧 + u8_t close_frame[128]; + size_t close_frame_len = 0; + + if (reason != WS_CLOSE_NO_STATUS) { + close_frame[0] = (reason >> 8) & 0xFF; + close_frame[1] = reason & 0xFF; + close_frame_len = 2; + + if (reason_msg != NULL && reason_msg_len > 0) { + if (reason_msg_len > sizeof(close_frame) - 2) { + reason_msg_len = sizeof(close_frame) - 2; + } + memcpy(&close_frame[2], reason_msg, reason_msg_len); + close_frame_len += reason_msg_len; + } + } + + // 发送关闭帧 + websocket_send_frame(conn, WS_OPCODE_CLOSE, close_frame, close_frame_len, 1); + + // 关闭连接 + altcp_arg(conn->pcb, NULL); + altcp_recv(conn->pcb, NULL); + altcp_err(conn->pcb, NULL); + altcp_poll(conn->pcb, NULL, 0); + + if (conn->is_secure) { + altcp_tls_free(&conn->ssl_ctx); + } + + if (conn->rx_buf != NULL) { + pbuf_free(conn->rx_buf); + } + + if (conn->settings.close_fn) { + conn->settings.close_fn(conn, conn->settings.arg); + } + + mem_free(conn); +} + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ \ No newline at end of file diff --git a/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.h b/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.h new file mode 100644 index 000000000..2e54e4456 --- /dev/null +++ b/code/sdk/zhihui/4g/cat1Service/lwip/src/apps/websocket/websocket.h @@ -0,0 +1,128 @@ +/** + * @file + * WebSocket server implementation (支持 HTTP/HTTPS 双协议) + */ + +#ifndef LWIP_HDR_APPS_WEBSOCKET_H +#define LWIP_HDR_APPS_WEBSOCKET_H + +#include "lwip/opt.h" +#include "lwip/err.h" +#include "lwip/altcp.h" +#include "lwip/apps/https_client.h" // 引入 HTTPS 相关头文件 + +#if LWIP_TCP && LWIP_CALLBACK_API + +#ifdef __cplusplus +extern "C" { +#endif + +/* WebSocket opcodes */ +#define WS_OPCODE_CONT 0x0 +#define WS_OPCODE_TEXT 0x1 +#define WS_OPCODE_BINARY 0x2 +#define WS_OPCODE_CLOSE 0x8 +#define WS_OPCODE_PING 0x9 +#define WS_OPCODE_PONG 0xA + +/* WebSocket close codes */ +#define WS_CLOSE_NORMAL 1000 +#define WS_CLOSE_GOING_AWAY 1001 +#define WS_CLOSE_PROTOCOL_ERROR 1002 +#define WS_CLOSE_UNSUPPORTED 1003 +#define WS_CLOSE_NO_STATUS 1005 +#define WS_CLOSE_ABNORMAL 1006 +#define WS_CLOSE_INVALID_DATA 1007 +#define WS_CLOSE_POLICY_VIOLATION 1008 +#define WS_CLOSE_TOO_LARGE 1009 +#define WS_CLOSE_MANDATORY_EXT 1010 +#define WS_CLOSE_SERVER_ERROR 1011 +#define WS_CLOSE_TLS_HANDSHAKE 1015 + +typedef struct _websocket_conn websocket_conn_t; + +/** + * WebSocket receive callback + * @param conn WebSocket connection + * @param opcode WebSocket opcode (WS_OPCODE_*) + * @param data received data + * @param len length of received data + * @param is_fin FIN flag + * @param arg callback argument + * @return ERR_OK if successful, else error + */ +typedef err_t (*websocket_recv_fn)(websocket_conn_t *conn, u8_t opcode, + const void *data, size_t len, u8_t is_fin, + void *arg); + +/** + * WebSocket close callback + * @param conn WebSocket connection + * @param arg callback argument + */ +typedef void (*websocket_close_fn)(websocket_conn_t *conn, void *arg); + +/** + * WebSocket connection settings (扩展支持 HTTPS) + */ +typedef struct { + /** Receive callback */ + websocket_recv_fn recv_fn; + /** Close callback */ + websocket_close_fn close_fn; + /** Callback argument */ + void *arg; + /** Maximum frame size (0 for unlimited) */ + size_t max_frame_size; + /** Enable ping/pong */ + u8_t enable_ping_pong; + /** SSL/TLS 配置 (用于 HTTPS/WSS) */ + const httpsc_ssl_config_t *ssl_config; + /** 分配器函数 (用于 HTTPS) */ + altcp_allocator_fn altcp_allocator; +} websocket_settings_t; + +/** + * 检查连接是否为 HTTPS/WSS + * @param conn WebSocket connection + * @return 1 if HTTPS, 0 if HTTP + */ +u8_t websocket_is_secure(websocket_conn_t *conn); + +/** + * Initialize WebSocket server + * @param port TCP port to listen on (80 for HTTP, 443 for HTTPS) + * @param settings WebSocket settings + * @return ERR_OK if successful, else error + */ +err_t websocket_server_init(u16_t port, const websocket_settings_t *settings); + +/** + * Send data over WebSocket connection + * @param conn WebSocket connection + * @param opcode WebSocket opcode (WS_OPCODE_*) + * @param data data to send + * @param len length of data + * @param is_fin FIN flag + * @return ERR_OK if successful, else error + */ +err_t websocket_send(websocket_conn_t *conn, u8_t opcode, + const void *data, size_t len, u8_t is_fin); + +/** + * Close WebSocket connection + * @param conn WebSocket connection + * @param reason close reason code + * @param reason_msg close reason message + * @param reason_msg_len length of reason message + * @return ERR_OK if successful, else error + */ +err_t websocket_close(websocket_conn_t *conn, u16_t reason, + const char *reason_msg, size_t reason_msg_len); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP && LWIP_CALLBACK_API */ +#endif /* LWIP_HDR_APPS_WEBSOCKET_H */ \ No newline at end of file diff --git a/code/sdk/zhihui/4g/cat1Service/lwip/src/include/lwip/apps/https_client.h b/code/sdk/zhihui/4g/cat1Service/lwip/src/include/lwip/apps/https_client.h new file mode 100644 index 000000000..d32292ba7 --- /dev/null +++ b/code/sdk/zhihui/4g/cat1Service/lwip/src/include/lwip/apps/https_client.h @@ -0,0 +1,201 @@ +/** + * @file + * HTTPS client + */ + +/* + * Copyright (c) 2023 DoubaoAssistant + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#ifndef LWIP_HDR_APPS_HTTPS_CLIENT_H +#define LWIP_HDR_APPS_HTTPS_CLIENT_H + +#include "lwip/opt.h" +#include "lwip/ip_addr.h" +#include "lwip/err.h" +#include "lwip/altcp.h" +#include "lwip/prot/iana.h" +#include "lwip/pbuf.h" + +/* 确保SSL/TLS支持已启用 */ +#if LWIP_TCP && LWIP_CALLBACK_API && LWIP_SSL + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @ingroup httpc + * HTTPSC_HAVE_FILE_IO: 定义为1以启用直接下载到磁盘的功能 + * 通过fopen/fwrite实现。这些函数仅是接口的示例实现。 + */ +#ifndef LWIP_HTTPSC_HAVE_FILE_IO +#define LWIP_HTTPSC_HAVE_FILE_IO 0 +#endif + + +/** + * @ingroup httpc + * HTTPS使用的默认TCP端口 + */ +#define HTTPS_DEFAULT_PORT LWIP_IANA_PORT_HTTPS + +/** + * @ingroup httpc + * HTTPS客户端结果码 + */ +typedef enum ehttpsc_result { + /** 文件成功接收 */ + HTTPSC_RESULT_OK = 0, + /** 未知错误 */ + HTTPSC_RESULT_ERR_UNKNOWN = 1, + /** 连接服务器失败 */ + HTTPSC_RESULT_ERR_CONNECT = 2, + /** 解析服务器主机名失败 */ + HTTPSC_RESULT_ERR_HOSTNAME = 3, + /** 远程服务器意外关闭连接 */ + HTTPSC_RESULT_ERR_CLOSED = 4, + /** 连接超时(服务器未及时响应) */ + HTTPSC_RESULT_ERR_TIMEOUT = 5, + /** 服务器返回错误码 */ + HTTPSC_RESULT_ERR_SVR_RESP = 6, + /** 本地内存错误 */ + HTTPSC_RESULT_ERR_MEM = 7, + /** 本地主动中止 */ + HTTPSC_RESULT_LOCAL_ABORT = 8, + /** 内容长度不匹配 */ + HTTPSC_RESULT_ERR_CONTENT_LEN = 9, + /** SSL/TLS握手失败 */ + HTTPSC_RESULT_ERR_SSL_HANDSHAKE = 10, + /** SSL/TLS证书验证失败 */ + HTTPSC_RESULT_ERR_SSL_CERT = 11, + /** SSL/TLS协议错误 */ + HTTPSC_RESULT_ERR_SSL_PROTOCOL = 12 +} httpsc_result_t; + +typedef struct _httpsc_state httpsc_state_t; + +/** + * @ingroup httpc + * HTTPS客户端回调函数原型 + * + * @param arg 发起请求时指定的参数 + * @param httpsc_result HTTPS传输结果(见enum httpsc_result_t) + * @param rx_content_len 接收的字节数(不含头部) + * @param srv_res 包含接收到的HTTP状态码(如果有) + * @param err 内部lwip函数返回的错误,可帮助确定错误源 + * 但不一定必须!=ERR_OK + */ +typedef void (*httpsc_result_fn)(void *arg, httpsc_result_t httpsc_result, u32_t rx_content_len, u32_t srv_res, err_t err); + +/** + * @ingroup httpc + * HTTPS客户端回调原型:接收到头部时调用 + * + * @param connection HTTPS客户端连接 + * @param arg 发起请求时指定的参数 + * @param hdr 头部pbuf(s)(可能也包含数据) + * @param hdr_len 'hdr'中头部的长度 + * @param content_len 头部中接收到的内容长度(-1表示未收到) + * @return 如果返回值!=ERR_OK,则中止连接 + */ +typedef err_t (*httpsc_headers_done_fn)(httpsc_state_t *connection, void *arg, struct pbuf *hdr, u16_t hdr_len, u32_t content_len); + +/** + * @ingroup httpc + * SSL证书验证回调原型 + * + * @param connection HTTPS客户端连接 + * @param cert 服务器证书 + * @param cert_len 证书长度 + * @param depth 证书链深度 + * @param is_valid 当前验证状态 + * @return 验证结果(0表示验证失败,非0表示验证成功) + */ +typedef int (*httpsc_verify_cert_fn)(httpsc_state_t *connection, const void *cert, size_t cert_len, int depth, int is_valid); + +/** + * SSL/TLS配置参数 + */ +typedef struct _httpsc_ssl_config { + /** 证书验证回调函数 */ + httpsc_verify_cert_fn verify_cert; + /** 信任的CA证书(如果为NULL,则使用系统默认CA) */ + const void *trust_anchor; + /** 信任的CA证书长度 */ + size_t trust_anchor_len; + /** 启用/禁用证书验证(不建议禁用) */ + int verify; + /** 支持的TLS版本掩码(如SSL_VERSION_TLSv1_2 | SSL_VERSION_TLSv1_3) */ + int tls_versions; + /** 密码套件列表(如果为NULL,则使用默认列表) */ + const char *ciphers; + /** SNI(服务器名称指示)主机名(如果为NULL,则使用连接的主机名) */ + const char *sni_hostname; +} httpsc_ssl_config_t; + +typedef struct _httpsc_connection { + ip_addr_t proxy_addr; + u16_t proxy_port; + u8_t use_proxy; + /* @todo: 添加用户名:密码? */ + + /* SSL/TLS配置 */ + httpsc_ssl_config_t *ssl_config; + +#if LWIP_ALTCP + altcp_allocator_t *altcp_allocator; +#endif + + /* 传输完成(或中止)时调用的回调 */ + httpsc_result_fn result_fn; + /* 接收到HTTP头部后调用的回调 + 可以通过返回!=ERR_OK来中止连接 */ + httpsc_headers_done_fn headers_done_fn; +} httpsc_connection_t; + +err_t httpsc_get_file(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpsc_connection_t *settings, + altcp_recv_fn recv_fn, void* callback_arg, httpsc_state_t **connection); +err_t httpsc_get_file_dns(const char* server_name, u16_t port, const char* uri, const httpsc_connection_t *settings, + altcp_recv_fn recv_fn, void* callback_arg, httpsc_state_t **connection); + +#if LWIP_HTTPSC_HAVE_FILE_IO +err_t httpsc_get_file_to_disk(const ip_addr_t* server_addr, u16_t port, const char* uri, const httpsc_connection_t *settings, + void* callback_arg, const char* local_file_name, httpsc_state_t **connection); +err_t httpsc_get_file_dns_to_disk(const char* server_name, u16_t port, const char* uri, const httpsc_connection_t *settings, + void* callback_arg, const char* local_file_name, httpsc_state_t **connection); +#endif /* LWIP_HTTPSC_HAVE_FILE_IO */ + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_TCP && LWIP_CALLBACK_API && LWIP_SSL */ + +#endif /* LWIP_HDR_APPS_HTTPS_CLIENT_H */ \ No newline at end of file -- 2.17.1