nx_https_get_stream.nx source
↩ module page · 163 lines · 9440 B
1// nx_https_get_stream.nx -- STREAMING TLS GET (the M3-into-fetch piece): parse response headers, then stream the
2// body straight to a dest fd. Multi-GB shards flow fetch -> decrypt -> disk through a small window, no 5GB buffer.
3// S-CLASS features: RESUME (range_start>0 -> "Range: bytes=N-" header, so a broken partial continues); WRITE-
4// BUFFERING (batch body into 8MB writes -> ~500x fewer 9p/NAS syscalls = faster + doesn't destabilize the mount);
5// redirect-aware (3xx -> Location); range-safety (a full 200 to a ranged request -> -9 so the caller truncates +
6// restarts, never appending a full body onto a partial). No sha256 here (M32 clash with TLS crypto -> verify in a
7// separate organ). Mirrors nx_https_get_complete's proven send + read/decrypt loop (only the sink changes). ORIGINAL
8import "nx_syscalls.nx"
9import "nx_tls13.nx"
10import "nx_tls13_record.nx"
11import "nx_tls13_read_record_from_fd.nx"
12import "nx_tls13_client_session.nx"
13import "nx_http_client.nx"
14import "nx_chacha20_poly1305.nx"
15
16const NX_GS_REQ_BUF_BYTES: i64 = 4096
17const NX_GS_RESP_RECORD_BYTES: i64 = 16645
18const NX_GS_HDR_CAP: i64 = 65536
19const NX_GS_WBUF_CAP: i64 = 67108864
20
21func gs_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
22func gs_puts(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){buf[o]=s[i];o=o+1;i=i+1} return o }
23func gs_wdec(buf: *u8, off: i64, v: i64) -> i64 { var o: i64=off; if v==0 { buf[o]=48 as u8; return o+1 } let t:*u8=sys_mmap(24); var k:i64=0; var m:i64=v; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } var j:i64=k-1; while j>=0 { buf[o]=t[j]; o=o+1; j=j-1 } return o }
24func gs_write_n(fd: i64, buf: *u8, n: i64) -> i64 { var off: i64=0; while off < n { let r: i64=sys_write(fd, ((buf as i64)+off) as *u8, n-off); if r <= 0 { return 0-1 } off = off + r } return 0 }
25// append n bytes from src to the 8MB write buffer, flushing to dest_fd when it would overflow. returns 0 / -1.
26func gs_bufapp(wbuf: *u8, wlenp: *i64, dest_fd: i64, src: *u8, n: i64) -> i64 {
27 var wl: i64 = wlenp[0]
28 if wl + n > NX_GS_WBUF_CAP {
29 if wl > 0 { if gs_write_n(dest_fd, wbuf, wl) < 0 { return 0 - 1 } wl = 0 }
30 if n >= NX_GS_WBUF_CAP { if gs_write_n(dest_fd, src, n) < 0 { return 0 - 1 } wlenp[0] = 0; return 0 }
31 }
32 var i: i64 = 0; while i < n { wbuf[wl+i] = src[i]; i = i + 1 }
33 wlenp[0] = wl + n
34 return 0
35}
36func gs_hdr_end(buf: *u8, n: i64) -> i64 { var i: i64=0; while i+4 <= n { if buf[i]==(13 as u8){ if buf[i+1]==(10 as u8){ if buf[i+2]==(13 as u8){ if buf[i+3]==(10 as u8){ return i } } } } i=i+1 } return 0-1 }
37func gs_status(buf: *u8, n: i64) -> i64 { if n < 12 { return 0 } return ((buf[9] as i64)-48)*100 + ((buf[10] as i64)-48)*10 + ((buf[11] as i64)-48) }
38func gs_ci_match(buf: *u8, off: i64, end: i64, pat: *u8, patlen: i64) -> i64 { if off+patlen > end { return 0 } var j: i64=0; while j < patlen { if gs_lc(buf[off+j] as i64) != gs_lc(pat[j] as i64) { return 0 } j=j+1 } return 1 }
39func gs_clen(buf: *u8, hdr_end: i64) -> i64 {
40 let pat: *u8 = "content-length:" as *u8
41 var i: i64=0
42 while i < hdr_end {
43 var atline: i64=0
44 if i==0 { atline=1 } else { if buf[i-1]==(10 as u8){ atline=1 } }
45 if atline==1 { if gs_ci_match(buf,i,hdr_end,pat,15)==1 {
46 var p: i64=i+15
47 var sk: i64=1; while sk==1 { if p<hdr_end { if buf[p]==(32 as u8){ p=p+1 } else { sk=0 } } else { sk=0 } }
48 var v: i64=0; var any: i64=0; var go: i64=1
49 while go==1 { if p>=hdr_end { go=0 } else { let c: i64=buf[p] as i64; if c>=48 { if c<=57 { v=v*10+(c-48); any=1; p=p+1 } else { go=0 } } else { go=0 } } }
50 if any==1 { return v }
51 return 0-1
52 } }
53 i=i+1
54 }
55 return 0-1
56}
57func gs_location(buf: *u8, hdr_end: i64, out: *u8, out_cap: i64) -> i64 {
58 let pat: *u8 = "location:" as *u8
59 var i: i64=0
60 while i < hdr_end {
61 var atline: i64=0
62 if i==0 { atline=1 } else { if buf[i-1]==(10 as u8){ atline=1 } }
63 if atline==1 { if gs_ci_match(buf,i,hdr_end,pat,9)==1 {
64 var p: i64=i+9
65 var sk: i64=1; while sk==1 { if p<hdr_end { if buf[p]==(32 as u8){ p=p+1 } else { sk=0 } } else { sk=0 } }
66 var o: i64=0
67 while p < hdr_end { if buf[p]==(13 as u8){ p=hdr_end } else { if o < out_cap-1 { out[o]=buf[p]; o=o+1 } p=p+1 } }
68 out[o]=0 as u8; return o
69 } }
70 i=i+1
71 }
72 out[0]=0 as u8; return 0-1
73}
74
75func nx_https_get_stream(s: *Tls13ClientSession, fd: i64, path: *u8, path_len: i64, host: *u8, host_len: i64,
76 range_start: i64, dest_fd: i64, out_status: *i64, loc_buf: *u8, loc_cap: i64) -> i64 {
77 out_status[0] = 0
78 if s.state != NX_TLS13_CSESSION_STATE_CONNECTED { return 0 - 2 }
79
80 let req: *u8 = sys_mmap(NX_GS_REQ_BUF_BYTES)
81 var rl: i64 = nx_http_client_build_request(path, path_len, host, host_len, req)
82 if rl <= 0 { return 0 - 3 }
83 if range_start > 0 {
84 var ro: i64 = rl - 2
85 ro = gs_puts(req, ro, "Range: bytes=" as *u8)
86 ro = gs_wdec(req, ro, range_start)
87 req[ro]=45 as u8; ro=ro+1
88 req[ro]=13 as u8; ro=ro+1; req[ro]=10 as u8; ro=ro+1
89 req[ro]=13 as u8; ro=ro+1; req[ro]=10 as u8; ro=ro+1
90 rl = ro
91 }
92 let rec_buf: *u8 = sys_mmap(rl + 64)
93 let header_out: *u8 = rec_buf
94 let ct_out: *u8 = rec_buf + NX_TLS13_RECORD_HEADER_LEN
95 let tag_out: *u8 = rec_buf + NX_TLS13_RECORD_HEADER_LEN + rl + 1
96 let enc_v: i64 = nx_tls13_record_encrypt_v2(s.cipher_suite, s.client_app_traffic_key, s.client_app_iv, s.client_app_seq, req, rl, NX_TLS13_CT_APPLICATION_DATA, 0, header_out, ct_out, tag_out)
97 s.client_app_seq = s.client_app_seq + 1
98 if enc_v != NX_TLS13_REC_VERDICT_OK { return 0 - 4 }
99 let total_rec_len: i64 = NX_TLS13_RECORD_HEADER_LEN + rl + 1 + NX_TLS13_RECORD_TAG_LEN
100 if gs_write_n(fd, rec_buf, total_rec_len) < 0 { return 0 - 5 }
101
102 let hdrbuf: *u8 = sys_mmap(NX_GS_HDR_CAP)
103 var hlen: i64 = 0
104 var hdr_done: i64 = 0
105 var status: i64 = 0
106 var body_target: i64 = 0
107 var body_written: i64 = 0
108 let rec_in: *u8 = sys_mmap(NX_GS_RESP_RECORD_BYTES)
109 let plaintext: *u8 = sys_mmap(NX_GS_RESP_RECORD_BYTES)
110 let pt_ct_p: *i64 = sys_mmap(16) as *i64
111 let pt_len_p: *i64 = sys_mmap(16) as *i64
112 let wbuf: *u8 = sys_mmap(NX_GS_WBUF_CAP)
113 let wlenp: *i64 = sys_mmap(16) as *i64
114 wlenp[0] = 0
115 var result: i64 = 0
116 var looping: i64 = 1
117 while looping == 1 {
118 let rin: i64 = nx_tls13_read_record_from_fd(fd, rec_in, NX_GS_RESP_RECORD_BYTES)
119 if rin < 0 {
120 let nv: i64 = 0 - rin
121 if nv == NX_TLS13_READ_REC_EOF { looping = 0 } else { if nv == NX_TLS13_READ_REC_PAYLOAD_EOF { looping = 0 } else { result = 0 - 6; looping = 0 } }
122 } else {
123 let ct_len: i64 = rin - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN
124 let dv: i64 = nx_tls13_record_decrypt_v2(s.cipher_suite, s.server_app_traffic_key, s.server_app_iv, s.server_app_seq, rec_in, rec_in + NX_TLS13_RECORD_HEADER_LEN, ct_len, rec_in + rin - NX_TLS13_RECORD_TAG_LEN, plaintext, pt_ct_p, pt_len_p)
125 s.server_app_seq = s.server_app_seq + 1
126 if dv != NX_TLS13_REC_VERDICT_OK { result = 0 - 7; looping = 0 } else {
127 if *pt_ct_p == NX_TLS13_CT_ALERT { looping = 0 } else {
128 if *pt_ct_p == NX_TLS13_CT_APPLICATION_DATA {
129 let ptl: i64 = *pt_len_p
130 if hdr_done == 0 {
131 var ii: i64 = 0
132 while ii < ptl { if hlen < NX_GS_HDR_CAP { hdrbuf[hlen] = plaintext[ii]; hlen = hlen + 1 } ii = ii + 1 }
133 let he: i64 = gs_hdr_end(hdrbuf, hlen)
134 if he >= 0 {
135 hdr_done = 1
136 status = gs_status(hdrbuf, hlen)
137 out_status[0] = status
138 if status >= 300 { if status < 400 { gs_location(hdrbuf, he, loc_buf, loc_cap); return 0 } }
139 if range_start > 0 { if status == 200 { return 0 - 9 } }
140 body_target = gs_clen(hdrbuf, he)
141 let bstart: i64 = he + 4
142 if hlen > bstart {
143 if gs_bufapp(wbuf, wlenp, dest_fd, ((hdrbuf as i64) + bstart) as *u8, hlen - bstart) < 0 { result = 0 - 8; looping = 0 } else { body_written = body_written + (hlen - bstart) }
144 }
145 if body_target > 0 { if body_written >= body_target { looping = 0 } }
146 }
147 } else {
148 if gs_bufapp(wbuf, wlenp, dest_fd, plaintext, ptl) < 0 { result = 0 - 8; looping = 0 } else {
149 body_written = body_written + ptl
150 if body_target > 0 { if body_written >= body_target { looping = 0 } }
151 }
152 }
153 }
154 }
155 }
156 }
157 }
158 if wlenp[0] > 0 { if gs_write_n(dest_fd, wbuf, wlenp[0]) < 0 { if result == 0 { result = 0 - 8 } } }
159 if result != 0 { return result }
160 return body_written
161}
162
163func main() -> i64 { return 0 }