Files
vdm/core/tests/net/url_test.cpp
samiandClaude Sonnet 5 201ebc55d4 core: net/probe + Content-Disposition parser + URL splitter (stage 3)
net/content_disposition — total parser for the mojibake-prone header:
RFC 6266 filename (quoted/token), RFC 5987 filename* ext-values
(charset'lang'pct-encoded, incl. RFC 2231 continuations), legacy RFC 2047
encoded-words (=?UTF-8?B?..?= / ?Q?), and raw Latin-1 bytes; prefers
filename* over filename; strips path components AFTER decoding (a base64
payload can hold '/'). 22-case test table.

net/text_codec (internal) — percent-decode, UTF-8 validation, Latin-1->
UTF-8, base64, RFC 2047 — shared by the CD parser and the URL splitter.

net/url — a small total URL splitter (scheme/userinfo/host/port/path/
query/fragment, http(s) validity) and url_filename() for the last path
segment; used for the filename fallback.

net/probe — HEAD then a ranged GET bytes=0-0 that PROVES resumability
(206 + matching Content-Range + a validator), rather than trusting
Accept-Ranges which servers lie about; the ranged GET is also the HEAD-
refused (403/405/501) fallback. 401/407 -> success result with
requires_auth, not an error. Runs on its own pool (max_concurrent,
default 4) outside the segment budget per ADR 0011 §5. suggest_filename()
does the resolution order (explicit -> disposition -> URL -> download.bin)
with a light strip; rules/ (stage 9) owns the authoritative sanitize.

tools/fuzz — libFuzzer targets for the CD parser and the URL splitter,
compiling the parser sources directly so they're fully instrumented;
self-guards on VELOX_BUILD_FUZZ + Clang (the top-level CMake adds every
tools/* unconditionally). Seed corpora included.

Fixed on the way: a p -> Transfer -> State -> cbs -> p reference cycle in
Prober that leaked every probe (drop the stored Transfer; the worker
keeps State alive). Tests green under ASan/UBSan and TSan.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
2026-09-09 23:53:55 +04:00

72 lines
2.3 KiB
C++

#include "vdm/net/url.hpp"
#include <string>
#include "vtest.hpp"
using vdm::net::split_url;
using vdm::net::SplitUrl;
using vdm::net::url_filename;
VT_TEST(url_basic_https) {
auto u = split_url("https://example.com/path/to/file.iso?x=1#frag");
VT_CHECK(u.valid);
VT_CHECK_EQ(u.scheme, std::string("https"));
VT_CHECK_EQ(u.host, std::string("example.com"));
VT_CHECK(!u.port.has_value());
VT_CHECK_EQ(u.path, std::string("/path/to/file.iso"));
VT_CHECK_EQ(u.query, std::string("x=1"));
VT_CHECK_EQ(u.fragment, std::string("frag"));
}
VT_TEST(url_port_userinfo_lowercasing) {
auto u = split_url("HTTP://User:[email protected]:8080/a");
VT_CHECK_EQ(u.scheme, std::string("http"));
VT_CHECK_EQ(u.userinfo, std::string("User:pw"));
VT_CHECK_EQ(u.host, std::string("host.example.com"));
VT_REQUIRE(u.port.has_value());
VT_CHECK_EQ(*u.port, 8080);
}
VT_TEST(url_ipv6_host) {
auto u = split_url("http://[2001:db8::1]:9000/file");
VT_CHECK_EQ(u.host, std::string("2001:db8::1"));
VT_REQUIRE(u.port.has_value());
VT_CHECK_EQ(*u.port, 9000);
}
VT_TEST(url_no_path) {
auto u = split_url("https://example.com");
VT_CHECK(u.valid);
VT_CHECK_EQ(u.path, std::string(""));
}
VT_TEST(url_non_http_is_invalid_but_parsed) {
auto u = split_url("ftp://host/file");
VT_CHECK(!u.valid); // not http/https
VT_CHECK_EQ(u.scheme, std::string("ftp"));
VT_CHECK(!u.is_http());
}
VT_TEST(url_garbage_does_not_crash) {
VT_CHECK(!split_url("").valid);
VT_CHECK(!split_url("not a url").valid);
VT_CHECK(!split_url("://noscheme/x").valid);
VT_CHECK(!split_url("http://").valid);
auto a = split_url("http://////");
auto b = split_url("https://h/%%%/%");
(void)a;
(void)b;
}
VT_TEST(url_filename_extraction) {
VT_CHECK_EQ(url_filename("https://x.com/a/b/report%20final.pdf"),
std::string("report final.pdf"));
VT_CHECK_EQ(url_filename("https://x.com/a/b/file.iso?sig=abc"), std::string("file.iso"));
VT_CHECK_EQ(url_filename("https://x.com/dir/"), std::string(""));
VT_CHECK_EQ(url_filename("https://x.com"), std::string(""));
VT_CHECK_EQ(url_filename("https://x.com/%2e%2e"), std::string("")); // ".." rejected
VT_CHECK_EQ(url_filename("https://x.com/a%2Fb"),
std::string("a_b")); // decoded '/' neutralised
}