@camera.ui/rpc 1.0.3 → 1.0.4
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/externals/nats.js/core/src/authenticator.ts +159 -0
- package/externals/nats.js/core/src/bench.ts +426 -0
- package/externals/nats.js/core/src/codec.ts +28 -0
- package/externals/nats.js/core/src/core.ts +1219 -0
- package/externals/nats.js/core/src/databuffer.ts +129 -0
- package/externals/nats.js/core/src/denobuffer.ts +248 -0
- package/externals/nats.js/core/src/encoders.ts +53 -0
- package/externals/nats.js/core/src/errors.ts +300 -0
- package/externals/nats.js/core/src/headers.ts +315 -0
- package/externals/nats.js/core/src/heartbeats.ts +114 -0
- package/externals/nats.js/core/src/idleheartbeat_monitor.ts +140 -0
- package/externals/nats.js/core/src/internal_mod.ts +167 -0
- package/externals/nats.js/core/src/ipparser.ts +215 -0
- package/externals/nats.js/core/src/mod.ts +113 -0
- package/externals/nats.js/core/src/msg.ts +120 -0
- package/externals/nats.js/core/src/muxsubscription.ts +111 -0
- package/externals/nats.js/core/src/nats.ts +650 -0
- package/externals/nats.js/core/src/nkeys.ts +1 -0
- package/externals/nats.js/core/src/nuid.ts +16 -0
- package/externals/nats.js/core/src/options.ts +202 -0
- package/externals/nats.js/core/src/parser.ts +756 -0
- package/externals/nats.js/core/src/protocol.ts +1304 -0
- package/externals/nats.js/core/src/queued_iterator.ts +171 -0
- package/externals/nats.js/core/src/request.ts +177 -0
- package/externals/nats.js/core/src/semver.ts +165 -0
- package/externals/nats.js/core/src/servers.ts +424 -0
- package/externals/nats.js/core/src/transport.ts +117 -0
- package/externals/nats.js/core/src/types.ts +17 -0
- package/externals/nats.js/core/src/util.ts +367 -0
- package/externals/nats.js/core/src/version.ts +2 -0
- package/externals/nats.js/core/src/ws_transport.ts +391 -0
- package/package.json +2 -1
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018-2024 The NATS Authors
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
import { getUrlParseFn } from "./transport.ts";
|
|
16
|
+
import { shuffle } from "./util.ts";
|
|
17
|
+
import { isIP } from "./ipparser.ts";
|
|
18
|
+
import type {
|
|
19
|
+
DnsResolveFn,
|
|
20
|
+
Server,
|
|
21
|
+
ServerInfo,
|
|
22
|
+
ServersChanged,
|
|
23
|
+
} from "./core.ts";
|
|
24
|
+
import { DEFAULT_PORT } from "./core.ts";
|
|
25
|
+
import { InvalidArgumentError } from "./errors.ts";
|
|
26
|
+
|
|
27
|
+
export function isIPV4OrHostname(hp: string): boolean {
|
|
28
|
+
// in the wild seeing IPv4s as IPv6s
|
|
29
|
+
// ::ffff:35.234.43.228 which incorrectly get mapped to IPv4 unless
|
|
30
|
+
// we add this test first
|
|
31
|
+
if (hp.indexOf("[") !== -1 || hp.indexOf("::") !== -1) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (hp.indexOf(".") !== -1) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
// if we have a plain hostname or host:port
|
|
38
|
+
if (hp.split(":").length <= 2) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isIPV6(hp: string) {
|
|
45
|
+
return !isIPV4OrHostname(hp);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function filterIpv6MappedToIpv4(hp: string): string {
|
|
49
|
+
const prefix = "::FFFF:";
|
|
50
|
+
const idx = hp.toUpperCase().indexOf(prefix);
|
|
51
|
+
if (idx !== -1 && hp.indexOf(".") !== -1) {
|
|
52
|
+
// we have something like: ::FFFF:127.0.0.1 or [::FFFF:127.0.0.1]:4222
|
|
53
|
+
let ip = hp.substring(idx + prefix.length);
|
|
54
|
+
ip = ip.replace("[", "");
|
|
55
|
+
return ip.replace("]", "");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return hp;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function hostPort(
|
|
62
|
+
u: string,
|
|
63
|
+
): { listen: string; hostname: string; port: number } {
|
|
64
|
+
u = u.trim();
|
|
65
|
+
// remove any protocol that may have been provided
|
|
66
|
+
if (u.match(/^(.*:\/\/)(.*)/m)) {
|
|
67
|
+
u = u.replace(/^(.*:\/\/)(.*)/gm, "$2");
|
|
68
|
+
}
|
|
69
|
+
// in web environments, URL may not be a living standard
|
|
70
|
+
// that means that protocols other than HTTP/S are not
|
|
71
|
+
// parsable correctly.
|
|
72
|
+
|
|
73
|
+
// the third complication is that we may have been given
|
|
74
|
+
// an IPv6 or worse IPv6 mapping an Ipv4
|
|
75
|
+
u = filterIpv6MappedToIpv4(u);
|
|
76
|
+
|
|
77
|
+
// we only wrap cases where they gave us a plain ipv6
|
|
78
|
+
// and we are not already bracketed
|
|
79
|
+
if (isIPV6(u) && u.indexOf("[") === -1) {
|
|
80
|
+
u = `[${u}]`;
|
|
81
|
+
}
|
|
82
|
+
// if we have ipv6, we expect port after ']:' otherwise after ':'
|
|
83
|
+
const op = isIPV6(u) ? u.match(/(]:)(\d+)/) : u.match(/(:)(\d+)/);
|
|
84
|
+
const port = op && op.length === 3 && op[1] && op[2]
|
|
85
|
+
? parseInt(op[2])
|
|
86
|
+
: DEFAULT_PORT;
|
|
87
|
+
|
|
88
|
+
// the next complication is that new URL() may
|
|
89
|
+
// eat ports which match the protocol - so for example
|
|
90
|
+
// port 80 may be eliminated - so we flip the protocol
|
|
91
|
+
// so that it always yields a value
|
|
92
|
+
const protocol = port === 80 ? "https" : "http";
|
|
93
|
+
const url = new URL(`${protocol}://${u}`);
|
|
94
|
+
url.port = `${port}`;
|
|
95
|
+
|
|
96
|
+
let hostname = url.hostname;
|
|
97
|
+
// if we are bracketed, we need to rip it out
|
|
98
|
+
if (hostname.charAt(0) === "[") {
|
|
99
|
+
hostname = hostname.substring(1, hostname.length - 1);
|
|
100
|
+
}
|
|
101
|
+
const listen = url.host;
|
|
102
|
+
|
|
103
|
+
return { listen, hostname, port };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @hidden
|
|
108
|
+
*/
|
|
109
|
+
export class ServerImpl implements Server {
|
|
110
|
+
src: string;
|
|
111
|
+
listen: string;
|
|
112
|
+
hostname: string;
|
|
113
|
+
port: number;
|
|
114
|
+
didConnect: boolean;
|
|
115
|
+
reconnects: number;
|
|
116
|
+
lastConnect: number;
|
|
117
|
+
gossiped: boolean;
|
|
118
|
+
tlsName: string;
|
|
119
|
+
resolves?: Server[];
|
|
120
|
+
|
|
121
|
+
constructor(u: string, gossiped = false) {
|
|
122
|
+
this.src = u;
|
|
123
|
+
this.tlsName = "";
|
|
124
|
+
const v = hostPort(u);
|
|
125
|
+
this.listen = v.listen;
|
|
126
|
+
this.hostname = v.hostname;
|
|
127
|
+
this.port = v.port;
|
|
128
|
+
this.didConnect = false;
|
|
129
|
+
this.reconnects = 0;
|
|
130
|
+
this.lastConnect = 0;
|
|
131
|
+
this.gossiped = gossiped;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
toString(): string {
|
|
135
|
+
return this.listen;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async resolve(
|
|
139
|
+
opts: Partial<
|
|
140
|
+
{
|
|
141
|
+
fn: DnsResolveFn;
|
|
142
|
+
randomize: boolean;
|
|
143
|
+
resolve: boolean;
|
|
144
|
+
debug: boolean;
|
|
145
|
+
}
|
|
146
|
+
>,
|
|
147
|
+
): Promise<Server[]> {
|
|
148
|
+
if (!opts.fn || opts.resolve === false) {
|
|
149
|
+
// we cannot resolve - transport doesn't support it
|
|
150
|
+
// or user opted out
|
|
151
|
+
// don't add - to resolves or we get a circ reference
|
|
152
|
+
return [this];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const buf: Server[] = [];
|
|
156
|
+
if (isIP(this.hostname)) {
|
|
157
|
+
// don't add - to resolves or we get a circ reference
|
|
158
|
+
return [this];
|
|
159
|
+
} else {
|
|
160
|
+
// resolve the hostname to ips
|
|
161
|
+
const ips = await opts.fn(this.hostname);
|
|
162
|
+
if (opts.debug) {
|
|
163
|
+
console.log(`resolve ${this.hostname} = ${ips.join(",")}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
for (const ip of ips) {
|
|
167
|
+
// letting URL handle the details of representing IPV6 ip with a port, etc
|
|
168
|
+
// careful to make sure the protocol doesn't line with standard ports or they
|
|
169
|
+
// get swallowed
|
|
170
|
+
const proto = this.port === 80 ? "https" : "http";
|
|
171
|
+
// ipv6 won't be bracketed here, because it came from resolve
|
|
172
|
+
const url = new URL(`${proto}://${isIPV6(ip) ? "[" + ip + "]" : ip}`);
|
|
173
|
+
url.port = `${this.port}`;
|
|
174
|
+
const ss = new ServerImpl(url.host, false);
|
|
175
|
+
ss.tlsName = this.hostname;
|
|
176
|
+
buf.push(ss);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (opts.randomize) {
|
|
180
|
+
shuffle(buf);
|
|
181
|
+
}
|
|
182
|
+
this.resolves = buf;
|
|
183
|
+
return buf;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @hidden
|
|
189
|
+
*/
|
|
190
|
+
export class Servers {
|
|
191
|
+
private firstSelect: boolean;
|
|
192
|
+
private servers: ServerImpl[];
|
|
193
|
+
private currentServer!: ServerImpl;
|
|
194
|
+
private tlsName: string;
|
|
195
|
+
private randomize: boolean;
|
|
196
|
+
|
|
197
|
+
constructor(opts: Partial<{ randomize: boolean }> = {}) {
|
|
198
|
+
this.firstSelect = true;
|
|
199
|
+
this.servers = [];
|
|
200
|
+
this.tlsName = "";
|
|
201
|
+
this.randomize = opts.randomize || false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Replace the server pool with the provided list of `host:port` entries.
|
|
206
|
+
*
|
|
207
|
+
* Throws `InvalidArgumentError` if `listens` is empty or not an array.
|
|
208
|
+
*
|
|
209
|
+
* Note: reconnect attempts continue to follow the configured reconnect
|
|
210
|
+
* policy, but if every entry in the new pool is unreachable the
|
|
211
|
+
* connection may be left unable to recover.
|
|
212
|
+
*/
|
|
213
|
+
setServers(listens: string[]): void {
|
|
214
|
+
if (!Array.isArray(listens) || listens.length === 0) {
|
|
215
|
+
throw InvalidArgumentError.format("servers", "cannot be empty");
|
|
216
|
+
}
|
|
217
|
+
const urlParseFn = getUrlParseFn();
|
|
218
|
+
const existing = new Map<string, ServerImpl>();
|
|
219
|
+
for (const s of this.servers) existing.set(s.listen, s);
|
|
220
|
+
|
|
221
|
+
const merged: ServerImpl[] = [];
|
|
222
|
+
for (let hp of listens) {
|
|
223
|
+
hp = urlParseFn ? urlParseFn(hp) : hp;
|
|
224
|
+
const { listen } = hostPort(hp);
|
|
225
|
+
const surviving = existing.get(listen);
|
|
226
|
+
if (surviving) {
|
|
227
|
+
// user-supplied = not gossiped. otherwise a later cluster update()
|
|
228
|
+
// could remove an entry the user explicitly asked for.
|
|
229
|
+
surviving.gossiped = false;
|
|
230
|
+
// camera.ui fork patch.
|
|
231
|
+
// `existing.get(listen)` keys only on `host:port`, so a re-issued
|
|
232
|
+
// URL that differs only in its query string (e.g., a rotated auth
|
|
233
|
+
// token in `?token=…`) would be silently dropped — the recycled
|
|
234
|
+
// ServerImpl still carries the OLD `src`, and the next reconnect
|
|
235
|
+
// would dial that stale URL. Update `src` in place so the new query
|
|
236
|
+
// is what gets used by the WS transport on the next dial.
|
|
237
|
+
if (surviving.src !== hp) {
|
|
238
|
+
surviving.src = hp;
|
|
239
|
+
}
|
|
240
|
+
merged.push(surviving);
|
|
241
|
+
} else {
|
|
242
|
+
merged.push(new ServerImpl(hp));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (this.randomize) shuffle(merged);
|
|
246
|
+
|
|
247
|
+
this.servers = merged;
|
|
248
|
+
if (
|
|
249
|
+
this.currentServer === undefined ||
|
|
250
|
+
!merged.includes(this.currentServer)
|
|
251
|
+
) {
|
|
252
|
+
this.currentServer = merged[0];
|
|
253
|
+
this.firstSelect = true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
clear(): void {
|
|
258
|
+
this.servers.length = 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
updateTLSName(): void {
|
|
262
|
+
const cs = this.getCurrentServer();
|
|
263
|
+
if (!isIP(cs.hostname)) {
|
|
264
|
+
this.tlsName = cs.hostname;
|
|
265
|
+
this.servers.forEach((s) => {
|
|
266
|
+
if (s.gossiped) {
|
|
267
|
+
s.tlsName = this.tlsName;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getCurrentServer(): ServerImpl {
|
|
274
|
+
return this.currentServer;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
addServer(u: string, implicit = false): void {
|
|
278
|
+
const urlParseFn = getUrlParseFn();
|
|
279
|
+
u = urlParseFn ? urlParseFn(u) : u;
|
|
280
|
+
const s = new ServerImpl(u, implicit);
|
|
281
|
+
if (isIP(s.hostname)) {
|
|
282
|
+
s.tlsName = this.tlsName;
|
|
283
|
+
}
|
|
284
|
+
this.servers.push(s);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
selectServer(): ServerImpl | undefined {
|
|
288
|
+
// allow using select without breaking the order of the servers
|
|
289
|
+
if (this.firstSelect) {
|
|
290
|
+
this.firstSelect = false;
|
|
291
|
+
return this.currentServer;
|
|
292
|
+
}
|
|
293
|
+
const t = this.servers.shift();
|
|
294
|
+
if (t) {
|
|
295
|
+
this.servers.push(t);
|
|
296
|
+
this.currentServer = t;
|
|
297
|
+
}
|
|
298
|
+
return t;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
removeCurrentServer(): void {
|
|
302
|
+
this.removeServer(this.currentServer);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
removeServer(server: ServerImpl | undefined): void {
|
|
306
|
+
if (server) {
|
|
307
|
+
const index = this.servers.indexOf(server);
|
|
308
|
+
this.servers.splice(index, 1);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Returns a frozen snapshot of the server pool in natural order.
|
|
314
|
+
* Each entry is a defensive copy — callers cannot mutate pool state.
|
|
315
|
+
*/
|
|
316
|
+
snapshot(): ReadonlyArray<Server> {
|
|
317
|
+
return Servers.freezeAll(this.servers);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Returns a frozen snapshot of the server pool with the current server
|
|
322
|
+
* (= next dial candidate) at index 0. Used to present the handler with
|
|
323
|
+
* the server the library would have selected.
|
|
324
|
+
*/
|
|
325
|
+
snapshotForHandler(): ReadonlyArray<Server> {
|
|
326
|
+
const cur = this.currentServer;
|
|
327
|
+
if (!cur) return this.snapshot();
|
|
328
|
+
const idx = this.servers.indexOf(cur);
|
|
329
|
+
if (idx <= 0) return this.snapshot();
|
|
330
|
+
return Servers.freezeAll(
|
|
331
|
+
[cur, ...this.servers.slice(0, idx), ...this.servers.slice(idx + 1)],
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private static freezeAll(arr: ServerImpl[]): ReadonlyArray<Server> {
|
|
336
|
+
return arr.map((s) =>
|
|
337
|
+
Object.freeze<Server>({
|
|
338
|
+
hostname: s.hostname,
|
|
339
|
+
port: s.port,
|
|
340
|
+
listen: s.listen,
|
|
341
|
+
src: s.src,
|
|
342
|
+
tlsName: s.tlsName,
|
|
343
|
+
reconnects: s.reconnects,
|
|
344
|
+
lastConnect: s.lastConnect,
|
|
345
|
+
gossiped: s.gossiped,
|
|
346
|
+
didConnect: s.didConnect,
|
|
347
|
+
})
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
find(server: Server): ServerImpl | undefined {
|
|
352
|
+
return this.servers.find((s) => s.listen === server.listen);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
setCurrent(server: ServerImpl): void {
|
|
356
|
+
this.currentServer = server;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
length(): number {
|
|
360
|
+
return this.servers.length;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
next(): ServerImpl | undefined {
|
|
364
|
+
return this.servers.length ? this.servers[0] : undefined;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
getServers(): ServerImpl[] {
|
|
368
|
+
return this.servers;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
update(info: ServerInfo, encrypted?: boolean): ServersChanged {
|
|
372
|
+
const added: string[] = [];
|
|
373
|
+
let deleted: string[] = [];
|
|
374
|
+
|
|
375
|
+
const urlParseFn = getUrlParseFn();
|
|
376
|
+
const discovered = new Map<string, ServerImpl>();
|
|
377
|
+
if (info.connect_urls && info.connect_urls.length > 0) {
|
|
378
|
+
info.connect_urls.forEach((hp) => {
|
|
379
|
+
hp = urlParseFn ? urlParseFn(hp, encrypted) : hp;
|
|
380
|
+
const s = new ServerImpl(hp, true);
|
|
381
|
+
discovered.set(hp, s);
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
// remove gossiped servers that are no longer reported
|
|
385
|
+
const toDelete: number[] = [];
|
|
386
|
+
this.servers.forEach((s, index) => {
|
|
387
|
+
const u = s.listen;
|
|
388
|
+
if (
|
|
389
|
+
s.gossiped && this.currentServer.listen !== u &&
|
|
390
|
+
discovered.get(u) === undefined
|
|
391
|
+
) {
|
|
392
|
+
// server was removed
|
|
393
|
+
toDelete.push(index);
|
|
394
|
+
}
|
|
395
|
+
// remove this entry from reported
|
|
396
|
+
discovered.delete(u);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// perform the deletion
|
|
400
|
+
toDelete.reverse();
|
|
401
|
+
toDelete.forEach((index) => {
|
|
402
|
+
const removed = this.servers.splice(index, 1);
|
|
403
|
+
deleted = deleted.concat(removed[0].listen);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// remaining servers are new
|
|
407
|
+
discovered.forEach((v, k) => {
|
|
408
|
+
this.servers.push(v);
|
|
409
|
+
added.push(k);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// shuffle the pool so reconnect picks distribute across the cluster.
|
|
413
|
+
// currentServer is held at the head — the live connection isn't
|
|
414
|
+
// disturbed, but rotation order through the rest is randomized.
|
|
415
|
+
if (this.randomize && added.length > 0) {
|
|
416
|
+
const cur = this.currentServer;
|
|
417
|
+
const others = this.servers.filter((s) => s !== cur);
|
|
418
|
+
shuffle(others);
|
|
419
|
+
this.servers = cur ? [cur, ...others] : others;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return { added, deleted };
|
|
423
|
+
}
|
|
424
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020-2024 The NATS Authors
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
import { TD } from "./encoders.ts";
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
ConnectionOptions,
|
|
19
|
+
DnsResolveFn,
|
|
20
|
+
Server,
|
|
21
|
+
URLParseFn,
|
|
22
|
+
} from "./core.ts";
|
|
23
|
+
|
|
24
|
+
import { DEFAULT_PORT } from "./core.ts";
|
|
25
|
+
import { DataBuffer } from "./databuffer.ts";
|
|
26
|
+
|
|
27
|
+
let transportConfig: TransportFactory;
|
|
28
|
+
export function setTransportFactory(config: TransportFactory): void {
|
|
29
|
+
transportConfig = config;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function defaultPort(): number {
|
|
33
|
+
return transportConfig !== undefined &&
|
|
34
|
+
transportConfig.defaultPort !== undefined
|
|
35
|
+
? transportConfig.defaultPort
|
|
36
|
+
: DEFAULT_PORT;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getUrlParseFn(): URLParseFn | undefined {
|
|
40
|
+
return transportConfig !== undefined && transportConfig.urlParseFn
|
|
41
|
+
? transportConfig.urlParseFn
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function newTransport(): Transport {
|
|
46
|
+
if (!transportConfig || typeof transportConfig.factory !== "function") {
|
|
47
|
+
throw new Error("transport fn is not set");
|
|
48
|
+
}
|
|
49
|
+
return transportConfig.factory();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getResolveFn(): DnsResolveFn | undefined {
|
|
53
|
+
return transportConfig !== undefined && transportConfig.dnsResolveFn
|
|
54
|
+
? transportConfig.dnsResolveFn
|
|
55
|
+
: undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface TransportFactory {
|
|
59
|
+
factory?: () => Transport;
|
|
60
|
+
defaultPort?: number;
|
|
61
|
+
urlParseFn?: URLParseFn;
|
|
62
|
+
dnsResolveFn?: DnsResolveFn;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface Transport extends AsyncIterable<Uint8Array> {
|
|
66
|
+
readonly isClosed: boolean;
|
|
67
|
+
readonly lang: string;
|
|
68
|
+
readonly version: string;
|
|
69
|
+
readonly closeError?: Error;
|
|
70
|
+
|
|
71
|
+
connect(
|
|
72
|
+
server: Server,
|
|
73
|
+
opts: ConnectionOptions,
|
|
74
|
+
): Promise<void>;
|
|
75
|
+
|
|
76
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;
|
|
77
|
+
|
|
78
|
+
isEncrypted(): boolean;
|
|
79
|
+
|
|
80
|
+
send(frame: Uint8Array): void;
|
|
81
|
+
|
|
82
|
+
close(err?: Error): Promise<void>;
|
|
83
|
+
|
|
84
|
+
disconnect(): void;
|
|
85
|
+
|
|
86
|
+
closed(): Promise<void | Error>;
|
|
87
|
+
|
|
88
|
+
// this is here for websocket implementations as some implementations
|
|
89
|
+
// (firefox) throttle connections that then resolve later
|
|
90
|
+
discard(): void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const CR_LF = "\r\n";
|
|
94
|
+
export const CR_LF_LEN = CR_LF.length;
|
|
95
|
+
export const CRLF = DataBuffer.fromAscii(CR_LF);
|
|
96
|
+
export const CR = new Uint8Array(CRLF)[0]; // 13
|
|
97
|
+
export const LF = new Uint8Array(CRLF)[1]; // 10
|
|
98
|
+
export function protoLen(ba: Uint8Array): number {
|
|
99
|
+
for (let i = 0; i < ba.length; i++) {
|
|
100
|
+
const n = i + 1;
|
|
101
|
+
if (ba.byteLength > n && ba[i] === CR && ba[n] === LF) {
|
|
102
|
+
return n + 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function extractProtocolMessage(a: Uint8Array): string {
|
|
109
|
+
// protocol messages are ascii, so Uint8Array
|
|
110
|
+
const len = protoLen(a);
|
|
111
|
+
if (len > 0) {
|
|
112
|
+
const ba = new Uint8Array(a);
|
|
113
|
+
const out = ba.slice(0, len);
|
|
114
|
+
return TD.decode(out);
|
|
115
|
+
}
|
|
116
|
+
return "";
|
|
117
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020-2023 The NATS Authors
|
|
3
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License.
|
|
5
|
+
* You may obtain a copy of the License at
|
|
6
|
+
*
|
|
7
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
*
|
|
9
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
* See the License for the specific language governing permissions and
|
|
13
|
+
* limitations under the License.
|
|
14
|
+
*/
|
|
15
|
+
export type { Dispatcher, MsgHdrs, QueuedIterator } from "./core.ts";
|
|
16
|
+
|
|
17
|
+
export { Empty } from "./encoders.ts";
|