feat(ViperBTests): ViperRequestsTest 大文件切片下载验证步骤

demo 新增第 6 步:httpbin /bytes/1048576 串行(1线程)与并行(4线程)下载后大小与内容逐字节比对一致
This commit is contained in:
TermiNexus committed 2026-09-03 08:27:58 +08:00
1 parent ac3e235406
commit 64a527c615
2 files changed
+347

No files matched your search

+303
View File
@@ -0,0 +1,303 @@
# main.vp — ViperRequestsTest:HTTP + HTTPS 客户端测试
#
# 覆盖:
# 1) requests.get — 纯 socket HTTP GET
# 2) rhttps.get verify=True — HTTPS 默认校验证书链(无 CA 时预期失败并给出原因)
# 3) rhttps.get verify=False — HTTPS 通道演示(打印 TLS 版本 / 套件 / 对端主题)
# 4) rhttps.post — HTTPS POST
#
# 用法: requests._mbuddy = my_mbuddy(https 复用同一内存池)
import t, c
from stdint import *
import stdio
import stdlib
import string
import socket
import memhub
import requests
import requests.https as rhttps
import requests.download as rd
import zlib.pyzlib as pyzlib
import w32.win32file as wf
import w32.win32base as wb
# 全局 MBuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
def _PrintBody(head: str, resp: requests.Response | t.CPtr):
"""打印响应状态与正文片段。"""
if resp is None:
return
code: INT = resp.status_code
length: INT = resp.body_len
stdio.printf("%s -> status=%d, body_len=%d\n", head, code, length)
if resp.text is not None and length > 0:
show: INT = 160
if length < show:
show = length
txt: str = resp.text
i: INT = 0
while i < show and txt[i] != 0:
stdio.printf("%c", txt[i])
i += 1
stdio.printf("\n")
def _PrintHeader(resp: requests.Response | t.CPtr, name: str):
"""演示大小写不敏感的响应头查询。"""
hbuf: str = _mbuddy.alloc(128)
if hbuf is None:
return
if requests.header_value(resp, name, hbuf, 128) == 0:
stdio.printf(" [header] %s: %s\n", name, hbuf)
else:
stdio.printf(" [header] %s: (missing)\n", name)
_mbuddy.free(hbuf)
@t.CExport
def main() -> int:
global _mbuddy
stdio.printf("ViperRequestsTest: HTTP + HTTPS client test\n")
# ---- 初始化内存池(16MB arena)并交给 requests 包 ----
arena: bytes = stdlib.malloc(16 * 1024 * 1024)
if arena is None:
stdio.printf("FATAL: malloc arena failed\n")
return 1
_mbuddy = memhub.MemBuddy(arena, 16 * 1024 * 1024)
requests._mbuddy = _mbuddy
# ---- 1) HTTP GET ----
stdio.printf("\n--- 1) HTTP GET (http://example.com/) ---\n")
resp = requests.get("http://example.com/")
if resp is not None:
_PrintBody("requests.get", resp)
_PrintHeader(resp, "Content-Type") # 头查询(大小写不敏感)
_PrintHeader(resp, "X-Not-Exist") # 不存在 → (missing)
requests.release(resp)
else:
stdio.printf("requests.get: FAIL (network unavailable)\n")
# ---- 2) HTTPS GET, 证书核验开启(显式 CA bundle 校验通过)----
stdio.printf("\n--- 2) HTTPS GET verify=True (Mozilla CA bundle) ---\n")
resp2 = rhttps.get("https://example.com/", True, "D:/msys64/mingw64/etc/ssl/certs/ca-bundle.crt")
if resp2 is not None:
_PrintBody("https.get(verify=True)", resp2)
stdio.printf(" verify result = %d (0=chain ok), enabled = %d\n",
rhttps.last_verify_result(), rhttps.last_verify_enabled())
requests.release(resp2)
else:
err: str = rhttps.last_error()
stdio.printf("https.get(verify=True): REJECTED (cert verify failed)\n")
stdio.printf(" reason: %s\n", err if err is not None else "?")
# ---- 3) HTTPS GET, 证书核验关闭(演示 TLS 通道 + 元信息)----
stdio.printf("\n--- 3) HTTPS GET verify=False (TLS info) ---\n")
resp3 = rhttps.get("https://example.com/", False)
if resp3 is not None:
_PrintBody("https.get(verify=False)", resp3)
_PrintHeader(resp3, "content-type") # https 响应同样可查头(小写演示)
tls_info: str = rhttps.last_tls_info()
stdio.printf(" tls: %s\n", tls_info if tls_info is not None else "?")
requests.release(resp3)
else:
err3: str = rhttps.last_error()
stdio.printf("https.get(verify=False): FAIL\n")
stdio.printf(" reason: %s\n", err3 if err3 is not None else "?")
# ---- 4) HTTPS POST(对端 405 亦属正常,验证请求可发送与响应解析)----
stdio.printf("\n--- 4) HTTPS POST (https://example.com/) ---\n")
resp4 = rhttps.post("https://example.com/", "hello=viper", "application/x-www-form-urlencoded", False)
if resp4 is not None:
_PrintBody("https.post(verify=False)", resp4)
requests.release(resp4)
else:
err4: str = rhttps.last_error()
stdio.printf("https.post: FAIL\n")
stdio.printf(" reason: %s\n", err4 if err4 is not None else "?")
# ---- 5) chunked 解码(RFC 7230 官方样例,离线验证)----
stdio.printf("\n--- 5) chunked decode (RFC7230 sample) ---\n")
chunked_src: str = "4\r\nWiki\r\n5\r\npedia\r\nE\r\n in\r\n\r\nchunks.\r\n0\r\n\r\n"
clen: INT = INT(string.strlen(chunked_src))
out: str = _mbuddy.alloc(128)
if out is not None:
dlen: INT = requests.decode_chunked(chunked_src, clen, out, 128)
stdio.printf("decode_chunked(%d bytes) -> %d bytes: ", clen, dlen)
if dlen > 0:
di: INT = 0
while di < dlen and out[di] != 0:
stdio.printf("%c", out[di])
di += 1
stdio.printf("\n")
_mbuddy.free(out)
else:
stdio.printf("decode_chunked: alloc failed\n")
# ---- 6) 重定向跟随(httpbin 3 跳)----
stdio.printf("\n--- 6) redirect follow (https://httpbin.org/redirect/3) ---\n")
resp6 = rhttps.get("https://httpbin.org/redirect/3", False)
if resp6 is not None:
_PrintBody("https.get(redirect/3)", resp6)
requests.release(resp6)
else:
err6: str = rhttps.last_error()
stdio.printf("redirect: FAIL\n reason: %s\n", err6 if err6 is not None else "?")
# ---- 7) gzip 解压(Content-Encoding: gzip 自动处理)----
stdio.printf("\n--- 7) gzip decode (https://httpbin.org/gzip) ---\n")
resp7 = rhttps.get("https://httpbin.org/gzip", False)
if resp7 is not None:
_PrintBody("https.get(gzip)", resp7)
gerr: str = requests.last_error()
if gerr is not None and gerr[0] != 0:
stdio.printf(" [gzip] %s\n", gerr)
tebuf: str = _mbuddy.alloc(64)
if tebuf is not None:
if requests.header_value(resp7, "Transfer-Encoding", tebuf, 64) == 0:
stdio.printf(" [te] %s\n", tebuf)
_mbuddy.free(tebuf)
requests.release(resp7)
else:
err7: str = rhttps.last_error()
stdio.printf("gzip: FAIL\n reason: %s\n", err7 if err7 is not None else "?")
# ---- 8) 证书校验拦截(badssl 自签不在 CA bundle → verify=True 拒绝)----
stdio.printf("\n--- 8) cert verify block (self-signed.badssl.com) ---\n")
resp8 = rhttps.get("https://self-signed.badssl.com/", True, "D:/msys64/mingw64/etc/ssl/certs/ca-bundle.crt")
if resp8 is not None:
stdio.printf("verify=True: PASS (verify result = %d, unexpected for self-signed)\n", rhttps.last_verify_result())
_PrintBody(" body", resp8)
requests.release(resp8)
else:
err8: str = rhttps.last_error()
stdio.printf("verify=True: REJECTED as expected (verify result = %d)\n reason: %s\n",
rhttps.last_verify_result(), err8 if err8 is not None else "?")
resp8b = rhttps.get("https://self-signed.badssl.com/", False)
if resp8b is not None:
_PrintBody("verify=False (bypass)", resp8b)
requests.release(resp8b)
else:
err8b: str = rhttps.last_error()
stdio.printf("verify=False: FAIL\n reason: %s\n", err8b if err8b is not None else "?")
# ---- 5b) pyzlib gzip 环回(pool=None / stdlib 路径对照)----
stdio.printf("\n--- 5b) pyzlib gzip round-trip (pool=None) ---\n")
gsrc: str = "The quick brown fox jumps over the lazy dog while the morning sun rises slowly above the quiet hills of the eastern valley. A gentle breeze carries the fresh scent of pine trees across the winding path. Birds gather on the wooden fence and sing their cheerful songs. Farmers prepare their fields for another day of honest work. Children laugh and play near the old stone bridge. Every small village tells its own story through the eyes of its people, their traditions, and the memories shared around warm evening fires under a sky full of stars that have watched over this land for centuries. The river continues its endless journey toward the distant sea, carrying with it the hopes and dreams of all who live along its banks."
glen: t.CSizeT = t.CSizeT(string.strlen(gsrc))
clen2: t.CSizeT = 0
gz1: BYTEPTR = pyzlib.compress(None, BYTEPTR(gsrc), glen, 6, 31, c.Addr(clen2))
if gz1 is not None:
stdio.printf("compress ok -> %zu bytes gzip\n", clen2)
olen2: t.CSizeT = 0
gz2: BYTEPTR = pyzlib.decompress(None, gz1, clen2, 15, 4096, c.Addr(olen2))
if gz2 is not None:
g2s: str = t.CChar(t.CUInt64T(gz2), t.CPtr)
g2s[olen2] = 0
stdio.printf("decompress ok -> %zu bytes: %s\n", olen2, g2s)
stdlib.free(gz1)
stdlib.free(gz2)
else:
stdio.printf("decompress FAIL: %s\n", pyzlib.get_error())
else:
stdio.printf("compress FAIL: %s\n", pyzlib.get_error())
# ---- 6) 大文件切片下载(httpbin 1MB:串行 + 多线程,逐字节比对)----
stdio.printf("\n--- 6) 大文件切片下载 (https://httpbin.org/bytes/1048576) ---\n")
DL_URL: str = "https://httpbin.org/bytes/1048576"
CA_BUNDLE: str = "D:/msys64/mingw64/etc/ssl/certs/ca-bundle.crt"
rc6a: INT = rd.download(DL_URL, "dl_serial.bin", 1, 262144, True, CA_BUNDLE)
stdio.printf("6a serial download rc=%d last_error=%s\n", rc6a, rd.last_error())
rc6b: INT = rd.download(DL_URL, "dl_parallel.bin", 4, 262144, True, CA_BUNDLE)
stdio.printf("6b parallel(4) download rc=%d last_error=%s\n", rc6b, rd.last_error())
# 校验两文件大小一致且等于 1048576
exp_size: LONGLONG = 1048576
ok6: INT = 1
i6: INT = 0
paths6: t.CArray[str, None] = ["dl_serial.bin", "dl_parallel.bin"]
sizes6: t.CArray[LONGLONG, None] = [0, 0]
for i6 in range(2):
h6: wb.HANDLE = wf.CreateFileA(
paths6[i6], wf.GENERIC_READ, wf.FILE_SHARE_READ, None,
wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
if h6 == wb.INVALID_HANDLE_VALUE:
stdio.printf("6 check: open %s failed\n", paths6[i6])
ok6 = 0
continue
li6: wb.LARGE_INTEGER = wb.LARGE_INTEGER()
if wf.GetFileSizeEx(h6, c.Addr(li6)) == 0:
stdio.printf("6 check: GetFileSizeEx %s failed\n", paths6[i6])
ok6 = 0
else:
sizes6[i6] = li6.QuadPart
stdio.printf("6 check: %s size=%lld\n", paths6[i6], li6.QuadPart)
wb.CloseHandle(h6)
if ok6 != 0 and sizes6[0] == exp_size and sizes6[1] == exp_size:
# 内容逐字节比对
same6: INT = 1
a6: str = stdlib.malloc(65536)
b6: str = stdlib.malloc(65536)
if a6 is None or b6 is None:
stdio.printf("6 compare: out of memory\n")
else:
ha6: wb.HANDLE = wf.CreateFileA(
paths6[0], wf.GENERIC_READ, wf.FILE_SHARE_READ, None,
wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
hb6: wb.HANDLE = wf.CreateFileA(
paths6[1], wf.GENERIC_READ, wf.FILE_SHARE_READ, None,
wf.OPEN_EXISTING, wf.FILE_ATTRIBUTE_NORMAL, None)
if ha6 == wb.INVALID_HANDLE_VALUE or hb6 == wb.INVALID_HANDLE_VALUE:
stdio.printf("6 compare: open failed\n")
else:
off6: LONGLONG = 0
while off6 < exp_size:
ra: ULONG = 0
rb: ULONG = 0
chunk6: ULONG = 65536
rem6: LONGLONG = exp_size - off6
if rem6 < LONGLONG(65536):
chunk6 = ULONG(rem6)
wf.ReadFile(ha6, a6, chunk6, c.Addr(ra), None)
wf.ReadFile(hb6, b6, chunk6, c.Addr(rb), None)
if ra != chunk6 or rb != chunk6:
stdio.printf("6 compare: short read at %lld\n", off6)
same6 = 0
break
k6: ULONG = 0
while k6 < chunk6:
if a6[k6] != b6[k6]:
stdio.printf("6 compare: MISMATCH at %lld\n", off6 + LONGLONG(k6))
same6 = 0
break
k6 += 1
if same6 == 0:
break
off6 += LONGLONG(chunk6)
stdio.printf("6 compare: serial == parallel(4) %s\n",
"YES" if same6 != 0 else "NO")
if ha6 != wb.INVALID_HANDLE_VALUE:
wb.CloseHandle(ha6)
if hb6 != wb.INVALID_HANDLE_VALUE:
wb.CloseHandle(hb6)
if a6 is not None:
stdlib.free(a6)
if b6 is not None:
stdlib.free(b6)
else:
stdio.printf("6 compare: size mismatch or download failed (rc6a=%d rc6b=%d)\n", rc6a, rc6b)
# 清理下载文件
wf.DeleteFileA("dl_serial.bin")
wf.DeleteFileA("dl_parallel.bin")
# ---- 清理 ----
socket.SocketCleanup()
stdio.printf("\nViperRequestsTest: DONE\n")
return 0
+44
View File
@@ -0,0 +1,44 @@
{
"name": "ViperRequestsTest - HTTP(S) Client Test",
"version": "1.0.0",
"source_dir": "./App",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": [
"-filetype=obj",
"-relocation-model=pic"
]
},
"linker": {
"cmd": "D:/msys64/mingw64/bin/clang++.exe",
"flags": [
"-Wl,--allow-multiple-definition",
"-LD:/Users/TermiNexus/Desktop/TransPyC/includes/openssl/.openssl_358_C",
"-lssl",
"-lcrypto",
"-lws2_32",
"-lcrypt32",
"-ladvapi32",
"-lmsvcrt",
"-lmingwex",
"-lgdi32",
"-lpthread",
"-lkernel32"
],
"static_libs": [],
"output": "app.exe"
},
"includes": [
"../../includes"
],
"target": {
"triple": "x86_64-pc-windows-gnu",
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"target": "llvm",
"strict_mode": true
}
}