code wiki / (root) / nx_https_get_stream.nx

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 }