code wiki / _hdl_build / nx_vroom.nx
nx_vroom.nx source
↩ module page · 216 lines · 9270 B
1// nx_vroom.nx -- the sovereign video-room request HANDLER (the daemon's brain),
2// kept separate from sockets/TLS so it is unit-gateable. Composes nx_room_relay
3// (per-room peer frames) into HTTP responses for the relay routes:
4// POST /video/post?room=R&id=N body=frame -> rr_post, "200 ok"
5// GET /video/frames?room=R&id=N -> octet body of OTHER peers'
6// frames as length-prefixed records: [id u64 LE][len u32 LE][frame bytes]*
7// GET /video/roster?room=R&id=N -> JSON [id,id,...]
8// Returns response length in `out`, or 0 if the route isn't a relay route (the
9// TLS/fork shell then serves the static page / client.wasm / 404).
10// Server-relay = ZERO WebRTC/STUN/TURN. All flag-style loops (no break/continue
11// in nested ifs -- that miscompiles and hangs, per the sites-daemon note).
12// license_tier: ORIGINAL
13import "nx_room_relay.nx"
14const K_MAGIC_2166136261: i64 = 2166136261
15const K_MAGIC_16777619: i64 = 16777619
16
17func vr_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
18func vr_lower(c: u8) -> u8 { if c >= (65 as u8) { if c <= (90 as u8) { return c + (32 as u8) } } return c }
19
20func vr_contains(hay: *u8, hlen: i64, needle: *u8, nlen: i64) -> i64 {
21 if nlen <= 0 { return 0 }
22 if hlen < nlen { return 0 }
23 var i: i64 = 0
24 let last: i64 = hlen - nlen
25 while i <= last {
26 var j: i64 = 0; var ok: i64 = 1
27 while j < nlen { if hay[i+j] != needle[j] { ok = 0; j = nlen } else { j = j + 1 } }
28 if ok == 1 { return 1 }
29 i = i + 1
30 }
31 return 0
32}
33
34// body offset after CRLFCRLF, or hlen if absent
35func vr_body_start(hay: *u8, hlen: i64) -> i64 {
36 var i: i64 = 0
37 while i + 4 <= hlen {
38 if hay[i]==(13 as u8) { if hay[i+1]==(10 as u8) { if hay[i+2]==(13 as u8) { if hay[i+3]==(10 as u8) { return i + 4 } } } }
39 i = i + 1
40 }
41 return hlen
42}
43
44func vr_content_length(req: *u8, reqn: i64) -> i64 {
45 let key: *u8 = "content-length:" as *u8
46 var i: i64 = 0
47 while i + 15 <= reqn {
48 var j: i64 = 0; var ok: i64 = 1
49 while j < 15 { if vr_lower(req[i+j]) != key[j] { ok = 0; j = 15 } else { j = j + 1 } }
50 if ok == 1 {
51 var k: i64 = i + 15
52 var sk: i64 = 1
53 while sk == 1 { if k < reqn { if req[k]==(32 as u8) { k = k + 1 } else { sk = 0 } } else { sk = 0 } }
54 var v: i64 = 0; var got: i64 = 0; var run: i64 = 1
55 while run == 1 {
56 if k >= reqn { run = 0 } else {
57 let c: i64 = req[k] as i64
58 var dig: i64 = 0
59 if c >= 48 { if c <= 57 { dig = 1 } }
60 if dig == 1 { v = v*10 + (c-48); got = 1; k = k + 1 } else { run = 0 }
61 }
62 }
63 if got == 1 { return v }
64 return 0 - 1
65 }
66 i = i + 1
67 }
68 return 0 - 1
69}
70
71// FNV-1a over the request-line value of key (e.g. "room="); 0 if absent.
72// Hash keeps rooms isolated without storing names. Always nonzero-ized (|1).
73func vr_room_hash(req: *u8, reqn: i64) -> i64 {
74 let key: *u8 = "room=" as *u8
75 // limit to request line
76 var lim: i64 = 0; var sc: i64 = 1
77 while sc == 1 { if lim < reqn { if req[lim]==(13 as u8) { sc = 0 } else { lim = lim + 1 } } else { sc = 0 } }
78 var found: i64 = 0 - 1; var i: i64 = 0
79 while i + 5 <= lim {
80 if found < 0 {
81 var j: i64 = 0; var ok: i64 = 1
82 while j < 5 { if req[i+j] != key[j] { ok = 0; j = 5 } else { j = j + 1 } }
83 if ok == 1 { found = i + 5 }
84 }
85 i = i + 1
86 }
87 if found < 0 { return 0 }
88 var h: i64 = K_MAGIC_2166136261
89 var k: i64 = found; var run: i64 = 1
90 while run == 1 {
91 if k >= lim { run = 0 } else {
92 let c: u8 = vr_lower(req[k])
93 var keepc: i64 = 0
94 if c >= (97 as u8) { if c <= (122 as u8) { keepc = 1 } }
95 if c >= (48 as u8) { if c <= (57 as u8) { keepc = 1 } }
96 if c == (45 as u8) { keepc = 1 }
97 if keepc == 0 { run = 0 } else { h = (h ^ (c as i64)) * K_MAGIC_16777619; h = h & 0xffffffff; k = k + 1 }
98 }
99 }
100 return h | 1
101}
102
103// integer value of the request-line value of "id=" (digits only), or -1
104func vr_id(req: *u8, reqn: i64) -> i64 {
105 let key: *u8 = "id=" as *u8
106 var lim: i64 = 0; var sc: i64 = 1
107 while sc == 1 { if lim < reqn { if req[lim]==(13 as u8) { sc = 0 } else { lim = lim + 1 } } else { sc = 0 } }
108 var found: i64 = 0 - 1; var i: i64 = 0
109 while i + 3 <= lim {
110 if found < 0 {
111 var j: i64 = 0; var ok: i64 = 1
112 while j < 3 { if req[i+j] != key[j] { ok = 0; j = 3 } else { j = j + 1 } }
113 if ok == 1 { found = i + 3 }
114 }
115 i = i + 1
116 }
117 if found < 0 { return 0 - 1 }
118 var v: i64 = 0; var got: i64 = 0; var k: i64 = found; var run: i64 = 1
119 while run == 1 {
120 if k >= lim { run = 0 } else {
121 let c: i64 = req[k] as i64
122 var dig: i64 = 0
123 if c >= 48 { if c <= 57 { dig = 1 } }
124 if dig == 1 { v = v*10 + (c-48); got = 1; k = k + 1 } else { run = 0 }
125 }
126 }
127 if got == 0 { return 0 - 1 }
128 return v
129}
130
131func vr_app(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { var i: i64=0; while i<n { dst[off+i]=src[i]; i=i+1 } return off+n }
132func vr_apps(dst: *u8, off: i64, s: *u8) -> i64 { return vr_app(dst, off, s, vr_strlen(s)) }
133func vr_wu64(dst: *u8, off: i64, v: i64) -> i64 { var i: i64=0; while i<8 { dst[off+i]=((v >> (i*8)) & 0xff) as u8; i=i+1 } return off+8 }
134func vr_wu32(dst: *u8, off: i64, v: i64) -> i64 { var i: i64=0; while i<4 { dst[off+i]=((v >> (i*8)) & 0xff) as u8; i=i+1 } return off+4 }
135func vr_wdec(dst: *u8, off: i64, v: i64) -> i64 {
136 if v == 0 { dst[off]=48; return off+1 }
137 var d: i64=0; var y: i64=v
138 while y>0 { d=d+1; y=y/10 }
139 var i: i64=d-1; y=v
140 while i>=0 { dst[off+i]=(48+(y%10)) as u8; y=y/10; i=i-1 }
141 return off+d
142}
143
144// Handle a relay route. Returns response bytes written to out, or 0 if not a relay route.
145func vr_handle(plain: *u8, plain_n: i64, rr_st: *i64, rr_arena: *u8, now: i64, ttl: i64,
146 out: *u8, out_cap: i64, ids: *i64) -> i64 {
147 let slotb: i64 = rr_st[1]
148
149 if vr_contains(plain, plain_n, "/post" as *u8, 5) == 1 {
150 let rm: i64 = vr_room_hash(plain, plain_n)
151 let pr: i64 = vr_id(plain, plain_n)
152 if rm == 0 { return vr_apps(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n" as *u8) }
153 if pr < 0 { return vr_apps(out, 0, "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n" as *u8) }
154 let bs: i64 = vr_body_start(plain, plain_n)
155 var fl: i64 = plain_n - bs
156 let cl: i64 = vr_content_length(plain, plain_n)
157 if cl >= 0 { if cl < fl { fl = cl } }
158 if fl < 0 { fl = 0 }
159 rr_post(rr_st, rr_arena, rm, pr, now, now, (plain as i64 + bs) as *u8, fl)
160 return vr_apps(out, 0, "HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: keep-alive\r\n\r\nok" as *u8)
161 }
162
163 if vr_contains(plain, plain_n, "/frames" as *u8, 7) == 1 {
164 let rm: i64 = vr_room_hash(plain, plain_n)
165 let pr: i64 = vr_id(plain, plain_n)
166 let cnt: i64 = rr_roster(rr_st, rm, pr, now, ttl, ids)
167 // pass 1: body length
168 var blen: i64 = 0; var a: i64 = 0
169 while a < cnt {
170 let s: i64 = rr_live(rr_st, rm, ids[a], now, ttl)
171 if s >= 0 { blen = blen + 12 + rr_flen(rr_st, s) }
172 a = a + 1
173 }
174 var o: i64 = vr_apps(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: " as *u8)
175 o = vr_wdec(out, o, blen)
176 o = vr_apps(out, o, "\r\nConnection: keep-alive\r\n\r\n" as *u8)
177 // pass 2: body records [id u64][len u32][frame]
178 var b: i64 = 0
179 while b < cnt {
180 let s: i64 = rr_live(rr_st, rm, ids[b], now, ttl)
181 if s >= 0 {
182 let fl: i64 = rr_flen(rr_st, s)
183 o = vr_wu64(out, o, ids[b])
184 o = vr_wu32(out, o, fl)
185 o = vr_app(out, o, (rr_arena as i64 + s*slotb) as *u8, fl)
186 }
187 b = b + 1
188 }
189 return o
190 }
191
192 if vr_contains(plain, plain_n, "/roster" as *u8, 7) == 1 {
193 let rm: i64 = vr_room_hash(plain, plain_n)
194 let pr: i64 = vr_id(plain, plain_n)
195 let cnt: i64 = rr_roster(rr_st, rm, pr, now, ttl, ids)
196 // build JSON body in the tail of out, then header (Content-Length)
197 let bodycap: i64 = out_cap - 256
198 var jb: i64 = bodycap
199 jb = vr_app(out, jb, "[" as *u8, 1)
200 var a: i64 = 0
201 while a < cnt {
202 if a > 0 { jb = vr_app(out, jb, "," as *u8, 1) }
203 jb = vr_wdec(out, jb, ids[a])
204 a = a + 1
205 }
206 jb = vr_app(out, jb, "]" as *u8, 1)
207 let blen: i64 = jb - bodycap
208 var o: i64 = vr_apps(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: " as *u8)
209 o = vr_wdec(out, o, blen)
210 o = vr_apps(out, o, "\r\nConnection: keep-alive\r\n\r\n" as *u8)
211 o = vr_app(out, o, (out as i64 + bodycap) as *u8, blen)
212 return o
213 }
214
215 return 0
216}