@noita-ts/pollnet 0.3.8 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.lua CHANGED
@@ -1,490 +1,171 @@
1
- -- From https://github.com/probable-basilisk/pollnet/commit/b9361aabd82e568d7e0c6b00bbe991e3dc8c56bf
2
- -- with minimal adjustment to use NoitaTS-specific path for ffi.load
3
-
4
- -- pollnet bindings for luajit+ffi
5
-
6
- local API_VERSION = "1.0.0"
7
-
8
- local ffi = require("ffi")
9
- ffi.cdef[[
10
- typedef struct pollnet_ctx pollnet_ctx;
11
- typedef uint64_t sockethandle_t;
12
- typedef uint32_t socketstatus_t;
13
-
14
- const char* pollnet_version();
15
- bool pollnet_handle_is_valid(sockethandle_t handle);
16
- sockethandle_t pollnet_invalid_handle();
17
-
18
- pollnet_ctx* pollnet_init();
19
- pollnet_ctx* pollnet_get_or_init_static();
20
- void pollnet_shutdown(pollnet_ctx* ctx);
21
- sockethandle_t pollnet_open_tcp(pollnet_ctx* ctx, const char* addr);
22
- sockethandle_t pollnet_listen_tcp(pollnet_ctx* ctx, const char* addr);
23
- sockethandle_t pollnet_open_ws(pollnet_ctx* ctx, const char* url);
24
- sockethandle_t pollnet_simple_http_get(pollnet_ctx* ctx, const char* url, const char* headers, bool ret_body_only);
25
- sockethandle_t pollnet_simple_http_post(pollnet_ctx* ctx, const char* url, const char* headers, const char* data, uint32_t datasize, bool ret_body_only);
26
- void pollnet_close(pollnet_ctx* ctx, sockethandle_t handle);
27
- void pollnet_close_all(pollnet_ctx* ctx);
28
- void pollnet_send(pollnet_ctx* ctx, sockethandle_t handle, const char* msg);
29
- void pollnet_send_binary(pollnet_ctx* ctx, sockethandle_t handle, const unsigned char* msg, uint32_t msgsize);
30
- socketstatus_t pollnet_update(pollnet_ctx* ctx, sockethandle_t handle);
31
- socketstatus_t pollnet_update_blocking(pollnet_ctx* ctx, sockethandle_t handle);
32
- uint32_t pollnet_get_data_size(pollnet_ctx* ctx, sockethandle_t handle);
33
- uint32_t pollnet_get_data(pollnet_ctx* ctx, sockethandle_t handle, char* dest, uint32_t dest_size);
34
- const uint8_t* pollnet_unsafe_get_data_ptr(pollnet_ctx* ctx, sockethandle_t handle);
35
- void pollnet_clear_data(pollnet_ctx* ctx, sockethandle_t handle);
36
- uint32_t pollnet_get_error(pollnet_ctx* ctx, sockethandle_t handle, char* dest, uint32_t dest_size);
37
- sockethandle_t pollnet_get_connected_client_handle(pollnet_ctx* ctx, sockethandle_t handle);
38
- sockethandle_t pollnet_listen_ws(pollnet_ctx* ctx, const char* addr);
39
- sockethandle_t pollnet_serve_static_http(pollnet_ctx* ctx, const char* addr, const char* serve_dir);
40
- sockethandle_t pollnet_serve_dynamic_http(pollnet_ctx* ctx, const char* addr, bool keep_alive);
41
- sockethandle_t pollnet_serve_http(pollnet_ctx* ctx, const char* addr);
42
- void pollnet_add_virtual_file(pollnet_ctx* ctx, sockethandle_t handle, const char* filename, const char* filedata, uint32_t filesize);
43
- void pollnet_remove_virtual_file(pollnet_ctx* ctx, sockethandle_t handle, const char* filename);
44
- uint32_t pollnet_get_nanoid(char* dest, uint32_t dest_size);
45
- void pollnet_sleep_ms(uint32_t milliseconds);
46
- ]]
47
-
48
- --- @noita-ts-include ./pollnet.dll
49
- local pollnet = ffi.load("mods/" .. require("$mod").MOD_ID .. "/lua_modules/@noita-ts/pollnet/dist/pollnet.dll")
50
-
51
- local POLLNET_VERSION = ffi.string(pollnet.pollnet_version())
52
-
53
- do
54
- local function split_version(v)
55
- local major, minor, patch = v:match("(%d+)%.(%d+)%.(%d+)")
56
- return tonumber(major), tonumber(minor), tonumber(patch)
57
- end
58
-
59
- local maj_req, min_req, pat_req = split_version(API_VERSION)
60
- local maj_dll, min_dll, pat_dll = split_version(POLLNET_VERSION)
61
- if maj_dll ~= maj_req then
62
- error("Incompatible Pollnet binary: expected " .. API_VERSION
63
- .. " got " .. POLLNET_VERSION)
64
- end
65
- if (min_dll < min_req) or (min_dll == min_req and pat_dll < pat_req) then
66
- error("Incompatible Pollnet binary: expected " .. API_VERSION
67
- .. " got " .. POLLNET_VERSION)
68
- end
69
- end
70
-
71
- local POLLNET_RESULT_CODES = {
72
- [0] = "invalid_handle",
73
- [1] = "error",
74
- [2] = "closed",
75
- [3] = "opening",
76
- [4] = "nodata",
77
- [5] = "hasdata",
78
- [6] = "newclient"
79
- }
80
-
81
- local _ctx = nil
82
-
83
- local function init_ctx()
84
- if _ctx then return end
85
- _ctx = ffi.gc(pollnet.pollnet_init(), pollnet.pollnet_shutdown)
86
- assert(_ctx ~= nil)
87
- end
88
-
89
- local function init_ctx_hack_static()
90
- if _ctx then return end
91
- _ctx = pollnet.pollnet_get_or_init_static()
92
- assert(_ctx ~= nil)
93
- pollnet.pollnet_close_all(_ctx)
94
- end
95
-
96
- local function shutdown_ctx()
97
- if not _ctx then return end
98
- pollnet.pollnet_shutdown(ffi.gc(_ctx, nil))
99
- _ctx = nil
100
- end
101
-
102
- local socket_mt = {}
103
- local function Socket()
104
- return setmetatable({}, {__index = socket_mt})
105
- end
106
-
107
- function socket_mt:_from_handle(handle)
108
- init_ctx()
109
- if self._socket then self:close() end
110
- self._socket = handle
111
- self._status = "unpolled"
112
- return self
113
- end
114
-
115
- function socket_mt:_open(opener, ...)
116
- init_ctx()
117
- if self._socket then self:close() end
118
- self._socket = opener(_ctx, ...)
119
- self._status = "unpolled"
120
- return self
121
- end
122
-
123
- local function format_headers(headers)
124
- if type(headers) == 'string' then return headers end
125
- if type(headers) ~= 'table' then
126
- error("HTTP headers must be table|string, got: " .. tostring(headers))
127
- end
128
- local keys = {}
129
- for name, _ in pairs(headers) do
130
- table.insert(keys, name)
131
- end
132
- table.sort(keys)
133
- local frags = {}
134
- for idx, name in ipairs(keys) do
135
- local val = headers[name]
136
- if type(val) == 'string' then
137
- table.insert(frags, ("%s:%s"):format(name, val))
138
- else -- assume table representing a duplicated header
139
- for _, subval in ipairs(val) do
140
- table.insert(frags, ("%s:%s"):format(name, subval))
141
- end
142
- end
143
- end
144
- return table.concat(frags, "\n")
145
- end
146
-
147
- local function parse_headers(headers_str)
148
- local headers = {}
149
- for line in headers_str:gmatch("[^\n]+") do
150
- local key, val = line:match("^([^:]*):(.*)$")
151
- if key then headers[key:lower()] = val end
152
- end
153
- return headers
154
- end
155
-
156
- local function parse_query(s)
157
- local queries = {[1]=s}
158
- for k, v in s:gmatch("([^=&]+)=([^&]+)") do
159
- queries[k] = v
160
- end
161
- return queries
162
- end
163
-
164
- local function parse_method(s)
165
- local method, path, query = s:match("^(%w+) ([^?]+)%??(.*)$")
166
- local queries = parse_query(query)
167
- return method, path, queries
168
- end
169
-
170
- function socket_mt:http_get(url, headers, ret_body_only)
171
- headers = format_headers(headers or "")
172
- ret_body_only = not not ret_body_only
173
- return self:_open(
174
- pollnet.pollnet_simple_http_get,
175
- url,
176
- headers,
177
- ret_body_only
178
- )
179
- end
180
-
181
- function socket_mt:http_post(url, headers, body, ret_body_only)
182
- body = body or ""
183
- headers = format_headers(headers or {
184
- ["content-type"] = "application/x-www-form-urlencoded"
185
- })
186
- ret_body_only = not not ret_body_only
187
- return self:_open(
188
- pollnet.pollnet_simple_http_post,
189
- url,
190
- headers,
191
- body,
192
- #body,
193
- ret_body_only
194
- )
195
- end
196
-
197
- function socket_mt:open_ws(url)
198
- return self:_open(pollnet.pollnet_open_ws, url)
199
- end
200
-
201
- function socket_mt:open_tcp(addr)
202
- return self:_open(pollnet.pollnet_open_tcp, addr)
203
- end
204
-
205
- function socket_mt:serve_http(addr, dir)
206
- self.is_http_server = true
207
- if dir and dir ~= "" then
208
- return self:_open(pollnet.pollnet_serve_static_http, addr, dir)
209
- else
210
- return self:_open(pollnet.pollnet_serve_http, addr)
211
- end
212
- end
213
-
214
- function socket_mt:add_virtual_file(filename, filedata)
215
- assert(filedata and type(filedata) == 'string', "filedata must be provided as string!")
216
- if filename:sub(1,1) ~= "/" then
217
- -- url paths start from root at "/"
218
- filename = "/" .. filename
219
- end
220
- local dsize = #filedata
221
- pollnet.pollnet_add_virtual_file(_ctx, self._socket, filename, filedata, dsize)
222
- end
223
-
224
- function socket_mt:remove_virtual_file(filename)
225
- pollnet.pollnet_remove_virtual_file(_ctx, self._socket, filename)
226
- end
227
-
228
- function socket_mt:listen_ws(addr, callback)
229
- if callback then self:on_connection(callback) end
230
- return self:_open(pollnet.pollnet_listen_ws, addr)
231
- end
232
-
233
- function socket_mt:listen_tcp(addr, callback)
234
- if callback then self:on_connection(callback) end
235
- return self:_open(pollnet.pollnet_listen_tcp, addr)
236
- end
237
-
238
- function socket_mt:serve_dynamic_http(addr, keep_alive, callback)
239
- if callback then self:on_connection(callback) end
240
- return self:_open(pollnet.pollnet_serve_dynamic_http, addr, keep_alive or false)
241
- end
242
-
243
- function socket_mt:on_connection(f)
244
- self._on_connection = f
245
- return self
246
- end
247
-
248
- function socket_mt:_get_message()
249
- local msg_size = pollnet.pollnet_get_data_size(_ctx, self._socket)
250
- if msg_size > 0 then
251
- -- Note: unsafe_get_data_ptr requires careful consideration to use safely!
252
- -- Here we are OK because ffi.string copies the data to a new Lua string,
253
- -- so we only hang on to the pointer long enough for the copy.
254
- local raw_pointer = pollnet.pollnet_unsafe_get_data_ptr(_ctx, self._socket)
255
- if raw_pointer == nil then
256
- error("Impossible situation: msg_size > 0 but null data pointer")
257
- end
258
- return ffi.string(raw_pointer, msg_size)
259
- else
260
- return ""
261
- end
262
- end
263
-
264
- function socket_mt:poll()
265
- self._last_message = nil
266
- if not self._socket then
267
- self._status = "invalid"
268
- return false, "invalid"
269
- end
270
- local res = POLLNET_RESULT_CODES[pollnet.pollnet_update(_ctx, self._socket)] or "error"
271
- self._status = res
272
- if res == "hasdata" then
273
- self._status = "open"
274
- self._last_message = self:_get_message()
275
- return true, self._last_message
276
- elseif res == "nodata" then
277
- self._status = "open"
278
- return true
279
- elseif res == "opening" then
280
- self._status = "opening"
281
- return true
282
- elseif res == "error" then
283
- self._status = "error"
284
- self._last_message = self:_get_message()
285
- return false, self._last_message
286
- elseif res == "closed" then
287
- self._status = "closed"
288
- self._last_message = "closed"
289
- return false, "closed"
290
- elseif res == "newclient" then
291
- self._status = "open"
292
- local client_addr = self:_get_message()
293
- local client_handle = pollnet.pollnet_get_connected_client_handle(_ctx, self._socket)
294
- assert(client_handle > 0)
295
- local client_sock = Socket():_from_handle(client_handle)
296
- client_sock.parent = self
297
- client_sock.remote_addr = client_addr
298
- if self._on_connection then
299
- self._on_connection(client_sock, client_addr)
300
- else
301
- print("Incoming connection but no :on_connection handler! Just closing it!")
302
- client_sock:close()
303
- end
304
- return true
305
- end
306
- end
307
-
308
- function socket_mt:await()
309
- local yield_count = 0
310
- while true do
311
- if self.timeout and (yield_count > self.timeout) then
312
- return false, "timeout"
313
- end
314
- local happy, msg = self:poll()
315
- if not happy then
316
- self:close()
317
- return false, "error: " .. tostring(msg)
318
- end
319
- if msg then return msg end
320
- yield_count = yield_count + 1
321
- coroutine.yield()
322
- end
323
- end
324
-
325
- function socket_mt:await_n(count)
326
- local parts = {}
327
- for idx = 1, count do
328
- local part, err = self:await()
329
- if not part then return false, err end
330
- parts[idx] = part
331
- end
332
- return parts
333
- end
334
-
335
- function socket_mt:last_message()
336
- return self._last_message
337
- end
338
-
339
- function socket_mt:status()
340
- return self._status
341
- end
342
-
343
- function socket_mt:send(msg)
344
- assert(self._socket)
345
- assert(type(msg) == 'string', "Argument to send must be a string")
346
- pollnet.pollnet_send(_ctx, self._socket, msg)
347
- end
348
-
349
- function socket_mt:send_binary(msg)
350
- assert(self._socket)
351
- assert(type(msg) == 'string', "Argument to send must be a string")
352
- pollnet.pollnet_send_binary(_ctx, self._socket, msg, #msg)
353
- end
354
-
355
- function socket_mt:close()
356
- if not self._socket then return end
357
- pollnet.pollnet_close(_ctx, self._socket)
358
- self._socket = nil
359
- end
360
-
361
- local function get_nanoid()
362
- local _id_scratch = ffi.new("int8_t[?]", 128)
363
- local msg_size = pollnet.pollnet_get_nanoid(_id_scratch, 128)
364
- return ffi.string(_id_scratch, msg_size)
365
- end
366
-
367
- local function sleep_ms(ms)
368
- pollnet.pollnet_sleep_ms(ms)
369
- end
370
-
371
- local reactor_mt = {}
372
- local function Reactor()
373
- local ret = setmetatable({}, {__index = reactor_mt})
374
- ret:init()
375
- return ret
376
- end
377
-
378
- function reactor_mt:init()
379
- self.threads = {}
380
- end
381
-
382
- function reactor_mt:run(thread_body)
383
- local thread = coroutine.create(function()
384
- thread_body(self)
385
- end)
386
- self.threads[thread] = true
387
- end
388
-
389
- function reactor_mt:run_server(server_sock, client_body)
390
- server_sock:on_connection(function(client_sock, addr)
391
- self:run(function()
392
- client_body(client_sock, addr)
393
- end)
394
- end)
395
- self:run(function()
396
- while true do server_sock:await() end
397
- end)
398
- end
399
-
400
- function reactor_mt:log(...)
401
- print(...)
402
- end
403
-
404
- function reactor_mt:update()
405
- local live_count = 0
406
- local cur_threads = self.threads
407
- self.threads = {}
408
- for thread, _ in pairs(cur_threads) do
409
- if coroutine.status(thread) == "dead" then
410
- cur_threads[thread] = nil
411
- else
412
- live_count = live_count + 1
413
- local happy, err = coroutine.resume(thread)
414
- if not happy then self:log("Error", err) end
415
- end
416
- end
417
- for thread, _ in pairs(self.threads) do
418
- live_count = live_count + 1
419
- cur_threads[thread] = true
420
- end
421
- self.threads = cur_threads
422
- return live_count
423
- end
424
-
425
- local function invoke_handler(handler, req, expose_errors)
426
- local happy, res = pcall(handler, req)
427
- if happy then
428
- return res
429
- else
430
- return {
431
- status = "500",
432
- body = (expose_errors and tostring(res)) or "Internal Error"
433
- }
434
- end
435
- end
436
-
437
- local function wrap_req_handler(handler, expose_errors)
438
- return function(req_sock, addr)
439
- while true do
440
- local raw_req = req_sock:await_n(3)
441
- if not raw_req then break end
442
- local method, path, query = parse_method(raw_req[1])
443
- local headers = parse_headers(raw_req[2])
444
- local reply = invoke_handler(handler, {
445
- addr = addr,
446
- method = method,
447
- path = path,
448
- query = query,
449
- headers = headers,
450
- body = raw_req[3],
451
- raw = raw_req
452
- }, expose_errors)
453
- req_sock:send(reply.status or "404")
454
- req_sock:send(format_headers(reply.headers or {}))
455
- req_sock:send_binary(reply.body or "")
456
- end
457
- req_sock:close()
458
- end
459
- end
460
-
461
- local exports = {
462
- VERSION = POLLNET_VERSION,
463
- init = init_ctx,
464
- init_hack_static = init_ctx_hack_static,
465
- shutdown = shutdown_ctx,
466
- Socket = Socket,
467
- Reactor = Reactor,
468
- pollnet = pollnet,
469
- nanoid = get_nanoid,
470
- sleep_ms = sleep_ms,
471
- format_headers = format_headers,
472
- parse_headers = parse_headers,
473
- parse_method = parse_method,
474
- wrap_req_handler = wrap_req_handler
475
- }
476
-
477
- local fnames = {
478
- "open_ws", "listen_ws", "open_tcp", "listen_tcp",
479
- "serve_http", "serve_dynamic_http", "http_get", "http_post"
480
- }
481
- for _, name in ipairs(fnames) do
482
- exports[name] = function(...)
483
- local sock = Socket()
484
- return sock[name](sock, ...)
485
- end
486
- end
487
-
488
- exports.default = exports
489
-
490
- return exports
1
+ local ____lualib = require("lualib_bundle")
2
+ local __TS__Class = ____lualib.__TS__Class
3
+ local Error = ____lualib.Error
4
+ local RangeError = ____lualib.RangeError
5
+ local ReferenceError = ____lualib.ReferenceError
6
+ local SyntaxError = ____lualib.SyntaxError
7
+ local TypeError = ____lualib.TypeError
8
+ local URIError = ____lualib.URIError
9
+ local __TS__New = ____lualib.__TS__New
10
+ local __TS__StringSplit = ____lualib.__TS__StringSplit
11
+ local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray
12
+ local ____exports = {}
13
+ local ____api = require("api")
14
+ local pollnet = ____api.default
15
+ ____exports.HttpResponse = __TS__Class()
16
+ local HttpResponse = ____exports.HttpResponse
17
+ HttpResponse.name = "HttpResponse"
18
+ function HttpResponse.prototype.____constructor(self, status, headers, body)
19
+ self.status = status
20
+ self.headers = headers
21
+ self.body = body
22
+ end
23
+ function HttpResponse.read(self, socket)
24
+ local msgs, ____error = socket:await_n(3)
25
+ if not msgs then
26
+ error(
27
+ __TS__New(Error, "socket error: " .. ____error),
28
+ 0
29
+ )
30
+ end
31
+ local statusStr, headersStr, respBody = unpack(msgs, 1, 3)
32
+ local status = tonumber((string.match(statusStr, "^%d+"))) or 500
33
+ return __TS__New(____exports.HttpResponse, status, headersStr, respBody)
34
+ end
35
+ function HttpResponse.prototype.parseHeaders(self)
36
+ local headers = {}
37
+ for ____, line in ipairs(__TS__StringSplit(self.headers, "\n")) do
38
+ do
39
+ local key, value = unpack(
40
+ __TS__StringSplit(line, ": ", 2),
41
+ 1,
42
+ 2
43
+ )
44
+ if not key or not value then
45
+ goto __continue6
46
+ end
47
+ local prev = headers[key]
48
+ if not prev then
49
+ headers[key] = value
50
+ goto __continue6
51
+ end
52
+ if __TS__ArrayIsArray(prev) then
53
+ prev[#prev + 1] = value
54
+ else
55
+ headers[key] = {prev, value}
56
+ end
57
+ end
58
+ ::__continue6::
59
+ end
60
+ return headers
61
+ end
62
+ --- Tiny async HTTP client built on top of a {@link Scheduler}.
63
+ --
64
+ -- Requests run in coroutines and resolve to an {@link HttpResponse}.
65
+ ____exports.HttpClient = __TS__Class()
66
+ local HttpClient = ____exports.HttpClient
67
+ HttpClient.name = "HttpClient"
68
+ function HttpClient.prototype.____constructor(self, scheduler)
69
+ self.scheduler = scheduler
70
+ end
71
+ function HttpClient.prototype.get(self, url, headers)
72
+ return self.scheduler:spawn(function() return ____exports.HttpResponse:read(pollnet.http_get(url, headers, false)) end)
73
+ end
74
+ function HttpClient.prototype.post(self, url, headers, body)
75
+ return self.scheduler:spawn(function() return ____exports.HttpResponse:read(pollnet.http_post(url, headers, body, false)) end)
76
+ end
77
+ --- Async WebSocket client built on top of an {@link AsyncReactor }.
78
+ -- Messages are delivered via the `onmessage` callback, and errors via
79
+ -- `onerror`.
80
+ -- Call {@link send} to send a message, and {@link close} to close the
81
+ -- connection and cleanup.
82
+ ____exports.WebSocket = __TS__Class()
83
+ local WebSocket = ____exports.WebSocket
84
+ WebSocket.name = "WebSocket"
85
+ function WebSocket.prototype.____constructor(self, scheduler, url)
86
+ self.timeout = 3600
87
+ self.onmessage = nil
88
+ self.onerror = nil
89
+ self.socket = pollnet.open_ws(url)
90
+ scheduler:spawn(function()
91
+ local timer = 0
92
+ while not not self.socket do
93
+ do
94
+ local ok = true
95
+ local msg
96
+ while true do
97
+ ok, msg = self.socket:poll()
98
+ if not ok or not msg then
99
+ break
100
+ end
101
+ timer = 0
102
+ local ____opt_0 = self.onmessage
103
+ if ____opt_0 ~= nil then
104
+ ____opt_0(msg)
105
+ end
106
+ if not self.socket then
107
+ return
108
+ end
109
+ end
110
+ local ____ok_2 = ok
111
+ if ____ok_2 then
112
+ timer = timer + 1
113
+ ____ok_2 = timer < self.timeout
114
+ end
115
+ if ____ok_2 then
116
+ coroutine.yield()
117
+ goto __continue19
118
+ end
119
+ timer = 0
120
+ self.socket:close()
121
+ local ____opt_3 = self.onerror
122
+ if ____opt_3 ~= nil then
123
+ ____opt_3(msg or "timeout")
124
+ end
125
+ do
126
+ local i = 0
127
+ while i < 60 do
128
+ coroutine.yield()
129
+ i = i + 1
130
+ end
131
+ end
132
+ if not not self.socket then
133
+ self.socket = pollnet.open_ws(url)
134
+ end
135
+ end
136
+ ::__continue19::
137
+ end
138
+ end)
139
+ end
140
+ function WebSocket.prototype.send(self, message)
141
+ local ____opt_5 = self.socket
142
+ if ____opt_5 ~= nil then
143
+ ____opt_5:send(message)
144
+ end
145
+ end
146
+ function WebSocket.prototype.sendBinary(self, data)
147
+ local ____opt_7 = self.socket
148
+ if ____opt_7 ~= nil then
149
+ ____opt_7:send_binary(data)
150
+ end
151
+ end
152
+ function WebSocket.prototype.close(self)
153
+ local ____opt_9 = self.socket
154
+ if ____opt_9 ~= nil then
155
+ ____opt_9:close()
156
+ end
157
+ self.socket = nil
158
+ end
159
+ ____exports.runServer = function(scheduler, serverSocket, clientBody)
160
+ serverSocket:on_connection(function(clientSocket, addr)
161
+ scheduler:spawn(function() return clientBody(clientSocket, addr) end):catch(function(____, e)
162
+ print((("Error in client handler for " .. addr) .. ": ") .. tostring(e))
163
+ clientSocket:close()
164
+ end)
165
+ end)
166
+ scheduler:spawn(function()
167
+ while (serverSocket:await()) do
168
+ end
169
+ end)
170
+ end
171
+ return ____exports