nx_acme_response_test.nx source
↩ module page · 164 lines · 7245 B
1// nx_acme_response_test.nx -- smoke for the ACME response parser.
2//
3// Covers:
4// 1. Verdict enum gates
5// 2. Status-code parse for several values
6// 3. Body offset finder (CRLF CRLF + LF LF tolerance)
7// 4. Full parse of a canned ACME new-account response
8// 5. Replay-Nonce extraction
9// 6. Location extraction
10// 7. Body offset/length correctness
11// 8. is_success / is_client_error / should_retry predicates
12// 9. BAD_ARG paths
13//
14// expect_exit: 0
15//
16// license_tier: ORIGINAL
17
18import "nx_syscalls_x86_64.nx"
19import "nx_acme_response.nx"
20
21func bytes_eq(a: *u8, b: *u8, n: i64) -> i64 {
22 var i: i64 = 0
23 while i < n {
24 if a[i] != b[i] { return 0 }
25 i = i + 1
26 }
27 return 1
28}
29
30func emit(buf: *u8, off: i64, s: *u8) -> i64 {
31 var i: i64 = 0
32 while s[i] != 0 {
33 buf[off + i] = s[i]
34 i = i + 1
35 }
36 return off + i
37}
38
39func main() -> i64 {
40 // ---- Verdict enum ----
41 if nxar_verdict_is_valid(NXAR_OK) != 1 { return 1 }
42 if nxar_verdict_is_valid(NXAR_BAD_ARG) != 1 { return 2 }
43 if nxar_verdict_is_valid(NXAR_VERDICT_N) != 0 { return 3 }
44 if bytes_eq(nxar_verdict_name(NXAR_OK), "OK" as *u8, 2) != 1 { return 4 }
45 if bytes_eq(nxar_verdict_name(NXAR_BAD_STATUS_LINE),
46 "BAD_STATUS_LINE" as *u8, 15) != 1 { return 5 }
47
48 // ---- Status-code parser ----
49 if nxar_parse_status_code("HTTP/1.1 200 OK\r\n" as *u8, 17) != 200 { return 10 }
50 if nxar_parse_status_code("HTTP/1.1 201 Created\r\n" as *u8, 22) != 201 { return 11 }
51 if nxar_parse_status_code("HTTP/1.1 204 No Content\r\n" as *u8, 25) != 204 { return 12 }
52 if nxar_parse_status_code("HTTP/1.0 400 Bad Request\r\n" as *u8, 26) != 400 { return 13 }
53 if nxar_parse_status_code("HTTP/1.1 429 Too Many\r\n" as *u8, 23) != 429 { return 14 }
54 if nxar_parse_status_code("HTTP/1.1 503 Service Unavailable\r\n" as *u8, 34) != 503 { return 15 }
55 // Out-of-range
56 if nxar_parse_status_code("HTTP/1.1 099 X\r\n" as *u8, 16) != -1 { return 16 }
57 if nxar_parse_status_code("HTTP/1.1 700 X\r\n" as *u8, 16) != -1 { return 17 }
58 // Too short
59 if nxar_parse_status_code("HTTP/1.1 20" as *u8, 11) != -1 { return 18 }
60
61 // ---- Body offset finder ----
62 let bo_buf: *u8 = sys_mmap(256)
63 var bo_n: i64 = emit(bo_buf, 0, "HTTP/1.1 200 OK\r\nFoo: bar\r\n\r\nBODY" as *u8)
64 let off_crlf: i64 = nxar_find_body_offset(bo_buf, bo_n)
65 if off_crlf != 29 { return 20 } // body starts at offset 29 ("BODY")
66 if bo_buf[off_crlf] != 0x42 as u8 { return 21 } // 'B'
67
68 // LF-only tolerance
69 bo_n = emit(bo_buf, 0, "HTTP/1.1 200 OK\nFoo: bar\n\nBODY" as *u8)
70 let off_lf: i64 = nxar_find_body_offset(bo_buf, bo_n)
71 if off_lf != 26 { return 22 }
72 if bo_buf[off_lf] != 0x42 as u8 { return 23 }
73
74 // Missing boundary
75 if nxar_find_body_offset("HTTP/1.1 200" as *u8, 12) != -1 { return 24 }
76
77 // ---- Canned ACME new-account 201 response ----
78 let resp_buf: *u8 = sys_mmap(2048)
79 var rn: i64 = 0
80 rn = emit(resp_buf, rn, "HTTP/1.1 201 Created\r\n" as *u8)
81 rn = emit(resp_buf, rn, "Server: nishi-acme/0.1\r\n" as *u8)
82 rn = emit(resp_buf, rn, "Replay-Nonce: oFvnlFP1wIhRlYS2jTaXbA\r\n" as *u8)
83 rn = emit(resp_buf, rn, "Location: https://acme/acct/12345\r\n" as *u8)
84 rn = emit(resp_buf, rn, "Content-Type: application/json\r\n" as *u8)
85 rn = emit(resp_buf, rn, "Content-Length: 42\r\n" as *u8)
86 rn = emit(resp_buf, rn, "\r\n" as *u8)
87 rn = emit(resp_buf, rn, "{\"status\":\"valid\",\"contact\":[\"mailto:e@x\"]}" as *u8)
88
89 let ar_raw: *u8 = sys_mmap(ACME_RESPONSE_BYTES)
90 let ar: *AcmeResponse = ar_raw as *AcmeResponse
91 let pv: i64 = nx_acme_response_parse(resp_buf, rn, ar)
92 if pv != NXAR_OK { return 30 }
93 if ar.status_code != 201 { return 31 }
94 if ar.nonce_len != 22 { return 32 }
95 let nonce_ptr: *u8 = ((resp_buf as i64) + ar.nonce_off) as *u8
96 if bytes_eq(nonce_ptr, "oFvnlFP1wIhRlYS2jTaXbA" as *u8, 22) != 1 { return 33 }
97 if ar.location_len != 23 { return 34 }
98 let loc_ptr: *u8 = ((resp_buf as i64) + ar.location_off) as *u8
99 if bytes_eq(loc_ptr, "https://acme/acct/12345" as *u8, 23) != 1 { return 35 }
100 // body
101 if ar.body_len != 43 { return 36 }
102 let body_ptr: *u8 = ((resp_buf as i64) + ar.body_off) as *u8
103 if bytes_eq(body_ptr,
104 "{\"status\":\"valid\",\"contact\":[\"mailto:e@x\"]}" as *u8,
105 43) != 1 { return 37 }
106
107 // ---- Predicates ----
108 if nx_acme_response_is_success(ar) != 1 { return 40 }
109 if nx_acme_response_is_client_error(ar) != 0 { return 41 }
110 if nx_acme_response_should_retry(ar) != 0 { return 42 }
111
112 // ---- 400 case: client_error true, retry false ----
113 let err_buf: *u8 = sys_mmap(256)
114 var en: i64 = 0
115 en = emit(err_buf, en, "HTTP/1.1 400 Bad Request\r\n" as *u8)
116 en = emit(err_buf, en, "Content-Type: application/problem+json\r\n" as *u8)
117 en = emit(err_buf, en, "\r\n" as *u8)
118 en = emit(err_buf, en, "{\"type\":\"badNonce\"}" as *u8)
119 let ar2: *AcmeResponse = (sys_mmap(ACME_RESPONSE_BYTES)) as *AcmeResponse
120 let pv2: i64 = nx_acme_response_parse(err_buf, en, ar2)
121 if pv2 != NXAR_OK { return 50 }
122 if ar2.status_code != 400 { return 51 }
123 if nx_acme_response_is_success(ar2) != 0 { return 52 }
124 if nx_acme_response_is_client_error(ar2) != 1 { return 53 }
125 if nx_acme_response_should_retry(ar2) != 0 { return 54 }
126 // Replay-Nonce absent in this response.
127 if ar2.nonce_len != 0 { return 55 }
128 if ar2.location_len != 0 { return 56 }
129
130 // ---- 429 case: retry true ----
131 let r429_buf: *u8 = sys_mmap(256)
132 var r429_n: i64 = 0
133 r429_n = emit(r429_buf, r429_n, "HTTP/1.1 429 Too Many Requests\r\n" as *u8)
134 r429_n = emit(r429_buf, r429_n, "Retry-After: 30\r\n" as *u8)
135 r429_n = emit(r429_buf, r429_n, "Replay-Nonce: xyz\r\n" as *u8)
136 r429_n = emit(r429_buf, r429_n, "\r\n" as *u8)
137 let ar3: *AcmeResponse = (sys_mmap(ACME_RESPONSE_BYTES)) as *AcmeResponse
138 if nx_acme_response_parse(r429_buf, r429_n, ar3) != NXAR_OK { return 60 }
139 if ar3.status_code != 429 { return 61 }
140 if nx_acme_response_should_retry(ar3) != 1 { return 62 }
141 if ar3.nonce_len != 3 { return 63 }
142
143 // ---- 503 case: retry true ----
144 let r503_buf: *u8 = sys_mmap(256)
145 var r503_n: i64 = 0
146 r503_n = emit(r503_buf, r503_n, "HTTP/1.1 503 Service Unavailable\r\n" as *u8)
147 r503_n = emit(r503_buf, r503_n, "\r\n" as *u8)
148 let ar4: *AcmeResponse = (sys_mmap(ACME_RESPONSE_BYTES)) as *AcmeResponse
149 if nx_acme_response_parse(r503_buf, r503_n, ar4) != NXAR_OK { return 70 }
150 if nx_acme_response_should_retry(ar4) != 1 { return 71 }
151
152 // ---- BAD_ARG paths ----
153 if nx_acme_response_parse(0 as *u8, 100, ar) != NXAR_BAD_ARG { return 80 }
154 if nx_acme_response_parse(resp_buf, 100, 0 as *AcmeResponse) != NXAR_BAD_ARG { return 81 }
155 if nx_acme_response_parse(resp_buf, 0, ar) != NXAR_BAD_ARG { return 82 }
156
157 // ---- BAD_STATUS_LINE ----
158 if nx_acme_response_parse("garbage\r\n\r\nBODY" as *u8, 15, ar) != NXAR_BAD_STATUS_LINE { return 90 }
159
160 // ---- NO_HEADER_END ----
161 if nx_acme_response_parse("HTTP/1.1 200 OK\r\nFoo: bar\r\n" as *u8, 27, ar) != NXAR_NO_HEADER_END { return 100 }
162
163 return 0
164}