nx_https_ka_probe.nx source
↩ module page · 158 lines · 9822 B
1// nx_https_ka_probe.nx -- R4 ISOLATED PROOF: HTTP/1.1 KEEP-ALIVE connection reuse over ONE TLS session.
2// Operator 2026-07-01 chose R4 (connection reuse). nx_https_get_complete has 30 callers -> CANNOT modify it.
3// So this is a SELF-CONTAINED probe that replicates the send/read loop with a "Connection: keep-alive" request and
4// sends TWO GETs over ONE handshake -> proves the ~300ms certloop is paid ONCE for N fetches. Touches nothing existing.
5// If GREEN, the next step is a connection-pool layer in the research engine (also isolated). license_tier: ORIGINAL
6// expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_tls13.nx"
9import "nx_tls13_record.nx"
10import "nx_tls13_read_record_from_fd.nx"
11import "nx_tls13_client_session.nx"
12import "nx_x509_trust_store.nx"
13import "nx_trust_store_load_from_certdata.nx"
14import "nx_tls13_client_validate_certificate.nx"
15import "nx_tls13_client_session_run.nx"
16import "nx_url.nx"
17import "nx_https_url_for_fetch.nx"
18import "nx_https_url_connect.nx"
19import "nx_http_response_parse.nx"
20const K_MAGIC_4096: i64 = 4096
21const K_MAGIC_16645: i64 = 16645
22const K_MAGIC_4194304: i64 = 4194304
23
24func kp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
25func kn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
26func ka_write_n(fd: i64, buf: *u8, n: i64) -> i64 { var off: i64=0; while off<n { let w: i64=sys_write(fd,(buf as i64+off) as *u8,n-off); if w<=0 {return 0-1} off=off+w } return 0 }
27
28// build "GET <path> HTTP/1.1\r\nHost: <host>\r\n<UA...>\r\nConnection: keep-alive\r\n\r\n" (mirrors nx_http_client_build_request but keep-alive)
29func ka_cat(out: *u8, o: i64, s: *u8) -> i64 { var j: i64=0; while s[j]!=(0 as u8) { out[o]=s[j]; o=o+1; j=j+1 } return o }
30func ka_build_req(path: *u8, path_len: i64, host: *u8, host_len: i64, out: *u8) -> i64 {
31 var o: i64 = 0
32 o = ka_cat(out, o, "GET " as *u8)
33 var pi: i64=0; while pi<path_len { out[o]=path[pi]; o=o+1; pi=pi+1 }
34 o = ka_cat(out, o, " HTTP/1.1\r\nHost: " as *u8)
35 var hi: i64=0; while hi<host_len { out[o]=host[hi]; o=o+1; hi=hi+1 }
36 o = ka_cat(out, o, "\r\nUser-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: identity\r\nConnection: keep-alive\r\n\r\n" as *u8)
37 return o
38}
39
40// find "\r\n\r\n"; -1 if absent
41func ka_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 }
42func ka_lc(c: i64) -> i64 { if c>=65 { if c<=90 { return c+32 } } return c }
43func ka_clen(buf: *u8, hdr_end: i64) -> i64 {
44 let pat: *u8 = "content-length:" as *u8
45 var i: i64=0
46 while i<hdr_end {
47 var atline: i64=0
48 if i==0 { atline=1 } else { if buf[i-1]==(10 as u8) { atline=1 } }
49 if atline==1 {
50 var mm: i64=1; var j: i64=0
51 while j<15 { if ka_lc(buf[i+j] as i64)!=ka_lc(pat[j] as i64) { mm=0; j=15 } else { j=j+1 } }
52 if mm==1 {
53 var p: i64=i+15
54 var d: i64=0
55 while d==0 { if p>=hdr_end { d=1 } else { if buf[p]==(32 as u8) { p=p+1 } else { d=1 } } }
56 var v: i64=0; var any: i64=0; var go: i64=1
57 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 } } }
58 if any==1 { return v }
59 return 0-1
60 }
61 }
62 i=i+1
63 }
64 return 0-1
65}
66
67// Send ONE keep-alive GET over the (already-connected) session+fd, drain the Content-Length body, LEAVE fd open.
68// Returns bytes accumulated (POSITIVE) or negative on error. Mirrors nx_https_get_complete's send/read + cl-stop.
69func ka_get(s: *Tls13ClientSession, fd: i64, path: *u8, path_len: i64, host: *u8, host_len: i64, out_buf: *u8, out_cap: i64) -> i64 {
70 if s.state != NX_TLS13_CSESSION_STATE_CONNECTED { return 0-100 }
71 let req: *u8 = sys_mmap(K_MAGIC_4096)
72 let req_len: i64 = ka_build_req(path, path_len, host, host_len, req)
73 let rec_buf: *u8 = sys_mmap(req_len + 64)
74 let header_out: *u8 = rec_buf
75 let ct_out: *u8 = rec_buf + NX_TLS13_RECORD_HEADER_LEN
76 let tag_out: *u8 = rec_buf + NX_TLS13_RECORD_HEADER_LEN + req_len + 1
77 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, req_len, NX_TLS13_CT_APPLICATION_DATA, 0, header_out, ct_out, tag_out)
78 s.client_app_seq = s.client_app_seq + 1
79 if enc_v != NX_TLS13_REC_VERDICT_OK { return 0-101 }
80 let total_rec_len: i64 = NX_TLS13_RECORD_HEADER_LEN + req_len + 1 + NX_TLS13_RECORD_TAG_LEN
81 if ka_write_n(fd, rec_buf, total_rec_len) < 0 { return 0-102 }
82 var accumulated: i64 = 0
83 var body_target: i64 = 0
84 var hdr_parsed: i64 = 0
85 let rec_in: *u8 = sys_mmap(K_MAGIC_16645)
86 let plaintext: *u8 = sys_mmap(K_MAGIC_16645)
87 let pct: *i64 = sys_mmap(16) as *i64
88 let plen: *i64 = sys_mmap(16) as *i64
89 while accumulated < out_cap {
90 let rt: i64 = nx_tls13_read_record_from_fd(fd, rec_in, K_MAGIC_16645)
91 if rt < 0 {
92 let nv: i64 = 0 - rt
93 if nv == NX_TLS13_READ_REC_EOF { return accumulated }
94 if nv == NX_TLS13_READ_REC_PAYLOAD_EOF { return accumulated }
95 return 0-103
96 }
97 let rec_ct: *u8 = rec_in + NX_TLS13_RECORD_HEADER_LEN
98 let rec_ct_len: i64 = rt - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN
99 let rec_tag: *u8 = rec_in + rt - NX_TLS13_RECORD_TAG_LEN
100 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_ct, rec_ct_len, rec_tag, plaintext, pct, plen)
101 s.server_app_seq = s.server_app_seq + 1
102 if dv != NX_TLS13_REC_VERDICT_OK { return 0-104 }
103 if *pct == NX_TLS13_CT_ALERT { return accumulated }
104 if *pct == NX_TLS13_CT_APPLICATION_DATA {
105 let to_copy: i64 = *plen
106 if to_copy > out_cap - accumulated { return 0-105 }
107 var i: i64=0; while i<to_copy { out_buf[accumulated+i]=plaintext[i]; i=i+1 }
108 accumulated = accumulated + to_copy
109 if hdr_parsed==0 { let he: i64=ka_hdr_end(out_buf, accumulated); if he>=0 { hdr_parsed=1; let cl: i64=ka_clen(out_buf, he); if cl>=0 { body_target=he+4+cl } } }
110 if body_target>0 { if accumulated>=body_target { return accumulated } } // cl-stop: leave fd OPEN for reuse
111 }
112 }
113 return accumulated
114}
115
116func main() -> i64 {
117 kp("=== nx_https_ka_probe -- R4 keep-alive connection-reuse proof (2 GETs over 1 TLS handshake) ===\n" as *u8)
118 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, K_MAGIC_4194304)
119 if r <= 0 { kp("trust load FAIL\n" as *u8); return 1 }
120 let store: *TrustStore = r as *TrustStore
121 let urlbuf: *u8 = sys_mmap(K_MAGIC_4096)
122 let u0: *u8 = "https://en.wikipedia.org/wiki/Three.js" as *u8
123 var ui: i64=0; while u0[ui]!=(0 as u8) { urlbuf[ui]=u0[ui]; ui=ui+1 } urlbuf[ui]=0 as u8
124 let target_raw: *u8 = sys_mmap(64); let target: *NxHttpsTarget = target_raw as *NxHttpsTarget
125 target.url = nx_url_new(); target.port = 0
126 if nx_https_url_for_fetch(urlbuf, target) != NX_HTTPS_URL_OK { kp("url FAIL\n" as *u8); return 1 }
127 let fd_p: *i64 = sys_mmap(16) as *i64
128 if nx_https_url_connect(target, urlbuf, sys_now_realtime_sec(), fd_p) != NX_HTTPS_CONNECT_OK { kp("connect FAIL\n" as *u8); return 1 }
129 let fd: i64 = fd_p[0]
130 let host: *u8 = urlbuf + target.url.host_off
131 let hlen: i64 = target.url.host_len
132 let cr: *u8 = sys_mmap(32); let priv: *u8 = sys_mmap(32)
133 var i: i64=0; while i<32 { cr[i]=(0xC0+i) as u8; priv[i]=(0xA0+i) as u8; i=i+1 }
134 let vc_raw: *u8 = sys_mmap(64); let vc: *TlsValidationContext = vc_raw as *TlsValidationContext
135 vc.store = store; vc.sni_host = host; vc.sni_host_len = hlen; vc.now_epoch = sys_now_realtime_sec()
136 kp(" handshake (ONE certloop for BOTH requests)...\n" as *u8)
137 let sr: i64 = nx_tls13_client_session_run(fd, host, hlen, cr, priv, vc)
138 if sr <= 0 { sys_close(fd); kp("handshake FAIL\n" as *u8); return 1 }
139 let session: *Tls13ClientSession = sr as *Tls13ClientSession
140
141 let cap: i64 = K_MAGIC_4194304
142 let out1: *u8 = sys_mmap(cap); let out2: *u8 = sys_mmap(cap)
143 let rr: *i64 = sys_mmap(128) as *i64
144 // REQUEST 1: /wiki/Three.js
145 let n1: i64 = ka_get(session, fd, "/wiki/WebGL" as *u8, 11, host, hlen, out1, cap)
146 var st1: i64 = 0; if n1>0 { if nx_http_response_parse(out1, n1, rr)==0 { st1=rr[1] } }
147 kp(" REQ1 /wiki/WebGL bytes="); kn(n1); kp(" status="); kn(st1); kp("\n" as *u8)
148 // REQUEST 2: /wiki/WebGL -- OVER THE SAME session+fd (no new handshake!)
149 let n2: i64 = ka_get(session, fd, "/wiki/WebGL" as *u8, 11, host, hlen, out2, cap)
150 var st2: i64 = 0; if n2>0 { if nx_http_response_parse(out2, n2, rr)==0 { st2=rr[1] } }
151 kp(" REQ2 /wiki/WebGL bytes="); kn(n2); kp(" status="); kn(st2); kp(" (reused connection, NO 2nd handshake)\n" as *u8)
152 sys_close(fd)
153
154 var green: i64=0; if n1>0 { if n2>0 { if st1==200 { if st2==200 { green=1 } } } }
155 if green==1 { kp("verdict=GREEN -- R4 PROVEN: 2 responses over 1 TLS handshake (certloop paid ONCE). Connection reuse works.\n" as *u8); return 0 }
156 kp("verdict=RED (n1="); kn(n1); kp(" st1="); kn(st1); kp(" n2="); kn(n2); kp(" st2="); kn(st2); kp(")\n" as *u8)
157 return 1
158}