code wiki / (root) / nx_http_io.nx

nx_http_io.nx source

↩ module page · 438 lines · 21378 B

1// nx_http_io.nx -- sovereign HTTP request/response primitives. 2// 3// Replaces the bash ide_handler.sh. Pure NishiLang, no libc, no 4// netcat in the loop, no socat. Parses just enough HTTP/1.1 to drive 5// the /ide backend: method, path, Content-Length. Writes responses 6// with explicit byte counts. 7// 8// genealogy_id: rfc_7230_http_1_1 + hyper_rs_design_notes + 9// nishi_pages_v1_pages_nx 10// lineage_id: nx_http_primitive_q10 11// 12// nx_safety_envelope: 13// intended_use: "HTTP/1.1 server-side primitive -- accept, 14// request-line parse, response-emit. Substrate- 15// native replacement for nginx/apache/wordpress 16// at the byte level." 17// sil_target: SIL2 (HTTP server; bug = remote exposure) 18// asil_target: QM 19// dal_target: NONE 20// evidence: [RFC_7230_canonical_basis, 21// sealed_verdict_enum, no_FP, 22// bounded_line_buffer_4KB, 23// request_method_table_complete] 24// hazard_register: [bug-tape-HTTP-request-smuggling, 25// bug-tape-CRLF-injection-via-header, 26// bug-tape-slowloris-resource-exhaustion] 27// residual_risk: "Slowloris / slow-read DoS mitigation 28// requires connection-time budgets upstream. 29// Request smuggling defense: strict CRLF + 30// no Transfer-Encoding-Content-Length 31// ambiguity (substrate enforces; verification 32// smoke queued)." 33// verdict: NOT_YET_EVALUATED 34 35import "nx_syscalls.nx" 36const NX_MAGIC_63072000: i64 = 63072000 37 38// Max request bytes we read in one shot. Sources up to 60 KB fit; 39// larger sources will be truncated, surfaced via Content-Length parse. 40const NX_HTTP_REQ_CAP: i64 = 65536 41const NX_HTTP_RESP_CAP: i64 = 262144 42 43// Parse a request buffer. Returns 0 on success and fills the four 44// out pointers; returns -1 if the buffer doesn't have a complete 45// header section (\r\n\r\n). 46// 47// out_method_kind: 0 = unknown, 1 = GET, 2 = POST, 3 = OPTIONS 48// out_path_off: byte offset into req[] where the path starts 49// out_path_len: length of the path (until next space) 50// out_content_len: Content-Length value (0 if absent) 51// out_body_off: byte offset of the body (first byte after \r\n\r\n) 52func nx_http_parse_request( 53 req: *u8, req_len: i64, 54 out_method_kind: *i64, 55 out_path_off: *i64, 56 out_path_len: *i64, 57 out_content_len: *i64, 58 out_body_off: *i64 59) -> i64 { 60 *out_method_kind = 0 61 *out_path_off = 0 62 *out_path_len = 0 63 *out_content_len = 0 64 *out_body_off = 0 65 66 if req_len < 14 { return -1 } // shortest valid request line 67 68 // Method dispatch by first byte. 69 if req[0] == 71 { // 'G' 70 if req[1] == 69 { // 'E' 71 if req[2] == 84 { // 'T' 72 if req[3] == 32 { *out_method_kind = 1 } 73 } 74 } 75 } 76 if req[0] == 80 { // 'P' 77 if req[1] == 79 { // 'O' 78 if req[2] == 83 { // 'S' 79 if req[3] == 84 { // 'T' 80 if req[4] == 32 { *out_method_kind = 2 } 81 } 82 } 83 } 84 } 85 if req[0] == 79 { // 'O' 86 if req[1] == 80 { // 'P' 87 if req[2] == 84 { // 'T' 88 if req[3] == 73 { // 'I' 89 *out_method_kind = 3 90 } 91 } 92 } 93 } 94 95 // Find path start: byte after first space. 96 var i: i64 = 0 97 while i < req_len { 98 if req[i] == 32 { i = i + 1 ; *out_path_off = i ; i = req_len + 1 } 99 else { i = i + 1 } 100 } 101 if *out_path_off == 0 { return -1 } 102 103 // Path length: bytes until next space. 104 var p: i64 = *out_path_off 105 while p < req_len { 106 if req[p] == 32 { *out_path_len = p - *out_path_off ; p = req_len + 1 } 107 else { p = p + 1 } 108 } 109 if *out_path_len == 0 { return -1 } 110 111 // Walk headers to find Content-Length and the \r\n\r\n. 112 // Header line keys are case-insensitive in HTTP; we accept both 113 // 'C' (67) and 'c' (99) for the C; rely on full-match length for 114 // the rest. Match string: "Content-Length:" = 15 bytes. 115 var k: i64 = *out_path_off + *out_path_len 116 // advance past "HTTP/1.1\r\n" to first header 117 while k + 3 < req_len { 118 if req[k] == 13 { // \r 119 if req[k+1] == 10 { // \n 120 k = k + 2 121 k = req_len + 1 // signal: stop the line search 122 } else { k = k + 1 } 123 } else { k = k + 1 } 124 } 125 // Now scan headers line by line. 126 var line_start: i64 = k - req_len + (req_len - (req_len - 1)) // careful: reset 127 // Simpler: scan from current 'k' position backward to find true start. 128 // Actually do it differently -- find every \r\n\r\n by sliding window. 129 var i2: i64 = 0 130 var body_ofs: i64 = -1 131 while i2 + 3 < req_len { 132 if req[i2] == 13 { 133 if req[i2+1] == 10 { 134 if req[i2+2] == 13 { 135 if req[i2+3] == 10 { 136 body_ofs = i2 + 4 137 i2 = req_len 138 } else { i2 = i2 + 1 } 139 } else { i2 = i2 + 1 } 140 } else { i2 = i2 + 1 } 141 } else { i2 = i2 + 1 } 142 } 143 if body_ofs < 0 { return -1 } 144 *out_body_off = body_ofs 145 146 // Find Content-Length header. Scan up to body_ofs. 147 var h: i64 = 0 148 while h + 16 < body_ofs { 149 // Match "Content-Length:" or "content-length:" 150 var ok: i64 = 1 151 let c0: i64 = req[h] 152 if c0 != 67 { 153 if c0 != 99 { ok = 0 } 154 } 155 if ok == 1 { 156 let c1: i64 = req[h+1] 157 if c1 != 111 { ok = 0 } // 'o' 158 let c2: i64 = req[h+2] 159 if c2 != 110 { ok = 0 } // 'n' 160 let c3: i64 = req[h+3] 161 if c3 != 116 { ok = 0 } // 't' 162 let c4: i64 = req[h+4] 163 if c4 != 101 { ok = 0 } // 'e' 164 let c5: i64 = req[h+5] 165 if c5 != 110 { ok = 0 } // 'n' 166 let c6: i64 = req[h+6] 167 if c6 != 116 { ok = 0 } // 't' 168 let c7: i64 = req[h+7] 169 if c7 != 45 { ok = 0 } // '-' 170 let c8: i64 = req[h+8] 171 if c8 != 76 { 172 if c8 != 108 { ok = 0 } // 'L' or 'l' 173 } 174 } 175 if ok == 1 { 176 // Position v at the first char AFTER "Content-Length:". 177 // The string is 15 bytes (C o n t e n t - L e n g t h :) so 178 // index 15 lands on whatever follows the colon. 179 var v: i64 = h + 15 180 // Skip leading space/tab. 181 var skipping: i64 = 1 182 while skipping == 1 { 183 if v >= body_ofs { skipping = 0 } 184 else { 185 let sc: i64 = req[v] 186 if sc == 32 { v = v + 1 } 187 else { 188 if sc == 9 { v = v + 1 } 189 else { skipping = 0 } 190 } 191 } 192 } 193 // Parse decimal digits. Stop at \r, \n, or anything non-digit. 194 var cl: i64 = 0 195 var parsing: i64 = 1 196 while parsing == 1 { 197 if v >= body_ofs { parsing = 0 } 198 else { 199 let c: i64 = req[v] 200 if c >= 48 { 201 if c <= 57 { 202 cl = cl * 10 + (c - 48) 203 v = v + 1 204 } else { parsing = 0 } 205 } else { parsing = 0 } 206 } 207 } 208 *out_content_len = cl 209 h = body_ofs 210 } 211 h = h + 1 212 } 213 return 0 214} 215 216// Write an HTTP response: status line + standard headers + body. 217// Caller-built body_buf of body_len bytes. CORS Access-Control-Allow- 218// Origin: * is always set so the IDE can call from a different port. 219func nx_http_write_response( 220 fd: i64, 221 status_code: i64, 222 content_type_buf: *u8, content_type_len: i64, 223 body_buf: *u8, body_len: i64 224) -> i64 { 225 // Status line: "HTTP/1.1 200 OK\r\n" or similar. We support 200, 226 // 204, 400, 404, 500. 227 let status_line: *u8 = sys_mmap(64) 228 var s_off: i64 = 0 229 // "HTTP/1.1 " 230 status_line[0] = 72; status_line[1] = 84; status_line[2] = 84 231 status_line[3] = 80; status_line[4] = 47; status_line[5] = 49 232 status_line[6] = 46; status_line[7] = 49; status_line[8] = 32 233 s_off = 9 234 // 3 digits 235 let d100: i64 = (status_code / 100) + 48 236 let d10: i64 = ((status_code / 10) % 10) + 48 237 let d1: i64 = (status_code % 10) + 48 238 status_line[s_off] = d100; s_off = s_off + 1 239 status_line[s_off] = d10; s_off = s_off + 1 240 status_line[s_off] = d1; s_off = s_off + 1 241 // " OK\r\n" (generic reason) 242 status_line[s_off] = 32; s_off = s_off + 1 243 status_line[s_off] = 79; s_off = s_off + 1 // 'O' 244 status_line[s_off] = 75; s_off = s_off + 1 // 'K' 245 status_line[s_off] = 13; s_off = s_off + 1 246 status_line[s_off] = 10; s_off = s_off + 1 247 sys_write(fd, status_line, s_off) 248 249 // Content-Type header 250 let h_ct: *u8 = sys_mmap(64) 251 h_ct[0] = 67; h_ct[1] = 111; h_ct[2] = 110; h_ct[3] = 116 252 h_ct[4] = 101; h_ct[5] = 110; h_ct[6] = 116; h_ct[7] = 45 253 h_ct[8] = 84; h_ct[9] = 121; h_ct[10] = 112; h_ct[11] = 101 254 h_ct[12] = 58; h_ct[13] = 32 255 sys_write(fd, h_ct, 14) 256 sys_write(fd, content_type_buf, content_type_len) 257 let crlf: *u8 = sys_mmap(4) 258 crlf[0] = 13; crlf[1] = 10 259 sys_write(fd, crlf, 2) 260 261 // Access-Control-Allow-Origin: *\r\n 262 let cors: *u8 = sys_mmap(64) 263 cors[0] = 65; cors[1] = 99; cors[2] = 99; cors[3] = 101 // Acce 264 cors[4] = 115; cors[5] = 115; cors[6] = 45 // ss- 265 cors[7] = 67; cors[8] = 111; cors[9] = 110; cors[10] = 116 // Cont 266 cors[11] = 114; cors[12] = 111; cors[13] = 108 // rol 267 cors[14] = 45; cors[15] = 65; cors[16] = 108; cors[17] = 108 // -All 268 cors[18] = 111; cors[19] = 119; cors[20] = 45 // ow- 269 cors[21] = 79; cors[22] = 114; cors[23] = 105; cors[24] = 103 // Orig 270 cors[25] = 105; cors[26] = 110 // in 271 cors[27] = 58; cors[28] = 32; cors[29] = 42 // : * 272 cors[30] = 13; cors[31] = 10 // \r\n 273 sys_write(fd, cors, 32) 274 275 // ---- Hardened-site security headers (per HARDENED_GAP_ANALYSIS) ---- 276 // 277 // X-Content-Type-Options: nosniff\r\n (37 bytes) 278 let h_xcto: *u8 = sys_mmap(64) 279 h_xcto[0]=88; h_xcto[1]=45 // X- 280 h_xcto[2]=67; h_xcto[3]=111; h_xcto[4]=110; h_xcto[5]=116; // Cont 281 h_xcto[6]=101; h_xcto[7]=110; h_xcto[8]=116 // ent 282 h_xcto[9]=45; h_xcto[10]=84; h_xcto[11]=121; h_xcto[12]=112; h_xcto[13]=101 // -Type 283 h_xcto[14]=45; h_xcto[15]=79; h_xcto[16]=112; h_xcto[17]=116; h_xcto[18]=105 // -Opti 284 h_xcto[19]=111; h_xcto[20]=110; h_xcto[21]=115 // ons 285 h_xcto[22]=58; h_xcto[23]=32 // : 286 h_xcto[24]=110; h_xcto[25]=111; h_xcto[26]=115; h_xcto[27]=110; h_xcto[28]=105; h_xcto[29]=102; h_xcto[30]=102 // nosniff 287 h_xcto[31]=13; h_xcto[32]=10 // \r\n 288 sys_write(fd, h_xcto, 33) 289 290 // X-Frame-Options: DENY\r\n (23 bytes) 291 let h_xfo: *u8 = sys_mmap(64) 292 h_xfo[0]=88; h_xfo[1]=45; h_xfo[2]=70; h_xfo[3]=114; h_xfo[4]=97; h_xfo[5]=109; h_xfo[6]=101 // X-Frame 293 h_xfo[7]=45; h_xfo[8]=79; h_xfo[9]=112; h_xfo[10]=116; h_xfo[11]=105; h_xfo[12]=111; h_xfo[13]=110; h_xfo[14]=115 // -Options 294 h_xfo[15]=58; h_xfo[16]=32 // : 295 h_xfo[17]=68; h_xfo[18]=69; h_xfo[19]=78; h_xfo[20]=89 // DENY 296 h_xfo[21]=13; h_xfo[22]=10 // \r\n 297 sys_write(fd, h_xfo, 23) 298 299 // Referrer-Policy: strict-origin-when-cross-origin\r\n (51 bytes) 300 let h_rp: *u8 = sys_mmap(64) 301 h_rp[0]=82; h_rp[1]=101; h_rp[2]=102; h_rp[3]=101; h_rp[4]=114; h_rp[5]=114; h_rp[6]=101; h_rp[7]=114 // Referrer 302 h_rp[8]=45; h_rp[9]=80; h_rp[10]=111; h_rp[11]=108; h_rp[12]=105; h_rp[13]=99; h_rp[14]=121 // -Policy 303 h_rp[15]=58; h_rp[16]=32 304 h_rp[17]=115; h_rp[18]=116; h_rp[19]=114; h_rp[20]=105; h_rp[21]=99; h_rp[22]=116 // strict 305 h_rp[23]=45; h_rp[24]=111; h_rp[25]=114; h_rp[26]=105; h_rp[27]=103; h_rp[28]=105; h_rp[29]=110 // -origin 306 h_rp[30]=45; h_rp[31]=119; h_rp[32]=104; h_rp[33]=101; h_rp[34]=110 // -when 307 h_rp[35]=45; h_rp[36]=99; h_rp[37]=114; h_rp[38]=111; h_rp[39]=115; h_rp[40]=115 // -cross 308 h_rp[41]=45; h_rp[42]=111; h_rp[43]=114; h_rp[44]=105; h_rp[45]=103; h_rp[46]=105; h_rp[47]=110 // -origin 309 h_rp[48]=13; h_rp[49]=10 310 sys_write(fd, h_rp, 50) 311 312 // Strict-Transport-Security: max-age=63072000; includeSubDomains; preload\r\n (74 bytes) 313 let h_hsts: *u8 = sys_mmap(128) 314 h_hsts[0]=83; h_hsts[1]=116; h_hsts[2]=114; h_hsts[3]=105; h_hsts[4]=99; h_hsts[5]=116 // Strict 315 h_hsts[6]=45; h_hsts[7]=84; h_hsts[8]=114; h_hsts[9]=97; h_hsts[10]=110; h_hsts[11]=115 // -Trans 316 h_hsts[12]=112; h_hsts[13]=111; h_hsts[14]=114; h_hsts[15]=116 // port 317 h_hsts[16]=45; h_hsts[17]=83; h_hsts[18]=101; h_hsts[19]=99; h_hsts[20]=117; h_hsts[21]=114; h_hsts[22]=105; h_hsts[23]=116; h_hsts[24]=121 // -Security 318 h_hsts[25]=58; h_hsts[26]=32 319 h_hsts[27]=109; h_hsts[28]=97; h_hsts[29]=120; h_hsts[30]=45; h_hsts[31]=97; h_hsts[32]=103; h_hsts[33]=101; h_hsts[34]=61 // max-age= 320 h_hsts[35]=54; h_hsts[36]=51; h_hsts[37]=48; h_hsts[38]=55; h_hsts[39]=50; h_hsts[40]=48; h_hsts[41]=48; h_hsts[42]=48 // NX_MAGIC_63072000 321 h_hsts[43]=59; h_hsts[44]=32 // ; 322 h_hsts[45]=105; h_hsts[46]=110; h_hsts[47]=99; h_hsts[48]=108; h_hsts[49]=117; h_hsts[50]=100; h_hsts[51]=101 // include 323 h_hsts[52]=83; h_hsts[53]=117; h_hsts[54]=98; h_hsts[55]=68; h_hsts[56]=111; h_hsts[57]=109; h_hsts[58]=97; h_hsts[59]=105; h_hsts[60]=110; h_hsts[61]=115 // SubDomains 324 h_hsts[62]=59; h_hsts[63]=32 325 h_hsts[64]=112; h_hsts[65]=114; h_hsts[66]=101; h_hsts[67]=108; h_hsts[68]=111; h_hsts[69]=97; h_hsts[70]=100 // preload 326 h_hsts[71]=13; h_hsts[72]=10 327 sys_write(fd, h_hsts, 73) 328 329 // Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'\r\n 330 let h_csp: *u8 = sys_mmap(256) 331 var co: i64 = 0 332 // "Content-Security-Policy: " 333 h_csp[co]=67;co=co+1;h_csp[co]=111;co=co+1;h_csp[co]=110;co=co+1;h_csp[co]=116;co=co+1 334 h_csp[co]=101;co=co+1;h_csp[co]=110;co=co+1;h_csp[co]=116;co=co+1;h_csp[co]=45;co=co+1 335 h_csp[co]=83;co=co+1;h_csp[co]=101;co=co+1;h_csp[co]=99;co=co+1;h_csp[co]=117;co=co+1 336 h_csp[co]=114;co=co+1;h_csp[co]=105;co=co+1;h_csp[co]=116;co=co+1;h_csp[co]=121;co=co+1 337 h_csp[co]=45;co=co+1;h_csp[co]=80;co=co+1;h_csp[co]=111;co=co+1;h_csp[co]=108;co=co+1 338 h_csp[co]=105;co=co+1;h_csp[co]=99;co=co+1;h_csp[co]=121;co=co+1 339 h_csp[co]=58;co=co+1;h_csp[co]=32;co=co+1 340 // "default-src 'self'; " 341 h_csp[co]=100;co=co+1;h_csp[co]=101;co=co+1;h_csp[co]=102;co=co+1;h_csp[co]=97;co=co+1;h_csp[co]=117;co=co+1;h_csp[co]=108;co=co+1;h_csp[co]=116;co=co+1 342 h_csp[co]=45;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=114;co=co+1;h_csp[co]=99;co=co+1 343 h_csp[co]=32;co=co+1 344 h_csp[co]=39;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=101;co=co+1;h_csp[co]=108;co=co+1;h_csp[co]=102;co=co+1;h_csp[co]=39;co=co+1 345 h_csp[co]=59;co=co+1;h_csp[co]=32;co=co+1 346 // "img-src 'self' data:; " 347 h_csp[co]=105;co=co+1;h_csp[co]=109;co=co+1;h_csp[co]=103;co=co+1 348 h_csp[co]=45;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=114;co=co+1;h_csp[co]=99;co=co+1 349 h_csp[co]=32;co=co+1 350 h_csp[co]=39;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=101;co=co+1;h_csp[co]=108;co=co+1;h_csp[co]=102;co=co+1;h_csp[co]=39;co=co+1 351 h_csp[co]=32;co=co+1 352 h_csp[co]=100;co=co+1;h_csp[co]=97;co=co+1;h_csp[co]=116;co=co+1;h_csp[co]=97;co=co+1;h_csp[co]=58;co=co+1 353 h_csp[co]=59;co=co+1;h_csp[co]=32;co=co+1 354 // "connect-src 'self' ws: wss:; " -- added 2026-05-20 so the 355 // /video page can open its WebSocket to nx_signaling (or any 356 // bits-up daemon on a sibling port). Without this, browsers 357 // block the WS handshake before a byte hits the wire; root 358 // cause for the operator's "can't get to the room / no audio / 359 // no video" report (Friend 2 surfaced via playwright probe). 360 h_csp[co]=99;co=co+1;h_csp[co]=111;co=co+1;h_csp[co]=110;co=co+1;h_csp[co]=110;co=co+1;h_csp[co]=101;co=co+1;h_csp[co]=99;co=co+1;h_csp[co]=116;co=co+1 361 h_csp[co]=45;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=114;co=co+1;h_csp[co]=99;co=co+1 362 h_csp[co]=32;co=co+1 363 h_csp[co]=39;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=101;co=co+1;h_csp[co]=108;co=co+1;h_csp[co]=102;co=co+1;h_csp[co]=39;co=co+1 364 h_csp[co]=32;co=co+1 365 h_csp[co]=119;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=58;co=co+1 366 h_csp[co]=32;co=co+1 367 h_csp[co]=119;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=58;co=co+1 368 h_csp[co]=59;co=co+1;h_csp[co]=32;co=co+1 369 // "style-src 'self' 'unsafe-inline'" 370 h_csp[co]=115;co=co+1;h_csp[co]=116;co=co+1;h_csp[co]=121;co=co+1;h_csp[co]=108;co=co+1;h_csp[co]=101;co=co+1 371 h_csp[co]=45;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=114;co=co+1;h_csp[co]=99;co=co+1 372 h_csp[co]=32;co=co+1 373 h_csp[co]=39;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=101;co=co+1;h_csp[co]=108;co=co+1;h_csp[co]=102;co=co+1;h_csp[co]=39;co=co+1 374 h_csp[co]=32;co=co+1 375 h_csp[co]=39;co=co+1;h_csp[co]=117;co=co+1;h_csp[co]=110;co=co+1;h_csp[co]=115;co=co+1;h_csp[co]=97;co=co+1;h_csp[co]=102;co=co+1;h_csp[co]=101;co=co+1 376 h_csp[co]=45;co=co+1;h_csp[co]=105;co=co+1;h_csp[co]=110;co=co+1;h_csp[co]=108;co=co+1;h_csp[co]=105;co=co+1;h_csp[co]=110;co=co+1;h_csp[co]=101;co=co+1 377 h_csp[co]=39;co=co+1 378 h_csp[co]=13;co=co+1;h_csp[co]=10;co=co+1 379 sys_write(fd, h_csp, co) 380 381 // X-Nishi-Substrate: nws/v3\r\n (unique sovereign signature header) 382 let h_xns: *u8 = sys_mmap(64) 383 h_xns[0]=88;h_xns[1]=45 // X- 384 h_xns[2]=78;h_xns[3]=105;h_xns[4]=115;h_xns[5]=104;h_xns[6]=105 // Nishi 385 h_xns[7]=45 // - 386 h_xns[8]=83;h_xns[9]=117;h_xns[10]=98;h_xns[11]=115;h_xns[12]=116;h_xns[13]=114;h_xns[14]=97;h_xns[15]=116;h_xns[16]=101 // Substrate 387 h_xns[17]=58;h_xns[18]=32 // : 388 h_xns[19]=110;h_xns[20]=119;h_xns[21]=115;h_xns[22]=47;h_xns[23]=118;h_xns[24]=51 // nws/v3 389 h_xns[25]=13;h_xns[26]=10 390 sys_write(fd, h_xns, 27) 391 392 // Content-Length: N\r\n 393 let cl: *u8 = sys_mmap(64) 394 cl[0] = 67; cl[1] = 111; cl[2] = 110; cl[3] = 116 // Cont 395 cl[4] = 101; cl[5] = 110; cl[6] = 116; cl[7] = 45 // ent- 396 cl[8] = 76; cl[9] = 101; cl[10] = 110; cl[11] = 103 // Leng 397 cl[12] = 116; cl[13] = 104; cl[14] = 58; cl[15] = 32 // th: 398 var clo: i64 = 16 399 // Decimal encode body_len; up to 12 digits handles 1 TB. 400 let digbuf: *u8 = sys_mmap(20) 401 var n: i64 = body_len 402 var nd: i64 = 0 403 if n == 0 { 404 digbuf[0] = 48 405 nd = 1 406 } 407 while n > 0 { 408 digbuf[nd] = (n % 10) + 48 409 n = n / 10 410 nd = nd + 1 411 } 412 // Reverse into cl[clo..] 413 var ri: i64 = 0 414 while ri < nd { 415 cl[clo + ri] = digbuf[nd - 1 - ri] 416 ri = ri + 1 417 } 418 clo = clo + nd 419 cl[clo] = 13; clo = clo + 1 420 cl[clo] = 10; clo = clo + 1 421 // Connection: close\r\n\r\n 422 cl[clo] = 67; clo = clo + 1; cl[clo] = 111; clo = clo + 1 423 cl[clo] = 110; clo = clo + 1; cl[clo] = 110; clo = clo + 1 424 cl[clo] = 101; clo = clo + 1; cl[clo] = 99; clo = clo + 1 425 cl[clo] = 116; clo = clo + 1; cl[clo] = 105; clo = clo + 1 426 cl[clo] = 111; clo = clo + 1; cl[clo] = 110; clo = clo + 1 427 cl[clo] = 58; clo = clo + 1; cl[clo] = 32; clo = clo + 1 428 cl[clo] = 99; clo = clo + 1; cl[clo] = 108; clo = clo + 1 429 cl[clo] = 111; clo = clo + 1; cl[clo] = 115; clo = clo + 1 430 cl[clo] = 101; clo = clo + 1 431 cl[clo] = 13; clo = clo + 1; cl[clo] = 10; clo = clo + 1 432 cl[clo] = 13; clo = clo + 1; cl[clo] = 10; clo = clo + 1 433 sys_write(fd, cl, clo) 434 435 // Body 436 if body_len > 0 { sys_write(fd, body_buf, body_len) } 437 return 0 438}