nx_ipp_transport_test.nx source
↩ module page · 107 lines · 4930 B
1// nx_ipp_transport_test.nx -- offline gate for IPP-over-HTTP transport (R2).
2//
3// Proves the HTTP-framing/dechunk logic deterministically (no network), with unique exit codes:
4// 1x Content-Length response -> body extracted byte-exact, status 200, diagnoses BLOCKED (stopped)
5// 2x chunked response -> de-chunked body byte-exact, status 200
6// 3x status-line parse (503) + bad-http (no header terminator) -> sealed BAD_HTTP
7// 4x build_request round-trips: printer-uri == ipp://1.2.3.4/ipp/print, op = Get-Printer-Attributes
8//
9// The LIVE proof against the real Brother DCP-L2540DW is recorded separately (that run IS the 3rd-party
10// oracle for this non-novel protocol). This gate locks the framing so regressions are caught without it.
11//
12// expect_exit: 0
13// license_tier: ORIGINAL
14
15import "nx_syscalls.nx"
16import "nx_ipp_codec.nx"
17import "nx_printer_health.nx"
18import "nx_ipp_transport.nx"
19
20func t_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
21
22// append minimal lower-hex of v (no leading zeros) -- for chunk sizes
23func put_hex(buf: *u8, off: i64, v: i64) -> i64 {
24 let hx: *u8 = "0123456789abcdef"
25 if v == 0 { buf[off] = 48 as u8; return off + 1 }
26 let tmp: *u8 = sys_mmap(16)
27 var m: i64 = v
28 var k: i64 = 0
29 while m > 0 { tmp[k] = hx[m & 0xf]; m = m >> 4; k = k + 1 }
30 var o: i64 = off
31 var i: i64 = k - 1
32 while i >= 0 { buf[o] = tmp[i]; o = o + 1; i = i - 1 }
33 return o
34}
35
36func bytes_eq(x: *u8, y: *u8, n: i64) -> i64 {
37 var i: i64 = 0
38 while i < n { if (x[i] as i64) != (y[i] as i64) { return 0 } i = i + 1 }
39 return 1
40}
41
42func main() -> i64 {
43 let sc: *i64 = sys_mmap(64) as *i64
44 let out2: *i64 = sys_mmap(16) as *i64
45 let body: *u8 = sys_mmap(4096)
46
47 // a real IPP response body: STOPPED + media-empty
48 let ipp: *u8 = sys_mmap(2048)
49 var L: i64 = nx_ipp_begin(ipp, 1, 1, NX_IPP_STATUS_OK, 1)
50 L = nx_ipp_group(ipp, L, NX_IPP_GRP_PRINTER)
51 L = nx_ipp_attr_int(ipp, L, NX_IPP_VT_ENUM, "printer-state", 13, NX_IPP_PSTATE_STOPPED)
52 L = nx_ipp_attr(ipp, L, NX_IPP_VT_KEYWORD, "printer-state-reasons", 21, "media-empty", 11)
53 L = nx_ipp_end(ipp, L)
54
55 // ===== KAT 1: Content-Length (non-chunked) =====
56 let r1: *u8 = sys_mmap(4096)
57 var t1: i64 = 0
58 t1 = nx_ipt_puts(r1, t1, "HTTP/1.1 200 OK\r\nContent-Type: application/ipp\r\nContent-Length: ")
59 t1 = nx_ipt_puti(r1, t1, L)
60 t1 = nx_ipt_puts(r1, t1, "\r\nConnection: close\r\n\r\n")
61 var j: i64 = 0
62 while j < L { r1[t1] = ipp[j]; t1 = t1 + 1; j = j + 1 }
63 if nx_ipt_extract_body(r1, t1, body, 4096, out2) != NX_IPT_OK { return 1 }
64 if out2[0] != 200 { return 2 }
65 if out2[1] != L { return 3 }
66 if bytes_eq(body, ipp, L) != 1 { return 4 }
67 if nx_printer_diagnose(body, out2[1], sc, sys_mmap(16) as *i64, sys_mmap(16) as *i64, sys_mmap(16) as *i64, sys_mmap(16) as *i64) != NX_PR_BLOCKED { return 5 }
68
69 // ===== KAT 2: chunked (single chunk + terminator), mixed-case header =====
70 let r2: *u8 = sys_mmap(4096)
71 var t2: i64 = 0
72 t2 = nx_ipt_puts(r2, t2, "HTTP/1.1 200 OK\r\nTransfer-Encoding: CHUNKED\r\n\r\n")
73 t2 = put_hex(r2, t2, L)
74 t2 = nx_ipt_puts(r2, t2, "\r\n")
75 var j2: i64 = 0
76 while j2 < L { r2[t2] = ipp[j2]; t2 = t2 + 1; j2 = j2 + 1 }
77 t2 = nx_ipt_puts(r2, t2, "\r\n0\r\n\r\n")
78 if nx_ipt_extract_body(r2, t2, body, 4096, out2) != NX_IPT_OK { return 20 }
79 if out2[0] != 200 { return 21 }
80 if out2[1] != L { return 22 }
81 if bytes_eq(body, ipp, L) != 1 { return 23 }
82
83 // ===== KAT 3: status parse + bad-http =====
84 let r3: *u8 = sys_mmap(512)
85 var t3: i64 = nx_ipt_puts(r3, 0, "HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\n\r\n")
86 if nx_ipt_extract_body(r3, t3, body, 4096, out2) != NX_IPT_OK { return 30 }
87 if out2[0] != 503 { return 31 }
88 // no header terminator -> BAD_HTTP (never silently "ok")
89 let r4: *u8 = sys_mmap(64)
90 let t4: i64 = nx_ipt_puts(r4, 0, "HTTP/1.1 200 OK garbage no crlfcrlf")
91 if nx_ipt_extract_body(r4, t4, body, 4096, out2) != NX_IPT_BAD_HTTP { return 32 }
92 // empty -> NO_RESP
93 if nx_ipt_extract_body(r4, 0, body, 4096, out2) != NX_IPT_NO_RESP { return 33 }
94
95 // ===== KAT 4: build_request round-trips through the codec parser =====
96 let req: *u8 = sys_mmap(1024)
97 let rl: i64 = nx_ipt_build_request(req, 1, 2, 3, 4, "/ipp/print")
98 if nx_ipp_op_id(req) != NX_IPP_OP_GET_PRINTER_ATTRIBUTES { return 40 }
99 let p_tag: *i64 = sc
100 let p_voff: *i64 = ((sc as i64) + 8) as *i64
101 let p_vlen: *i64 = ((sc as i64) + 16) as *i64
102 if nx_ipp_find(req, rl, "printer-uri", p_tag, p_voff, p_vlen) != NX_IPP_OK { return 41 }
103 if nx_ipp_name_eq(req, p_voff[0], "ipp://1.2.3.4/ipp/print", 23) != 1 { return 42 }
104
105 t_puts("nx_ipp_transport: 4/4 KAT groups PASS (content-length + chunked + status/bad-http + request round-trip)\n")
106 return 0
107}