@naanlang/naan 1.0.16 → 1.2.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/LICENSE.md +2 -2
- package/README.md +13 -11
- package/bin/index.js +4 -4
- package/dist/naan.min.js +14 -14
- package/frameworks/browser/https_request.nlg +26 -10
- package/frameworks/browser/sworker.js +26 -25
- package/frameworks/browser/terminals.nlg +118 -55
- package/frameworks/browser/ws_client.nlg +124 -0
- package/frameworks/client/apiclient.nlg +82 -116
- package/frameworks/client/psm_client.nlg +37 -16
- package/frameworks/client/relaycon.nlg +308 -0
- package/frameworks/common/common.nlg +1 -1
- package/frameworks/common/utils.nlg +40 -2
- package/frameworks/node/apiserver.nlg +342 -103
- package/frameworks/node/https_request.nlg +32 -6
- package/frameworks/node/node.nlg +60 -2
- package/frameworks/node/psm_server.nlg +11 -7
- package/frameworks/node/worker.nlg +22 -9
- package/frameworks/node/ws_client.nlg +127 -0
- package/frameworks/project/build.nlg +3 -2
- package/frameworks/project/projects.nlg +8 -2
- package/frameworks/running/debugnub.nlg +2 -2
- package/frameworks/running/debugutil.nlg +1 -1
- package/frameworks/running/executors.nlg +69 -17
- package/frameworks/running/sourcecode.nlg +2 -2
- package/frameworks/running/taskexec.nlg +25 -25
- package/frameworks/storage/dbt_pouch.nlg +8 -6
- package/frameworks/storage/file_manager.nlg +6 -3
- package/frameworks/storage/psm.nlg +5 -3
- package/frameworks/storage/psm_dbtables.nlg +75 -53
- package/frameworks/storage/resources.nlg +4 -2
- package/lib/browser/env_web.js +6 -6
- package/lib/browser/env_webworker.js +1 -1
- package/lib/core/naanlib.js +14 -14
- package/lib/env_node.js +97 -8
- package/lib/env_nodeworker.js +22 -4
- package/package.json +1 -1
- package/plugins/serviceAws/aws/lambda_rest_index.js +5 -1
- package/plugins/serviceAws/aws_dynamo.nlg +91 -32
- package/plugins/serviceAws/dbt_aws.nlg +5 -5
- package/plugins/serviceAws/psm_aws.nlg +2 -2
- package/test/harness.nlg +1 -1
- package/test/test_01_core.nlg +30 -4
- package/test/test_02_context.nlg +2 -2
- package/test/test_07_lingo.nlg +3 -0
|
@@ -6,49 +6,107 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
/*
|
|
15
|
-
*
|
|
15
|
+
* APIServer
|
|
16
16
|
*
|
|
17
17
|
* Make an API server object.
|
|
18
18
|
*
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
closure
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
else
|
|
25
|
-
options = new(options)
|
|
26
|
-
if !options.relpath
|
|
27
|
-
options.relpath = ""
|
|
28
|
-
|
|
29
|
-
chroot = JSpath.join(js.d, options.relpath)
|
|
30
|
-
if not string(chroot) || chroot.length < 2 {
|
|
31
|
-
debuglog("APIserver: invalid path", chroot)
|
|
32
|
-
return (false)
|
|
33
|
-
}
|
|
34
|
-
|
|
21
|
+
closure APIServer(options, local server, chroot) {
|
|
22
|
+
global(JSpath, js, bodyParser, express, psmServerMod, workerMod)
|
|
23
|
+
apisLoadLibs()
|
|
35
24
|
server = new(object, this)
|
|
36
|
-
server.psm = require("./psm_server.nlg").MakeServerPSM(server)
|
|
37
|
-
server.root = chroot
|
|
38
25
|
server.app = express()
|
|
39
26
|
server.guid = options.guid
|
|
40
27
|
server.notifyq = []
|
|
41
28
|
server.responseq = []
|
|
42
|
-
|
|
29
|
+
options = merge({
|
|
30
|
+
relpath: ""
|
|
31
|
+
name: "NaanServer"
|
|
32
|
+
}, options)
|
|
33
|
+
chroot = JSpath.join(js.d, options.relpath)
|
|
34
|
+
server.root = chroot
|
|
35
|
+
|
|
36
|
+
// updatePort
|
|
37
|
+
// Update our state based on the specified port number.
|
|
38
|
+
function updatePort(port) {
|
|
39
|
+
server.host = "localhost:${tostring(port)}"
|
|
40
|
+
server.oururl = "http://${server.host}/nide.html"
|
|
41
|
+
server.port = port
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// open
|
|
45
|
+
//
|
|
46
|
+
// Open the server, returning a result tuple. A fixed port may be specified.
|
|
47
|
+
//
|
|
48
|
+
server.open = closure open(port, local error, pending, result, hostfs) {
|
|
49
|
+
if server.http
|
|
50
|
+
return (list(false, { open: true })) // already open
|
|
51
|
+
if !string(chroot) || chroot.length < 2 {
|
|
52
|
+
error = Error("APIserver: invalid path", chroot) // invalid option
|
|
53
|
+
return (list(error))
|
|
54
|
+
}
|
|
55
|
+
if port
|
|
56
|
+
options.fixedPort = port // override fixed port from argument
|
|
57
|
+
else if options.fixedPort
|
|
58
|
+
port = options.fixedPort // use fixed port option
|
|
59
|
+
else
|
|
60
|
+
port = options.basePort // search for port from base
|
|
61
|
+
if options.fixedPort
|
|
62
|
+
updatePort(port) // need this to access in-use server
|
|
63
|
+
pending = new(nonce)
|
|
64
|
+
while !result {
|
|
65
|
+
server.http = server.app.listen(port, function() {
|
|
66
|
+
pending.signal(list(false, port)) // success
|
|
67
|
+
}).on("error", function (err) {
|
|
68
|
+
if err.code == "EADDRINUSE" && !options.fixedPort {
|
|
69
|
+
++port // try next port
|
|
70
|
+
pending.signal(false)
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
pending.signal(list(Error("server listen error", err, { code: err.code })))
|
|
74
|
+
})
|
|
75
|
+
result = pending.wait()
|
|
76
|
+
pending.reset()
|
|
77
|
+
}
|
|
78
|
+
if result.0 { // open failed
|
|
79
|
+
server.http = false
|
|
80
|
+
return (result)
|
|
81
|
+
}
|
|
82
|
+
updatePort(port)
|
|
83
|
+
if server.guid
|
|
84
|
+
server.oururl = server.oururl.concat("?guid=", server.guid)
|
|
85
|
+
App.api = server // used remotely by client/relaycon.nlg
|
|
86
|
+
server.ws = WebSocketServer(server)
|
|
87
|
+
WesRelay(server) // registers as subsystem
|
|
88
|
+
server.psm = psmServerMod.MakeServerPSM(server, options.name)
|
|
89
|
+
`(error, hostfs) = server.psm.conns.NodeFS.connect("Host", "NideFS", list(""))
|
|
90
|
+
if error {
|
|
91
|
+
error = Error("APIServer: can't access HostFS", error)
|
|
92
|
+
ErrorDebuglog(error)
|
|
93
|
+
return (list(error))
|
|
94
|
+
}
|
|
95
|
+
server.hostfs = hostfs
|
|
96
|
+
workerMod.WorkerController(server)
|
|
97
|
+
list(false, port)
|
|
98
|
+
}
|
|
43
99
|
|
|
44
100
|
// close
|
|
45
101
|
//
|
|
46
102
|
// Close the server.
|
|
47
|
-
|
|
103
|
+
//
|
|
48
104
|
server.close = function close() {
|
|
49
105
|
if server.ws
|
|
50
106
|
server.ws.close()
|
|
51
107
|
server.http.close(function(){debuglog("API Server closed")})
|
|
108
|
+
server.ws = false
|
|
109
|
+
server.http = false
|
|
52
110
|
}
|
|
53
111
|
|
|
54
112
|
// Register
|
|
@@ -57,10 +115,18 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
57
115
|
// have the wsReceive method invoked:
|
|
58
116
|
// wsReceive(message, conn)
|
|
59
117
|
// And can send a reply on the specified connection.
|
|
60
|
-
|
|
118
|
+
//
|
|
61
119
|
server.Register = function Register(serverID, subsystem) {
|
|
62
120
|
server.ws.subsystems[serverID] = subsystem
|
|
63
121
|
}
|
|
122
|
+
|
|
123
|
+
// Relays
|
|
124
|
+
//
|
|
125
|
+
// Report our relay destIDs.
|
|
126
|
+
//
|
|
127
|
+
server.Relays = function Relays() {
|
|
128
|
+
server.ws.subsystems.Relay.destList()
|
|
129
|
+
}
|
|
64
130
|
|
|
65
131
|
//
|
|
66
132
|
// status and message notification
|
|
@@ -182,7 +248,7 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
182
248
|
if responder.active {
|
|
183
249
|
queued = server.notifyq.filter(function(item,index,array) { item.stamp > after })
|
|
184
250
|
if queued.length > 0
|
|
185
|
-
res.send(
|
|
251
|
+
res.send(JSONstringify(queued))
|
|
186
252
|
else if timeout
|
|
187
253
|
res.status(200).end()
|
|
188
254
|
else
|
|
@@ -209,47 +275,8 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
209
275
|
server.hostfs.api(req, res)
|
|
210
276
|
})
|
|
211
277
|
|
|
212
|
-
//
|
|
213
|
-
// initialize the server
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
let(local pending, result) {
|
|
217
|
-
pending = new(nonce)
|
|
218
|
-
|
|
219
|
-
// find our port
|
|
220
|
-
|
|
221
|
-
loop {
|
|
222
|
-
server.http = server.app.listen(port, function(){
|
|
223
|
-
pending.signal({}) // success
|
|
224
|
-
}).on("error", function (err) {
|
|
225
|
-
if err.code == "EADDRINUSE"
|
|
226
|
-
++port // try next port
|
|
227
|
-
else {
|
|
228
|
-
pending.result = Error("server listen error", err) // permanent failure
|
|
229
|
-
callback(err, false)
|
|
230
|
-
}
|
|
231
|
-
pending.signal(pending.result)
|
|
232
|
-
})
|
|
233
|
-
if pending.wait()
|
|
234
|
-
break
|
|
235
|
-
pending.reset()
|
|
236
|
-
}
|
|
237
|
-
if pending.result
|
|
238
|
-
return (false) // failed to open
|
|
239
|
-
|
|
240
|
-
// start the services
|
|
241
|
-
|
|
242
|
-
server.ws = WebSocketServer(server)
|
|
243
|
-
server.port = port
|
|
244
|
-
callback(false, port, server) // notify of success
|
|
245
|
-
result = server.psm.conns.NodeFS.connect("Host", "NideFS", list("")) // ### this should go through resource tracker
|
|
246
|
-
if result.0
|
|
247
|
-
ErrorDebuglog(Error("can't access HostFS", result.0))
|
|
248
|
-
else
|
|
249
|
-
server.hostfs = result.1
|
|
250
|
-
}()
|
|
278
|
+
// finis
|
|
251
279
|
|
|
252
|
-
// return new API server object
|
|
253
280
|
server
|
|
254
281
|
};
|
|
255
282
|
|
|
@@ -262,6 +289,7 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
262
289
|
*/
|
|
263
290
|
|
|
264
291
|
closure WebSocketServer(server, local websocks) {
|
|
292
|
+
global(ws)
|
|
265
293
|
websocks = new(object, this)
|
|
266
294
|
websocks.conns = [] // our connections
|
|
267
295
|
websocks.subsystems = { } // registered subsystems
|
|
@@ -277,26 +305,31 @@ closure WebSocketServer(server, local websocks) {
|
|
|
277
305
|
|
|
278
306
|
// Event handlers
|
|
279
307
|
|
|
280
|
-
websocks.wss.on("connection", function(socket, request) {
|
|
281
|
-
|
|
308
|
+
websocks.wss.on("connection", function(socket, request, local conn, serverID) {
|
|
309
|
+
conn = WesCon(server, websocks, socket, request)
|
|
310
|
+
websocks.conns.push(conn)
|
|
311
|
+
for serverID in websocks.subsystems
|
|
312
|
+
websocks.subsystems[serverID].wsOpen(conn) // notify of our openings
|
|
282
313
|
})
|
|
283
|
-
|
|
314
|
+
|
|
284
315
|
server.wss.on("error", function(error) {
|
|
285
|
-
ErrorDebuglog(Error("
|
|
316
|
+
ErrorDebuglog(Error("WebSocketServer: connection error", error))
|
|
286
317
|
})
|
|
287
318
|
|
|
288
319
|
server.wss.on("close", function() {
|
|
289
320
|
debuglog("WebSocketServer has closed")
|
|
290
321
|
websocks.closed = true // note that we are closed
|
|
291
322
|
})
|
|
292
|
-
|
|
323
|
+
|
|
293
324
|
// closeConn
|
|
294
325
|
// respond to connection closing
|
|
295
|
-
|
|
296
|
-
websocks.closeConn = function closeConn(conn) {
|
|
326
|
+
|
|
327
|
+
websocks.closeConn = function closeConn(conn, local serverID) {
|
|
297
328
|
websocks.conns = websocks.conns.filter(function(item) {
|
|
298
|
-
|
|
329
|
+
item !== conn
|
|
299
330
|
})
|
|
331
|
+
for serverID in websocks.subsystems
|
|
332
|
+
websocks.subsystems[serverID].wsClose(conn) // notify of our closures
|
|
300
333
|
}
|
|
301
334
|
|
|
302
335
|
// finis
|
|
@@ -318,23 +351,34 @@ closure WebSocketServer(server, local websocks) {
|
|
|
318
351
|
*/
|
|
319
352
|
|
|
320
353
|
closure WesCon(server, websocks, socket, request, local wescon) {
|
|
354
|
+
global()
|
|
321
355
|
wescon = new(object, this)
|
|
322
356
|
wescon.socket = socket
|
|
323
|
-
wescon.
|
|
357
|
+
wescon.info = {
|
|
358
|
+
destID: UUID()
|
|
359
|
+
address: new(socket._socket.address())
|
|
360
|
+
}
|
|
324
361
|
|
|
325
362
|
// on message
|
|
326
363
|
//
|
|
327
364
|
// Incoming data.
|
|
328
|
-
|
|
329
|
-
socket.on("message",
|
|
365
|
+
//
|
|
366
|
+
socket.on("message", closure onmessage(text, local message, error) {
|
|
330
367
|
try {
|
|
331
368
|
message = new(js.g.JSON.parse(text))
|
|
332
369
|
if server.guid && message.guid != server.guid
|
|
333
370
|
error = Error("unauthorized")
|
|
334
|
-
else if message.
|
|
335
|
-
|
|
336
|
-
|
|
371
|
+
else if message.init {
|
|
372
|
+
if message.init.destID {
|
|
373
|
+
wescon.relay = true
|
|
374
|
+
websocks.subsystems.Relay.wsChangeDestID(wescon, wescon.info.destID, message.init.destID)
|
|
375
|
+
}
|
|
376
|
+
wescon.info = merge(wescon.info, message.init)
|
|
337
377
|
}
|
|
378
|
+
else if message.respOp && websocks.subsystems.Relay
|
|
379
|
+
websocks.subsystems.Relay.wsReceive(message, wescon) // with respOp, must be a relay
|
|
380
|
+
else if message.clientID && websocks.subsystems[message.serverID]
|
|
381
|
+
websocks.subsystems[message.serverID].wsReceive(message, wescon)
|
|
338
382
|
else
|
|
339
383
|
error = Error("can't route message")
|
|
340
384
|
} catch {
|
|
@@ -342,43 +386,41 @@ closure WesCon(server, websocks, socket, request, local wescon) {
|
|
|
342
386
|
error = Error("can't decode message JSON", exception)
|
|
343
387
|
}
|
|
344
388
|
if error
|
|
345
|
-
socket.send(
|
|
389
|
+
socket.send(JSONstringify([error]))
|
|
346
390
|
})
|
|
347
391
|
|
|
348
392
|
// on error
|
|
349
393
|
//
|
|
350
394
|
// Error has occurred on this connection.
|
|
351
|
-
|
|
352
|
-
socket.on("error",
|
|
353
|
-
debuglog("connection error", error)
|
|
395
|
+
//
|
|
396
|
+
socket.on("error", closure onerror(error) {
|
|
397
|
+
debuglog("Wescon: connection error", error)
|
|
354
398
|
})
|
|
355
399
|
|
|
356
400
|
// on close
|
|
357
401
|
//
|
|
358
402
|
// Connection has closed.
|
|
359
|
-
|
|
360
|
-
socket.on("close",
|
|
361
|
-
if code != 1000 && code != 1001 && code != 1005
|
|
362
|
-
debuglog("connection has closed:", code, reason)
|
|
363
|
-
|
|
364
|
-
wecson.closed = {
|
|
403
|
+
//
|
|
404
|
+
socket.on("close", closure onclose(code, reason) {
|
|
405
|
+
if code != 1000 && code != 1001 && code != 1005 && code != 1006
|
|
406
|
+
debuglog("Wescon: connection has closed:", code, reason, JSONstringify(wescon.info))
|
|
407
|
+
wescon.closed = {
|
|
365
408
|
code: code
|
|
366
409
|
reason: reason
|
|
367
410
|
}
|
|
368
|
-
|
|
369
|
-
websocks.subsystems[message.serverID].wsClose(wescon) // notify of our closures
|
|
411
|
+
websocks.closeConn(wescon)
|
|
370
412
|
})
|
|
371
413
|
|
|
372
414
|
// send
|
|
373
415
|
//
|
|
374
416
|
// Send a dictionary, which must have a clientID defined.
|
|
375
|
-
|
|
417
|
+
//
|
|
376
418
|
wescon.send = function send(message) {
|
|
377
419
|
if !message.clientID || !message.serverID
|
|
378
420
|
throw("Wescon.send: client and server IDs required")
|
|
379
421
|
if wescon.closed
|
|
380
422
|
list(Error("Socket connection closed", wescon.closed))
|
|
381
|
-
socket.send(
|
|
423
|
+
socket.send(JSONstringify(message)),
|
|
382
424
|
[false, {ok: true}]
|
|
383
425
|
}
|
|
384
426
|
|
|
@@ -387,6 +429,201 @@ closure WesCon(server, websocks, socket, request, local wescon) {
|
|
|
387
429
|
};
|
|
388
430
|
|
|
389
431
|
|
|
432
|
+
/*
|
|
433
|
+
* WesRelay
|
|
434
|
+
*
|
|
435
|
+
* Web socket relay controller. This allows apiserver clients to discover each other and connect
|
|
436
|
+
* for remote evaluation, etc. A source connection makes a request of a destination connection, and
|
|
437
|
+
* the response is routed back to the original connection.
|
|
438
|
+
*
|
|
439
|
+
* This uses the following message fields for the requesting source:
|
|
440
|
+
* guid: security token
|
|
441
|
+
* clientID: defined by the source, this string defines where replies should be sent
|
|
442
|
+
* serverID: "Relay"
|
|
443
|
+
* destID: the destination connection
|
|
444
|
+
* endServerID: the serverID to use at the destination
|
|
445
|
+
* The message is modified with a virtual clientID to send to the destination, and then changed back
|
|
446
|
+
* for return to the requester.
|
|
447
|
+
*
|
|
448
|
+
* To discover the available destination connections a source should use:
|
|
449
|
+
* clientID: requester
|
|
450
|
+
* serverID: "Relay"
|
|
451
|
+
* relayOp: "getdests"
|
|
452
|
+
* which returns:
|
|
453
|
+
* relayOp: "destlist"
|
|
454
|
+
* destinations: [
|
|
455
|
+
* destID: guid for the connection
|
|
456
|
+
* address: socket port / family / ip address
|
|
457
|
+
* name: <string>
|
|
458
|
+
* workerID: id of destination worker
|
|
459
|
+
* ]
|
|
460
|
+
* New and closed destinations are sent asynchronously to each source:
|
|
461
|
+
* relayOp: "opened" / "closed"
|
|
462
|
+
* info: {
|
|
463
|
+
* destID: guid for the connection
|
|
464
|
+
* address: socket port / family / ip address
|
|
465
|
+
* name: <string>
|
|
466
|
+
* workerID: id of destination worker
|
|
467
|
+
* }
|
|
468
|
+
*
|
|
469
|
+
*/
|
|
470
|
+
|
|
471
|
+
closure WesRelay(server, local welay) {
|
|
472
|
+
global()
|
|
473
|
+
welay = new(object, this)
|
|
474
|
+
welay.serverID = "Relay" // our serverID as a subsystem
|
|
475
|
+
welay.destIDs = { } // destination conns index
|
|
476
|
+
welay.virtualIDs = { } // virtual clientID index
|
|
477
|
+
welay.links = new(weakmap) // source and dest connections
|
|
478
|
+
server.Register(welay.serverID, welay)
|
|
479
|
+
|
|
480
|
+
// getVirtualID
|
|
481
|
+
// Return the virtual clientID for a requesting source/clientID and destination.
|
|
482
|
+
function getVirtualID(sconn, clientID, dconn, local sdict, ddict, vid) {
|
|
483
|
+
sdict = welay.links[sconn]
|
|
484
|
+
if !sdict
|
|
485
|
+
welay.links[sconn] = sdict = {
|
|
486
|
+
virtualIDs: { }
|
|
487
|
+
}
|
|
488
|
+
ddict = welay.links[dconn]
|
|
489
|
+
if !ddict
|
|
490
|
+
welay.links[dconn] = ddict = {
|
|
491
|
+
virtualIDs: { }
|
|
492
|
+
}
|
|
493
|
+
vid = ddict.virtualIDs[clientID]
|
|
494
|
+
if !vid {
|
|
495
|
+
sdict.virtualIDs[clientID] = vid = UUID()
|
|
496
|
+
welay.virtualIDs[vid] = {
|
|
497
|
+
sconn: sconn
|
|
498
|
+
dconn: dconn
|
|
499
|
+
clientID: clientID
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
vid
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// addConnection
|
|
506
|
+
// Record a new connection as a possible destination.
|
|
507
|
+
function addConnection(conn) {
|
|
508
|
+
welay.destIDs[conn.info.destID] = conn
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// removeConnection
|
|
512
|
+
// Cleanup for a closed connection, be it source or destination or both.
|
|
513
|
+
function removeConnection(conn, local vid, cdict, did, dconn) {
|
|
514
|
+
welay.links[conn] = undefined
|
|
515
|
+
for `(vid, cdict) in welay.virtualIDs
|
|
516
|
+
if cdict.sconn === conn || cdict.dconn === conn
|
|
517
|
+
welay.virtualIDs[vid] = undefined
|
|
518
|
+
for `(did, dconn) in welay.destIDs
|
|
519
|
+
if dconn === conn
|
|
520
|
+
welay.destIDs[did] = undefined
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// destList
|
|
524
|
+
// Make an array of destination connections, excluding the specified one.
|
|
525
|
+
//
|
|
526
|
+
welay.destList = function destList(conn, local output, did, dconn) {
|
|
527
|
+
output = []
|
|
528
|
+
for `(did, dconn) in welay.destIDs
|
|
529
|
+
if dconn !== conn && dconn.relay
|
|
530
|
+
output.push(dconn.info)
|
|
531
|
+
output
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// wsReceive
|
|
535
|
+
//
|
|
536
|
+
// Process a message from the websocket API.
|
|
537
|
+
//
|
|
538
|
+
welay.wsReceive = function wsReceive(message, sconn, local dconn, error, cdict) {
|
|
539
|
+
if message.relayOp == "getdests" { // get destination list
|
|
540
|
+
message.relayOp = "destlist"
|
|
541
|
+
message.destinations = destList(sconn)
|
|
542
|
+
sconn.send(message)
|
|
543
|
+
return
|
|
544
|
+
}
|
|
545
|
+
else if message.destID && message.endServerID { // request from a source
|
|
546
|
+
dconn = welay.destIDs[message.destID]
|
|
547
|
+
if !dconn
|
|
548
|
+
error = Error("destID or endServerID invalid", message.relayOp)
|
|
549
|
+
else {
|
|
550
|
+
message.serverID = message.endServerID
|
|
551
|
+
message.clientID = getVirtualID(sconn, message.clientID, dconn)
|
|
552
|
+
message.endServerID = undefined
|
|
553
|
+
message.destID = undefined
|
|
554
|
+
dconn.send(message)
|
|
555
|
+
return
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else { // response from a destination
|
|
559
|
+
cdict = welay.virtualIDs[message.clientID]
|
|
560
|
+
dconn = cdict.sconn // this is original source
|
|
561
|
+
if !cdict || !dconn
|
|
562
|
+
error = Error("cannot route reply", message.relayOp)
|
|
563
|
+
else {
|
|
564
|
+
message.endServerID = message.serverID
|
|
565
|
+
message.serverID = welay.serverID
|
|
566
|
+
message.clientID = cdict.clientID
|
|
567
|
+
message.destID = dconn.info.destID
|
|
568
|
+
dconn.send(message)
|
|
569
|
+
return
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
sconn.send({
|
|
573
|
+
serverID: message.serverID
|
|
574
|
+
clientID: message.clientID
|
|
575
|
+
respOp: "error"
|
|
576
|
+
payload: [error]
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// wsOpen
|
|
581
|
+
//
|
|
582
|
+
// Note that a new connection was opened.
|
|
583
|
+
//
|
|
584
|
+
welay.wsOpen = function wsOpen(conn) {
|
|
585
|
+
addConnection(conn)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// wsClose
|
|
589
|
+
//
|
|
590
|
+
// Note that an existing connection was closed.
|
|
591
|
+
//
|
|
592
|
+
welay.wsClose = function wsClose(conn, local destID, sconn) {
|
|
593
|
+
removeConnection(conn)
|
|
594
|
+
if conn.relay
|
|
595
|
+
for `(destID, sconn) in welay.destIDs
|
|
596
|
+
sconn.send({
|
|
597
|
+
serverID: welay.serverID
|
|
598
|
+
clientID: "*"
|
|
599
|
+
relayOp: "closed"
|
|
600
|
+
info: conn.info
|
|
601
|
+
})
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// wsChangeDestID
|
|
605
|
+
//
|
|
606
|
+
// The destID has changed (via the client init message.)
|
|
607
|
+
//
|
|
608
|
+
welay.wsChangeDestID = function wsChangeDestID(conn, prevID, newID, local destID, sconn) {
|
|
609
|
+
welay.destIDs[prevID] = undefined
|
|
610
|
+
welay.destIDs[newID] = conn
|
|
611
|
+
conn.info.destID = newID
|
|
612
|
+
for `(destID, sconn) in welay.destIDs
|
|
613
|
+
if destID != newID
|
|
614
|
+
sconn.send({
|
|
615
|
+
serverID: welay.serverID
|
|
616
|
+
clientID: "*"
|
|
617
|
+
relayOp: "opened"
|
|
618
|
+
info: conn.info
|
|
619
|
+
})
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// finis
|
|
623
|
+
welay
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
|
|
390
627
|
/*
|
|
391
628
|
* ApisInit
|
|
392
629
|
*
|
|
@@ -394,22 +631,24 @@ closure WesCon(server, websocks, socket, request, local wescon) {
|
|
|
394
631
|
*
|
|
395
632
|
*/
|
|
396
633
|
|
|
397
|
-
function
|
|
634
|
+
function apisLoadLibs() {
|
|
635
|
+
if typeof(express) === xobject
|
|
636
|
+
return
|
|
637
|
+
express = js.r("express")
|
|
638
|
+
bodyParser = js.r("body-parser")
|
|
639
|
+
textParser = bodyParser.text()
|
|
640
|
+
ws = js.r("ws")
|
|
641
|
+
};
|
|
398
642
|
|
|
399
|
-
|
|
643
|
+
function ApisInit(local manifest) {
|
|
644
|
+
manifest = `(APIServer, WebSocketServer, WesCon, WesRelay, apisLoadLibs, ApisInit)
|
|
400
645
|
|
|
401
|
-
Naan.module.build(module.id, "
|
|
646
|
+
Naan.module.build(module.id, "apiserver", function(modobj, compobj) {
|
|
402
647
|
require("./node.nlg")
|
|
403
648
|
compobj.manifest = manifest
|
|
404
|
-
modobj.exports.
|
|
405
|
-
|
|
406
|
-
express = js.r("express")
|
|
407
|
-
bodyParser = js.r("body-parser")
|
|
408
|
-
textParser = bodyParser.text()
|
|
409
|
-
ws = js.r("ws")
|
|
410
|
-
|
|
411
|
-
require("./filesystem.nlg")
|
|
412
|
-
require("./psm_server.nlg")
|
|
413
|
-
require("./worker.nlg")
|
|
649
|
+
modobj.exports.APIServer = APIServer
|
|
414
650
|
})
|
|
651
|
+
require("./filesystem.nlg")
|
|
652
|
+
psmServerMod = require("./psm_server.nlg")
|
|
653
|
+
workerMod = require("./worker.nlg")
|
|
415
654
|
}();
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2021-
|
|
8
|
+
* Copyright (c) 2021-2024 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
/*
|
|
14
|
-
*
|
|
14
|
+
* httpsApiReqOnce
|
|
15
15
|
*
|
|
16
|
-
* Perform an API request from
|
|
16
|
+
* Perform an API request from NodeJS. This operates the same as HttpsApiRequest for browsers.
|
|
17
17
|
*
|
|
18
18
|
* Options:
|
|
19
19
|
* {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
*
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
closure
|
|
48
|
+
closure httpsApiReqOnce(url, options,
|
|
49
49
|
local urlq, reqOptions, putdata, item, xurl, pending, chunks, request) {
|
|
50
50
|
if options.query
|
|
51
51
|
urlq = url.concat(EncodeQuery("?", options.query))
|
|
@@ -105,7 +105,7 @@ closure HttpsApiRequest(url, options,
|
|
|
105
105
|
})
|
|
106
106
|
response.on("end", function (local content, error, headers) {
|
|
107
107
|
content = js.g.Buffer.concat(chunks)
|
|
108
|
-
if response.statusCode < 200 || response.statusCode >=
|
|
108
|
+
if response.statusCode < 200 || response.statusCode >= 299 { // 299 is "cancelled"
|
|
109
109
|
if options.debug
|
|
110
110
|
debuglog("HttpsApiRequest status:", url, response.statusCode, "message:", content)
|
|
111
111
|
pending.signal(list(Error("HttpsApiRequest statusCode:", url, response.statusCode, {
|
|
@@ -132,6 +132,7 @@ closure HttpsApiRequest(url, options,
|
|
|
132
132
|
pending.signal(list(error, content, {
|
|
133
133
|
headers: response.headers
|
|
134
134
|
status: response.statusCode
|
|
135
|
+
contentType: response.headers["content-type"]
|
|
135
136
|
}))
|
|
136
137
|
})
|
|
137
138
|
})
|
|
@@ -148,6 +149,31 @@ closure HttpsApiRequest(url, options,
|
|
|
148
149
|
};
|
|
149
150
|
|
|
150
151
|
|
|
152
|
+
/*
|
|
153
|
+
* HttpsApiRequest
|
|
154
|
+
*
|
|
155
|
+
* Make the request with optional retry logic.
|
|
156
|
+
*
|
|
157
|
+
* Options:
|
|
158
|
+
* {
|
|
159
|
+
* retrier: function(error) { error.status == 502 } // retry 502 errors
|
|
160
|
+
* }
|
|
161
|
+
*
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
closure HttpsApiRequest(url, options, local result) {
|
|
165
|
+
loop {
|
|
166
|
+
result = httpsApiReqOnce(url, options)
|
|
167
|
+
if result.0 && result.0.status { // check for retries
|
|
168
|
+
if !options.retrier(result.0)
|
|
169
|
+
break
|
|
170
|
+
} else
|
|
171
|
+
break
|
|
172
|
+
}
|
|
173
|
+
result
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
|
|
151
177
|
/*
|
|
152
178
|
* https_nodeInit
|
|
153
179
|
*
|
|
@@ -156,7 +182,7 @@ closure HttpsApiRequest(url, options,
|
|
|
156
182
|
*/
|
|
157
183
|
|
|
158
184
|
function https_nodeInit(local manifest) {
|
|
159
|
-
manifest = `(HttpsApiRequest, https_nodeInit)
|
|
185
|
+
manifest = `(httpsApiReqOnce, HttpsApiRequest, https_nodeInit)
|
|
160
186
|
|
|
161
187
|
Naan.module.build(module.id, "https_request", function(modobj, compobj) {
|
|
162
188
|
require("./node.nlg")
|