@floegence/flowersec-core 2.5.3 → 3.0.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/README.md +46 -18
- package/dist/browser/connectSessionV3.d.ts +13 -0
- package/dist/browser/connectSessionV3.js +75 -0
- package/dist/browser/index.d.ts +3 -2
- package/dist/browser/index.js +2 -1
- package/dist/browser/v2.d.ts +3 -0
- package/dist/browser/v2.js +2 -0
- package/dist/facade.d.ts +15 -8
- package/dist/facade.js +5 -4
- package/dist/framing/jsonframe.d.ts +1 -1
- package/dist/framing/jsonframe.js +3 -1
- package/dist/interop/proxyServerPeer.js +1 -1
- package/dist/interop/serverParityPeer.js +8 -1
- package/dist/node/acceptor.d.ts +10 -0
- package/dist/node/acceptor.js +53 -7
- package/dist/node/acceptorV3.d.ts +57 -0
- package/dist/node/acceptorV3.js +227 -0
- package/dist/node/connectSessionV3.d.ts +21 -0
- package/dist/node/connectSessionV3.js +73 -0
- package/dist/node/index.d.ts +15 -10
- package/dist/node/index.js +6 -5
- package/dist/node/nativeTransportAddon.d.ts +39 -1
- package/dist/node/nativeTransportAddon.js +29 -5
- package/dist/node/rawQuicAdapterV3.d.ts +10 -0
- package/dist/node/rawQuicAdapterV3.js +76 -0
- package/dist/node/rawQuicServerV3.d.ts +24 -0
- package/dist/node/rawQuicServerV3.js +34 -0
- package/dist/node/runtimeAuthorizationV3.d.ts +29 -0
- package/dist/node/runtimeAuthorizationV3.js +126 -0
- package/dist/node/tunnelRuntimeV3.d.ts +57 -0
- package/dist/node/tunnelRuntimeV3.js +857 -0
- package/dist/node/v2.d.ts +11 -0
- package/dist/node/v2.js +6 -0
- package/dist/node/webSocketServerV3.d.ts +27 -0
- package/dist/node/webSocketServerV3.js +219 -0
- package/dist/proxy/runtime.js +39 -6
- package/dist/proxy/serviceWorker.d.ts +2 -0
- package/dist/proxy/serviceWorker.js +63 -7
- package/dist/proxy/windowBridge.d.ts +1 -0
- package/dist/proxy/windowBridge.js +93 -33
- package/dist/proxy/wsPatch.d.ts +1 -0
- package/dist/proxy/wsPatch.js +39 -6
- package/dist/public/streamHandlers.d.ts +3 -0
- package/dist/public/streamHandlers.js +6 -1
- package/dist/rpc/client.js +9 -7
- package/dist/rpc/server.js +44 -20
- package/dist/rpc/validate.d.ts +1 -0
- package/dist/rpc/validate.js +18 -6
- package/dist/v2/index.d.ts +14 -0
- package/dist/v2/index.js +7 -0
- package/dist/v2/protocol.js +23 -32
- package/dist/v2/publicSession.js +4 -0
- package/dist/v2/session.d.ts +5 -0
- package/dist/v2/session.js +94 -35
- package/dist/v3/admissionError.d.ts +4 -0
- package/dist/v3/admissionError.js +8 -0
- package/dist/v3/artifact.d.ts +145 -0
- package/dist/v3/artifact.js +1162 -0
- package/dist/v3/artifactLease.d.ts +17 -0
- package/dist/v3/artifactLease.js +109 -0
- package/dist/v3/browserRuntime.d.ts +37 -0
- package/dist/v3/browserRuntime.js +197 -0
- package/dist/v3/browserSessionRuntime.d.ts +2 -0
- package/dist/v3/browserSessionRuntime.js +21 -0
- package/dist/v3/capability.d.ts +32 -0
- package/dist/v3/capability.js +345 -0
- package/dist/v3/carrier.d.ts +84 -0
- package/dist/v3/carrier.js +411 -0
- package/dist/v3/connectionController.d.ts +87 -0
- package/dist/v3/connectionController.js +805 -0
- package/dist/v3/contract.d.ts +65 -0
- package/dist/v3/contract.js +3 -0
- package/dist/v3/controller.d.ts +43 -0
- package/dist/v3/controller.js +303 -0
- package/dist/v3/handshake.d.ts +81 -0
- package/dist/v3/handshake.js +471 -0
- package/dist/v3/jcs.d.ts +6 -0
- package/dist/v3/jcs.js +69 -0
- package/dist/v3/jsonPreflight.d.ts +1 -0
- package/dist/v3/jsonPreflight.js +251 -0
- package/dist/v3/nodeRuntime.d.ts +40 -0
- package/dist/v3/nodeRuntime.js +315 -0
- package/dist/v3/nodeSessionRuntime.d.ts +2 -0
- package/dist/v3/nodeSessionRuntime.js +13 -0
- package/dist/v3/protocol.d.ts +99 -0
- package/dist/v3/protocol.js +773 -0
- package/dist/v3/publicApi.d.ts +12 -0
- package/dist/v3/publicApi.js +46 -0
- package/dist/v3/publicSession.d.ts +1 -0
- package/dist/v3/publicSession.js +281 -0
- package/dist/v3/retryDisposition.d.ts +16 -0
- package/dist/v3/retryDisposition.js +30 -0
- package/dist/v3/runtimeAdapters.d.ts +9 -0
- package/dist/v3/runtimeAdapters.js +109 -0
- package/dist/v3/security.d.ts +34 -0
- package/dist/v3/security.js +100 -0
- package/dist/v3/serverAdmission.d.ts +38 -0
- package/dist/v3/serverAdmission.js +223 -0
- package/dist/v3/session.d.ts +318 -0
- package/dist/v3/session.js +2289 -0
- package/dist/v3/sessionConnector.d.ts +37 -0
- package/dist/v3/sessionConnector.js +440 -0
- package/dist/v3/streamLifetimeLedger.d.ts +31 -0
- package/dist/v3/streamLifetimeLedger.js +131 -0
- package/dist/v3/streamMetadata.d.ts +1 -0
- package/dist/v3/streamMetadata.js +7 -0
- package/dist/v3/transportConstants.d.ts +56 -0
- package/dist/v3/transportConstants.js +59 -0
- package/dist/v3/unreliableMessage.d.ts +44 -0
- package/dist/v3/unreliableMessage.js +284 -0
- package/dist/v3/webSocketCarrier.d.ts +31 -0
- package/dist/v3/webSocketCarrier.js +370 -0
- package/dist/v3/webTransportCarrier.d.ts +27 -0
- package/dist/v3/webTransportCarrier.js +534 -0
- package/dist/yamux/stream.d.ts +3 -0
- package/dist/yamux/stream.js +17 -4
- package/package.json +4 -4
- package/sbom/cyclonedx.json +6 -6
- package/sbom/spdx.json +11 -11
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { projectSessionV3 } from "../v3/publicSession.js";
|
|
2
|
+
import { acceptCarrierSessionV3, createAdmissionReasonRegistryV3, } from "../v3/serverAdmission.js";
|
|
3
|
+
import { nodeSessionRuntimeV3 } from "../v3/nodeSessionRuntime.js";
|
|
4
|
+
import { AdmissionStatusV3 } from "../v3/artifact.js";
|
|
5
|
+
import { unwrapArtifactHandleV3, } from "../v3/publicApi.js";
|
|
6
|
+
import { SessionHandlersV3, createRPCRouter, freezeSessionHandlersV3, } from "./acceptor.js";
|
|
7
|
+
import { serveFrozenStreamHandlers } from "../public/streamHandlers.js";
|
|
8
|
+
import { createNativeRawQuicDriverV3, loadNativeTransportAddon, } from "./nativeTransportAddon.js";
|
|
9
|
+
import { startNodeRawQuicListenerV3, } from "./rawQuicServerV3.js";
|
|
10
|
+
import { startNodeWebSocketListenerV3, } from "./webSocketServerV3.js";
|
|
11
|
+
import { runtimeAuthorizationRequestV3FromDecoded, } from "./runtimeAuthorizationV3.js";
|
|
12
|
+
const DEFAULT_ADMISSION_TIMEOUT_MS = 10_000;
|
|
13
|
+
export class AcceptedSessionV3 {
|
|
14
|
+
constructor() { }
|
|
15
|
+
get session() {
|
|
16
|
+
return acceptedSessionStateV3(this).session;
|
|
17
|
+
}
|
|
18
|
+
async serve(options = {}) {
|
|
19
|
+
const state = acceptedSessionStateV3(this);
|
|
20
|
+
await serveFrozenStreamHandlers(state.handlers.streams, state.session, options, async () => await this.close());
|
|
21
|
+
}
|
|
22
|
+
async close() {
|
|
23
|
+
const state = acceptedSessionStateV3(this);
|
|
24
|
+
state.closePromise ??= state.session.close();
|
|
25
|
+
await state.closePromise;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class AcceptorV3 {
|
|
29
|
+
constructor() { }
|
|
30
|
+
addresses() {
|
|
31
|
+
return acceptorState(this).listeners.map((listener) => listener.address());
|
|
32
|
+
}
|
|
33
|
+
async accept(options = {}) {
|
|
34
|
+
const state = acceptorState(this);
|
|
35
|
+
if (state.closed)
|
|
36
|
+
throw new Error("Flowersec v3 acceptor is closed");
|
|
37
|
+
const controller = new AbortController();
|
|
38
|
+
const unlinkExternal = linkAbort(options.signal, controller);
|
|
39
|
+
const unlinkClose = linkAbort(state.abort.signal, controller);
|
|
40
|
+
try {
|
|
41
|
+
const carrier = await acceptAny(state, controller.signal);
|
|
42
|
+
const admission = new AbortController();
|
|
43
|
+
const unlinkAdmission = linkAbort(controller.signal, admission);
|
|
44
|
+
const deadline = setTimeout(() => {
|
|
45
|
+
admission.abort(new Error("Flowersec v3 admission timed out"));
|
|
46
|
+
}, state.admissionTimeoutMs);
|
|
47
|
+
try {
|
|
48
|
+
let handlers;
|
|
49
|
+
const authorize = async (request, signal) => {
|
|
50
|
+
const authorizationRequest = runtimeAuthorizationRequestV3FromDecoded(request);
|
|
51
|
+
const decision = await state.authorize(authorizationRequest, signal === undefined ? {} : { signal });
|
|
52
|
+
if (decision.accepted) {
|
|
53
|
+
return { accepted: true, artifact: unwrapArtifactHandleV3(decision.artifact) };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
accepted: false,
|
|
57
|
+
status: decision.retryable
|
|
58
|
+
? AdmissionStatusV3.Retryable
|
|
59
|
+
: AdmissionStatusV3.Reject,
|
|
60
|
+
reason: decision.reason,
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const internal = await acceptCarrierSessionV3(carrier, authorize, {
|
|
64
|
+
runtime: nodeSessionRuntimeV3,
|
|
65
|
+
admissionReasons: state.admissionReasons,
|
|
66
|
+
resolveRPCRouter: async (request, signal) => {
|
|
67
|
+
const authorizationRequest = runtimeAuthorizationRequestV3FromDecoded(request);
|
|
68
|
+
const registry = state.resolveHandlers === undefined
|
|
69
|
+
? new SessionHandlersV3()
|
|
70
|
+
: await state.resolveHandlers(authorizationRequest, signal === undefined ? {} : { signal });
|
|
71
|
+
if (signal?.aborted === true) {
|
|
72
|
+
throw signal.reason instanceof Error ? signal.reason : new Error("Flowersec v3 admission canceled");
|
|
73
|
+
}
|
|
74
|
+
handlers = freezeSessionHandlersV3(registry);
|
|
75
|
+
return createRPCRouter(handlers.rpc);
|
|
76
|
+
},
|
|
77
|
+
signal: admission.signal,
|
|
78
|
+
});
|
|
79
|
+
if (handlers === undefined)
|
|
80
|
+
throw new Error("Flowersec v3 handlers were not resolved");
|
|
81
|
+
return createAcceptedSessionV3(projectSessionV3(internal), handlers);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
carrier.abort({ code: 6, reason: "admission failed" });
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
clearTimeout(deadline);
|
|
89
|
+
unlinkAdmission();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
unlinkExternal();
|
|
94
|
+
unlinkClose();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async close() {
|
|
98
|
+
const state = acceptorState(this);
|
|
99
|
+
const existing = closes.get(state);
|
|
100
|
+
if (existing !== undefined)
|
|
101
|
+
return await existing;
|
|
102
|
+
const closing = (async () => {
|
|
103
|
+
await Promise.resolve();
|
|
104
|
+
state.closed = true;
|
|
105
|
+
state.abort.abort(new Error("Flowersec v3 acceptor closed"));
|
|
106
|
+
const results = await Promise.allSettled(state.listeners.map(async (listener) => await listener.close()));
|
|
107
|
+
if (results.some(({ status }) => status === "rejected"))
|
|
108
|
+
throw new Error("Flowersec v3 acceptor cleanup failed");
|
|
109
|
+
})();
|
|
110
|
+
closes.set(state, closing);
|
|
111
|
+
return await closing;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const acceptorStatesV3 = new WeakMap();
|
|
115
|
+
const closes = new WeakMap();
|
|
116
|
+
const acceptedSessionStatesV3 = new WeakMap();
|
|
117
|
+
export async function createAcceptorV3(options) {
|
|
118
|
+
validateOptions(options);
|
|
119
|
+
const admissionReasons = createAdmissionReasonRegistryV3(["expired_artifact"], options.admissionReasons);
|
|
120
|
+
const listeners = [];
|
|
121
|
+
let rawDriver;
|
|
122
|
+
try {
|
|
123
|
+
for (const listener of options.listeners) {
|
|
124
|
+
const running = listener.carrier === "websocket"
|
|
125
|
+
? await startNodeWebSocketListenerV3({
|
|
126
|
+
...listener,
|
|
127
|
+
inboundBidirectionalStreamCapacity: options.maxInboundStreams + 2,
|
|
128
|
+
})
|
|
129
|
+
: await startNodeRawQuicListenerV3(rawDriver ??= createNativeRawQuicDriverV3(loadNativeTransportAddon()), {
|
|
130
|
+
...listener,
|
|
131
|
+
inboundBidirectionalStreamCapacity: options.maxInboundStreams + 2,
|
|
132
|
+
});
|
|
133
|
+
listeners.push(running);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
await Promise.allSettled(listeners.map(async (listener) => await listener.close()));
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
const acceptor = new AcceptorV3();
|
|
141
|
+
acceptorStatesV3.set(acceptor, {
|
|
142
|
+
listeners: Object.freeze(listeners),
|
|
143
|
+
authorize: options.authorize,
|
|
144
|
+
...(options.resolveHandlers === undefined ? {} : { resolveHandlers: options.resolveHandlers }),
|
|
145
|
+
admissionReasons,
|
|
146
|
+
admissionTimeoutMs: options.admissionTimeoutMs ?? DEFAULT_ADMISSION_TIMEOUT_MS,
|
|
147
|
+
abort: new AbortController(),
|
|
148
|
+
closed: false,
|
|
149
|
+
cursor: 0,
|
|
150
|
+
});
|
|
151
|
+
return Object.freeze(acceptor);
|
|
152
|
+
}
|
|
153
|
+
function createAcceptedSessionV3(session, handlers) {
|
|
154
|
+
const accepted = new AcceptedSessionV3();
|
|
155
|
+
acceptedSessionStatesV3.set(accepted, { session, handlers });
|
|
156
|
+
return Object.freeze(accepted);
|
|
157
|
+
}
|
|
158
|
+
function acceptedSessionStateV3(accepted) {
|
|
159
|
+
const state = acceptedSessionStatesV3.get(accepted);
|
|
160
|
+
if (state === undefined)
|
|
161
|
+
throw new Error("invalid Flowersec v3 accepted session");
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
164
|
+
function acceptorState(value) {
|
|
165
|
+
const state = acceptorStatesV3.get(value);
|
|
166
|
+
if (state === undefined)
|
|
167
|
+
throw new Error("invalid Flowersec v3 acceptor");
|
|
168
|
+
return state;
|
|
169
|
+
}
|
|
170
|
+
async function acceptAny(state, signal) {
|
|
171
|
+
if (state.listeners.length === 1)
|
|
172
|
+
return await state.listeners[0].accept({ signal });
|
|
173
|
+
const controller = new AbortController();
|
|
174
|
+
const unlink = linkAbort(signal, controller);
|
|
175
|
+
try {
|
|
176
|
+
return await new Promise((resolve, reject) => {
|
|
177
|
+
let settled = false;
|
|
178
|
+
let remaining = state.listeners.length;
|
|
179
|
+
const errors = [];
|
|
180
|
+
state.listeners.forEach((_, index) => {
|
|
181
|
+
const selected = state.listeners[(index + state.cursor) % state.listeners.length];
|
|
182
|
+
void selected.accept({ signal: controller.signal }).then((carrier) => {
|
|
183
|
+
if (!settled) {
|
|
184
|
+
settled = true;
|
|
185
|
+
state.cursor = (state.cursor + 1) % state.listeners.length;
|
|
186
|
+
controller.abort(new Error("listener race complete"));
|
|
187
|
+
resolve(carrier);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
carrier.abort({ code: 6, reason: "listener race lost" });
|
|
191
|
+
}
|
|
192
|
+
}, (error) => {
|
|
193
|
+
errors.push(error);
|
|
194
|
+
remaining -= 1;
|
|
195
|
+
if (!settled && remaining === 0) {
|
|
196
|
+
settled = true;
|
|
197
|
+
reject(new AggregateError(errors, "all Flowersec v3 listeners failed"));
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
unlink();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function validateOptions(options) {
|
|
208
|
+
if (options.listeners.length === 0 ||
|
|
209
|
+
!Number.isSafeInteger(options.maxInboundStreams) || options.maxInboundStreams < 1 ||
|
|
210
|
+
options.maxInboundStreams > 128 || typeof options.authorize !== "function" ||
|
|
211
|
+
(options.admissionTimeoutMs !== undefined &&
|
|
212
|
+
(!Number.isSafeInteger(options.admissionTimeoutMs) ||
|
|
213
|
+
options.admissionTimeoutMs < 1 || options.admissionTimeoutMs > 600_000)) ||
|
|
214
|
+
(options.resolveHandlers !== undefined && typeof options.resolveHandlers !== "function") ||
|
|
215
|
+
options.listeners.some(({ path }) => path !== "direct")) {
|
|
216
|
+
throw new TypeError("invalid Flowersec v3 Acceptor options");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function linkAbort(source, target) {
|
|
220
|
+
if (source === undefined)
|
|
221
|
+
return () => undefined;
|
|
222
|
+
const abort = () => target.abort(source.reason);
|
|
223
|
+
source.addEventListener("abort", abort, { once: true });
|
|
224
|
+
if (source.aborted)
|
|
225
|
+
abort();
|
|
226
|
+
return () => source.removeEventListener("abort", abort);
|
|
227
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Session } from "../public/contract.js";
|
|
2
|
+
import type { ArtifactLeaseV3 } from "../v3/artifactLease.js";
|
|
3
|
+
import { type ArtifactSourceV3, type ConnectionControllerV3 } from "../v3/connectionController.js";
|
|
4
|
+
import { type RPCHandlers } from "./acceptor.js";
|
|
5
|
+
export type NodeTLSRootsV3 = string | Uint8Array | readonly (string | Uint8Array)[];
|
|
6
|
+
export type SessionOptionsV3 = Readonly<{
|
|
7
|
+
origin?: string;
|
|
8
|
+
roots?: NodeTLSRootsV3;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
connectTimeoutMs?: number;
|
|
11
|
+
rpcHandlers?: RPCHandlers;
|
|
12
|
+
}>;
|
|
13
|
+
export type ConnectionControllerOptionsV3 = Readonly<{
|
|
14
|
+
origin?: string;
|
|
15
|
+
roots?: NodeTLSRootsV3;
|
|
16
|
+
connectTimeoutMs?: number;
|
|
17
|
+
maximumAttempts?: number;
|
|
18
|
+
rpcHandlers?: RPCHandlers;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function connectV3(lease: ArtifactLeaseV3, options: SessionOptionsV3): Promise<Session>;
|
|
21
|
+
export declare function createConnectionControllerV3(source: ArtifactSourceV3, options: ConnectionControllerOptionsV3): ConnectionControllerV3<Session>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { SDK_DEFAULTS } from "../defaults.js";
|
|
2
|
+
import { createConnectionControllerV3 as createCoreControllerV3, } from "../v3/connectionController.js";
|
|
3
|
+
import { connectNodeWebSocketV3, detectNodeRuntimeCapabilityV3, } from "../v3/nodeRuntime.js";
|
|
4
|
+
import { attemptClaimedArtifactLeaseV3, connectArtifactLeaseV3 } from "../v3/sessionConnector.js";
|
|
5
|
+
import { readyNativeAdmissionV3, readyWebSocketAdmissionV3 } from "../v3/runtimeAdapters.js";
|
|
6
|
+
import { ConnectErrorV3, TransportFailureV3 } from "../v3/security.js";
|
|
7
|
+
import { nodeSessionRuntimeV3 } from "../v3/nodeSessionRuntime.js";
|
|
8
|
+
import { createNativeRawQuicDriverV3, tryLoadNativeTransportAddon, } from "./nativeTransportAddon.js";
|
|
9
|
+
import { createNodeRawQuicClientV3 } from "./rawQuicAdapterV3.js";
|
|
10
|
+
import { createRPCRouter, freezeRPCHandlers, } from "./acceptor.js";
|
|
11
|
+
export async function connectV3(lease, options) {
|
|
12
|
+
return await connectArtifactLeaseV3(lease, nodeRuntime(options), options.signal);
|
|
13
|
+
}
|
|
14
|
+
export function createConnectionControllerV3(source, options) {
|
|
15
|
+
const runtime = nodeRuntime(options);
|
|
16
|
+
const coreOptions = {
|
|
17
|
+
capabilitySnapshot: runtime.capabilitySnapshot,
|
|
18
|
+
projectSessionFailure,
|
|
19
|
+
...(options.maximumAttempts === undefined ? {} : { maximumAttempts: options.maximumAttempts }),
|
|
20
|
+
};
|
|
21
|
+
return createCoreControllerV3(source, async (context) => await attemptClaimedArtifactLeaseV3(context, runtime), coreOptions);
|
|
22
|
+
}
|
|
23
|
+
function nodeRuntime(options) {
|
|
24
|
+
const connectTimeoutMilliseconds = options.connectTimeoutMs ?? SDK_DEFAULTS.transport.connectTimeoutMs;
|
|
25
|
+
if (!Number.isSafeInteger(connectTimeoutMilliseconds) || connectTimeoutMilliseconds < 1) {
|
|
26
|
+
throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
|
|
27
|
+
}
|
|
28
|
+
const addon = tryLoadNativeTransportAddon();
|
|
29
|
+
const rawQuic = addon === undefined ? undefined : createNativeRawQuicDriverV3(addon);
|
|
30
|
+
const capability = detectNodeRuntimeCapabilityV3(rawQuic !== undefined, options.origin !== undefined);
|
|
31
|
+
let rpcSnapshot;
|
|
32
|
+
try {
|
|
33
|
+
rpcSnapshot = options.rpcHandlers === undefined ? undefined : freezeRPCHandlers(options.rpcHandlers);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
capabilitySnapshot: () => capability,
|
|
40
|
+
connectTimeoutMilliseconds,
|
|
41
|
+
protocolRuntime: nodeSessionRuntimeV3,
|
|
42
|
+
...(rpcSnapshot === undefined ? {} : { createRPCRouter: () => createRPCRouter(rpcSnapshot) }),
|
|
43
|
+
dial: async (candidate, artifact, attemptNow, _capability, signal) => {
|
|
44
|
+
if (candidate.carrier === "websocket") {
|
|
45
|
+
if (options.origin === undefined)
|
|
46
|
+
throw new TransportFailureV3("tls_unsupported");
|
|
47
|
+
const socket = await connectNodeWebSocketV3(candidate, attemptNow, {
|
|
48
|
+
origin: options.origin,
|
|
49
|
+
signal,
|
|
50
|
+
...(options.roots === undefined ? {} : { roots: options.roots }),
|
|
51
|
+
timeoutMilliseconds: connectTimeoutMilliseconds,
|
|
52
|
+
});
|
|
53
|
+
return await readyWebSocketAdmissionV3(candidate, artifact, socket, signal);
|
|
54
|
+
}
|
|
55
|
+
if (candidate.carrier === "raw_quic" && rawQuic !== undefined) {
|
|
56
|
+
const carrier = await createNodeRawQuicClientV3(rawQuic, candidate, artifact, attemptNow, {
|
|
57
|
+
signal,
|
|
58
|
+
...(options.roots === undefined ? {} : { roots: options.roots }),
|
|
59
|
+
handshakeTimeoutMs: connectTimeoutMilliseconds,
|
|
60
|
+
});
|
|
61
|
+
return readyNativeAdmissionV3(candidate, carrier);
|
|
62
|
+
}
|
|
63
|
+
throw new TransportFailureV3("tls_unsupported");
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function projectSessionFailure(error) {
|
|
68
|
+
const code = error.code;
|
|
69
|
+
const retryable = new Set([
|
|
70
|
+
"closed", "going_away", "timeout", "resource_exhausted", "stream_reset", "rekey_failed", "liveness_failed",
|
|
71
|
+
]).has(String(code));
|
|
72
|
+
return new ConnectErrorV3("connection_failed", { kind: retryable ? "retryable" : "terminal" });
|
|
73
|
+
}
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export
|
|
4
|
-
export {
|
|
5
|
-
export
|
|
6
|
-
export type {
|
|
7
|
-
export { AuthorizationRecord, AuthorizationResponse, TunnelAuthorizationResponse, ControlPlaneError, EndpointSet, IssuedArtifact, Issuer, RuntimeAuthorizationRequest, authorizeRuntime, authorizeTunnelRuntime, createEndpointSet, parseAuthorizationRecord, parseRuntimeAuthorizationRequest, rejectRuntime, rejectTunnelRuntime, } from "./controlplane.js";
|
|
8
|
-
export type { ArtifactMetadata, ControlPlaneErrorCode, DirectIssueOptions, IssuedTunnelPair, Scope, SessionOptions as ControlPlaneSessionOptions, TunnelIssueOptions, } from "./controlplane.js";
|
|
9
|
-
export { ProxyServer, ProxyServerError } from "./proxyServer.js";
|
|
10
|
-
export type { ProxyServerOptions, StreamHandlerRegistrar } from "./proxyServer.js";
|
|
1
|
+
export { AcceptedSessionV3 as AcceptedSession, AcceptedSessionV3, AcceptorV3 as Acceptor, AcceptorV3, createAcceptorV3 as createAcceptor, createAcceptorV3, } from "./acceptorV3.js";
|
|
2
|
+
export type { AcceptorListenerV3 as AcceptorListener, AcceptorListenerV3, AcceptorAuthorizationDecisionV3 as AcceptorAuthorizationDecision, AcceptorAuthorizationDecisionV3, AcceptorAuthorizerV3 as AcceptorAuthorizer, AcceptorAuthorizerV3, AcceptorOptionsV3 as AcceptorOptions, AcceptorOptionsV3, } from "./acceptorV3.js";
|
|
3
|
+
export { TunnelRuntimeV3 as TunnelRuntime, TunnelRuntimeV3, createTunnelRuntimeV3 as createTunnelRuntime, createTunnelRuntimeV3, } from "./tunnelRuntimeV3.js";
|
|
4
|
+
export type { TunnelAuthorizationDecisionV3 as TunnelAuthorizationDecision, TunnelAuthorizationDecisionV3, TunnelRuntimeListenerV3 as TunnelRuntimeListener, TunnelRuntimeListenerV3, TunnelRuntimeOptionsV3 as TunnelRuntimeOptions, TunnelRuntimeOptionsV3, } from "./tunnelRuntimeV3.js";
|
|
5
|
+
export { RuntimeAuthorizationRequestV3 as RuntimeAuthorizationRequest, RuntimeAuthorizationRequestV3, TunnelAuthorizationGrantV3 as TunnelAuthorizationGrant, TunnelAuthorizationGrantV3, verifyTunnelAuthorizationGrantV3 as verifyTunnelAuthorizationGrant, verifyTunnelAuthorizationGrantV3, } from "./runtimeAuthorizationV3.js";
|
|
6
|
+
export type { TunnelAuthorizationGrantOptionsV3 as TunnelAuthorizationGrantOptions, TunnelAuthorizationGrantOptionsV3, } from "./runtimeAuthorizationV3.js";
|
|
11
7
|
export * from "../facade.js";
|
|
8
|
+
export * as v2 from "./v2.js";
|
|
9
|
+
import type { NodeTLSRootsV3 } from "./connectSessionV3.js";
|
|
10
|
+
export { connectV3 as connect, connectV3, createConnectionControllerV3 as createConnectionController, createConnectionControllerV3, } from "./connectSessionV3.js";
|
|
11
|
+
export type { ConnectionControllerOptionsV3 as ConnectionControllerOptions, ConnectionControllerOptionsV3, NodeTLSRootsV3, SessionOptionsV3 as SessionOptions, SessionOptionsV3, } from "./connectSessionV3.js";
|
|
12
|
+
export type SessionTLSOptions = Readonly<{
|
|
13
|
+
ca?: NodeTLSRootsV3;
|
|
14
|
+
}>;
|
|
15
|
+
export { HandlerRegistrationError, RPCHandlers, SessionHandlersV3 as SessionHandlers, } from "./acceptor.js";
|
|
16
|
+
export type { NotificationHandler, RPCHandler, RPCHandlerResult, SessionHandlerOptions, } from "./acceptor.js";
|
package/dist/node/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export { AuthorizationRecord, AuthorizationResponse, TunnelAuthorizationResponse, ControlPlaneError, EndpointSet, IssuedArtifact, Issuer, RuntimeAuthorizationRequest, authorizeRuntime, authorizeTunnelRuntime, createEndpointSet, parseAuthorizationRecord, parseRuntimeAuthorizationRequest, rejectRuntime, rejectTunnelRuntime, } from "./controlplane.js";
|
|
5
|
-
export { ProxyServer, ProxyServerError } from "./proxyServer.js";
|
|
1
|
+
export { AcceptedSessionV3 as AcceptedSession, AcceptedSessionV3, AcceptorV3 as Acceptor, AcceptorV3, createAcceptorV3 as createAcceptor, createAcceptorV3, } from "./acceptorV3.js";
|
|
2
|
+
export { TunnelRuntimeV3 as TunnelRuntime, TunnelRuntimeV3, createTunnelRuntimeV3 as createTunnelRuntime, createTunnelRuntimeV3, } from "./tunnelRuntimeV3.js";
|
|
3
|
+
export { RuntimeAuthorizationRequestV3 as RuntimeAuthorizationRequest, RuntimeAuthorizationRequestV3, TunnelAuthorizationGrantV3 as TunnelAuthorizationGrant, TunnelAuthorizationGrantV3, verifyTunnelAuthorizationGrantV3 as verifyTunnelAuthorizationGrant, verifyTunnelAuthorizationGrantV3, } from "./runtimeAuthorizationV3.js";
|
|
6
4
|
export * from "../facade.js";
|
|
5
|
+
export * as v2 from "./v2.js";
|
|
6
|
+
export { connectV3 as connect, connectV3, createConnectionControllerV3 as createConnectionController, createConnectionControllerV3, } from "./connectSessionV3.js";
|
|
7
|
+
export { HandlerRegistrationError, RPCHandlers, SessionHandlersV3 as SessionHandlers, } from "./acceptor.js";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type NativeCarrierSessionV2 } from "../v2/carrier.js";
|
|
2
2
|
import type { PathKind } from "../v2/contract.js";
|
|
3
|
-
|
|
3
|
+
import type { NativeCarrierSessionV3 } from "../v3/carrier.js";
|
|
4
|
+
export declare const NATIVE_TRANSPORT_CONTRACT_VERSION = 2;
|
|
4
5
|
export type NativeRawQuicConnectOptions = Readonly<{
|
|
5
6
|
host: string;
|
|
6
7
|
port: number;
|
|
@@ -19,6 +20,23 @@ export type NativeRawQuicBindOptions = Readonly<{
|
|
|
19
20
|
inboundBidirectionalStreamCapacity: number;
|
|
20
21
|
handshakeTimeoutMs?: number;
|
|
21
22
|
}>;
|
|
23
|
+
type NativeRawQuicConnectOptionsV3Base = Readonly<{
|
|
24
|
+
host: string;
|
|
25
|
+
port: number;
|
|
26
|
+
serverName: string;
|
|
27
|
+
path: PathKind;
|
|
28
|
+
inboundBidirectionalStreamCapacity: number;
|
|
29
|
+
handshakeTimeoutMs: number;
|
|
30
|
+
}>;
|
|
31
|
+
export type NativeRawQuicConnectOptionsV3 = NativeRawQuicConnectOptionsV3Base & (Readonly<{
|
|
32
|
+
tlsMode: "ca";
|
|
33
|
+
trustRootsDer: readonly Uint8Array[];
|
|
34
|
+
activeLeafDerSha256?: never;
|
|
35
|
+
}> | Readonly<{
|
|
36
|
+
tlsMode: "pin";
|
|
37
|
+
activeLeafDerSha256: readonly Uint8Array[];
|
|
38
|
+
trustRootsDer?: never;
|
|
39
|
+
}>);
|
|
22
40
|
export type NativeRawQuicListener = Readonly<{
|
|
23
41
|
address(): Readonly<{
|
|
24
42
|
host: string;
|
|
@@ -35,6 +53,22 @@ export type NativeRawQuicDriver = Readonly<{
|
|
|
35
53
|
}>): Promise<NativeCarrierSessionV2>;
|
|
36
54
|
bindRawQuic(options: NativeRawQuicBindOptions): Promise<NativeRawQuicListener>;
|
|
37
55
|
}>;
|
|
56
|
+
export type NativeRawQuicListenerV3 = Readonly<{
|
|
57
|
+
address(): Readonly<{
|
|
58
|
+
host: string;
|
|
59
|
+
port: number;
|
|
60
|
+
}>;
|
|
61
|
+
accept(options?: Readonly<{
|
|
62
|
+
signal?: AbortSignal;
|
|
63
|
+
}>): Promise<NativeCarrierSessionV3>;
|
|
64
|
+
close(): Promise<void>;
|
|
65
|
+
}>;
|
|
66
|
+
export type NativeRawQuicDriverV3 = Readonly<{
|
|
67
|
+
connectRawQuic(options: NativeRawQuicConnectOptionsV3, operation?: Readonly<{
|
|
68
|
+
signal?: AbortSignal;
|
|
69
|
+
}>): Promise<NativeCarrierSessionV3>;
|
|
70
|
+
bindRawQuic(options: NativeRawQuicBindOptions): Promise<NativeRawQuicListenerV3>;
|
|
71
|
+
}>;
|
|
38
72
|
type NativeOperation<T> = Readonly<{
|
|
39
73
|
result(): Promise<T>;
|
|
40
74
|
cancel(): void;
|
|
@@ -51,6 +85,7 @@ type NativeRawQuicStreamBinding = Readonly<{
|
|
|
51
85
|
type NativeRawQuicSessionBinding = Readonly<{
|
|
52
86
|
kind: "raw_quic";
|
|
53
87
|
path: PathKind;
|
|
88
|
+
wireVersion: 2 | 3;
|
|
54
89
|
inboundBidirectionalStreamCapacity: number;
|
|
55
90
|
maxDatagramSize?: number;
|
|
56
91
|
localAddress(): Readonly<{
|
|
@@ -82,6 +117,8 @@ export type NativeTransportAddonBinding = Readonly<{
|
|
|
82
117
|
contractVersion(): number;
|
|
83
118
|
connectRawQuic(options: NativeRawQuicConnectOptions): NativeOperation<NativeRawQuicSessionBinding>;
|
|
84
119
|
bindRawQuic(options: NativeRawQuicBindOptions): Promise<NativeRawQuicListenerBinding>;
|
|
120
|
+
connectRawQuicV3(options: NativeRawQuicConnectOptionsV3): NativeOperation<NativeRawQuicSessionBinding>;
|
|
121
|
+
bindRawQuicV3(options: NativeRawQuicBindOptions): Promise<NativeRawQuicListenerBinding>;
|
|
85
122
|
}>;
|
|
86
123
|
export type NativeTransportAddon = NativeTransportAddonBinding;
|
|
87
124
|
export declare class NativeTransportUnavailableError extends Error {
|
|
@@ -92,4 +129,5 @@ type RequireFunction = (specifier: string) => unknown;
|
|
|
92
129
|
export declare function loadNativeTransportAddon(requireFunction?: RequireFunction): NativeTransportAddonBinding;
|
|
93
130
|
export declare function tryLoadNativeTransportAddon(requireFunction?: RequireFunction): NativeTransportAddonBinding | undefined;
|
|
94
131
|
export declare function createNativeRawQuicDriver(addon?: NativeTransportAddonBinding): NativeRawQuicDriver;
|
|
132
|
+
export declare function createNativeRawQuicDriverV3(addon?: NativeTransportAddonBinding): NativeRawQuicDriverV3;
|
|
95
133
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { CarrierError, } from "../v2/carrier.js";
|
|
3
|
-
export const NATIVE_TRANSPORT_CONTRACT_VERSION =
|
|
3
|
+
export const NATIVE_TRANSPORT_CONTRACT_VERSION = 2;
|
|
4
4
|
const NATIVE_PACKAGE = "@floegence/flowersec-node-native";
|
|
5
5
|
const SERVER_PARITY_NATIVE_ADDON = "FLOWERSEC_SERVER_PARITY_NATIVE_ADDON";
|
|
6
6
|
export class NativeTransportUnavailableError extends Error {
|
|
@@ -45,13 +45,31 @@ export function createNativeRawQuicDriver(addon = loadNativeTransportAddon()) {
|
|
|
45
45
|
return Object.freeze({
|
|
46
46
|
async connectRawQuic(options, operation = {}) {
|
|
47
47
|
const native = await settle(addon.connectRawQuic(options), operation.signal);
|
|
48
|
-
return wrapSession(native);
|
|
48
|
+
return wrapSession(native, 2);
|
|
49
49
|
},
|
|
50
50
|
async bindRawQuic(options) {
|
|
51
51
|
const native = await addon.bindRawQuic(options);
|
|
52
52
|
return Object.freeze({
|
|
53
53
|
address: () => native.address(),
|
|
54
|
-
accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal)),
|
|
54
|
+
accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal), 2),
|
|
55
|
+
async close() {
|
|
56
|
+
await native.close();
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function createNativeRawQuicDriverV3(addon = loadNativeTransportAddon()) {
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
async connectRawQuic(options, operation = {}) {
|
|
65
|
+
const native = await settle(addon.connectRawQuicV3(options), operation.signal);
|
|
66
|
+
return wrapSession(native, 3);
|
|
67
|
+
},
|
|
68
|
+
async bindRawQuic(options) {
|
|
69
|
+
const native = await addon.bindRawQuicV3(options);
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
address: () => native.address(),
|
|
72
|
+
accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal), 3),
|
|
55
73
|
async close() {
|
|
56
74
|
await native.close();
|
|
57
75
|
},
|
|
@@ -74,9 +92,15 @@ function isNativeTransportAddon(value) {
|
|
|
74
92
|
}
|
|
75
93
|
return contractVersion === NATIVE_TRANSPORT_CONTRACT_VERSION &&
|
|
76
94
|
typeof candidate.connectRawQuic === "function" &&
|
|
77
|
-
typeof candidate.bindRawQuic === "function"
|
|
95
|
+
typeof candidate.bindRawQuic === "function" &&
|
|
96
|
+
typeof candidate.connectRawQuicV3 === "function" &&
|
|
97
|
+
typeof candidate.bindRawQuicV3 === "function";
|
|
78
98
|
}
|
|
79
|
-
function wrapSession(native) {
|
|
99
|
+
function wrapSession(native, expectedWireVersion) {
|
|
100
|
+
if (native.wireVersion !== expectedWireVersion) {
|
|
101
|
+
native.abort();
|
|
102
|
+
throw new NativeTransportUnavailableError();
|
|
103
|
+
}
|
|
80
104
|
const unreliableDatagrams = native.maxDatagramSize === undefined
|
|
81
105
|
? undefined
|
|
82
106
|
: Object.freeze({
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ArtifactV3, CanonicalArtifactCandidateV3 } from "../v3/artifact.js";
|
|
2
|
+
import type { NativeCarrierSessionV3 } from "../v3/carrier.js";
|
|
3
|
+
import type { NativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
|
|
4
|
+
type NodeTLSRootsV3 = string | Uint8Array | readonly (string | Uint8Array)[];
|
|
5
|
+
export declare function createNodeRawQuicClientV3(driver: NativeRawQuicDriverV3, candidate: CanonicalArtifactCandidateV3, artifact: ArtifactV3, attemptNowUnixSeconds: number, options?: Readonly<{
|
|
6
|
+
roots?: NodeTLSRootsV3;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
handshakeTimeoutMs?: number;
|
|
9
|
+
}>): Promise<NativeCarrierSessionV3>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { rootCertificates } from "node:tls";
|
|
2
|
+
import { snapshotTransportSecurityPolicyV3, TransportFailureV3, } from "../v3/security.js";
|
|
3
|
+
import { normalizeCertificateChain } from "./rawQuicAdapter.js";
|
|
4
|
+
import { alpnForPathV3 } from "../v3/transportConstants.js";
|
|
5
|
+
const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
|
|
6
|
+
export async function createNodeRawQuicClientV3(driver, candidate, artifact, attemptNowUnixSeconds, options = {}) {
|
|
7
|
+
if (candidate.carrier !== "raw_quic")
|
|
8
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
9
|
+
if (candidate.wire_profile !== alpnForPathV3(artifact.path.kind)) {
|
|
10
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
11
|
+
}
|
|
12
|
+
let url;
|
|
13
|
+
try {
|
|
14
|
+
url = new URL(candidate.normalized_url);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
18
|
+
}
|
|
19
|
+
if (url.protocol !== "quic:" || url.pathname !== "" || url.search !== "" || url.hash !== "" ||
|
|
20
|
+
url.username !== "" || url.password !== "") {
|
|
21
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
22
|
+
}
|
|
23
|
+
const handshakeTimeoutMs = options.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS;
|
|
24
|
+
if (!Number.isSafeInteger(handshakeTimeoutMs) || handshakeTimeoutMs < 1 || handshakeTimeoutMs > 120_000) {
|
|
25
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
26
|
+
}
|
|
27
|
+
const policy = snapshotTransportSecurityPolicyV3(candidate.tls, attemptNowUnixSeconds, ["ca", "pin"]);
|
|
28
|
+
const common = {
|
|
29
|
+
host: unbracket(url.hostname),
|
|
30
|
+
port: url.port === "" ? 443 : Number(url.port),
|
|
31
|
+
serverName: unbracket(url.hostname),
|
|
32
|
+
path: artifact.path.kind,
|
|
33
|
+
inboundBidirectionalStreamCapacity: artifact.session.max_inbound_streams + 2,
|
|
34
|
+
handshakeTimeoutMs,
|
|
35
|
+
};
|
|
36
|
+
const nativeOptions = policy.mode === "ca"
|
|
37
|
+
? {
|
|
38
|
+
...common,
|
|
39
|
+
tlsMode: "ca",
|
|
40
|
+
trustRootsDer: normalizeCertificateChain(options.roots ?? rootCertificates),
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
...common,
|
|
44
|
+
tlsMode: "pin",
|
|
45
|
+
activeLeafDerSha256: policy.activeLeafDerSHA256,
|
|
46
|
+
};
|
|
47
|
+
try {
|
|
48
|
+
return await driver.connectRawQuic(nativeOptions, {
|
|
49
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof TransportFailureV3)
|
|
54
|
+
throw error;
|
|
55
|
+
if (options.signal?.aborted === true)
|
|
56
|
+
throw options.signal.reason;
|
|
57
|
+
const reason = error instanceof Error ? error.message : "";
|
|
58
|
+
switch (reason) {
|
|
59
|
+
case "pin_mismatch":
|
|
60
|
+
throw new TransportFailureV3("tls_failed", "pin_mismatch", error);
|
|
61
|
+
case "pin_certificate_invalid":
|
|
62
|
+
case "handshake_failed":
|
|
63
|
+
case "invalid_alpn":
|
|
64
|
+
throw new TransportFailureV3("tls_failed", "unknown", error);
|
|
65
|
+
case "invalid_tls_policy":
|
|
66
|
+
case "invalid_trust_roots":
|
|
67
|
+
case "invalid_limits":
|
|
68
|
+
throw new TransportFailureV3("invalid_artifact", undefined, error);
|
|
69
|
+
default:
|
|
70
|
+
throw new TransportFailureV3("connection_failed", undefined, error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function unbracket(host) {
|
|
75
|
+
return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
76
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CarrierSessionV3 } from "../v3/carrier.js";
|
|
2
|
+
import type { PathKind } from "../v3/contract.js";
|
|
3
|
+
import type { NativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
|
|
4
|
+
export type NodeRawQuicListenerOptionsV3 = Readonly<{
|
|
5
|
+
host: string;
|
|
6
|
+
port: number;
|
|
7
|
+
path: PathKind;
|
|
8
|
+
tls: Readonly<{
|
|
9
|
+
certificate: string | Uint8Array;
|
|
10
|
+
privateKey: string | Uint8Array;
|
|
11
|
+
}>;
|
|
12
|
+
inboundBidirectionalStreamCapacity: number;
|
|
13
|
+
}>;
|
|
14
|
+
export type NodeRawQuicListenerV3 = Readonly<{
|
|
15
|
+
address(): Readonly<{
|
|
16
|
+
host: string;
|
|
17
|
+
port: number;
|
|
18
|
+
}>;
|
|
19
|
+
accept(options?: Readonly<{
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
}>): Promise<CarrierSessionV3>;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function startNodeRawQuicListenerV3(driver: NativeRawQuicDriverV3, options: NodeRawQuicListenerOptionsV3): Promise<NodeRawQuicListenerV3>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { adaptNativeCarrierSessionV3 } from "../v3/carrier.js";
|
|
2
|
+
import { normalizeCertificateChain, normalizePrivateKey } from "./rawQuicAdapter.js";
|
|
3
|
+
export async function startNodeRawQuicListenerV3(driver, options) {
|
|
4
|
+
validateOptions(options);
|
|
5
|
+
let listener;
|
|
6
|
+
try {
|
|
7
|
+
listener = await driver.bindRawQuic({
|
|
8
|
+
host: options.host,
|
|
9
|
+
port: options.port,
|
|
10
|
+
path: options.path,
|
|
11
|
+
certificateChainDer: normalizeCertificateChain(options.tls.certificate),
|
|
12
|
+
privateKeyDer: normalizePrivateKey(options.tls.privateKey),
|
|
13
|
+
inboundBidirectionalStreamCapacity: options.inboundBidirectionalStreamCapacity,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error instanceof TypeError)
|
|
18
|
+
throw error;
|
|
19
|
+
throw new Error("Flowersec raw QUIC v3 listener failed to bind");
|
|
20
|
+
}
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
address: () => listener.address(),
|
|
23
|
+
accept: async (operation = {}) => adaptNativeCarrierSessionV3(await listener.accept(operation)),
|
|
24
|
+
close: async () => await listener.close(),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function validateOptions(options) {
|
|
28
|
+
if (!Number.isInteger(options.port) || options.port < 0 || options.port > 65_535 ||
|
|
29
|
+
!Number.isInteger(options.inboundBidirectionalStreamCapacity) ||
|
|
30
|
+
options.inboundBidirectionalStreamCapacity < 3 || options.inboundBidirectionalStreamCapacity > 130 ||
|
|
31
|
+
options.tls.certificate.length === 0 || options.tls.privateKey.length === 0) {
|
|
32
|
+
throw new TypeError("invalid Node raw QUIC v3 listener options");
|
|
33
|
+
}
|
|
34
|
+
}
|