code wiki / (root) / nx_http_dechunk_boundary_gate.nx

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}