nx_http_dechunk_boundary_gate.nx source
↩ module page · 303 lines · 11715 B
1import "nx_http_response_parse.nx"
2import "nx_https_fetch_lib.nx"
3func main() -> i64 {
4 let shared_out: *u8=sys_mmap(32)
5 if hfd_dechunk("1\r\nA0\r\n\r\n" as *u8,9,shared_out,32) >= 0 { sys_write(1,"FAIL shared fetch framing\n" as *u8,26); return 1 }
6 if parser_legacy_suite() != 0 { return 1 }
7 let out: *u8=sys_mmap(33)
8 out[32]=99 as u8
9 let r0: i64=nx_http_dechunk("1\r\nAxx0\r\n\r\n" as *u8,11,out,32)
10 if r0 >= 0 { sys_write(1,"FAIL case 0\n" as *u8,12); return 1 }
11 let r1: i64=nx_http_dechunk("1\r\nA\r\n0\r\n" as *u8,9,out,32)
12 if r1 >= 0 { sys_write(1,"FAIL case 1\n" as *u8,12); return 1 }
13 let r2: i64=nx_http_dechunk("0\r\n" as *u8,3,out,32)
14 if r2 >= 0 { sys_write(1,"FAIL case 2\n" as *u8,12); return 1 }
15 let r3: i64=nx_http_dechunk("0\r\n\r\n" as *u8,5,out,32)
16 if r3 != 0 { sys_write(1,"FAIL case 3\n" as *u8,12); return 1 }
17 let r4: i64=nx_http_dechunk("1\r\nA\r\n0\r\n\r\n" as *u8,11,out,32)
18 if r4 != 1 { sys_write(1,"FAIL case 4\n" as *u8,12); return 1 }
19 let r5: i64=nx_http_dechunk("1;foo=bar\r\nA\r\n0\r\nX-Test: yes\r\n\r\n" as *u8,32,out,32)
20 if r5 != 1 { sys_write(1,"FAIL case 5\n" as *u8,12); return 1 }
21 let r6: i64=nx_http_dechunk("1\r\nA\r\n0\r\nX-Test: yes\r\n" as *u8,22,out,32)
22 if r6 >= 0 { sys_write(1,"FAIL case 6\n" as *u8,12); return 1 }
23 let r7: i64=nx_http_dechunk("ffffffffffffffffffffffffffffffff\r\nA\r\n0\r\n\r\n" as *u8,42,out,32)
24 if r7 >= 0 { sys_write(1,"FAIL case 7\n" as *u8,12); return 1 }
25 let r8: i64=nx_http_dechunk("2\r\nAB\r\n0\r\n\r\n" as *u8,12,out,1)
26 if r8 >= 0 { sys_write(1,"FAIL case 8\n" as *u8,12); return 1 }
27 let r9: i64=nx_http_dechunk("1z\r\nA\r\n0\r\n\r\n" as *u8,12,out,32)
28 if r9 >= 0 { sys_write(1,"FAIL case 9\n" as *u8,12); return 1 }
29 let r10: i64=nx_http_dechunk("1\r\nA\r\n1\r\nB\r\n0\r\n\r\n" as *u8,17,out,2)
30 if r10 != 2 { sys_write(1,"FAIL case 10\n" as *u8,13); return 1 }
31 if out[0] != 65 as u8 || out[1] != 66 as u8 || out[32] != 99 as u8 { return 1 }
32 sys_write(1,"PASS: chunk boundaries and decoded bytes\n" as *u8,41)
33 return 0
34}
35
36// nx_http_response_parse_test.nx -- KATs for HTTP/1.1 client-side
37// response parser.
38//
39// Covers:
40// K1 minimal 200 OK with Content-Length + body -> verdict OK,
41// code=200, body_len=5, body bytes = "hello"
42// K2 404 Not Found with Content-Length=0 -> verdict OK,
43// code=404, body_len=0
44// K3 chunked-encoded body (two chunks) -> dechunk yields
45// concatenated bytes
46// K4 case-insensitive header lookup -> finds "Content-
47// Type" via lowercase query
48// K5 malformed status line (bad version) -> verdict ERR_
49// BAD_VERSION
50// K6 Transfer-Encoding: chunked classification -> transfer_kind
51// == NX_HTTP_BODY_CHUNKED
52//
53// Built per F7 post-order DFS.
54// expect_exit: 0
55// license_tier: ORIGINAL
56
57
58// ===== build_msg helpers (LEAF) ====================================
59
60func t_write_str(out: *u8, off: i64, s: *u8, n: i64) -> i64 {
61 var i: i64 = 0
62 while i < n { out[off + i] = s[i]; i = i + 1 }
63 return off + n
64}
65
66func t_make_200_hello(out: *u8) -> i64 {
67 var p: i64 = 0
68 let line: *u8 = sys_mmap(64)
69 // "HTTP/1.1 200 OK\r\n"
70 line[0]=0x48; line[1]=0x54; line[2]=0x54; line[3]=0x50
71 line[4]=0x2f; line[5]=0x31; line[6]=0x2e; line[7]=0x31
72 line[8]=0x20; line[9]=0x32; line[10]=0x30; line[11]=0x30
73 line[12]=0x20; line[13]=0x4f; line[14]=0x4b
74 line[15]=0x0d; line[16]=0x0a
75 p = t_write_str(out, p, line, 17)
76 let cl: *u8 = sys_mmap(64)
77 cl[0]=0x43; cl[1]=0x6f; cl[2]=0x6e; cl[3]=0x74 // Cont
78 cl[4]=0x65; cl[5]=0x6e; cl[6]=0x74; cl[7]=0x2d // ent-
79 cl[8]=0x4c; cl[9]=0x65; cl[10]=0x6e; cl[11]=0x67 // Leng
80 cl[12]=0x74; cl[13]=0x68; cl[14]=0x3a; cl[15]=0x20 // th:_
81 cl[16]=0x35 // 5
82 cl[17]=0x0d; cl[18]=0x0a // \r\n
83 cl[19]=0x0d; cl[20]=0x0a // \r\n
84 p = t_write_str(out, p, cl, 21)
85 let body: *u8 = sys_mmap(16)
86 body[0]=0x68; body[1]=0x65; body[2]=0x6c; body[3]=0x6c; body[4]=0x6f
87 p = t_write_str(out, p, body, 5)
88 return p
89}
90
91func t_make_404_empty(out: *u8) -> i64 {
92 var p: i64 = 0
93 let line: *u8 = sys_mmap(64)
94 // "HTTP/1.1 404 Not Found\r\n"
95 line[0]=0x48; line[1]=0x54; line[2]=0x54; line[3]=0x50
96 line[4]=0x2f; line[5]=0x31; line[6]=0x2e; line[7]=0x31
97 line[8]=0x20; line[9]=0x34; line[10]=0x30; line[11]=0x34
98 line[12]=0x20; line[13]=0x4e; line[14]=0x6f; line[15]=0x74
99 line[16]=0x20; line[17]=0x46; line[18]=0x6f; line[19]=0x75
100 line[20]=0x6e; line[21]=0x64
101 line[22]=0x0d; line[23]=0x0a
102 p = t_write_str(out, p, line, 24)
103 let cl: *u8 = sys_mmap(32)
104 cl[0]=0x43; cl[1]=0x6f; cl[2]=0x6e; cl[3]=0x74
105 cl[4]=0x65; cl[5]=0x6e; cl[6]=0x74; cl[7]=0x2d
106 cl[8]=0x4c; cl[9]=0x65; cl[10]=0x6e; cl[11]=0x67
107 cl[12]=0x74; cl[13]=0x68; cl[14]=0x3a; cl[15]=0x20
108 cl[16]=0x30 // 0
109 cl[17]=0x0d; cl[18]=0x0a
110 cl[19]=0x0d; cl[20]=0x0a
111 p = t_write_str(out, p, cl, 21)
112 return p
113}
114
115func t_make_chunked(out: *u8) -> i64 {
116 var p: i64 = 0
117 let line: *u8 = sys_mmap(64)
118 line[0]=0x48; line[1]=0x54; line[2]=0x54; line[3]=0x50
119 line[4]=0x2f; line[5]=0x31; line[6]=0x2e; line[7]=0x31
120 line[8]=0x20; line[9]=0x32; line[10]=0x30; line[11]=0x30
121 line[12]=0x20; line[13]=0x4f; line[14]=0x4b
122 line[15]=0x0d; line[16]=0x0a
123 p = t_write_str(out, p, line, 17)
124 let te: *u8 = sys_mmap(64)
125 // "Transfer-Encoding: chunked\r\n\r\n"
126 te[0]=0x54; te[1]=0x72; te[2]=0x61; te[3]=0x6e
127 te[4]=0x73; te[5]=0x66; te[6]=0x65; te[7]=0x72
128 te[8]=0x2d; te[9]=0x45; te[10]=0x6e; te[11]=0x63
129 te[12]=0x6f; te[13]=0x64; te[14]=0x69; te[15]=0x6e
130 te[16]=0x67; te[17]=0x3a; te[18]=0x20
131 te[19]=0x63; te[20]=0x68; te[21]=0x75; te[22]=0x6e
132 te[23]=0x6b; te[24]=0x65; te[25]=0x64
133 te[26]=0x0d; te[27]=0x0a; te[28]=0x0d; te[29]=0x0a
134 p = t_write_str(out, p, te, 30)
135 return p
136}
137
138// chunked body bytes: "5\r\nhello\r\n6\r\n world\r\n0\r\n\r\n"
139func t_make_chunked_body(out: *u8) -> i64 {
140 var p: i64 = 0
141 let buf: *u8 = sys_mmap(64)
142 // 5\r\n
143 buf[0]=0x35; buf[1]=0x0d; buf[2]=0x0a
144 // hello
145 buf[3]=0x68; buf[4]=0x65; buf[5]=0x6c; buf[6]=0x6c; buf[7]=0x6f
146 // \r\n
147 buf[8]=0x0d; buf[9]=0x0a
148 // 6\r\n
149 buf[10]=0x36; buf[11]=0x0d; buf[12]=0x0a
150 // " world" (space + world)
151 buf[13]=0x20; buf[14]=0x77; buf[15]=0x6f; buf[16]=0x72; buf[17]=0x6c; buf[18]=0x64
152 buf[19]=0x0d; buf[20]=0x0a
153 // 0\r\n\r\n
154 buf[21]=0x30; buf[22]=0x0d; buf[23]=0x0a
155 buf[24]=0x0d; buf[25]=0x0a
156 p = t_write_str(out, p, buf, 26)
157 return p
158}
159
160func t_make_200_with_ct(out: *u8) -> i64 {
161 var p: i64 = 0
162 let line: *u8 = sys_mmap(64)
163 line[0]=0x48; line[1]=0x54; line[2]=0x54; line[3]=0x50
164 line[4]=0x2f; line[5]=0x31; line[6]=0x2e; line[7]=0x31
165 line[8]=0x20; line[9]=0x32; line[10]=0x30; line[11]=0x30
166 line[12]=0x20; line[13]=0x4f; line[14]=0x4b
167 line[15]=0x0d; line[16]=0x0a
168 p = t_write_str(out, p, line, 17)
169 let ct: *u8 = sys_mmap(64)
170 // "Content-Type: text/html\r\n"
171 ct[0]=0x43; ct[1]=0x6f; ct[2]=0x6e; ct[3]=0x74
172 ct[4]=0x65; ct[5]=0x6e; ct[6]=0x74; ct[7]=0x2d
173 ct[8]=0x54; ct[9]=0x79; ct[10]=0x70; ct[11]=0x65
174 ct[12]=0x3a; ct[13]=0x20
175 ct[14]=0x74; ct[15]=0x65; ct[16]=0x78; ct[17]=0x74
176 ct[18]=0x2f; ct[19]=0x68; ct[20]=0x74; ct[21]=0x6d; ct[22]=0x6c
177 ct[23]=0x0d; ct[24]=0x0a
178 p = t_write_str(out, p, ct, 25)
179 let cl: *u8 = sys_mmap(32)
180 cl[0]=0x43; cl[1]=0x6f; cl[2]=0x6e; cl[3]=0x74
181 cl[4]=0x65; cl[5]=0x6e; cl[6]=0x74; cl[7]=0x2d
182 cl[8]=0x4c; cl[9]=0x65; cl[10]=0x6e; cl[11]=0x67
183 cl[12]=0x74; cl[13]=0x68; cl[14]=0x3a; cl[15]=0x20
184 cl[16]=0x33
185 cl[17]=0x0d; cl[18]=0x0a
186 cl[19]=0x0d; cl[20]=0x0a
187 p = t_write_str(out, p, cl, 21)
188 let body: *u8 = sys_mmap(8)
189 body[0]=0x61; body[1]=0x62; body[2]=0x63
190 p = t_write_str(out, p, body, 3)
191 return p
192}
193
194func t_make_bad_version(out: *u8) -> i64 {
195 var p: i64 = 0
196 let line: *u8 = sys_mmap(32)
197 // "FTP/9.9 200 OK\r\n\r\n"
198 line[0]=0x46; line[1]=0x54; line[2]=0x50 // FTP
199 line[3]=0x2f; line[4]=0x39; line[5]=0x2e; line[6]=0x39
200 line[7]=0x20; line[8]=0x32; line[9]=0x30; line[10]=0x30
201 line[11]=0x20; line[12]=0x4f; line[13]=0x4b
202 line[14]=0x0d; line[15]=0x0a
203 line[16]=0x0d; line[17]=0x0a
204 p = t_write_str(out, p, line, 18)
205 return p
206}
207
208// ===== KAT runners (post-order from leaf helpers) =================
209
210func k1_minimal_200_hello() -> i64 {
211 let buf: *u8 = sys_mmap(256)
212 let n: i64 = t_make_200_hello(buf)
213 let r: *i64 = nx_http_resp_alloc()
214 let v: i64 = nx_http_response_parse(buf, n, r)
215 if v != NX_HTTP_RESP_OK { return 11 }
216 if r[1] != 200 { return 12 }
217 if r[8] != NX_HTTP_BODY_CONTENT_LENGTH { return 13 }
218 if r[7] != 5 { return 14 }
219 if (buf[r[6] + 0] & 0xff) != 0x68 { return 15 } // h
220 if (buf[r[6] + 4] & 0xff) != 0x6f { return 16 } // o
221 return 0
222}
223
224func k2_404_empty() -> i64 {
225 let buf: *u8 = sys_mmap(256)
226 let n: i64 = t_make_404_empty(buf)
227 let r: *i64 = nx_http_resp_alloc()
228 let v: i64 = nx_http_response_parse(buf, n, r)
229 if v != NX_HTTP_RESP_OK { return 21 }
230 if r[1] != 404 { return 22 }
231 if r[8] != NX_HTTP_BODY_CONTENT_LENGTH { return 23 }
232 if r[7] != 0 { return 24 }
233 return 0
234}
235
236func k3_chunked_decode() -> i64 {
237 let body: *u8 = sys_mmap(64)
238 let bn: i64 = t_make_chunked_body(body)
239 let out: *u8 = sys_mmap(64)
240 let dn: i64 = nx_http_dechunk(body, bn, out, 64)
241 if dn != 11 { return 31 }
242 if (out[0] & 0xff) != 0x68 { return 32 } // h
243 if (out[4] & 0xff) != 0x6f { return 33 } // o
244 if (out[5] & 0xff) != 0x20 { return 34 } // ' '
245 if (out[6] & 0xff) != 0x77 { return 35 } // w
246 if (out[10] & 0xff) != 0x64 { return 36 } // d
247 return 0
248}
249
250func k4_header_lookup_ci() -> i64 {
251 let buf: *u8 = sys_mmap(256)
252 let n: i64 = t_make_200_with_ct(buf)
253 let r: *i64 = nx_http_resp_alloc()
254 let v: i64 = nx_http_response_parse(buf, n, r)
255 if v != NX_HTTP_RESP_OK { return 41 }
256 let nm: *u8 = sys_mmap(16)
257 // lowercase "content-type"
258 nm[0]=0x63; nm[1]=0x6f; nm[2]=0x6e; nm[3]=0x74
259 nm[4]=0x65; nm[5]=0x6e; nm[6]=0x74; nm[7]=0x2d
260 nm[8]=0x74; nm[9]=0x79; nm[10]=0x70; nm[11]=0x65
261 let vo: *i64 = sys_mmap(8) as *i64
262 let vl: *i64 = sys_mmap(8) as *i64
263 let f: i64 = nx_http_response_header(buf, n, r, nm, 12, vo, vl)
264 if f != 1 { return 42 }
265 if vl[0] != 9 { return 43 } // "text/html"
266 if (buf[vo[0]] & 0xff) != 0x74 { return 44 } // t
267 return 0
268}
269
270func k5_bad_version_rejected() -> i64 {
271 let buf: *u8 = sys_mmap(64)
272 let n: i64 = t_make_bad_version(buf)
273 let r: *i64 = nx_http_resp_alloc()
274 let v: i64 = nx_http_response_parse(buf, n, r)
275 if v != NX_HTTP_RESP_ERR_BAD_VERSION { return 51 }
276 return 0
277}
278
279func k6_chunked_classification() -> i64 {
280 let buf: *u8 = sys_mmap(256)
281 let n: i64 = t_make_chunked(buf)
282 let r: *i64 = nx_http_resp_alloc()
283 let v: i64 = nx_http_response_parse(buf, n, r)
284 if v != NX_HTTP_RESP_OK { return 61 }
285 if r[8] != NX_HTTP_BODY_CHUNKED { return 62 }
286 return 0
287}
288
289func parser_legacy_suite() -> i64 {
290 let r1: i64 = k1_minimal_200_hello()
291 if r1 != 0 { return r1 }
292 let r2: i64 = k2_404_empty()
293 if r2 != 0 { return r2 }
294 let r3: i64 = k3_chunked_decode()
295 if r3 != 0 { return r3 }
296 let r4: i64 = k4_header_lookup_ci()
297 if r4 != 0 { return r4 }
298 let r5: i64 = k5_bad_version_rejected()
299 if r5 != 0 { return r5 }
300 let r6: i64 = k6_chunked_classification()
301 if r6 != 0 { return r6 }
302 return 0
303}