code wiki / (root) / nx_motion_serve.nx

nx_motion_serve.nx source

↩ module page · 370 lines · 21464 B

1// nx_motion_serve.nx -- /motion AS A TOOL, NOT A BROCHURE (2026-09-06): a visitor uploads a mesh, the sovereign auto-rig 2// rigs it, the page shows it from three sides with its own numbers, a library clip retargets onto it, the result exports, 3// and the operator's ACCEPT or REJECT lands from the same page. The mesh2motion-class surface, on the estate's own organs. 4// 5// nx_motion_serve [port] serve on LOOPBACK (default 18132); the edge proxies /motion to it 6// nx_motion_serve emit <out.html> write the static fallback -- THE SAME RENDERER, the empty form, byte-identical 7// 8// SHAPE, and why (the /math pattern): plain GET and POST forms, no script, no fetch, no client runtime, so it works 9// with JavaScript off, every job is a shareable URL, and there is no third party anywhere in the path. Routing is 10// prefix-agnostic on purpose: the edge forwards /motion/upload and a loopback test sends /upload, and both route the same. 11// The daemon never does the work: it types the upload by MAGIC, writes a digit-named job directory, and spawns the job 12// organ DETACHED (double fork), so accept() is never held by a rig. It reads status rows the job writes and shows them. 13// license_tier: ORIGINAL 14import "nx_syscalls.nx" 15import "nx_http_server.nx" 16import "nx_motion_lib.nx" 17 18const MS_PORT_DEFAULT: i64 = 18132 19const MS_BACKLOG: i64 = 64 20const MS_RESP_RESERVE: i64 = 1024 21const MS_METHOD_GET: i64 = 1 22const MS_METHOD_POST: i64 = 2 23const MS_CELLS_MIN: i64 = 48 24const MS_CELLS_MAX: i64 = 256 25const MS_CELLS_DEFAULT: i64 = 192 26const MS_WORDS_CAP: i64 = 200 27const MS_EXIT_USAGE: i64 = 2 28const MS_REFRESH_HEAD: *u8 = "<meta http-equiv=\"refresh\" content=\"4\">" 29const MS_RIG_ELF: *u8 = "_offc/nx_motion_rig.elf" 30const MS_STORE_ELF: *u8 = "_offc/nx_store_put.elf" 31const MS_ACCEPT_PLANE: *u8 = "knowledge/store/accept-" 32 33func msw(s: *u8) -> i64 { sys_write(1, s, mo_slen(s)); return 0 } 34// one response: status line, type, length, no-store, nosniff, close -- then the body 35func ms_send(cfd: i64, status: *u8, ctype: *u8, body: *u8, n: i64) -> i64 { 36 let buf: *u8 = sys_mmap(n + MS_RESP_RESERVE) 37 var o: i64 = mo_cat(buf, 0, "HTTP/1.1 " as *u8) 38 o = mo_cat(buf, o, status) 39 o = mo_cat(buf, o, "\r\nContent-Type: " as *u8) 40 o = mo_cat(buf, o, ctype) 41 o = mo_cat(buf, o, "\r\nContent-Length: " as *u8) 42 o = mo_num(buf, o, n) 43 o = mo_cat(buf, o, "\r\nCache-Control: no-store\r\nX-Content-Type-Options: nosniff\r\nReferrer-Policy: strict-origin-when-cross-origin\r\nConnection: close\r\n\r\n" as *u8) 44 o = mo_catb(buf, o, body, n) 45 nx_http_server_send_response(cfd, buf, o) 46 sys_munmap(buf, n + MS_RESP_RESERVE) 47 return 0 48} 49func ms_text(cfd: i64, status: *u8, msg: *u8) -> i64 { return ms_send(cfd, status, "text/plain; charset=utf-8" as *u8, msg, mo_slen(msg)) } 50func ms_redirect(cfd: i64, loc: *u8) -> i64 { 51 let buf: *u8 = sys_mmap(MS_RESP_RESERVE) 52 var o: i64 = mo_cat(buf, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8) 53 o = mo_cat(buf, o, loc) 54 o = mo_cat(buf, o, "\r\nContent-Length: 0\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n" as *u8) 55 nx_http_server_send_response(cfd, buf, o) 56 sys_munmap(buf, MS_RESP_RESERVE) 57 return 0 58} 59// the page: template + head + body, spliced by the ONE renderer; -1 when the template is unreadable or the buffer would overflow 60func ms_render(out: *u8, cap: i64, head: *u8, body: *u8, bn: i64) -> i64 { 61 let lp: *i64 = sys_mmap(16) as *i64 62 let tmpl: *u8 = mo_read(MO_TMPL, lp) 63 if (tmpl as i64) == 0 { return 0 - 1 } 64 return mo_splice(out, cap, tmpl, lp[0], head, mo_slen(head), body, bn) 65} 66// the index page's bytes into out -- serve and emit both call THIS, so the fallback file cannot drift from the live page 67func ms_render_index(out: *u8, cap: i64) -> i64 { 68 let lc: *i64 = sys_mmap(16) as *i64 69 let ld: *i64 = sys_mmap(16) as *i64 70 let clips: *u8 = mo_read(MO_CLIPS_ROWS, lc) 71 let demos: *u8 = mo_read(MO_DEMOS_ROWS, ld) 72 let body: *u8 = sys_mmap(MO_PAGE_CAP) 73 let bn: i64 = mo_body_index(body, clips, lc[0], demos, ld[0]) 74 let n: i64 = ms_render(out, cap, "" as *u8, body, bn) 75 sys_munmap(body, MO_PAGE_CAP) 76 return n 77} 78func ms_page(cfd: i64, head: *u8, body: *u8, bn: i64) -> i64 { 79 let out: *u8 = sys_mmap(MO_PAGE_CAP) 80 let n: i64 = ms_render(out, MO_PAGE_CAP, head, body, bn) 81 if n < 0 { ms_text(cfd, "500 Internal Server Error" as *u8, "the page renderer refused: template unreadable or buffer exhausted\n" as *u8) } else { 82 ms_send(cfd, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, out, n) 83 } 84 sys_munmap(out, MO_PAGE_CAP) 85 return 0 86} 87func ms_index(cfd: i64) -> i64 { 88 let out: *u8 = sys_mmap(MO_PAGE_CAP) 89 let n: i64 = ms_render_index(out, MO_PAGE_CAP) 90 if n < 0 { ms_text(cfd, "500 Internal Server Error" as *u8, "the page renderer refused: template unreadable or buffer exhausted\n" as *u8) } else { 91 ms_send(cfd, "200 OK" as *u8, "text/html; charset=utf-8" as *u8, out, n) 92 } 93 sys_munmap(out, MO_PAGE_CAP) 94 return 0 95} 96func ms_jobdir(id: *u8) -> *u8 { return mo_join(MO_JOBS_DIR, id) } 97// the job page: RUNNING (with a refresh) until the job's done marker exists, then the rig, the clips and the verdict form 98func ms_job(cfd: i64, id: *u8) -> i64 { 99 if mo_digits_only(id) == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "a job id is digits only\n" as *u8) } 100 let jobdir: *u8 = ms_jobdir(id) 101 let stpath: *u8 = mo_join(jobdir, "status" as *u8) 102 if mo_exists(stpath) == 0 { return ms_text(cfd, "404 Not Found" as *u8, "no such job\n" as *u8) } 103 let ls: *i64 = sys_mmap(16) as *i64 104 let lc: *i64 = sys_mmap(16) as *i64 105 let la: *i64 = sys_mmap(16) as *i64 106 let lv: *i64 = sys_mmap(16) as *i64 107 let st: *u8 = mo_read(stpath, ls) 108 let done: i64 = mo_exists(mo_join(jobdir, "done" as *u8)) 109 let clips: *u8 = mo_read(MO_CLIPS_ROWS, lc) 110 let applied: *u8 = mo_read(mo_join(jobdir, "clips" as *u8), la) 111 let verdicts: *u8 = mo_read(MO_VERDICTS, lv) 112 let body: *u8 = sys_mmap(MO_PAGE_CAP) 113 let bn: i64 = mo_body_job(body, id, st, ls[0], done, clips, lc[0], applied, la[0], verdicts, lv[0]) 114 var head: *u8 = "" as *u8 115 if done == 0 { head = MS_REFRESH_HEAD } 116 ms_page(cfd, head, body, bn) 117 sys_munmap(body, MO_PAGE_CAP) 118 return 0 119} 120// serve one artifact from a job directory (name allowlisted by shape and suffix) or from the demos directory 121func ms_ctype(name: *u8) -> *u8 { 122 let n: i64 = mo_slen(name) 123 if n > 4 { if mo_streq(((name as i64) + n - 4) as *u8, ".png" as *u8) == 1 { return "image/png" as *u8 } } 124 if n > 4 { if mo_streq(((name as i64) + n - 4) as *u8, ".glb" as *u8) == 1 { return "model/gltf-binary" as *u8 } } 125 if n > 4 { if mo_streq(((name as i64) + n - 4) as *u8, ".nxa" as *u8) == 1 { return "application/octet-stream" as *u8 } } 126 if n > 4 { if mo_streq(((name as i64) + n - 4) as *u8, ".txt" as *u8) == 1 { return "text/plain; charset=utf-8" as *u8 } } 127 return "" as *u8 128} 129func ms_file(cfd: i64, dir: *u8, name: *u8) -> i64 { 130 if mo_safe_name(name) == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "artifact name refused by shape\n" as *u8) } 131 let ct: *u8 = ms_ctype(name) 132 if mo_slen(ct) == 0 { return ms_text(cfd, "404 Not Found" as *u8, "only png, glb, nxa and txt artifacts are served\n" as *u8) } 133 let lp: *i64 = sys_mmap(16) as *i64 134 let b: *u8 = mo_read(mo_join(dir, name), lp) 135 if (b as i64) == 0 { return ms_text(cfd, "404 Not Found" as *u8, "artifact absent (the job may still be running, or that stage refused)\n" as *u8) } 136 return ms_send(cfd, "200 OK" as *u8, ct, b, lp[0]) 137} 138func ms_art(cfd: i64, path: *u8, plen: i64) -> i64 { 139 let id: *u8 = sys_mmap(MO_VAL_CAP) 140 let name: *u8 = sys_mmap(MO_VAL_CAP) 141 mo_prev_seg(path, plen, id, MO_VAL_CAP) 142 mo_last_seg(path, plen, name, MO_VAL_CAP) 143 if mo_digits_only(id) == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "a job id is digits only\n" as *u8) } 144 return ms_file(cfd, ms_jobdir(id), name) 145} 146func ms_demo(cfd: i64, path: *u8, plen: i64) -> i64 { 147 let name: *u8 = sys_mmap(MO_VAL_CAP) 148 mo_last_seg(path, plen, name, MO_VAL_CAP) 149 return ms_file(cfd, MO_DEMOS_DIR, name) 150} 151// POST /upload: multipart -> typed by magic -> a job directory -> the job organ spawned detached -> 303 to the job page 152func ms_upload(cfd: i64, req: *u8, hend: i64, body: *u8, blen: i64) -> i64 { 153 let bnd: *u8 = sys_mmap(MO_VAL_CAP) 154 let bl: i64 = mo_boundary(req, hend, bnd, MO_VAL_CAP) 155 if bl < 1 { return ms_text(cfd, "400 Bad Request" as *u8, "no multipart boundary in the request\n" as *u8) } 156 let off: *i64 = sys_mmap(16) as *i64 157 let len: *i64 = sys_mmap(16) as *i64 158 let f: i64 = mo_part_find(body, blen, bnd, bl, "model" as *u8, off, len) 159 if f < 0 { return ms_text(cfd, "400 Bad Request" as *u8, "malformed multipart body: a part without its closing boundary is refused, never half-read\n" as *u8) } 160 if f == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "no part named model in the upload\n" as *u8) } 161 if len[0] < 8 { return ms_text(cfd, "400 Bad Request" as *u8, "the model part is empty\n" as *u8) } 162 let data: *u8 = ((body as i64) + off[0]) as *u8 163 let kind: i64 = mo_kind_of(data, len[0]) 164 if kind == MO_KIND_UNKNOWN { return ms_text(cfd, "415 Unsupported Media Type" as *u8, "unknown bytes: this door accepts GLB (binary glTF), binary FBX and NXA, typed by their first bytes\n" as *u8) } 165 if kind == MO_KIND_OBJ { return ms_text(cfd, "415 Unsupported Media Type" as *u8, "OBJ recognised and refused by name: no sovereign OBJ reader is composed on this door yet (motion.plan MO2)\n" as *u8) } 166 var cells: i64 = MS_CELLS_DEFAULT 167 let coff: *i64 = sys_mmap(16) as *i64 168 let clen: *i64 = sys_mmap(16) as *i64 169 if mo_part_find(body, blen, bnd, bl, "cells" as *u8, coff, clen) == 1 { cells = mo_atoi(((body as i64) + coff[0]) as *u8, clen[0]) } 170 if cells < MS_CELLS_MIN { cells = MS_CELLS_DEFAULT } 171 if cells > MS_CELLS_MAX { cells = MS_CELLS_DEFAULT } 172 // the job: digit-named by the clock in milliseconds, so two uploads in one second still differ 173 let id: *u8 = sys_mmap(32) 174 let il: i64 = mo_num(id, 0, sys_clock_now_us() / 1000) 175 id[il] = 0 as u8 176 let jobdir: *u8 = ms_jobdir(id) 177 sys_mkdir(MO_JOBS_DIR, 493) 178 if sys_mkdir(jobdir, 493) < 0 { return ms_text(cfd, "500 Internal Server Error" as *u8, "could not create the job directory\n" as *u8) } 179 let inname: *u8 = sys_mmap(32) 180 var io: i64 = mo_cat(inname, 0, "in." as *u8) 181 io = mo_cat(inname, io, mo_kind_name(kind)) 182 inname[io] = 0 as u8 183 if mo_write_all(mo_join(jobdir, inname), data, len[0]) != len[0] { return ms_text(cfd, "500 Internal Server Error" as *u8, "could not store the upload\n" as *u8) } 184 let kb: *u8 = sys_mmap(32) 185 var ko: i64 = mo_cat(kb, 0, mo_kind_name(kind)) 186 kb[ko] = 10 as u8 187 mo_write_all(mo_join(jobdir, "kind" as *u8), kb, ko + 1) 188 let sb: *u8 = sys_mmap(256) 189 var so: i64 = mo_cat(sb, 0, "stage=queued\nok=0\nkind=" as *u8) 190 so = mo_cat(sb, so, mo_kind_name(kind)) 191 so = mo_cat(sb, so, "\ncells=" as *u8) 192 so = mo_num(sb, so, cells) 193 so = mo_cat(sb, so, "\nbytes_in=" as *u8) 194 so = mo_num(sb, so, len[0]) 195 sb[so] = 10 as u8 196 mo_write_all(mo_join(jobdir, "status" as *u8), sb, so + 1) 197 let cellstr: *u8 = sys_mmap(32) 198 let cl: i64 = mo_num(cellstr, 0, cells) 199 cellstr[cl] = 0 as u8 200 let words: *i64 = sys_mmap(MO_ARGV_CAP * 8) as *i64 201 let vrig: *u8 = "rig" as *u8 202 words[0] = vrig as i64; words[1] = jobdir as i64; words[2] = cellstr as i64 203 if mo_run_detached(MS_RIG_ELF, words, 3, mo_join(jobdir, "spawn.log" as *u8)) < 0 { return ms_text(cfd, "503 Service Unavailable" as *u8, "could not spawn the rig job\n" as *u8) } 204 let loc: *u8 = sys_mmap(MO_VAL_CAP) 205 var lo: i64 = mo_cat(loc, 0, "/motion/job/" as *u8) 206 lo = mo_cat(loc, lo, id) 207 loc[lo] = 0 as u8 208 return ms_redirect(cfd, loc) 209} 210// GET /anim?job=<id>&clip=<key>: the clip row names the donor and pose; the job organ is spawned detached; 303 back 211func ms_anim(cfd: i64, path: *u8, plen: i64) -> i64 { 212 let id: *u8 = sys_mmap(MO_VAL_CAP) 213 let key: *u8 = sys_mmap(MO_VAL_CAP) 214 mo_qparam(path, plen, "job" as *u8, id, MO_VAL_CAP) 215 mo_qparam(path, plen, "clip" as *u8, key, MO_VAL_CAP) 216 if mo_digits_only(id) == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "a job id is digits only\n" as *u8) } 217 if mo_safe_name(key) == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "clip key refused by shape\n" as *u8) } 218 let jobdir: *u8 = ms_jobdir(id) 219 if mo_exists(mo_join(jobdir, "done" as *u8)) == 0 { return ms_text(cfd, "409 Conflict" as *u8, "the rig job has not finished; apply a clip once the job page reads DONE\n" as *u8) } 220 if mo_exists(mo_join(jobdir, "rig.nxa" as *u8)) == 0 { return ms_text(cfd, "409 Conflict" as *u8, "the job has no rig (it failed at a named stage); nothing to animate\n" as *u8) } 221 let lc: *i64 = sys_mmap(16) as *i64 222 let rows: *u8 = mo_read(MO_CLIPS_ROWS, lc) 223 if (rows as i64) == 0 { return ms_text(cfd, "503 Service Unavailable" as *u8, "the clip rows are unreadable\n" as *u8) } 224 let r: i64 = mo_row_find(rows, lc[0], key) 225 if r < 0 { return ms_text(cfd, "404 Not Found" as *u8, "no clip row with that key: a clip is a data row, and an absent row is refused by name\n" as *u8) } 226 let donor: *u8 = sys_mmap(MO_VAL_CAP) 227 let pose: *u8 = sys_mmap(MO_VAL_CAP) 228 mo_row_field(rows, lc[0], r, 1, donor, MO_VAL_CAP) 229 mo_row_field(rows, lc[0], r, 2, pose, MO_VAL_CAP) 230 if mo_exists(donor) == 0 { return ms_text(cfd, "409 Conflict" as *u8, "the clip's donor asset is ABSENT on this host; the row stays, the apply refuses\n" as *u8) } 231 let words: *i64 = sys_mmap(MO_ARGV_CAP * 8) as *i64 232 let vanim: *u8 = "anim" as *u8 233 words[0] = vanim as i64; words[1] = jobdir as i64; words[2] = donor as i64; words[3] = pose as i64; words[4] = key as i64 234 let lg: *u8 = sys_mmap(MO_VAL_CAP) 235 var lo: i64 = mo_cat(lg, 0, "spawn_anim_" as *u8) 236 lo = mo_cat(lg, lo, key) 237 lo = mo_cat(lg, lo, ".log" as *u8) 238 lg[lo] = 0 as u8 239 if mo_run_detached(MS_RIG_ELF, words, 5, mo_join(jobdir, lg)) < 0 { return ms_text(cfd, "503 Service Unavailable" as *u8, "could not spawn the anim job\n" as *u8) } 240 let loc: *u8 = sys_mmap(MO_VAL_CAP) 241 var o: i64 = mo_cat(loc, 0, "/motion/job/" as *u8) 242 o = mo_cat(loc, o, id) 243 loc[o] = 0 as u8 244 return ms_redirect(cfd, loc) 245} 246// POST /verdict: job, v (accept or reject), words -> one journal row here AND one row on the acceptance plane 247func ms_verdict(cfd: i64, body: *u8, blen: i64) -> i64 { 248 let id: *u8 = sys_mmap(MO_VAL_CAP) 249 let v: *u8 = sys_mmap(MO_VAL_CAP) 250 let words: *u8 = sys_mmap(MO_VAL_CAP) 251 mo_kv(body, blen, "job" as *u8, id, MO_VAL_CAP) 252 mo_kv(body, blen, "v" as *u8, v, MO_VAL_CAP) 253 let wl: i64 = mo_kv(body, blen, "words" as *u8, words, MO_VAL_CAP) 254 if mo_digits_only(id) == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "a job id is digits only\n" as *u8) } 255 if mo_exists(mo_join(ms_jobdir(id), "status" as *u8)) == 0 { return ms_text(cfd, "404 Not Found" as *u8, "a verdict on an absent job is refused by name\n" as *u8) } 256 var verdict: *u8 = "" as *u8 257 if mo_streq(v, "accept" as *u8) == 1 { verdict = "ACCEPT" as *u8 } 258 if mo_streq(v, "reject" as *u8) == 1 { verdict = "REJECT" as *u8 } 259 if mo_slen(verdict) == 0 { return ms_text(cfd, "400 Bad Request" as *u8, "v must be accept or reject\n" as *u8) } 260 // the words are stored as given, minus the two bytes that would break a row (pipe and newline), bounded 261 var i: i64 = 0 262 var wn: i64 = wl 263 if wn < 0 { wn = 0 } 264 if wn > MS_WORDS_CAP { wn = MS_WORDS_CAP } 265 while i < wn { let c: i64 = words[i] as i64; if c == 124 { words[i] = 32 as u8 } if c == 10 { words[i] = 32 as u8 } if c == 13 { words[i] = 32 as u8 } i = i + 1 } 266 words[wn] = 0 as u8 267 let epoch: i64 = sys_clock_now_us() / 1000000 268 let row: *u8 = sys_mmap(MO_VAL_CAP * 2) 269 var o: i64 = mo_cat(row, 0, id) 270 row[o] = 124 as u8; o = mo_cat(row, o + 1, verdict) 271 row[o] = 124 as u8; o = mo_num(row, o + 1, epoch) 272 row[o] = 124 as u8; o = mo_catb(row, o + 1, words, wn) 273 row[o] = 10 as u8; o = o + 1 274 mo_append(MO_VERDICTS, row, o) 275 // the acceptance plane: put <actor> <id> <title> <sev> <status> <owner> <scope> <note> -- the estate's own store verb 276 let subject: *u8 = sys_mmap(MO_VAL_CAP) 277 var so: i64 = mo_cat(subject, 0, "motion-job-" as *u8); so = mo_cat(subject, so, id); subject[so] = 0 as u8 278 let target: *u8 = sys_mmap(MO_VAL_CAP) 279 var to: i64 = mo_cat(target, 0, "/motion/job/" as *u8); to = mo_cat(target, to, id); target[to] = 0 as u8 280 let aw: *i64 = sys_mmap(MO_ARGV_CAP * 8) as *i64 281 let vput: *u8 = "put" as *u8 282 let voper: *u8 = "operator" as *u8 283 let vsev: *u8 = "0" as *u8 284 aw[0] = MS_ACCEPT_PLANE as i64; aw[1] = vput as i64; aw[2] = voper as i64; aw[3] = id as i64; aw[4] = subject as i64; aw[5] = vsev as i64; aw[6] = verdict as i64; aw[7] = voper as i64; aw[8] = target as i64; aw[9] = words as i64 285 mo_run_detached(MS_STORE_ELF, aw, 10, mo_join(ms_jobdir(id), "verdict_put.log" as *u8)) 286 let loc: *u8 = sys_mmap(MO_VAL_CAP) 287 var lo: i64 = mo_cat(loc, 0, "/motion/job/" as *u8) 288 lo = mo_cat(loc, lo, id) 289 loc[lo] = 0 as u8 290 return ms_redirect(cfd, loc) 291} 292func ms_emit(outpath: *u8) -> i64 { 293 let out: *u8 = sys_mmap(MO_PAGE_CAP) 294 let n: i64 = ms_render_index(out, MO_PAGE_CAP) 295 if n < 0 { msw("MOTION-EMIT REFUSED: template unreadable or buffer exhausted\n" as *u8); return 1 } 296 let w: i64 = mo_write_all(outpath, out, n) 297 if w != n { msw("MOTION-EMIT REFUSED: write failed\n" as *u8); return 1 } 298 msw("MOTION-EMIT OK bytes=" as *u8) 299 let nb: *u8 = sys_mmap(64) 300 var no: i64 = mo_num(nb, 0, n); no = mo_cat(nb, no, " path=" as *u8); no = mo_cat(nb, no, outpath); no = mo_cat(nb, no, "\n" as *u8) 301 sys_write(1, nb, no) 302 return 0 303} 304func main(argc: i64, argv: *i64) -> i64 { 305 var port: i64 = MS_PORT_DEFAULT 306 if argc >= 2 { 307 let a: *u8 = argv[1] as *u8 308 if mo_streq(a, "emit" as *u8) == 1 { 309 if argc < 3 { msw("usage: nx_motion_serve [port] | emit <out.html>\n" as *u8); return MS_EXIT_USAGE } 310 return ms_emit(argv[2] as *u8) 311 } 312 let v: i64 = mo_atoi(a, mo_slen(a)) 313 if v > 0 { port = v } 314 } 315 let addr: *u8 = sys_mmap(64) 316 nx_http_server_addr_loopback(addr, port) 317 let vd: *i64 = (sys_mmap(8)) as *i64 318 let lfd: i64 = nx_http_server_listen(addr, MS_BACKLOG, vd) 319 if lfd < 0 { 320 sys_write(2, "[nx_motion_serve] LISTEN-FAILED verdict=" as *u8, 40) 321 sys_write(2, nxs_verdict_name(vd[0]), mo_slen(nxs_verdict_name(vd[0]))) 322 sys_write(2, "\n" as *u8, 1) 323 sys_exit(1) 324 return 1 325 } 326 msw("[nx_motion_serve] listening on loopback\n" as *u8) 327 let req: *u8 = sys_mmap(MO_REQ_CAP) 328 let mth: *i64 = (sys_mmap(8)) as *i64 329 let poff: *i64 = (sys_mmap(8)) as *i64 330 let plen: *i64 = (sys_mmap(8)) as *i64 331 let clen: *i64 = (sys_mmap(8)) as *i64 332 let boff: *i64 = (sys_mmap(8)) as *i64 333 let rn: *i64 = (sys_mmap(8)) as *i64 334 var alive: i64 = 1 335 while alive == 1 { 336 let cfd: i64 = nx_http_server_accept_one(lfd, vd) 337 if cfd >= 0 { 338 let rr: i64 = nx_http_server_read_request(cfd, req, MO_REQ_CAP, mth, poff, plen, clen, boff, rn) 339 let path: *u8 = ((req as i64) + poff[0]) as *u8 340 if rr != NXS_OK { 341 if clen[0] > MO_UPLOAD_CAP { ms_text(cfd, "413 Content Too Large" as *u8, "the direct door takes a body up to the named cap printed on the page; larger models belong to the chunked door (motion.plan MO13)\n" as *u8) } else { 342 ms_text(cfd, "400 Bad Request" as *u8, "the request did not parse as HTTP/1.1, or its body ended early\n" as *u8) 343 } 344 } else { 345 var blen: i64 = clen[0] 346 if rn[0] - boff[0] < blen { blen = rn[0] - boff[0] } 347 if blen < 0 { blen = 0 } 348 let body: *u8 = ((req as i64) + boff[0]) as *u8 349 if mth[0] == MS_METHOD_GET { 350 if mo_path_has(path, plen[0], "/art/" as *u8) == 1 { ms_art(cfd, path, plen[0]) } else { 351 if mo_path_has(path, plen[0], "/demo/" as *u8) == 1 { ms_demo(cfd, path, plen[0]) } else { 352 if mo_path_has(path, plen[0], "/job/" as *u8) == 1 { let id: *u8 = sys_mmap(MO_VAL_CAP); mo_last_seg(path, plen[0], id, MO_VAL_CAP); ms_job(cfd, id) } else { 353 if mo_path_has(path, plen[0], "/anim" as *u8) == 1 { ms_anim(cfd, path, plen[0]) } else { 354 ms_index(cfd) 355 } } } } 356 } else { 357 if mth[0] == MS_METHOD_POST { 358 if mo_path_has(path, plen[0], "/upload" as *u8) == 1 { ms_upload(cfd, req, boff[0], body, blen) } else { 359 if mo_path_has(path, plen[0], "/verdict" as *u8) == 1 { ms_verdict(cfd, body, blen) } else { 360 ms_text(cfd, "404 Not Found" as *u8, "POST routes: /motion/upload and /motion/verdict\n" as *u8) 361 } } 362 } else { 363 ms_text(cfd, "405 Method Not Allowed" as *u8, "GET and POST only\n" as *u8) 364 } } 365 } 366 } 367 } 368 sys_exit(0) 369 return 0 370}