@inkbox/sdk 0.4.16 → 0.4.18
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 +17 -1
- package/dist/_http.d.ts +2 -1
- package/dist/_http.d.ts.map +1 -1
- package/dist/_http.js +6 -1
- package/dist/_http.js.map +1 -1
- package/dist/agent_identity.d.ts +10 -0
- package/dist/agent_identity.d.ts.map +1 -1
- package/dist/agent_identity.js +11 -0
- package/dist/agent_identity.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inkbox.d.ts +6 -0
- package/dist/inkbox.d.ts.map +1 -1
- package/dist/inkbox.js +22 -9
- package/dist/inkbox.js.map +1 -1
- package/dist/mail/resources/messages.d.ts +13 -2
- package/dist/mail/resources/messages.d.ts.map +1 -1
- package/dist/mail/resources/messages.js +24 -12
- package/dist/mail/resources/messages.js.map +1 -1
- package/dist/tunnels/client/_listener.d.ts +1 -1
- package/dist/tunnels/client/_listener.d.ts.map +1 -1
- package/dist/tunnels/client/_runtime.d.ts +29 -1
- package/dist/tunnels/client/_runtime.d.ts.map +1 -1
- package/dist/tunnels/client/_runtime.js +165 -14
- package/dist/tunnels/client/_runtime.js.map +1 -1
- package/dist/tunnels/client/index.d.ts +1 -1
- package/dist/tunnels/client/index.d.ts.map +1 -1
- package/dist/tunnels/client/index.js +1 -1
- package/dist/tunnels/client/index.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +1 -1
|
@@ -68,7 +68,9 @@ export class MessagesResource {
|
|
|
68
68
|
* replied to. Threads the reply automatically.
|
|
69
69
|
* @param options.attachments - Optional file attachments. Each entry must have
|
|
70
70
|
* `filename`, `contentType` (MIME type), and `contentBase64` (base64-encoded
|
|
71
|
-
* file content). Max total size: 25 MB. Blocked: `.exe`, `.bat`, `.scr`.
|
|
71
|
+
* file content). Max total size: 25 MB. Blocked: `.exe`, `.bat`, `.scr`. Set
|
|
72
|
+
* `contentId` on an entry to render it inline in the HTML body instead of as
|
|
73
|
+
* a download (see the field doc).
|
|
72
74
|
* @param options.trackOpens - Embed an open-tracking pixel in the HTML body.
|
|
73
75
|
* Requires `bodyHtml`; a plain-text-only send with `trackOpens` is rejected
|
|
74
76
|
* with 422. Opens surface as `firstOpenedAt`/`openCount`; `openCount` is
|
|
@@ -93,11 +95,16 @@ export class MessagesResource {
|
|
|
93
95
|
body["in_reply_to_message_id"] = options.inReplyToMessageId;
|
|
94
96
|
}
|
|
95
97
|
if (options.attachments !== undefined) {
|
|
96
|
-
body["attachments"] = options.attachments.map((a) =>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
body["attachments"] = options.attachments.map((a) => {
|
|
99
|
+
const att = {
|
|
100
|
+
filename: a.filename,
|
|
101
|
+
content_type: a.contentType,
|
|
102
|
+
content_base64: a.contentBase64,
|
|
103
|
+
};
|
|
104
|
+
if (a.contentId !== undefined)
|
|
105
|
+
att["content_id"] = a.contentId;
|
|
106
|
+
return att;
|
|
107
|
+
});
|
|
101
108
|
}
|
|
102
109
|
if (options.trackOpens)
|
|
103
110
|
body["track_opens"] = true;
|
|
@@ -115,7 +122,7 @@ export class MessagesResource {
|
|
|
115
122
|
* @param options.bodyText - Plain-text reply body.
|
|
116
123
|
* @param options.bodyHtml - HTML reply body.
|
|
117
124
|
* @param options.attachments - Optional file attachments. Same shape as
|
|
118
|
-
* `send({ attachments })
|
|
125
|
+
* `send({ attachments })`, including `contentId` for inline images.
|
|
119
126
|
* @param options.replyTo - Optional Reply-To address.
|
|
120
127
|
*/
|
|
121
128
|
async replyAll(emailAddress, messageId, options = {}) {
|
|
@@ -127,11 +134,16 @@ export class MessagesResource {
|
|
|
127
134
|
if (options.bodyHtml !== undefined)
|
|
128
135
|
body["body_html"] = options.bodyHtml;
|
|
129
136
|
if (options.attachments !== undefined) {
|
|
130
|
-
body["attachments"] = options.attachments.map((a) =>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
body["attachments"] = options.attachments.map((a) => {
|
|
138
|
+
const att = {
|
|
139
|
+
filename: a.filename,
|
|
140
|
+
content_type: a.contentType,
|
|
141
|
+
content_base64: a.contentBase64,
|
|
142
|
+
};
|
|
143
|
+
if (a.contentId !== undefined)
|
|
144
|
+
att["content_id"] = a.contentId;
|
|
145
|
+
return att;
|
|
146
|
+
});
|
|
135
147
|
}
|
|
136
148
|
if (options.replyTo !== undefined)
|
|
137
149
|
body["reply_to"] = options.replyTo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/mail/resources/messages.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,WAAW,EAMX,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,MAAM,OAAO,gBAAgB;IACE;IAA7B,YAA6B,IAAmB;QAAnB,SAAI,GAAJ,IAAI,CAAe;IAAG,CAAC;IAEpD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,CAAC,IAAI,CACT,YAAoB,EACpB,OAA6D;QAE7D,MAAM,KAAK,GAAG,OAAO,EAAE,QAAQ,IAAI,iBAAiB,CAAC;QACrD,IAAI,MAA0B,CAAC;QAE/B,OAAO,IAAI,EAAE,CAAC;YACZ,MAAM,MAAM,GAAgD,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YAC9E,IAAI,OAAO,EAAE,SAAS,KAAK,SAAS;gBAAE,MAAM,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC;YAC9E,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAC9B,cAAc,YAAY,WAAW,EACrC,MAAM,CACP,CAAC;YACF,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC9B,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,MAAM;YAC1B,MAAM,GAAG,IAAI,CAAC,WAAW,IAAI,SAAS,CAAC;QACzC,CAAC;IACH,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,GAAG,CAAC,YAAoB,EAAE,SAAiB;QAC/C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAC9B,cAAc,YAAY,aAAa,SAAS,EAAE,CACnD,CAAC;QACF,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/mail/resources/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { Tunnel } from "../types.js";
|
|
9
9
|
import type { TunnelRuntime } from "./_runtime.js";
|
|
10
|
-
export type TunnelStatusCallback = (status: "connecting" | "connected" | "reconnecting" | "closed") => void;
|
|
10
|
+
export type TunnelStatusCallback = (status: "connecting" | "connected" | "reconnecting" | "closed" | "superseded") => void;
|
|
11
11
|
/**
|
|
12
12
|
* Options that affect how the listener behaves around process signals.
|
|
13
13
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_listener.d.ts","sourceRoot":"","sources":["../../../src/tunnels/client/_listener.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,MAAM,oBAAoB,GAAG,CACjC,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"_listener.d.ts","sourceRoot":"","sources":["../../../src/tunnels/client/_listener.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,MAAM,oBAAoB,GAAG,CACjC,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,QAAQ,GAAG,YAAY,KAC1E,IAAI,CAAC;AAEV;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,6CAA6C;IAC7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,0CAA0C;IAC1C,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,qCAAqC;IACrC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,qBAAa,kBAAmB,YAAW,cAAc;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,sBAAsB,CAA6B;IAC3D,OAAO,CAAC,uBAAuB,CAA6B;IAC5D,OAAO,CAAC,gBAAgB,CAAS;gBAErB,IAAI,EAAE;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,aAAa,CAAC;QACvB,YAAY,CAAC,EAAE,kBAAkB,CAAC;KACnC;IAkDK,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAY7B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAoB9B"}
|
|
@@ -36,10 +36,17 @@ export declare const HANDOFF_SETTLE_MS = 2000;
|
|
|
36
36
|
export declare const POST_ACTIVE_WAIT_MS = 5000;
|
|
37
37
|
export declare const DEFAULT_INBOUND_BODY_BYTES: number;
|
|
38
38
|
export declare const DEFAULT_OUTBOUND_BODY_BYTES: number;
|
|
39
|
+
export declare const SUPERSEDED_GOAWAY_ERROR_CODE = 4609;
|
|
40
|
+
export declare const GOAWAY_REASON_SUPERSEDED = "superseded";
|
|
41
|
+
export declare const INTAKE_REASON_SUPERSEDED = "intake-superseded";
|
|
42
|
+
export declare const HELLO_REASON_SUPERSEDED = "hello-superseded";
|
|
39
43
|
export declare class TunnelAuthError extends Error {
|
|
40
44
|
constructor(message: string);
|
|
41
45
|
}
|
|
42
|
-
export
|
|
46
|
+
export declare class TunnelSupersededError extends Error {
|
|
47
|
+
constructor(message: string);
|
|
48
|
+
}
|
|
49
|
+
export type StatusCallback = (status: "connecting" | "connected" | "reconnecting" | "closed" | "superseded") => void;
|
|
43
50
|
/**
|
|
44
51
|
* What this runtime forwards to. URL is required; `httpHandler` and
|
|
45
52
|
* `wsHandler` activate the in-process callable paths.
|
|
@@ -107,6 +114,7 @@ export declare class TunnelRuntime {
|
|
|
107
114
|
private handoffPromise;
|
|
108
115
|
private wakeSupervisor;
|
|
109
116
|
private stop;
|
|
117
|
+
private superseded;
|
|
110
118
|
private readonly tasks;
|
|
111
119
|
private passthroughDispatch;
|
|
112
120
|
private readonly undiciAgentCache;
|
|
@@ -132,6 +140,26 @@ export declare class TunnelRuntime {
|
|
|
132
140
|
/** Resolve once the supervised conn closes OR a handoff swaps active. */
|
|
133
141
|
private waitCloseOrHandoff;
|
|
134
142
|
private signalSupervisor;
|
|
143
|
+
/**
|
|
144
|
+
* True iff a takeover signal on `conn` should stop the runtime. Ignored only
|
|
145
|
+
* for a conn we ourselves put into make-before-break drain (our own
|
|
146
|
+
* predecessor, tracked in `this.draining`); a not-yet-active replacement
|
|
147
|
+
* mid-handoff is a real external takeover, so it stays terminal.
|
|
148
|
+
*/
|
|
149
|
+
private supersededIsTerminal;
|
|
150
|
+
/** Record the takeover; the supervisor will stop and not reconnect. */
|
|
151
|
+
private markSuperseded;
|
|
152
|
+
/**
|
|
153
|
+
* Log, notify, and throw the terminal takeover error (no reconnect).
|
|
154
|
+
* Rethrows the original error when given, so its message (which
|
|
155
|
+
* channel/slot observed the takeover) survives to the caller.
|
|
156
|
+
*/
|
|
157
|
+
private stopSuperseded;
|
|
158
|
+
/**
|
|
159
|
+
* Classify a non-zero GOAWAY: takeover (terminal) vs infra fault (reconnect).
|
|
160
|
+
* The dedicated code is authoritative; the reason is a belt cross-check.
|
|
161
|
+
*/
|
|
162
|
+
private maybeMarkSupersededGoaway;
|
|
135
163
|
/**
|
|
136
164
|
* On a NO_ERROR GOAWAY, mark the old conn draining and stand up a fresh
|
|
137
165
|
* connection before closing it. In-band: never trips the backoff loop.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_runtime.d.ts","sourceRoot":"","sources":["../../../src/tunnels/client/_runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAGpC,OAAO,KAAK,EAAE,kBAAkB,EAAqB,MAAM,YAAY,CAAC;AAiBxE,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AAiBvB,OAAO,KAAK,EAAc,aAAa,EAAE,MAAM,WAAW,CAAC;AAY3D,OAAO,EAGL,KAAK,eAAe,EAErB,MAAM,UAAU,CAAC;AAQlB,eAAO,MAAM,gBAAgB,QAAS,CAAC;AACvC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAC1C,eAAO,MAAM,eAAe,KAAO,CAAC;AACpC,eAAO,MAAM,cAAc,OAAO,CAAC;AAGnC,eAAO,MAAM,oCAAoC,QAAS,CAAC;AAI3D,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAI/C,eAAO,MAAM,iBAAiB,OAAQ,CAAC;AAIvC,eAAO,MAAM,mBAAmB,OAAQ,CAAC;AAEzC,eAAO,MAAM,0BAA0B,QAAmB,CAAC;AAC3D,eAAO,MAAM,2BAA2B,QAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"_runtime.d.ts","sourceRoot":"","sources":["../../../src/tunnels/client/_runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -57,12 +57,26 @@ export const HANDOFF_SETTLE_MS = 2_000;
|
|
|
57
57
|
export const POST_ACTIVE_WAIT_MS = 5_000;
|
|
58
58
|
export const DEFAULT_INBOUND_BODY_BYTES = 32 * 1024 * 1024;
|
|
59
59
|
export const DEFAULT_OUTBOUND_BODY_BYTES = 32 * 1024 * 1024;
|
|
60
|
+
// Signals that another client connected to this tunnel and took over: this
|
|
61
|
+
// client must stop and not reconnect. Delivered as a dedicated GOAWAY error
|
|
62
|
+
// code (the reliable channel) plus matching reason strings on the GOAWAY debug
|
|
63
|
+
// data and the intake / hello responses. Must stay in lockstep with the server.
|
|
64
|
+
export const SUPERSEDED_GOAWAY_ERROR_CODE = 0x1201;
|
|
65
|
+
export const GOAWAY_REASON_SUPERSEDED = "superseded";
|
|
66
|
+
export const INTAKE_REASON_SUPERSEDED = "intake-superseded";
|
|
67
|
+
export const HELLO_REASON_SUPERSEDED = "hello-superseded";
|
|
60
68
|
export class TunnelAuthError extends Error {
|
|
61
69
|
constructor(message) {
|
|
62
70
|
super(message);
|
|
63
71
|
this.name = "TunnelAuthError";
|
|
64
72
|
}
|
|
65
73
|
}
|
|
74
|
+
export class TunnelSupersededError extends Error {
|
|
75
|
+
constructor(message) {
|
|
76
|
+
super(message);
|
|
77
|
+
this.name = "TunnelSupersededError";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
66
80
|
class OwnerTokenInvalidError extends Error {
|
|
67
81
|
constructor(message) {
|
|
68
82
|
super(message);
|
|
@@ -95,6 +109,23 @@ function isSessionTerminalError(err) {
|
|
|
95
109
|
code === "ERR_HTTP2_STREAM_CANCEL" ||
|
|
96
110
|
code === "ERR_HTTP2_SESSION_ERROR");
|
|
97
111
|
}
|
|
112
|
+
/** Extract a ``reason`` string from a JSON ``{"reason": ...}`` payload. */
|
|
113
|
+
function parseReasonJson(buf) {
|
|
114
|
+
if (buf === undefined || buf === null || buf.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
try {
|
|
117
|
+
const parsed = JSON.parse(buf.toString("utf-8"));
|
|
118
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
119
|
+
const reason = parsed.reason;
|
|
120
|
+
if (typeof reason === "string")
|
|
121
|
+
return reason;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
98
129
|
/**
|
|
99
130
|
* One persistent h2 connection's state. The runtime holds a single
|
|
100
131
|
* `active` Connection (the pool that parks new intakes) plus zero-or-more
|
|
@@ -158,6 +189,9 @@ export class TunnelRuntime {
|
|
|
158
189
|
// Resolves the supervisor's wait when the active conn is swapped.
|
|
159
190
|
wakeSupervisor = null;
|
|
160
191
|
stop = false;
|
|
192
|
+
// Set when another client took over this tunnel: serveForever stops and does
|
|
193
|
+
// not reconnect. Distinct from a transient drop or a drain.
|
|
194
|
+
superseded = false;
|
|
161
195
|
// Dispatch tasks are runtime-scoped (not per-connection): a handoff must
|
|
162
196
|
// let an in-flight handler finish and post its reply on the NEW conn.
|
|
163
197
|
tasks = new Set();
|
|
@@ -206,6 +240,15 @@ export class TunnelRuntime {
|
|
|
206
240
|
this.notifyStatus("closed");
|
|
207
241
|
throw err;
|
|
208
242
|
}
|
|
243
|
+
if (err instanceof TunnelSupersededError) {
|
|
244
|
+
this.stopSuperseded(err);
|
|
245
|
+
}
|
|
246
|
+
// A takeover GOAWAY that lands mid-hello sets `superseded` but surfaces
|
|
247
|
+
// as a plain connection error; go terminal here so we don't reconnect
|
|
248
|
+
// and boot the client that replaced us.
|
|
249
|
+
if (this.superseded) {
|
|
250
|
+
this.stopSuperseded();
|
|
251
|
+
}
|
|
209
252
|
consecutiveFailures += 1;
|
|
210
253
|
// eslint-disable-next-line no-console
|
|
211
254
|
console.warn(`tunnel runtime: connection error (#${consecutiveFailures}); reconnecting`, err);
|
|
@@ -340,6 +383,10 @@ export class TunnelRuntime {
|
|
|
340
383
|
if (session === null || session.closed || session.destroyed)
|
|
341
384
|
break;
|
|
342
385
|
}
|
|
386
|
+
// Another client took over this tunnel: stop, do not reconnect.
|
|
387
|
+
if (this.superseded) {
|
|
388
|
+
throw new TunnelSupersededError("another client connected to this tunnel; not reconnecting");
|
|
389
|
+
}
|
|
343
390
|
}
|
|
344
391
|
finally {
|
|
345
392
|
this.stopPingLoop(conn);
|
|
@@ -383,6 +430,57 @@ export class TunnelRuntime {
|
|
|
383
430
|
if (w !== null)
|
|
384
431
|
w();
|
|
385
432
|
}
|
|
433
|
+
// --- takeover (superseded) --------------------------------------------
|
|
434
|
+
/**
|
|
435
|
+
* True iff a takeover signal on `conn` should stop the runtime. Ignored only
|
|
436
|
+
* for a conn we ourselves put into make-before-break drain (our own
|
|
437
|
+
* predecessor, tracked in `this.draining`); a not-yet-active replacement
|
|
438
|
+
* mid-handoff is a real external takeover, so it stays terminal.
|
|
439
|
+
*/
|
|
440
|
+
supersededIsTerminal(conn) {
|
|
441
|
+
return !this.draining.has(conn);
|
|
442
|
+
}
|
|
443
|
+
/** Record the takeover; the supervisor will stop and not reconnect. */
|
|
444
|
+
markSuperseded() {
|
|
445
|
+
this.superseded = true;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Log, notify, and throw the terminal takeover error (no reconnect).
|
|
449
|
+
* Rethrows the original error when given, so its message (which
|
|
450
|
+
* channel/slot observed the takeover) survives to the caller.
|
|
451
|
+
*/
|
|
452
|
+
stopSuperseded(err) {
|
|
453
|
+
// eslint-disable-next-line no-console
|
|
454
|
+
console.warn("tunnel taken over: another client connected to this tunnel, so " +
|
|
455
|
+
"this client is stopping and will not reconnect. If this is " +
|
|
456
|
+
"unexpected, check for a second instance running the same " +
|
|
457
|
+
"identity (only one live connection per tunnel is kept).");
|
|
458
|
+
this.notifyStatus("superseded");
|
|
459
|
+
throw err ??
|
|
460
|
+
new TunnelSupersededError("another client connected to this tunnel; not reconnecting");
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Classify a non-zero GOAWAY: takeover (terminal) vs infra fault (reconnect).
|
|
464
|
+
* The dedicated code is authoritative; the reason is a belt cross-check.
|
|
465
|
+
*/
|
|
466
|
+
maybeMarkSupersededGoaway(conn, errorCode, opaqueData) {
|
|
467
|
+
const reason = parseReasonJson(opaqueData);
|
|
468
|
+
const isTakeover = errorCode === SUPERSEDED_GOAWAY_ERROR_CODE ||
|
|
469
|
+
reason === GOAWAY_REASON_SUPERSEDED;
|
|
470
|
+
if (!isTakeover) {
|
|
471
|
+
if (reason === null) {
|
|
472
|
+
// eslint-disable-next-line no-console
|
|
473
|
+
console.warn(`tunnel runtime: non-zero GOAWAY (code=${errorCode}) without a reason; reconnecting`);
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (!this.supersededIsTerminal(conn)) {
|
|
478
|
+
// eslint-disable-next-line no-console
|
|
479
|
+
console.info("tunnel runtime: takeover signal on our own draining predecessor; ignoring");
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
this.markSuperseded();
|
|
483
|
+
}
|
|
386
484
|
// --- make-before-break handoff ----------------------------------------
|
|
387
485
|
/**
|
|
388
486
|
* On a NO_ERROR GOAWAY, mark the old conn draining and stand up a fresh
|
|
@@ -413,16 +511,28 @@ export class TunnelRuntime {
|
|
|
413
511
|
this.signalSupervisor();
|
|
414
512
|
}
|
|
415
513
|
catch (err) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
514
|
+
if (err instanceof TunnelSupersededError) {
|
|
515
|
+
// External takeover during the handoff hello: `superseded` is set, so
|
|
516
|
+
// end the old conn and wake the supervisor to stop terminally (no cold
|
|
517
|
+
// redial, which would boot the client that replaced us).
|
|
518
|
+
try {
|
|
519
|
+
oldConn.session?.destroy();
|
|
520
|
+
}
|
|
521
|
+
catch { /* swallow */ }
|
|
522
|
+
this.signalSupervisor();
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
// Redial budget exhausted (or auth failure): give up on
|
|
526
|
+
// make-before-break and fall to the cold reconnect path by forcing
|
|
527
|
+
// the old session closed so the supervisor returns.
|
|
528
|
+
// eslint-disable-next-line no-console
|
|
529
|
+
console.warn("tunnel runtime: handoff failed; reconnecting cold", err);
|
|
530
|
+
try {
|
|
531
|
+
oldConn.session?.destroy();
|
|
532
|
+
}
|
|
533
|
+
catch { /* swallow */ }
|
|
534
|
+
this.signalSupervisor();
|
|
423
535
|
}
|
|
424
|
-
catch { /* swallow */ }
|
|
425
|
-
this.signalSupervisor();
|
|
426
536
|
}
|
|
427
537
|
finally {
|
|
428
538
|
this.handoffInFlight = false;
|
|
@@ -448,8 +558,17 @@ export class TunnelRuntime {
|
|
|
448
558
|
await this.closeConnection(conn);
|
|
449
559
|
}
|
|
450
560
|
catch { /* swallow */ }
|
|
451
|
-
|
|
561
|
+
// A rejected key or a takeover is terminal; never retry either
|
|
562
|
+
// (retrying a takeover would boot the client that replaced us).
|
|
563
|
+
if (err instanceof TunnelAuthError || err instanceof TunnelSupersededError) {
|
|
452
564
|
throw err;
|
|
565
|
+
}
|
|
566
|
+
// A takeover GOAWAY that landed mid-hello set `superseded` but surfaced
|
|
567
|
+
// here as a plain reset/status-0 error; stop now so we don't re-hello
|
|
568
|
+
// and boot the client that replaced us.
|
|
569
|
+
if (this.superseded) {
|
|
570
|
+
throw new TunnelSupersededError("another client connected to this tunnel during handoff");
|
|
571
|
+
}
|
|
453
572
|
if (Date.now() - start > HANDOFF_REDIAL_BUDGET_MS) {
|
|
454
573
|
throw new Error("handoff redial budget exhausted");
|
|
455
574
|
}
|
|
@@ -508,15 +627,19 @@ export class TunnelRuntime {
|
|
|
508
627
|
// eslint-disable-next-line no-console
|
|
509
628
|
console.warn("tunnel runtime: h2 session error", err);
|
|
510
629
|
});
|
|
511
|
-
session.on("goaway", (errorCode, lastStreamId) => {
|
|
630
|
+
session.on("goaway", (errorCode, lastStreamId, opaqueData) => {
|
|
512
631
|
// eslint-disable-next-line no-console
|
|
513
632
|
console.info(`tunnel runtime: GOAWAY received error_code=${errorCode} last_stream_id=${lastStreamId}`);
|
|
514
|
-
// NO_ERROR GOAWAY
|
|
515
|
-
//
|
|
516
|
-
//
|
|
633
|
+
// NO_ERROR GOAWAY = drain (make-before-break handoff). A non-zero code
|
|
634
|
+
// is a takeover (stop) or a real fault (reconnect cold), classified
|
|
635
|
+
// below; setting the takeover flag here wins over the 'error'/'close'
|
|
636
|
+
// reconnect path that the same GOAWAY also drives.
|
|
517
637
|
if (errorCode === 0) {
|
|
518
638
|
this.beginHandoff(conn);
|
|
519
639
|
}
|
|
640
|
+
else {
|
|
641
|
+
this.maybeMarkSupersededGoaway(conn, errorCode, opaqueData);
|
|
642
|
+
}
|
|
520
643
|
});
|
|
521
644
|
// Watch the underlying TCP/TLS socket directly. Node's h2 client
|
|
522
645
|
// sometimes loses the connection without emitting ``error`` or
|
|
@@ -612,6 +735,15 @@ export class TunnelRuntime {
|
|
|
612
735
|
if (status === 401 || status === 403) {
|
|
613
736
|
throw new TunnelAuthError(`${ControlPaths.HELLO} returned ${status}; the API key was rejected (check the key matches the tunnel's identity scope, or use an admin-scoped key in the tunnel's org)`);
|
|
614
737
|
}
|
|
738
|
+
// Displaced during hello: a strictly-later hello (another client) won the
|
|
739
|
+
// tunnel. Always terminal, even mid-handoff: the server sends this only
|
|
740
|
+
// when a newer client has connected, so retrying would re-hello and boot
|
|
741
|
+
// the client that replaced us.
|
|
742
|
+
if (status === 409 &&
|
|
743
|
+
parseReasonJson(body) === HELLO_REASON_SUPERSEDED) {
|
|
744
|
+
this.superseded = true;
|
|
745
|
+
throw new TunnelSupersededError("another client connected to this tunnel during hello");
|
|
746
|
+
}
|
|
615
747
|
if (status !== 200) {
|
|
616
748
|
throw new Error(`${ControlPaths.HELLO} returned ${status}; transient — will retry`);
|
|
617
749
|
}
|
|
@@ -755,6 +887,16 @@ export class TunnelRuntime {
|
|
|
755
887
|
envelope = await this.parkOneIntake(conn, slot);
|
|
756
888
|
}
|
|
757
889
|
catch (err) {
|
|
890
|
+
if (err instanceof TunnelSupersededError) {
|
|
891
|
+
// Another client took over: force this conn down so the supervisor
|
|
892
|
+
// observes the terminal flag and stops (no reconnect).
|
|
893
|
+
this.markSuperseded();
|
|
894
|
+
try {
|
|
895
|
+
conn.session?.destroy();
|
|
896
|
+
}
|
|
897
|
+
catch { /* swallow */ }
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
758
900
|
if (err instanceof OwnerTokenInvalidError) {
|
|
759
901
|
// eslint-disable-next-line no-console
|
|
760
902
|
console.warn(`intake slot ${slot}: owner_token rejected; ` +
|
|
@@ -843,6 +985,15 @@ export class TunnelRuntime {
|
|
|
843
985
|
const reason = recvHeaders.find(([k]) => k === TunnelMetaHeader.REASON)?.[1] ?? "";
|
|
844
986
|
// eslint-disable-next-line no-console
|
|
845
987
|
console.warn(`${ControlPaths.INTAKE} slot=${slot} -> status=${status} reason=${reason}`);
|
|
988
|
+
// Another client took over this tunnel. Terminal, unless this is our own
|
|
989
|
+
// draining predecessor (a normal reconnect). A drain uses a different
|
|
990
|
+
// reason and falls through to re-park below.
|
|
991
|
+
if (reason === INTAKE_REASON_SUPERSEDED) {
|
|
992
|
+
if (this.supersededIsTerminal(conn)) {
|
|
993
|
+
throw new TunnelSupersededError(`slot=${slot}: another client connected to this tunnel`);
|
|
994
|
+
}
|
|
995
|
+
return null;
|
|
996
|
+
}
|
|
846
997
|
if (status === "401") {
|
|
847
998
|
throw new OwnerTokenInvalidError(`slot=${slot} status=401 reason=${reason}`);
|
|
848
999
|
}
|