code wiki / (root) / nx_http_get_ua.nx

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}