code wiki / _hdl_build / nx_http_chunked.nx

nx_http_chunked.nx source

↩ module page · 73 lines · 3281 B

1// nx_http_chunked.nx -- LIB: HTTP/1.1 chunked transfer-encoding DECODER (RFC 7230 ยง4.1). THE concrete M2 unblock: 2// HuggingFace's dynamic API responses (the repo /tree enumerate) are chunked with NO Content-Length, so our 3// Content-Length reader blocks on them (this is exactly why nx_mirror_live_probe's tree fetch hung). This decodes a 4// chunked body buffer -> the raw bytes, so enumerate can proceed. Pure parsing (no crypto, no TLS) -> imports 5// nx_syscalls only (composes cleanly beside the TLS fetch stack, unlike nx_sha256). No 'break' (nx) -> flag-driven 6// loops. Bounded by the input length (never reads past src[n]). license_tier: ORIGINAL 7import "nx_syscalls.nx" 8 9// hex digit -> value, or -1 if not a hex char. 10func hc_hexval(c: i64) -> i64 { 11 if c >= 48 { if c <= 57 { return c - 48 } } // 0-9 12 if c >= 97 { if c <= 102 { return c - 87 } } // a-f 13 if c >= 65 { if c <= 70 { return c - 55 } } // A-F 14 return 0 - 1 15} 16 17// substring search: 1 if needle (NUL-terminated) occurs in hay[0..n), else 0. 18func hc_has(hay: *u8, n: i64, needle: *u8) -> i64 { 19 var m: i64 = 0; while needle[m] != (0 as u8) { m = m + 1 } 20 if m == 0 { return 0 } 21 var i: i64 = 0 22 while i + m <= n { 23 var j: i64 = 0; var ok: i64 = 1 24 while j < m { if hay[i+j] != needle[j] { ok = 0; j = m } else { j = j + 1 } } 25 if ok == 1 { return 1 } 26 i = i + 1 27 } 28 return 0 29} 30 31// True if a response header block advertises Transfer-Encoding: chunked (case tolerant on the value token). 32func hc_is_chunked(hdr: *u8, n: i64) -> i64 { 33 if hc_has(hdr, n, "chunked" as *u8) == 1 { return 1 } 34 if hc_has(hdr, n, "Chunked" as *u8) == 1 { return 1 } 35 return 0 36} 37 38// Decode chunked body src[0..n) -> out (raw bytes). Returns the decoded byte count (>=0), or -1 if the first 39// chunk-size line is malformed (no hex). Tolerates chunk extensions (";ext" after the size) and stops at the 40// terminating 0-size chunk (trailers, if any, are ignored). 41func chunked_decode(src: *u8, n: i64, out: *u8) -> i64 { 42 var i: i64 = 0 43 var o: i64 = 0 44 var done: i64 = 0 45 while done == 0 { 46 // 1) parse hex chunk-size 47 var sz: i64 = 0 48 var got: i64 = 0 49 var st: i64 = 0 50 while st == 0 { 51 if i >= n { st = 1 } else { 52 let hv: i64 = hc_hexval(src[i] as i64) 53 if hv >= 0 { sz = (sz * 16) + hv; got = got + 1; i = i + 1 } else { st = 1 } 54 } 55 } 56 if got == 0 { return 0 - 1 } 57 // 2) skip chunk extensions (up to CR), then the CRLF 58 var st2: i64 = 0 59 while st2 == 0 { if i >= n { st2 = 1 } else { if (src[i] as i64) == 13 { st2 = 1 } else { i = i + 1 } } } 60 if i + 1 < n { if (src[i] as i64) == 13 { if (src[i+1] as i64) == 10 { i = i + 2 } } } 61 // 3) terminating chunk? 62 if sz == 0 { done = 1 } else { 63 // 4) copy sz data bytes (bounded) 64 var j: i64 = 0 65 while j < sz { 66 if i < n { out[o] = src[i]; o = o + 1; i = i + 1; j = j + 1 } else { j = sz; done = 1 } 67 } 68 // 5) skip the CRLF after the data 69 if i + 1 < n { if (src[i] as i64) == 13 { if (src[i+1] as i64) == 10 { i = i + 2 } } } 70 } 71 } 72 return o 73}