geonix 1.12.2 → 1.20.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/eslint.config.js +25 -0
- package/examples/ServeStatic/index.js +5 -5
- package/examples/ServeStatic/package.json +14 -14
- package/examples/SimpleService/index.js +8 -8
- package/examples/SimpleService/package.json +14 -14
- package/exports.js +14 -43
- package/index.d.ts +20 -20
- package/package.json +10 -6
- package/src/Connection.js +78 -55
- package/src/Gateway.js +214 -198
- package/src/Registry.js +38 -32
- package/src/Remote.js +3 -3
- package/src/Request.js +66 -52
- package/src/RequestOptions.js +4 -4
- package/src/Service.js +128 -121
- package/src/Stream.js +48 -139
- package/src/Util.js +85 -124
- package/src/WebServer.js +68 -65
- package/test/gateway.js +15 -0
- package/test/package.json +16 -0
- package/test/stream.js +38 -0
- package/tsconfig.json +10 -10
- package/src/status/deps/babel.development.js +0 -135978
- package/src/status/deps/babel.min.js +0 -17
- package/src/status/deps/react-dom.development.js +0 -29869
- package/src/status/deps/react-dom.production.min.js +0 -267
- package/src/status/deps/react.development.js +0 -3342
- package/src/status/deps/react.production.min.js +0 -31
- package/src/status/index.html +0 -13
- package/src/status/main.js +0 -30
package/src/Gateway.js
CHANGED
|
@@ -1,236 +1,258 @@
|
|
|
1
|
-
import { connection } from
|
|
2
|
-
import { registry } from
|
|
3
|
-
import { createTCPServer, GeonixVersion, picoid, proxyHttp, sleep } from
|
|
4
|
-
import express, { Router } from
|
|
5
|
-
import { Request } from
|
|
6
|
-
import { HEALTH_CHECK_ENDPOINT } from
|
|
7
|
-
import expressWs from
|
|
8
|
-
import querystring from
|
|
9
|
-
import semver from
|
|
10
|
-
import { WebSocket } from
|
|
11
|
-
|
|
12
|
-
const raw = express.raw({ limit:
|
|
13
|
-
|
|
14
|
-
const DEBUG_ENDPOINT =
|
|
15
|
-
const endpointMatcher = /^((?<options>.+)\|)?(?<verb>WS|GET|POST|PATCH|PUT|DELETE|HEAD|OPTIONS|ALL)\s(?<url>.*)
|
|
1
|
+
import { connection } from "./Connection.js";
|
|
2
|
+
import { registry } from "./Registry.js";
|
|
3
|
+
import { createTCPServer, GeonixVersion, picoid, proxyHttp, sleep } from "./Util.js";
|
|
4
|
+
import express, { Router } from "express";
|
|
5
|
+
import { Request } from "./Request.js";
|
|
6
|
+
import { HEALTH_CHECK_ENDPOINT } from "./WebServer.js";
|
|
7
|
+
import expressWs from "express-ws";
|
|
8
|
+
import querystring from "querystring";
|
|
9
|
+
import semver from "semver";
|
|
10
|
+
import { WebSocket } from "ws";
|
|
11
|
+
|
|
12
|
+
const raw = express.raw({ limit: "100mb" });
|
|
13
|
+
|
|
14
|
+
const DEBUG_ENDPOINT = "/lZ6jD2eC3iP0zB3jJ1yJ9pM8gG3yI3vS";
|
|
15
|
+
const endpointMatcher = /^((?<options>.+)\|)?(?<verb>WS|GET|POST|PATCH|PUT|DELETE|HEAD|OPTIONS|ALL)\s(?<url>.*)/;
|
|
16
16
|
|
|
17
17
|
const logger = (req, res, next) => {
|
|
18
|
-
console.info(`HTTP ${req.method} ${req.url}`)
|
|
18
|
+
console.info(`HTTP ${req.method} ${req.url}`);
|
|
19
19
|
|
|
20
|
-
next()
|
|
21
|
-
}
|
|
20
|
+
next();
|
|
21
|
+
};
|
|
22
22
|
|
|
23
23
|
const stats = {
|
|
24
24
|
requests: 0,
|
|
25
25
|
proxied: 0,
|
|
26
26
|
proxied_over_nats: 0,
|
|
27
27
|
debug_requests: 0
|
|
28
|
-
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const defaultOpts = {
|
|
31
|
+
beforeRequest: (_req, _res) => { },
|
|
32
|
+
afterRequest: (_req, _res) => { }
|
|
33
|
+
};
|
|
29
34
|
|
|
30
35
|
export class Gateway {
|
|
31
36
|
|
|
32
|
-
static start() {
|
|
33
|
-
return new Gateway()
|
|
37
|
+
static start(opts) {
|
|
38
|
+
return new Gateway(opts);
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#port
|
|
41
|
+
#opts = defaultOpts;
|
|
42
|
+
#api = express();
|
|
43
|
+
#router = (req, res, next) => next();
|
|
44
|
+
#port;
|
|
40
45
|
|
|
41
|
-
#rebuildRouter = false
|
|
42
|
-
#buildRouterRunning = false
|
|
43
|
-
#endpoints = []
|
|
46
|
+
#rebuildRouter = false;
|
|
47
|
+
#buildRouterRunning = false;
|
|
48
|
+
#endpoints = [];
|
|
44
49
|
|
|
45
|
-
#registry = {}
|
|
50
|
+
#registry = {};
|
|
46
51
|
|
|
47
|
-
constructor() {
|
|
48
|
-
expressWs(this.#api)
|
|
52
|
+
constructor(opts) {
|
|
53
|
+
expressWs(this.#api);
|
|
49
54
|
|
|
50
|
-
this.#
|
|
55
|
+
this.#opts = { ...this.#opts, ...opts };
|
|
56
|
+
|
|
57
|
+
this.#start();
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
async #start(port = 8080) {
|
|
54
|
-
await connection.waitUntilReady()
|
|
61
|
+
await connection.waitUntilReady();
|
|
55
62
|
|
|
56
|
-
this.#port = process.env.PORT || port
|
|
57
|
-
|
|
63
|
+
this.#port = process.env.PORT || port;
|
|
64
|
+
this.#api.listen(this.#port);
|
|
58
65
|
|
|
59
|
-
console.debug(`geonix.gateway: listening on http://0.0.0.0:${this.#port}`)
|
|
66
|
+
console.debug(`geonix.gateway: listening on http://0.0.0.0:${this.#port}`);
|
|
60
67
|
|
|
61
68
|
// logging
|
|
62
|
-
this.#api.use(logger)
|
|
69
|
+
this.#api.use(logger);
|
|
63
70
|
|
|
64
71
|
// cors
|
|
65
72
|
this.#api.use((req, res, next) => {
|
|
66
|
-
const origin = req.headers[
|
|
67
|
-
const allMethods =
|
|
68
|
-
const allHeaders =
|
|
69
|
-
const requestMethod = req.headers[
|
|
70
|
-
const requestHeaders = req.headers[
|
|
73
|
+
const origin = req.headers["origin"];
|
|
74
|
+
const allMethods = "GET,PUT,POST,DELETE,OPTIONS,HEAD";
|
|
75
|
+
const allHeaders = "*";
|
|
76
|
+
const requestMethod = req.headers["access-control-request-method"];
|
|
77
|
+
const requestHeaders = req.headers["access-control-request-headers"];
|
|
71
78
|
|
|
72
|
-
res.set(
|
|
73
|
-
res.set(
|
|
74
|
-
res.set(
|
|
75
|
-
res.set(
|
|
76
|
-
res.set(
|
|
79
|
+
res.set("Access-Control-Allow-Credentials", "true");
|
|
80
|
+
res.set("Access-Control-Allow-Origin", origin || "*");
|
|
81
|
+
res.set("Access-Control-Allow-Methods", requestMethod || allMethods);
|
|
82
|
+
res.set("Allow", requestMethod || allMethods);
|
|
83
|
+
res.set("Access-Control-Allow-Headers", requestHeaders || allHeaders);
|
|
77
84
|
|
|
78
|
-
next()
|
|
79
|
-
})
|
|
85
|
+
next();
|
|
86
|
+
});
|
|
80
87
|
|
|
81
88
|
// debug router (only available in non-production environments)
|
|
82
|
-
if (process.env.NODE_ENV !==
|
|
83
|
-
this.#api.use(DEBUG_ENDPOINT, this.#debugRouter())
|
|
89
|
+
if (process.env.NODE_ENV !== "production")
|
|
90
|
+
this.#api.use(DEBUG_ENDPOINT, this.#debugRouter());
|
|
91
|
+
|
|
92
|
+
this.#api.use((req, res, next) => {
|
|
93
|
+
if (this.#opts.beforeRequest) {
|
|
94
|
+
this.#opts.beforeRequest(req, res);
|
|
95
|
+
}
|
|
96
|
+
next();
|
|
97
|
+
});
|
|
84
98
|
|
|
85
99
|
// handle mapped endpoints as service calls
|
|
86
100
|
this.#api.use(raw, (req, res, next) => {
|
|
87
|
-
stats.requests
|
|
101
|
+
stats.requests++;
|
|
88
102
|
|
|
89
103
|
if (this.#router)
|
|
90
|
-
this.#router(req, res, next)
|
|
104
|
+
this.#router(req, res, next);
|
|
91
105
|
else
|
|
92
|
-
next()
|
|
93
|
-
})
|
|
106
|
+
next();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
this.#api.use((req, res, next) => {
|
|
110
|
+
if (this.#opts.afterRequest) {
|
|
111
|
+
this.#opts.afterRequest(req, res);
|
|
112
|
+
}
|
|
113
|
+
next();
|
|
114
|
+
});
|
|
94
115
|
|
|
95
116
|
// config
|
|
96
|
-
this.#api.disable(
|
|
97
|
-
this.#api.disable(
|
|
117
|
+
this.#api.disable("x-powered-by");
|
|
118
|
+
this.#api.disable("etag");
|
|
98
119
|
|
|
99
120
|
// default answer
|
|
100
|
-
this.#api.all(
|
|
121
|
+
this.#api.all("*", (req, res) => {
|
|
101
122
|
res.status(404).send({
|
|
102
123
|
error: 404,
|
|
103
|
-
source:
|
|
104
|
-
})
|
|
105
|
-
})
|
|
124
|
+
source: "gw"
|
|
125
|
+
});
|
|
126
|
+
});
|
|
106
127
|
|
|
107
128
|
setInterval(() => {
|
|
108
129
|
if (this.#rebuildRouter)
|
|
109
|
-
this.#buildRouter()
|
|
110
|
-
}, 1000)
|
|
130
|
+
this.#buildRouter();
|
|
131
|
+
}, 1000);
|
|
111
132
|
|
|
112
133
|
while (true) {
|
|
113
134
|
try {
|
|
114
135
|
// send keeplive to check if connection is still alive
|
|
115
|
-
await connection.publish(
|
|
136
|
+
await connection.publish("gx.gateway.keepalive", Date.now());
|
|
116
137
|
|
|
117
|
-
await this.#handleAddedServics()
|
|
118
|
-
await this.#handleRemovedServices()
|
|
119
|
-
await sleep(1000)
|
|
138
|
+
await this.#handleAddedServics();
|
|
139
|
+
await this.#handleRemovedServices();
|
|
140
|
+
await sleep(1000);
|
|
120
141
|
|
|
121
|
-
if (connection.isClosed())
|
|
122
|
-
break
|
|
142
|
+
if (connection.isClosed()) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
123
145
|
} catch (e) {
|
|
124
|
-
console.error(e)
|
|
146
|
+
console.error(e);
|
|
125
147
|
}
|
|
126
148
|
}
|
|
127
149
|
|
|
128
150
|
// terminate process
|
|
129
|
-
console.debug(
|
|
130
|
-
process.exit(0)
|
|
151
|
+
console.debug("geonix.gateway: stopped");
|
|
152
|
+
process.exit(0);
|
|
131
153
|
}
|
|
132
154
|
|
|
133
155
|
async #handleAddedServics() {
|
|
134
|
-
let entries = Object.values(registry.getEntries())
|
|
156
|
+
let entries = Object.values(registry.getEntries());
|
|
135
157
|
|
|
136
158
|
const processEntry = async (entry) => {
|
|
137
159
|
if (this.#registry[entry.i] !== undefined)
|
|
138
|
-
return false
|
|
160
|
+
return false;
|
|
139
161
|
|
|
140
|
-
console.log(`gateway.onServiceAdded: ${entry.n}@${entry.v} (#${entry.i})`)
|
|
162
|
+
console.log(`gateway.onServiceAdded: ${entry.n}@${entry.v} (#${entry.i})`);
|
|
141
163
|
|
|
142
164
|
// figure out if endpoints is reachable via direct http call
|
|
143
|
-
let backend
|
|
165
|
+
let backend;
|
|
144
166
|
for (let address of entry.a)
|
|
145
167
|
try {
|
|
146
|
-
const ac = new AbortController()
|
|
147
|
-
const timeout = setTimeout(() => ac.abort(), 500)
|
|
148
|
-
const result = await (await fetch(`http://${address}${HEALTH_CHECK_ENDPOINT}`, { signal: ac.signal })).json()
|
|
149
|
-
clearTimeout(timeout)
|
|
150
|
-
if (result.status ===
|
|
151
|
-
backend = address
|
|
152
|
-
console.log(`${
|
|
153
|
-
break
|
|
168
|
+
const ac = new AbortController();
|
|
169
|
+
const timeout = setTimeout(() => ac.abort(), 500);
|
|
170
|
+
const result = await (await fetch(`http://${address}${HEALTH_CHECK_ENDPOINT}`, { signal: ac.signal })).json();
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
if (result.status === "healthy" && result.services?.includes(entry.n)) {
|
|
173
|
+
backend = address;
|
|
174
|
+
console.log(`${entry.n}@${entry.v} (#${entry.i}) directly reachable @ ${address}`);
|
|
175
|
+
break;
|
|
154
176
|
}
|
|
155
|
-
} catch
|
|
177
|
+
} catch {
|
|
156
178
|
// silently ignore errors
|
|
157
179
|
}
|
|
158
180
|
|
|
159
|
-
let proxy
|
|
181
|
+
let proxy;
|
|
160
182
|
if (!backend) {
|
|
161
183
|
// create proxy over nats
|
|
162
184
|
proxy = await createTCPServer(async (client) => {
|
|
163
|
-
const streamId = picoid()
|
|
164
|
-
stats.proxied_over_nats
|
|
185
|
+
const streamId = picoid();
|
|
186
|
+
stats.proxied_over_nats++;
|
|
165
187
|
|
|
166
188
|
try {
|
|
167
|
-
await this.#proxyHttpOverNats(streamId, entry, client)
|
|
189
|
+
await this.#proxyHttpOverNats(streamId, entry, client);
|
|
168
190
|
} catch (e) {
|
|
169
|
-
console.error(
|
|
170
|
-
client.destroy()
|
|
191
|
+
console.error("nats.proxy.error", e);
|
|
192
|
+
client.destroy();
|
|
171
193
|
}
|
|
172
|
-
}, 50000, 10000)
|
|
173
|
-
backend = `127.0.0.1:${proxy.port}
|
|
194
|
+
}, 50000, 10000);
|
|
195
|
+
backend = `127.0.0.1:${proxy.port}`;
|
|
174
196
|
}
|
|
175
197
|
|
|
176
|
-
this.#registry[entry.i] = { entry, proxy, backend }
|
|
198
|
+
this.#registry[entry.i] = { entry, proxy, backend };
|
|
177
199
|
|
|
178
|
-
return true
|
|
179
|
-
}
|
|
200
|
+
return true;
|
|
201
|
+
};
|
|
180
202
|
|
|
181
|
-
entries = (await Promise.all(entries.map(processEntry))).filter(result => result === true)
|
|
203
|
+
entries = (await Promise.all(entries.map(processEntry))).filter(result => result === true);
|
|
182
204
|
|
|
183
205
|
if (entries.length > 0)
|
|
184
|
-
this.#rebuildRouter = true
|
|
206
|
+
this.#rebuildRouter = true;
|
|
185
207
|
}
|
|
186
208
|
|
|
187
209
|
async #handleRemovedServices() {
|
|
188
|
-
const localEntries = Object.values(this.#registry)
|
|
189
|
-
const registryEntries = registry.getEntries()
|
|
210
|
+
const localEntries = Object.values(this.#registry);
|
|
211
|
+
const registryEntries = registry.getEntries();
|
|
190
212
|
|
|
191
213
|
for (let { entry, proxy } of localEntries) {
|
|
192
214
|
if (registryEntries[entry.i] === undefined) {
|
|
193
|
-
proxy?.server?.close()
|
|
194
|
-
console.log(`gateway.onServiceRemoved: ${entry.n}@${entry.v}`)
|
|
195
|
-
delete this.#registry[entry.i]
|
|
215
|
+
proxy?.server?.close();
|
|
216
|
+
console.log(`gateway.onServiceRemoved: ${entry.n}@${entry.v}`);
|
|
217
|
+
delete this.#registry[entry.i];
|
|
196
218
|
|
|
197
|
-
this.#rebuildRouter = true
|
|
219
|
+
this.#rebuildRouter = true;
|
|
198
220
|
}
|
|
199
221
|
}
|
|
200
222
|
}
|
|
201
223
|
|
|
202
224
|
#debugRouter() {
|
|
203
|
-
const router = Router()
|
|
225
|
+
const router = Router();
|
|
204
226
|
|
|
205
227
|
router.use((req, res, next) => {
|
|
206
|
-
stats.debug_requests
|
|
228
|
+
stats.debug_requests++;
|
|
207
229
|
|
|
208
|
-
next()
|
|
209
|
-
})
|
|
230
|
+
next();
|
|
231
|
+
});
|
|
210
232
|
|
|
211
|
-
router.get(
|
|
212
|
-
const services = Object.values(registry.getEntries()).map(e => (`${e.n}@${e.v}`))
|
|
213
|
-
services.sort()
|
|
214
|
-
res.send(services)
|
|
215
|
-
})
|
|
233
|
+
router.get("/services", (req, res) => {
|
|
234
|
+
const services = Object.values(registry.getEntries()).map(e => (`${e.n}@${e.v}`));
|
|
235
|
+
services.sort();
|
|
236
|
+
res.send(services);
|
|
237
|
+
});
|
|
216
238
|
|
|
217
|
-
router.get(
|
|
218
|
-
res.send(this.#endpoints)
|
|
219
|
-
})
|
|
239
|
+
router.get("/endpoints", (req, res) => {
|
|
240
|
+
res.send(this.#endpoints);
|
|
241
|
+
});
|
|
220
242
|
|
|
221
|
-
router.get(
|
|
222
|
-
res.send(this.#registry)
|
|
223
|
-
})
|
|
243
|
+
router.get("/router-registry", (req, res) => {
|
|
244
|
+
res.send(this.#registry);
|
|
245
|
+
});
|
|
224
246
|
|
|
225
|
-
router.get(
|
|
226
|
-
res.send(registry.getEntries())
|
|
227
|
-
})
|
|
247
|
+
router.get("/registry", (req, res) => {
|
|
248
|
+
res.send(registry.getEntries());
|
|
249
|
+
});
|
|
228
250
|
|
|
229
|
-
router.get(
|
|
230
|
-
res.send(stats)
|
|
231
|
-
})
|
|
251
|
+
router.get("/stats", (req, res) => {
|
|
252
|
+
res.send(stats);
|
|
253
|
+
});
|
|
232
254
|
|
|
233
|
-
router.get(
|
|
255
|
+
router.get("/info", (req, res) => {
|
|
234
256
|
res.send({
|
|
235
257
|
geonix: GeonixVersion,
|
|
236
258
|
node: {
|
|
@@ -242,39 +264,35 @@ export class Gateway {
|
|
|
242
264
|
mem: process.memoryUsage(),
|
|
243
265
|
rss: process.memoryUsage.rss(),
|
|
244
266
|
cpu: process.cpuUsage()
|
|
245
|
-
})
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
router.get('/rebuild', (req, res) => {
|
|
249
|
-
this.#rebuildRouter = true
|
|
250
|
-
res.send({ result: 'ok' })
|
|
251
|
-
})
|
|
267
|
+
});
|
|
268
|
+
});
|
|
252
269
|
|
|
253
|
-
router.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
})
|
|
270
|
+
router.all("*", (req, res) => {
|
|
271
|
+
res.status(404).send({ error: 404 });
|
|
272
|
+
});
|
|
257
273
|
|
|
258
|
-
return router
|
|
274
|
+
return router;
|
|
259
275
|
}
|
|
260
276
|
|
|
261
277
|
async #proxyHttpOverNats(streamId, entry, client) {
|
|
262
|
-
if (await Request(entry.n,
|
|
263
|
-
const ingress = await connection.subscribe(`gx2.stream.${streamId}.a`)
|
|
264
|
-
|
|
265
|
-
client.on(
|
|
266
|
-
client.on(
|
|
267
|
-
connection.unsubscribe(ingress)
|
|
268
|
-
connection.publish(`gx2.stream.${streamId}.c`, Buffer.from(
|
|
269
|
-
})
|
|
270
|
-
client.on(
|
|
278
|
+
if (await Request(entry.n, "SYS_createConnection", [streamId])) {
|
|
279
|
+
const ingress = await connection.subscribe(`gx2.stream.${streamId}.a`);
|
|
280
|
+
|
|
281
|
+
client.on("data", (chunk) => connection.publishRaw(`gx2.stream.${streamId}.b`, chunk));
|
|
282
|
+
client.on("close", () => {
|
|
283
|
+
connection.unsubscribe(ingress);
|
|
284
|
+
connection.publish(`gx2.stream.${streamId}.c`, Buffer.from("end"));
|
|
285
|
+
});
|
|
286
|
+
client.on("error", (_error) => {
|
|
287
|
+
// silently ignore errors
|
|
288
|
+
});
|
|
271
289
|
|
|
272
290
|
const dataLoop = async () => {
|
|
273
291
|
for await (const event of ingress)
|
|
274
|
-
client.write(event.data)
|
|
275
|
-
}
|
|
292
|
+
client.write(event.data);
|
|
293
|
+
};
|
|
276
294
|
|
|
277
|
-
dataLoop()
|
|
295
|
+
dataLoop();
|
|
278
296
|
}
|
|
279
297
|
}
|
|
280
298
|
|
|
@@ -291,45 +309,44 @@ export class Gateway {
|
|
|
291
309
|
headers: {
|
|
292
310
|
...req.headers
|
|
293
311
|
}
|
|
294
|
-
})
|
|
312
|
+
});
|
|
295
313
|
|
|
296
|
-
backend.on(
|
|
297
|
-
backend.on(
|
|
298
|
-
inbound.on(
|
|
314
|
+
backend.on("open", () => {
|
|
315
|
+
backend.on("message", (data, isBinary) => inbound.send(isBinary ? data : data.toString()));
|
|
316
|
+
inbound.on("message", data => backend.send(data));
|
|
299
317
|
|
|
300
|
-
backend.on(
|
|
301
|
-
inbound.on(
|
|
302
|
-
})
|
|
318
|
+
backend.on("close", () => inbound.close());
|
|
319
|
+
inbound.on("close", () => backend.close());
|
|
320
|
+
});
|
|
303
321
|
} catch (e) {
|
|
304
|
-
console.error(e)
|
|
322
|
+
console.error(e);
|
|
305
323
|
}
|
|
306
324
|
}
|
|
307
325
|
|
|
308
|
-
|
|
309
326
|
async #buildRouter() {
|
|
310
327
|
if (this.#buildRouterRunning)
|
|
311
|
-
return
|
|
328
|
+
return;
|
|
312
329
|
|
|
313
|
-
console.debug(
|
|
330
|
+
console.debug("gateway.buildRouter");
|
|
314
331
|
|
|
315
|
-
this.#rebuildRouter = false
|
|
316
|
-
this.#buildRouterRunning = true
|
|
332
|
+
this.#rebuildRouter = false;
|
|
333
|
+
this.#buildRouterRunning = true;
|
|
317
334
|
|
|
318
335
|
try {
|
|
319
|
-
const router = Router()
|
|
320
|
-
const entries = Object.values(this.#registry)
|
|
336
|
+
const router = Router();
|
|
337
|
+
const entries = Object.values(this.#registry);
|
|
321
338
|
|
|
322
|
-
const endpoints = []
|
|
339
|
+
const endpoints = [];
|
|
323
340
|
|
|
324
341
|
for (let { entry, backend } of entries) {
|
|
325
342
|
// generate global endpoint list
|
|
326
343
|
for (let e of entry.m) {
|
|
327
344
|
if (endpointMatcher.test(e)) {
|
|
328
|
-
const endpoint = endpointMatcher.exec(e).groups
|
|
345
|
+
const endpoint = endpointMatcher.exec(e).groups;
|
|
329
346
|
let options = {
|
|
330
347
|
order: 100,
|
|
331
348
|
...(endpoint.options ? querystring.parse(endpoint.options) : {})
|
|
332
|
-
}
|
|
349
|
+
};
|
|
333
350
|
|
|
334
351
|
try {
|
|
335
352
|
endpoints.push({
|
|
@@ -338,67 +355,66 @@ export class Gateway {
|
|
|
338
355
|
options,
|
|
339
356
|
endpoint,
|
|
340
357
|
backend: [backend]
|
|
341
|
-
})
|
|
358
|
+
});
|
|
342
359
|
} catch (e) {
|
|
343
|
-
console.error(
|
|
344
|
-
console.error(
|
|
360
|
+
console.error("gateway.buildRouter.error:", entry);
|
|
361
|
+
console.error("gateway.buildRouter.error:", e);
|
|
345
362
|
}
|
|
346
363
|
}
|
|
347
364
|
}
|
|
348
365
|
}
|
|
349
366
|
|
|
350
367
|
// handle duplicates (round-robin)
|
|
351
|
-
let index = endpoints.length
|
|
368
|
+
let index = endpoints.length;
|
|
352
369
|
while (index--) {
|
|
353
|
-
const version = endpoints[index].version
|
|
354
|
-
const url = `${endpoints[index].endpoint.verb} ${endpoints[index].endpoint.url}
|
|
370
|
+
const version = endpoints[index].version;
|
|
371
|
+
const url = `${endpoints[index].endpoint.verb} ${endpoints[index].endpoint.url}`;
|
|
355
372
|
|
|
356
373
|
for (let n = 0; n < index; n++)
|
|
357
374
|
if (`${endpoints[n].endpoint.verb} ${endpoints[n].endpoint.url}` === url && endpoints[n].version === version) {
|
|
358
|
-
endpoints[n].backend = endpoints[n].backend.concat(endpoints[index].backend)
|
|
359
|
-
endpoints.splice(index, 1)
|
|
360
|
-
break
|
|
375
|
+
endpoints[n].backend = endpoints[n].backend.concat(endpoints[index].backend);
|
|
376
|
+
endpoints.splice(index, 1);
|
|
377
|
+
break;
|
|
361
378
|
}
|
|
362
379
|
}
|
|
363
380
|
|
|
364
381
|
// sort endpoints by order, if there is one
|
|
365
|
-
endpoints.sort((a, b) => semver.rcompare(a.version, b.version))
|
|
366
|
-
endpoints.sort((a, b) => parseInt(a.options.order) - parseInt(b.options.order))
|
|
382
|
+
endpoints.sort((a, b) => semver.rcompare(a.version, b.version));
|
|
383
|
+
endpoints.sort((a, b) => parseInt(a.options.order) - parseInt(b.options.order));
|
|
367
384
|
|
|
368
|
-
this.#endpoints = endpoints
|
|
385
|
+
this.#endpoints = endpoints;
|
|
369
386
|
|
|
370
387
|
// build the router
|
|
371
388
|
for (let endpoint of endpoints) {
|
|
372
|
-
let { verb, url: uri } = endpoint.endpoint
|
|
373
|
-
let backend = endpoint.backend[endpoint.requests++ % endpoint.backend.length]
|
|
389
|
+
let { verb, url: uri } = endpoint.endpoint;
|
|
390
|
+
let backend = endpoint.backend[endpoint.requests++ % endpoint.backend.length];
|
|
374
391
|
|
|
375
|
-
verb = verb.toLowerCase()
|
|
392
|
+
verb = verb.toLowerCase();
|
|
376
393
|
|
|
377
|
-
if (verb ===
|
|
394
|
+
if (verb === "ws") {
|
|
378
395
|
router.ws(uri, (ws, req) => {
|
|
379
|
-
const url = req.originalUrl.replace(/\/\.websocket$/,
|
|
396
|
+
const url = req.originalUrl.replace(/\/\.websocket$/, "");
|
|
380
397
|
|
|
381
|
-
console.debug(
|
|
382
|
-
this.#proxyWebsocketOverNats(`ws://${backend}${url}`, ws, req)
|
|
383
|
-
})
|
|
398
|
+
console.debug("proxy.web.ws.to:", backend + req.originalUrl);
|
|
399
|
+
this.#proxyWebsocketOverNats(`ws://${backend}${url}`, ws, req);
|
|
400
|
+
});
|
|
384
401
|
} else
|
|
385
|
-
router[verb](uri, async (req, res,
|
|
386
|
-
stats.proxied
|
|
387
|
-
backend = endpoint.backend[endpoint.requests++ % endpoint.backend.length]
|
|
402
|
+
router[verb](uri, async (req, res, _next) => {
|
|
403
|
+
stats.proxied++;
|
|
404
|
+
backend = endpoint.backend[endpoint.requests++ % endpoint.backend.length];
|
|
388
405
|
|
|
389
406
|
try {
|
|
390
|
-
console.debug(
|
|
391
|
-
await proxyHttp(`http://${backend}`, req, res)
|
|
407
|
+
console.debug("proxy.web.to:", backend + req.originalUrl);
|
|
408
|
+
await proxyHttp(`http://${backend}`, req, res);
|
|
392
409
|
} catch (e) {
|
|
393
|
-
console.error(
|
|
394
|
-
} finally {
|
|
410
|
+
console.error("proxy.web.error:", e);
|
|
395
411
|
}
|
|
396
|
-
})
|
|
412
|
+
});
|
|
397
413
|
}
|
|
398
414
|
|
|
399
|
-
this.#router = router
|
|
415
|
+
this.#router = router;
|
|
400
416
|
} finally {
|
|
401
|
-
this.#buildRouterRunning = false
|
|
417
|
+
this.#buildRouterRunning = false;
|
|
402
418
|
}
|
|
403
419
|
}
|
|
404
420
|
|