Files
2026-10-04 13:50:33 +08:00

115 lines
5.2 KiB
C

/* test_http.c - HTTP 层纯逻辑测试 (不联网) */
#include "http.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int failures = 0;
static int checks = 0;
#define CHECK(cond, ...) \
do { \
checks++; \
if (!(cond)) { \
failures++; \
printf(" ✗ %s:%d: ", __FILE__, __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
} \
} while (0)
int main(void) {
puts("重试判定: 只重试服务端明确报错的情况");
/* 应当重试 */
long retryable[] = {408, 409, 429, 500, 502, 503, 504};
for (size_t i = 0; i < sizeof retryable / sizeof retryable[0]; i++)
CHECK(http_status_retryable(retryable[i]) == 1, "HTTP %ld 应当可重试",
retryable[i]);
/* 绝不应当重试: 这些是确定性错误, 重试只是浪费时间和钱 */
long permanent[] = {400, 401, 403, 404, 405, 413, 415, 422,
501, 200, 201, 204, 301, 0, -1};
for (size_t i = 0; i < sizeof permanent / sizeof permanent[0]; i++)
CHECK(http_status_retryable(permanent[i]) == 0, "HTTP %ld 不应重试",
permanent[i]);
/* 默认策略与上限的合理性 */
HttpRetry def = HTTP_RETRY_DEFAULT;
HttpRetry none = HTTP_RETRY_NONE;
CHECK(def.max_retries > 0, "默认应当会重试");
CHECK(none.max_retries == 0, "HTTP_RETRY_NONE 应当不重试");
CHECK(HTTP_MAX_BODY >= 1024 * 1024, "响应体上限不该小到影响正常使用");
CHECK(HTTP_MAX_ERRBODY < HTTP_MAX_BODY, "错误体上限应远小于正文上限");
/* ---- 调试输出的密钥脱敏 ---- */
puts("--verbose 输出脱敏");
{
const char *key = "sk-0f114e3dac8249f0a36fd8fd5c517360";
char *buf = NULL;
size_t sz = 0;
FILE *f = open_memstream(&buf, &sz);
/* 这条正是曾经漏出去的: libcurl 的 HTTP/2 调试行走的是 CURLINFO_TEXT,
* 不是 HEADER_OUT, 所以只处理 HEADER_OUT 的做法根本挡不住 */
const char *h2 = "== Info: [HTTP/2] [1] [authorization: Bearer "
"sk-0f114e3dac8249f0a36fd8fd5c517360]\n";
http_write_redacted(f, h2, strlen(h2), key);
/* 整块请求头 (HEADER_OUT 是整块传来的, 不是逐行) */
const char *hdrs =
"POST /v1/chat/completions HTTP/2\r\n"
"Authorization: Bearer sk-0f114e3dac8249f0a36fd8fd5c517360\r\n"
"Content-Type: application/json\r\n";
http_write_redacted(f, hdrs, strlen(hdrs), key);
/* 其它内容不能被改动 */
const char *plain =
"== Info: TLSv1.3 (IN), TLS handshake, Finished (20):\n";
http_write_redacted(f, plain, strlen(plain), key);
/* 没有密钥时原样输出 */
http_write_redacted(f, "no key here\n", 12, "");
fclose(f);
CHECK(strstr(buf, key) == NULL, "输出里不应残留完整密钥");
CHECK(strstr(buf, "<已隐藏>") != NULL, "应替换为占位符");
CHECK(strstr(buf, "[HTTP/2]") != NULL, "HTTP/2 调试行本身应保留");
CHECK(strstr(buf, "Authorization: Bearer <已隐藏>") != NULL,
"请求头里的密钥应脱敏: %.200s", buf);
CHECK(strstr(buf, "TLS handshake") != NULL, "其它调试信息不能被误改");
CHECK(strstr(buf, "no key here") != NULL, "无密钥时原样输出");
/* 一次替换必须整段吃掉, 不能留下半个密钥 */
CHECK(strstr(buf, "0f114e3dac") == NULL, "不应留下密钥片段");
free(buf);
}
puts("\n空请求体: POST 分支也要扛得住 body == NULL");
{
/* body 允许为 NULL (http_probe 就是这么调的), 但 POST 分支以前写的是
* 无条件的 strlen(body) —— 传 NULL 会在 curl_easy_setopt 期间就段错误.
*
* 这里故意用一个格式非法的 URL: libcurl 会在真正联网之前就拒绝它,
* 所以本测试仍然不碰网络. 而 strlen(NULL) 发生在设置 POSTFIELDS 期间、
* curl_easy_perform 之前, 也就是说旧代码在这里必崩 —— 这个断言对旧
* 代码是有效的回归守卫, 不是走过场. */
static const HttpRetry no_retry = HTTP_RETRY_NONE;
char errbuf[256] = "";
int attempts = 0;
long status = 0;
http_global_init();
int rc = http_post(":::", "sk-test", NULL, 5, 0, &no_retry, NULL,
&status, &attempts, errbuf, sizeof errbuf);
http_global_cleanup();
CHECK(rc == -1, "非法 URL 应当返回 -1 (实际返回 %d)", rc);
CHECK(errbuf[0] != '\0', "失败时必须给出错误说明");
}
printf("\n%d 项检查, %d 项失败\n", checks, failures);
return failures ? 1 : 0;
}