Files
MeowOS-For-R3P2/patch_v2/ncm_app/bench/http_truncate_sim.c
T
AskaEth 3d70a3dd2c feat: M6.10–M6.19 批量收编 + M6.19 推卡验收
- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘)
- OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复
- ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640
- 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫)
- 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
2026-09-03 12:16:01 +08:00

292 lines
13 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.
/* http_truncate_sim.c — 截断守卫宿主测试(0 真实网络)
*
* 背景(agent-355 挖出):core/ncm_http.c 的 http_run() 在 body 读取循环里,
* 对端于 Content-Length 未收满时提前关闭(mbedtls_ssl_read 返回 0 /
* MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY),loop 后仅判 overflow,否则一律 NCM_OK,
* 于是残缺 body 被调用方当完整成功 → ncm_cache_commit 把半截 .part/.tmp 转正。
*
* 本 sim mock 整个 mbedtls 传输层(仅符号桩,不链真实 libmbedtls),脚本化喂给
* ncm_http.c 一个可分块的对端响应流,验证 http_run(经公开入口
* ncm_http_download_status)对以下语义的判定:
* 定长(Content-Length) 未收满即关闭 → 截断 → 非 NCM_OK(NCM_ERR_IO)
* 定长(Content-Length) 收满 → BS_DONE → NCM_OK
* Content-Length: 0(空 body) → BS_DONE → NCM_OK
* 无 Content-Length、靠对端关闭结束(BS_CLOSE) → 合法 → NCM_OK
* chunked 完整(0 分块末端) → BS_DONE → NCM_OK
* chunked 中途关闭 → 截断 → 非 NCM_OK(NCM_ERR_IO)
*
* 编译(在 ncm_app 下):
* gcc -O2 -Wall -Wextra -DJSMN_HEADER -I. -Icore -Ivendor/mbedtls/include \
* -c bench/http_truncate_sim.c -o /tmp/t.o
* gcc -O2 -Wall -Wextra -DJSMN_HEADER -I. -Icore -Ivendor/mbedtls/include \
* -ffunction-sections -c core/ncm_http.c -o /tmp/t_http.o
* gcc -O2 -Wall -Wextra -DJSMN_HEADER -I. -Icore -Ivendor/mbedtls/include \
* -ffunction-sections -c core/ncm_common.c -o /tmp/t_common.o
* gcc -Wl,--gc-sections -o http_truncate_sim /tmp/t.o /tmp/t_http.o /tmp/t_common.o
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "ncm_http.h"
#include "ncm_common.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/x509_crt.h"
/* ============================================================
* 传输层桩:脚本化"对端读流"。
* 每次 ncm_http_download_status 调用前用 tr_script(...) 装填脚本,
* 脚本用尽后 mbedtls_ssl_read 返回 g_close_rc(0=有序关闭;或
* MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY 模拟 TLS close_notify)。
* ============================================================ */
typedef struct { const unsigned char *buf; size_t len; } tr_chunk_t;
static const tr_chunk_t *g_script;
static int g_script_n;
static int g_script_i;
static int g_close_rc; /* 脚本耗尽后的 ssl_read 返回值 */
static void tr_script(const tr_chunk_t *chunks, int n, int close_rc) {
g_script = chunks; g_script_n = n; g_script_i = 0; g_close_rc = close_rc;
}
int mbedtls_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len) {
(void)ssl;
if (g_script_i < g_script_n) {
const tr_chunk_t *c = &g_script[g_script_i];
size_t n = c->len;
if (n > len) n = len; /* 脚本块恒 <= 4096(tmp),一般整块发出 */
memcpy(buf, c->buf, n);
g_script_i++;
return (int)n;
}
return g_close_rc;
}
int mbedtls_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len) {
(void)ssl; (void)buf; return (int)len; /* 请求头/体全部写成功 */
}
/* 其余 mbedtls 桩:连接/握手/配置全部成功,不触碰任何真实网络。 */
void mbedtls_net_init(mbedtls_net_context *ctx) { (void)ctx; }
int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto) {
(void)ctx; (void)host; (void)port; (void)proto; return 0;
}
int mbedtls_net_set_nonblock(mbedtls_net_context *ctx) { (void)ctx; return 0; }
int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout) {
(void)ctx; (void)rw; (void)timeout; return 1; /* 就绪 */
}
void mbedtls_net_free(mbedtls_net_context *ctx) { (void)ctx; }
int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len) {
(void)ctx; (void)buf; (void)len; return MBEDTLS_ERR_SSL_WANT_READ; /* 实际读由 mbedtls_ssl_read 桩接管 */
}
int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len) {
(void)ctx; (void)buf; return (int)len;
}
void mbedtls_ssl_init(mbedtls_ssl_context *ssl) { (void)ssl; }
void mbedtls_ssl_config_init(mbedtls_ssl_config *conf) { (void)conf; }
int mbedtls_ssl_config_defaults(mbedtls_ssl_config *conf, int endpoint, int transport, int preset) {
(void)conf; (void)endpoint; (void)transport; (void)preset; return 0;
}
void mbedtls_ssl_conf_authmode(mbedtls_ssl_config *conf, int authmode) { (void)conf; (void)authmode; }
void mbedtls_ssl_conf_ca_chain(mbedtls_ssl_config *conf, mbedtls_x509_crt *ca_chain, mbedtls_x509_crl *ca_crl) {
(void)conf; (void)ca_chain; (void)ca_crl;
}
void mbedtls_ssl_conf_rng(mbedtls_ssl_config *conf,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) {
(void)conf; (void)f_rng; (void)p_rng;
}
int mbedtls_ssl_setup(mbedtls_ssl_context *ssl, const mbedtls_ssl_config *conf) {
(void)ssl; (void)conf; return 0;
}
int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname) {
(void)ssl; (void)hostname; return 0;
}
void mbedtls_ssl_set_bio(mbedtls_ssl_context *ssl, void *p_bio,
mbedtls_ssl_send_t *f_send, mbedtls_ssl_recv_t *f_recv,
mbedtls_ssl_recv_timeout_t *f_recv_timeout) {
(void)ssl; (void)p_bio; (void)f_send; (void)f_recv; (void)f_recv_timeout;
}
int mbedtls_ssl_handshake(mbedtls_ssl_context *ssl) { (void)ssl; return 0; }
void mbedtls_ssl_free(mbedtls_ssl_context *ssl) { (void)ssl; }
void mbedtls_ssl_config_free(mbedtls_ssl_config *conf) { (void)conf; }
void mbedtls_x509_crt_init(mbedtls_x509_crt *crt) { (void)crt; }
int mbedtls_x509_crt_parse_file(mbedtls_x509_crt *crt, const char *path) {
(void)crt; (void)path; return 0; /* CA 加载成功(validate=0 时不走此径,桩兜底) */
}
void mbedtls_x509_crt_free(mbedtls_x509_crt *crt) { (void)crt; }
/* ============================================================
* 下载回调 sink:累加接收到的 body 字节。
* ============================================================ */
typedef struct { unsigned char data[1 << 16]; size_t len; } sink_cb_t;
static int on_body(void *ctx, const unsigned char *data, size_t len) {
sink_cb_t *s = (sink_cb_t *)ctx;
if (s->len + len > sizeof s->data) return -1; /* 上限(不触发) */
memcpy(s->data + s->len, data, len);
s->len += len;
return 0;
}
/* 运行一个用例:装填脚本 → 发起 GET 下载 → 记录 rc / body_len / content_length */
typedef struct { int rc; size_t body_len; size_t content_length; } case_result_t;
static case_result_t run_case(const tr_chunk_t *script, int script_n, int close_rc) {
ncm_http_req_t req;
memset(&req, 0, sizeof req);
req.url = "https://ex.test/song.mp3";
req.method = "GET";
req.verify_ca = 0; /* 跳过 CA 校验,聚焦 body 终态判定 */
req.timeout_ms = 3000;
sink_cb_t sink; memset(&sink, 0, sizeof sink);
ncm_http_resp_t resp; memset(&resp, 0, sizeof resp);
tr_script(script, script_n, close_rc);
int rc = ncm_http_download_status(&req, on_body, &sink, 0, &resp, NULL);
case_result_t r;
r.rc = rc;
r.body_len = sink.len;
r.content_length = resp.content_length;
return r;
}
static int g_fail = 0;
static void check(const char *name, int ok, const char *detail) {
if (!ok) g_fail++;
printf(" %s %s\n", ok ? "PASS" : "FAIL", name);
if (detail) printf(" %s\n", detail);
}
int main(void) {
char dbg[160];
/* ---- 场景 1:定长(CL=100) 对端提前关闭(只发 50 字节)→ 截断 ---- */
{
static const unsigned char hdrs[] =
"HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\n";
static unsigned char chunk[512];
size_t hlen = sizeof hdrs - 1;
memcpy(chunk, hdrs, hlen);
for (size_t i = 0; i < 50; i++) chunk[hlen + i] = 'A';
tr_chunk_t script[1];
script[0].buf = chunk; script[0].len = hlen + 50;
case_result_t r = run_case(script, 1, 0 /* 对端关闭 */);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu content_length=%zu",
r.rc, r.body_len, r.content_length);
check("truncate_fixed(CL=100, 50B, close) -> NCM_ERR_IO",
(r.rc != NCM_OK) && (r.rc == NCM_ERR_IO) && (r.body_len == 50), dbg);
}
/* ---- 场景 2:定长(CL=100) 完整收满 → BS_DONE → NCM_OK ---- */
{
static const unsigned char hdrs[] =
"HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\n";
static unsigned char chunk[512];
size_t hlen = sizeof hdrs - 1;
memcpy(chunk, hdrs, hlen);
for (size_t i = 0; i < 100; i++) chunk[hlen + i] = 'B';
tr_chunk_t script[1];
script[0].buf = chunk; script[0].len = hlen + 100;
case_result_t r = run_case(script, 1, 0);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu", r.rc, r.body_len);
check("normal_fixed(CL=100, 100B) -> NCM_OK",
(r.rc == NCM_OK) && (r.body_len == 100), dbg);
}
/* ---- 场景 3:定长(CL=100) 两次读到收满(多块)→ NCM_OK ---- */
{
static const unsigned char hdrs[] =
"HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\n";
static unsigned char part1[512], part2[64];
size_t hlen = sizeof hdrs - 1;
memcpy(part1, hdrs, hlen);
for (size_t i = 0; i < 60; i++) part1[hlen + i] = 'C';
for (size_t i = 0; i < 40; i++) part2[i] = 'D';
tr_chunk_t script[2];
script[0].buf = part1; script[0].len = hlen + 60;
script[1].buf = part2; script[1].len = 40;
case_result_t r = run_case(script, 2, 0);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu", r.rc, r.body_len);
check("normal_fixed_multi(CL=100, 60+40B) -> NCM_OK",
(r.rc == NCM_OK) && (r.body_len == 100), dbg);
}
/* ---- 场景 4:Content-Length: 0(空 body)→ BS_DONE → NCM_OK ---- */
{
static const unsigned char chunk[] =
"HTTP/1.1 204 No Content\r\nContent-Length: 0\r\n\r\n";
static const tr_chunk_t script[] = { { chunk, sizeof chunk - 1 } };
case_result_t r = run_case(script, 1, 0);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu", r.rc, r.body_len);
check("cl_zero(CL=0) -> NCM_OK empty",
(r.rc == NCM_OK) && (r.body_len == 0), dbg);
}
/* ---- 场景 5:无 Content-Length、靠对端关闭结束(BS_CLOSE) → 合法 → NCM_OK ---- */
{
static const unsigned char hdrs[] =
"HTTP/1.1 200 OK\r\n\r\n"; /* 无 CL、无 chunked */
static unsigned char chunk[512];
size_t hlen = sizeof hdrs - 1;
memcpy(chunk, hdrs, hlen);
for (size_t i = 0; i < 100; i++) chunk[hlen + i] = 'E';
tr_chunk_t script[1];
script[0].buf = chunk; script[0].len = hlen + 100;
case_result_t r = run_case(script, 1, 0);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu", r.rc, r.body_len);
check("close_delimited(no CL, 100B, close) -> NCM_OK",
(r.rc == NCM_OK) && (r.body_len == 100), dbg);
}
/* ---- 场景 6:定长(CL=100) 提前关闭,且用 TLS close_notify 模拟 → 截断 ---- */
{
static const unsigned char hdrs[] =
"HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\n";
static unsigned char chunk[512];
size_t hlen = sizeof hdrs - 1;
memcpy(chunk, hdrs, hlen);
for (size_t i = 0; i < 50; i++) chunk[hlen + i] = 'F';
tr_chunk_t script[1];
script[0].buf = chunk; script[0].len = hlen + 50;
case_result_t r = run_case(script, 1, MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu", r.rc, r.body_len);
check("truncate_close_notify(CL=100, 50B) -> NCM_ERR_IO",
(r.rc != NCM_OK) && (r.rc == NCM_ERR_IO) && (r.body_len == 50), dbg);
}
/* ---- 场景 7:chunked 完整(0 分块末端)→ BS_DONE → NCM_OK ---- */
{
static const unsigned char chunk[] =
"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
"A\r\n0123456789\r\n" /* 16 进制 0xA = 10 字节 */
"0\r\n\r\n";
static const tr_chunk_t script[] = { { chunk, sizeof chunk - 1 } };
case_result_t r = run_case(script, 1, 0);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu", r.rc, r.body_len);
check("chunked_full(10B + 0-chunk) -> NCM_OK",
(r.rc == NCM_OK) && (r.body_len == 10), dbg);
}
/* ---- 场景 8:chunked 中途关闭(数据未收完)→ 截断 → 非 NCM_OK ---- */
{
static const unsigned char chunk[] =
"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
"5\r\nhel"; /* 宣称 5 字节,只发了 3,未到 0 分块 */
static const tr_chunk_t script[] = { { chunk, sizeof chunk - 1 } };
case_result_t r = run_case(script, 1, 0);
snprintf(dbg, sizeof dbg, "rc=%d body=%zu", r.rc, r.body_len);
check("chunked_truncated(3/5B, close) -> NCM_ERR_IO",
(r.rc != NCM_OK) && (r.rc == NCM_ERR_IO), dbg);
}
printf("\n== HTTP TRUNCATE SIM: %s ==\n", g_fail ? "FAIL" : "ALL PASS");
return g_fail ? 1 : 0;
}