nx_http_get_ua.nx source
↩ module page · 68 lines · 2962 B
1// nx_http_get_ua.nx -- reusable HTTP/1.1 GET with a User-Agent (bits-up).
2//
3// module: nishi-core.net.http_get_ua
4// depends: nx_str.nx, nx_syscalls.nx
5// capability: CORE_IO
6// wired_status: FULLY_WIRED
7//
8// The plain-TCP GET-with-identifying-UA the crawlers all needed (was duplicated
9// in nx_crawl_polite + nx_crawl_main). Extracted to ONE reusable primitive
10// (DRY, Rule #15): socket -> connect -> build "GET ... + Host + User-Agent +
11// Connection: close" -> drain -> parse status. Any HTTP client reuses it
12// (crawler, torrent HTTP-tracker fallback, health probes). The request builder
13// is pure (KAT'd); the fetch adds the socket round-trip.
14
15import "nx_str.nx"
16import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
17import "nx_syscalls.nx"
18
19func _hua_lit(out: *u8, o: i64, s: *u8) -> i64 {
20 let n: i64 = nx_str_len(s)
21 var i: i64 = 0
22 while i < n { out[o + i] = s[i]; i = i + 1 }
23 return o + n
24}
25func _hua_buf(out: *u8, o: i64, b: *u8, n: i64) -> i64 {
26 var i: i64 = 0
27 while i < n { out[o + i] = b[i]; i = i + 1 }
28 return o + n
29}
30
31// Build "GET <path> HTTP/1.1\r\nHost: <host>\r\nUser-Agent: <ua>\r\n
32// Connection: close\r\n\r\n" into out. Returns the byte length. Pure.
33func nx_http_ua_build_request(path: *u8, plen: i64, host: *u8, hlen: i64,
34 ua: *u8, ualen: i64, out: *u8) -> i64 {
35 var o: i64 = 0
36 o = _hua_lit(out, o, "GET ")
37 o = _hua_buf(out, o, path, plen)
38 o = _hua_lit(out, o, " HTTP/1.1\r\nHost: ")
39 o = _hua_buf(out, o, host, hlen)
40 o = _hua_lit(out, o, "\r\nUser-Agent: ")
41 o = _hua_buf(out, o, ua, ualen)
42 o = _hua_lit(out, o, "\r\nConnection: close\r\n\r\n")
43 return o
44}
45
46// Connect to addr_packed (sockaddr_in), send the UA GET, drain the response
47// into out (<= cap), parse the status code into out_status[0]. Returns bytes
48// read, or -1 on socket error.
49func nx_http_get_ua(addr_packed: *u8, path: *u8, plen: i64, host: *u8, hlen: i64,
50 ua: *u8, ualen: i64, out: *u8, cap: i64, out_status: *i64) -> i64 {
51 out_status[0] = 0
52 let fd: i64 = sys_socket(2, 1, 0)
53 if fd < 0 { return 0 - 1 }
54 sys_set_socket_timeout(fd, 10) // 10s read/write timeout so a hung server never blocks forever
55 if nx_connect_bounded(fd, addr_packed, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); return 0 - 1 }
56 let req: *u8 = sys_mmap(4096)
57 let rlen: i64 = nx_http_ua_build_request(path, plen, host, hlen, ua, ualen, req)
58 if sys_write(fd, req, rlen) != rlen { sys_close(fd); return 0 - 1 }
59 var off: i64 = 0
60 var keep: i64 = 1
61 while keep == 1 {
62 if off >= cap { keep = 0 }
63 else { let r: i64 = sys_read(fd, ((out as i64) + off) as *u8, cap - off); if r <= 0 { keep = 0 } else { off = off + r } }
64 }
65 sys_close(fd)
66 if off > 12 { out_status[0] = (out[9] as i64 - 0x30) * 100 + (out[10] as i64 - 0x30) * 10 + (out[11] as i64 - 0x30) }
67 return off
68}