35#define CHECK(cond, msg) \
38 n_log(LOG_ERR, "FAIL: %s", (msg)); \
53 CHECK(
uparse(
"https://e.org/a/b?x=1#frag", &u),
"valid https parses");
55 CHECK(strcmp(u.
host,
"e.org") == 0,
"host");
56 CHECK(strcmp(u.
path,
"/a/b?x=1") == 0,
"path, fragment dropped");
59 "http with explicit port");
63 strcmp(u.
host,
"2606:4700::1") == 0,
64 "IPv6 literal brackets stripped");
67 CHECK(!
uparse(
"ftp://e.org/", &u),
"non-http(s) scheme rejected");
69 "userinfo rejected (SSRF/phishing)");
70 CHECK(!
uparse(
"https:///x", &u),
"empty host rejected");
71 CHECK(!
uparse(
"https://e.org:99999/", &u),
"bad port rejected");
78 "HTTP/1.1 301 Moved\r\nLocation: https://e.org/new\r\n"
79 "Content-Type: text/html; charset=utf-8\r\n"
80 "Content-Length: 12\r\n\r\nbodybytes...";
84 CHECK(strcmp(h.
location,
"https://e.org/new") == 0,
"location");
86 "content-type, params dropped");
90 "head shorter than CRLFCRLF rejected");
92 "partial head returns 0");
98 const char* base =
"https://e.org/dir/page";
99 const char* abs =
"https://x.org/y";
100 const char* rel =
"//x.org/y";
101 const char* root =
"/abs";
102 const char* bare =
"rel/path";
105 strcmp(out,
"https://x.org/y") == 0,
106 "absolute redirect");
109 strcmp(out,
"https://x.org/y") == 0,
110 "scheme-relative redirect");
113 strcmp(out,
"https://e.org/abs") == 0,
114 "root-relative redirect");
117 "bare relative redirect refused");
123 const char* t =
"see https://e.org/p. thanks";
125 strcmp(out,
"https://e.org/p") == 0,
126 "find URL, trailing '.' trimmed");
128 "malformed URL not surfaced");
static int uparse(const char *s, n_url_safe *out)
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_NOTICE
normal but significant condition
int n_url_safe_resolve(const char *base, size_t base_len, const char *loc, size_t loc_len, char *out, size_t cap)
resolve a redirect Location against a request URL
int n_url_safe_find(const char *text, size_t len, char *out, size_t cap)
find the first http(s) URL in a block of text
int n_url_safe_head_parse(const char *buf, size_t len, n_url_safe_head *out)
parse an HTTP/1.x response head from [buf, buf+len)
int n_url_safe_parse(const char *url, size_t len, n_url_safe *out)
parse an absolute http(s) URL [url, url+len) into a bounded struct
Hardened, bounded URL + HTTP/1.x response-head parsing for a safe fetcher.
char location[1024u+1]
Location (redirects).
long long content_length
Content-Length, or -1 if absent.
#define N_URL_SAFE_MAX
Longest URL / path / Location accepted.
int status
HTTP status code, e.g.
char content_type[127u+1]
mime, params dropped
int https
1 = https (default port 443), 0 = http (80)
The parts of an absolute http(s) URL.
The response-head fields a fetcher acts on.