snyk-broker 5.23.0 → 5.23.1
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/dist/lib/hybrid-sdk/LegacyStreamResponseHandler.js +92 -30
- package/dist/lib/hybrid-sdk/LegacyStreamResponseHandler.js.map +1 -1
- package/dist/lib/hybrid-sdk/clientRequestHelpers.js +9 -15
- package/dist/lib/hybrid-sdk/clientRequestHelpers.js.map +1 -1
- package/dist/lib/hybrid-sdk/http/response-frame-decoder.js +144 -0
- package/dist/lib/hybrid-sdk/http/response-frame-decoder.js.map +1 -0
- package/dist/lib/hybrid-sdk/http/server-post-stream-handler.js +186 -44
- package/dist/lib/hybrid-sdk/http/server-post-stream-handler.js.map +1 -1
- package/dist/lib/hybrid-sdk/server/routesHandlers/postResponseHandler.js +64 -125
- package/dist/lib/hybrid-sdk/server/routesHandlers/postResponseHandler.js.map +1 -1
- package/dist/lib/hybrid-sdk/server/socketHandlers/connectionHandler.js +17 -1
- package/dist/lib/hybrid-sdk/server/socketHandlers/connectionHandler.js.map +1 -1
- package/dist/lib/hybrid-sdk/server/socketHandlers/identifyHandler.js +5 -4
- package/dist/lib/hybrid-sdk/server/socketHandlers/identifyHandler.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,43 +1,105 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.legacyStreamResponseHandler = void 0;
|
|
4
|
-
const
|
|
4
|
+
const stream_1 = require("stream");
|
|
5
|
+
const promises_1 = require("stream/promises");
|
|
5
6
|
const logger_1 = require("../logs/logger");
|
|
6
|
-
const
|
|
7
|
+
const server_post_stream_handler_1 = require("./http/server-post-stream-handler");
|
|
8
|
+
const token_1 = require("./server/utils/token");
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
* Adapts the callback-based websocket chunk protocol to a claimed response.
|
|
11
|
+
*
|
|
12
|
+
* The PassThrough is a source owned only by this legacy delivery. The actual
|
|
13
|
+
* HTTP response remains in pipeline(), so its backpressure, errors and final
|
|
14
|
+
* completion are included in the delivery lifecycle.
|
|
15
|
+
*/
|
|
16
|
+
class LegacyResponseDelivery {
|
|
17
|
+
constructor(pending, logContext, onSettled) {
|
|
18
|
+
this.pending = pending;
|
|
19
|
+
this.logContext = logContext;
|
|
20
|
+
this.source = new stream_1.PassThrough({ highWaterMark: 1024 * 1024 });
|
|
21
|
+
this.bodyBytes = 0;
|
|
22
|
+
this.ended = false;
|
|
23
|
+
void (0, promises_1.pipeline)(this.source, pending.destination)
|
|
24
|
+
.then(() => pending.complete(this.bodyBytes))
|
|
25
|
+
.catch((error) => {
|
|
26
|
+
pending.cancel(error);
|
|
27
|
+
logger_1.log.error({ ...this.logContext, error }, 'Failed forwarding legacy websocket response stream.');
|
|
28
|
+
})
|
|
29
|
+
.finally(onSettled);
|
|
30
|
+
}
|
|
31
|
+
write(chunk, finished, metadata) {
|
|
32
|
+
if (this.ended) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
if (metadata) {
|
|
37
|
+
this.pending.applyMetadata(metadata);
|
|
32
38
|
}
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
if (chunk) {
|
|
40
|
+
// Preserve the legacy receiver metric semantics for string chunks.
|
|
41
|
+
this.bodyBytes += chunk.length;
|
|
42
|
+
// The legacy event contract has no acknowledgement with which to
|
|
43
|
+
// propagate drain. PassThrough preserves its buffering boundary while
|
|
44
|
+
// pipeline coordinates the readable side and the real destination.
|
|
45
|
+
this.source.write(chunk);
|
|
35
46
|
}
|
|
47
|
+
if (finished) {
|
|
48
|
+
this.ended = true;
|
|
49
|
+
this.source.end();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
this.ended = true;
|
|
54
|
+
this.source.destroy(error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
cancel(error) {
|
|
58
|
+
if (this.ended) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.ended = true;
|
|
62
|
+
this.source.destroy(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const legacyStreamResponseHandler = (token, registry = server_post_stream_handler_1.pendingResponseRegistry) => {
|
|
66
|
+
const deliveries = new Map();
|
|
67
|
+
let disposed = false;
|
|
68
|
+
const handleChunk = (streamingID, chunk, finished, metadata) => {
|
|
69
|
+
if (disposed) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let delivery = deliveries.get(streamingID);
|
|
73
|
+
if (!delivery) {
|
|
74
|
+
const claim = registry.claim(streamingID, {
|
|
75
|
+
legacyConnectionIdentifier: token,
|
|
76
|
+
enforceLegacyOwnership: true,
|
|
77
|
+
});
|
|
78
|
+
if (claim.status !== 'claimed') {
|
|
79
|
+
const { maskedToken, hashedToken } = (0, token_1.getDesensitizedToken)(token);
|
|
80
|
+
logger_1.log.warn({
|
|
81
|
+
streamingID,
|
|
82
|
+
claimStatus: claim.status,
|
|
83
|
+
maskedToken,
|
|
84
|
+
hashedToken,
|
|
85
|
+
}, 'Trying to write into a closed or claimed stream.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
delivery = new LegacyResponseDelivery(claim.pending, { streamingID }, () => deliveries.delete(streamingID));
|
|
89
|
+
deliveries.set(streamingID, delivery);
|
|
90
|
+
}
|
|
91
|
+
delivery.write(chunk, finished, metadata);
|
|
92
|
+
};
|
|
93
|
+
handleChunk.dispose = (error) => {
|
|
94
|
+
if (disposed) {
|
|
95
|
+
return;
|
|
36
96
|
}
|
|
37
|
-
|
|
38
|
-
|
|
97
|
+
disposed = true;
|
|
98
|
+
for (const delivery of deliveries.values()) {
|
|
99
|
+
delivery.cancel(error);
|
|
39
100
|
}
|
|
40
101
|
};
|
|
102
|
+
return handleChunk;
|
|
41
103
|
};
|
|
42
104
|
exports.legacyStreamResponseHandler = legacyStreamResponseHandler;
|
|
43
105
|
//# sourceMappingURL=LegacyStreamResponseHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LegacyStreamResponseHandler.js","sourceRoot":"","sources":["../../../lib/hybrid-sdk/LegacyStreamResponseHandler.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"LegacyStreamResponseHandler.js","sourceRoot":"","sources":["../../../lib/hybrid-sdk/LegacyStreamResponseHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,6 @@ const config_1 = require("./common/config/config");
|
|
|
9
9
|
const metrics_1 = require("./common/utils/metrics");
|
|
10
10
|
const undefsafe_1 = __importDefault(require("undefsafe"));
|
|
11
11
|
const uuid_1 = require("./common/utils/uuid");
|
|
12
|
-
const stream_1 = __importDefault(require("stream"));
|
|
13
12
|
const server_post_stream_handler_1 = require("./http/server-post-stream-handler");
|
|
14
13
|
const token_1 = require("./common/utils/token");
|
|
15
14
|
const request_1 = require("./http/request");
|
|
@@ -42,26 +41,21 @@ class HybridClientRequestHandler {
|
|
|
42
41
|
}
|
|
43
42
|
makeWebsocketRequestWithStreamingResponse() {
|
|
44
43
|
const streamingID = (0, uuid_1.uuidv4)();
|
|
45
|
-
const streamBuffer = new stream_1.default.PassThrough({ highWaterMark: 1048576 });
|
|
46
|
-
streamBuffer.on('error', (error) => {
|
|
47
|
-
// This may be a duplicate error, as the most likely cause of this is the POST handler calling destroy.
|
|
48
|
-
logger_1.log.error({
|
|
49
|
-
...this.logContext,
|
|
50
|
-
error,
|
|
51
|
-
stackTrace: new Error('stacktrace generator').stack,
|
|
52
|
-
}, '[HTTP Flow][Relay] Error piping POST response stream through to HTTP response');
|
|
53
|
-
this.res.destroy(error);
|
|
54
|
-
});
|
|
55
44
|
this.logContext.streamingID = streamingID;
|
|
56
45
|
logger_1.log.debug(this.logContext, '[HTTP Flow][Relay] Sending request over websocket connection expecting POST stream response');
|
|
57
|
-
server_post_stream_handler_1.
|
|
46
|
+
const registration = server_post_stream_handler_1.pendingResponseRegistry.register(streamingID, {
|
|
58
47
|
response: this.res,
|
|
59
|
-
streamBuffer,
|
|
60
|
-
streamSize: 0,
|
|
61
48
|
brokerAppClientId: this.res.locals.brokerAppClientId ?? null,
|
|
49
|
+
legacyConnectionIdentifier: this.req.params?.token ??
|
|
50
|
+
(this.options.universalBrokerEnabled
|
|
51
|
+
? this.res.locals.websocket.identifier
|
|
52
|
+
: this.options.brokerToken),
|
|
62
53
|
});
|
|
54
|
+
if (registration.status === 'destination-unavailable') {
|
|
55
|
+
logger_1.log.debug(this.logContext, '[HTTP Flow][Relay] Requester disconnected before POST stream registration.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
63
58
|
this.res.setHeader('snyk-request-id', this.req.requestId);
|
|
64
|
-
streamBuffer.pipe(this.res);
|
|
65
59
|
const simplifiedContextWithStreamingID = this.simplifiedContext;
|
|
66
60
|
simplifiedContextWithStreamingID['streamingID'] = streamingID;
|
|
67
61
|
logger_1.log.debug(simplifiedContextWithStreamingID, '[HTTP Flow] Brokering request through Websocket.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientRequestHelpers.js","sourceRoot":"","sources":["../../../lib/hybrid-sdk/clientRequestHelpers.ts"],"names":[],"mappings":";;;;;;AACA,2CAA+C;AAC/C,mDAAmD;AAEnD,oDAIgC;AAChC,0DAAkC;AAElC,8CAA6C;AAC7C,
|
|
1
|
+
{"version":3,"file":"clientRequestHelpers.js","sourceRoot":"","sources":["../../../lib/hybrid-sdk/clientRequestHelpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResponseFrameDecoder = exports.ResponseFrameError = void 0;
|
|
4
|
+
const node_stream_1 = require("node:stream");
|
|
5
|
+
/** A decoder-owned failure with safe context suitable for structured logs. */
|
|
6
|
+
class ResponseFrameError extends Error {
|
|
7
|
+
constructor(message, diagnostics, options) {
|
|
8
|
+
super(message, options);
|
|
9
|
+
this.diagnostics = diagnostics;
|
|
10
|
+
this.name = 'ResponseFrameError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.ResponseFrameError = ResponseFrameError;
|
|
14
|
+
const PREFIX_BYTES = 4;
|
|
15
|
+
// Defensive implementation bound only. Choosing a protocol-level limit needs
|
|
16
|
+
// a separate compatibility decision across deployed Broker versions.
|
|
17
|
+
// TODO: replace this with an explicitly versioned protocol limit.
|
|
18
|
+
const DEFAULT_MAX_METADATA_BYTES = 1024 * 1024;
|
|
19
|
+
/**
|
|
20
|
+
* Decodes one Broker POST response frame:
|
|
21
|
+
*
|
|
22
|
+
* 4-byte little-endian metadata byte length | JSON metadata | body bytes
|
|
23
|
+
*
|
|
24
|
+
* Only body bytes are emitted from the readable side. Metadata is delivered
|
|
25
|
+
* synchronously before the first body byte through the callback/event.
|
|
26
|
+
*/
|
|
27
|
+
class ResponseFrameDecoder extends node_stream_1.Transform {
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
super();
|
|
30
|
+
this.bodyBytes = 0;
|
|
31
|
+
this.prefix = Buffer.alloc(PREFIX_BYTES);
|
|
32
|
+
this.prefixBytes = 0;
|
|
33
|
+
this.metadataBytesExpected = null;
|
|
34
|
+
this.metadataBytes = 0;
|
|
35
|
+
this.metadataChunks = [];
|
|
36
|
+
this.metadataDecoded = false;
|
|
37
|
+
this.maxMetadataBytes =
|
|
38
|
+
options.maxMetadataBytes ?? DEFAULT_MAX_METADATA_BYTES;
|
|
39
|
+
this.onMetadata = options.onMetadata;
|
|
40
|
+
}
|
|
41
|
+
_transform(chunk, encoding, callback) {
|
|
42
|
+
try {
|
|
43
|
+
const data = Buffer.isBuffer(chunk)
|
|
44
|
+
? chunk
|
|
45
|
+
: Buffer.from(chunk, encoding);
|
|
46
|
+
let offset = 0;
|
|
47
|
+
if (this.metadataBytesExpected === null) {
|
|
48
|
+
const prefixBytesToCopy = Math.min(PREFIX_BYTES - this.prefixBytes, data.length);
|
|
49
|
+
data.copy(this.prefix, this.prefixBytes, offset, offset + prefixBytesToCopy);
|
|
50
|
+
this.prefixBytes += prefixBytesToCopy;
|
|
51
|
+
offset += prefixBytesToCopy;
|
|
52
|
+
if (this.prefixBytes < PREFIX_BYTES) {
|
|
53
|
+
callback();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.metadataBytesExpected = this.prefix.readUInt32LE(0);
|
|
57
|
+
if (this.metadataBytesExpected === 0) {
|
|
58
|
+
throw new ResponseFrameError('Response metadata must not be empty.', {
|
|
59
|
+
failureReason: 'invalid-metadata',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (this.metadataBytesExpected > this.maxMetadataBytes) {
|
|
63
|
+
throw new ResponseFrameError(`Response metadata length ${this.metadataBytesExpected} exceeds the ${this.maxMetadataBytes}-byte limit.`, { failureReason: 'metadata-too-large' });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!this.metadataDecoded && offset < data.length) {
|
|
67
|
+
const metadataBytesToCopy = Math.min(this.metadataBytesExpected - this.metadataBytes, data.length - offset);
|
|
68
|
+
if (metadataBytesToCopy > 0) {
|
|
69
|
+
this.metadataChunks.push(data.subarray(offset, offset + metadataBytesToCopy));
|
|
70
|
+
this.metadataBytes += metadataBytesToCopy;
|
|
71
|
+
offset += metadataBytesToCopy;
|
|
72
|
+
}
|
|
73
|
+
if (this.metadataBytes === this.metadataBytesExpected) {
|
|
74
|
+
const metadata = this.decodeMetadata();
|
|
75
|
+
this.metadataDecoded = true;
|
|
76
|
+
this.onMetadata?.(metadata);
|
|
77
|
+
this.emit('metadata', metadata);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (this.metadataDecoded && offset < data.length) {
|
|
81
|
+
const body = data.subarray(offset);
|
|
82
|
+
this.bodyBytes += body.length;
|
|
83
|
+
this.push(body);
|
|
84
|
+
}
|
|
85
|
+
callback();
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
callback(error);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
_flush(callback) {
|
|
92
|
+
if (this.prefixBytes < PREFIX_BYTES) {
|
|
93
|
+
callback(new ResponseFrameError(`Incomplete metadata-length prefix: received ${this.prefixBytes} of ${PREFIX_BYTES} bytes.`, {
|
|
94
|
+
failureReason: 'incomplete-prefix',
|
|
95
|
+
receivedPrefixBytes: this.prefixBytes,
|
|
96
|
+
expectedPrefixBytes: PREFIX_BYTES,
|
|
97
|
+
}));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!this.metadataDecoded) {
|
|
101
|
+
callback(new ResponseFrameError(`Incomplete response metadata: received ${this.metadataBytes} of ${this.metadataBytesExpected} bytes.`, {
|
|
102
|
+
failureReason: 'incomplete-metadata',
|
|
103
|
+
receivedMetadataBytes: this.metadataBytes,
|
|
104
|
+
expectedMetadataBytes: this.metadataBytesExpected,
|
|
105
|
+
}));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
callback();
|
|
109
|
+
}
|
|
110
|
+
decodeMetadata() {
|
|
111
|
+
const encoded = Buffer.concat(this.metadataChunks, this.metadataBytesExpected).toString('utf8');
|
|
112
|
+
let value;
|
|
113
|
+
try {
|
|
114
|
+
value = JSON.parse(encoded);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
throw new ResponseFrameError('Malformed response metadata JSON.', { failureReason: 'malformed-metadata' }, { cause: error });
|
|
118
|
+
}
|
|
119
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
120
|
+
throw new ResponseFrameError('Response metadata must be a JSON object.', {
|
|
121
|
+
failureReason: 'invalid-metadata',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const metadata = value;
|
|
125
|
+
if (!Number.isInteger(metadata.status) ||
|
|
126
|
+
metadata.status < 100 ||
|
|
127
|
+
metadata.status > 999) {
|
|
128
|
+
throw new ResponseFrameError('Response metadata must contain a valid status.', { failureReason: 'invalid-metadata' });
|
|
129
|
+
}
|
|
130
|
+
if (metadata.headers !== undefined &&
|
|
131
|
+
(typeof metadata.headers !== 'object' ||
|
|
132
|
+
metadata.headers === null ||
|
|
133
|
+
Array.isArray(metadata.headers))) {
|
|
134
|
+
throw new ResponseFrameError('Response metadata headers must be an object.', { failureReason: 'invalid-metadata' });
|
|
135
|
+
}
|
|
136
|
+
if (metadata.errorType !== undefined &&
|
|
137
|
+
typeof metadata.errorType !== 'string') {
|
|
138
|
+
throw new ResponseFrameError('Response metadata errorType must be a string.', { failureReason: 'invalid-metadata' });
|
|
139
|
+
}
|
|
140
|
+
return metadata;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.ResponseFrameDecoder = ResponseFrameDecoder;
|
|
144
|
+
//# sourceMappingURL=response-frame-decoder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-frame-decoder.js","sourceRoot":"","sources":["../../../../lib/hybrid-sdk/http/response-frame-decoder.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,59 +3,201 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.pendingResponseRegistry = exports.PendingResponseRegistry = exports.PendingResponse = void 0;
|
|
7
7
|
const node_cache_1 = __importDefault(require("node-cache"));
|
|
8
8
|
const config_1 = require("../common/config/config");
|
|
9
9
|
const metrics_1 = require("../common/utils/metrics");
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
/** A one-shot capability returned by PendingResponseRegistry.claim(). */
|
|
11
|
+
class PendingResponse {
|
|
12
|
+
constructor(streamingID, response, settle) {
|
|
13
|
+
this.streamingID = streamingID;
|
|
14
|
+
this.response = response;
|
|
15
|
+
this.settle = settle;
|
|
16
|
+
// The original response is deliberately the destination. In particular,
|
|
17
|
+
// POST delivery must not terminate at an intermediate buffer.
|
|
18
|
+
this.destination = response;
|
|
19
|
+
}
|
|
20
|
+
applyMetadata(metadata) {
|
|
21
|
+
if (isResponseUnavailable(this.response)) {
|
|
22
|
+
throw new Error(`Pending response ${this.streamingID} cannot accept response metadata.`);
|
|
23
|
+
}
|
|
24
|
+
this.response.status(metadata.status);
|
|
25
|
+
if (metadata.headers) {
|
|
26
|
+
this.response.set(metadata.headers);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
complete(bodyBytes) {
|
|
30
|
+
return this.settle(this, { type: 'complete', bodyBytes });
|
|
31
|
+
}
|
|
32
|
+
cancel(error) {
|
|
33
|
+
return this.settle(this, { type: 'cancel', error });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.PendingResponse = PendingResponse;
|
|
37
|
+
/**
|
|
38
|
+
* Owns pending-response registration, broker ownership checks, one-shot claims,
|
|
39
|
+
* cancellation and expiry. Cache records never escape this class.
|
|
40
|
+
*/
|
|
41
|
+
class PendingResponseRegistry {
|
|
42
|
+
constructor(store = new node_cache_1.default({
|
|
43
|
+
stdTTL: parseInt((0, config_1.getConfig)().cacheExpiry) || 3600,
|
|
44
|
+
checkperiod: parseInt((0, config_1.getConfig)().cacheCheckPeriod) || 60,
|
|
45
|
+
useClones: false,
|
|
46
|
+
})) {
|
|
47
|
+
this.store = store;
|
|
48
|
+
this.activeClaims = new Map();
|
|
49
|
+
this.responseWatchers = new Map();
|
|
50
|
+
this.onExpired = (streamingID, registration) => {
|
|
51
|
+
this.expireRegistration(streamingID, registration);
|
|
52
|
+
};
|
|
53
|
+
this.expireRegistration = (streamingID, registration) => {
|
|
54
|
+
this.detachResponseWatchers(streamingID);
|
|
55
|
+
if (!registration.response.destroyed) {
|
|
56
|
+
registration.response.destroy();
|
|
57
|
+
}
|
|
32
58
|
};
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
59
|
+
this.store.on('expired', this.onExpired);
|
|
60
|
+
}
|
|
61
|
+
register(streamingID, registration, ttlSeconds) {
|
|
62
|
+
if (this.activeClaims.has(streamingID) || this.store.has(streamingID)) {
|
|
63
|
+
throw new Error(`Pending response ${streamingID} is already registered.`);
|
|
64
|
+
}
|
|
65
|
+
if (isResponseUnavailable(registration.response)) {
|
|
66
|
+
return { status: 'destination-unavailable' };
|
|
67
|
+
}
|
|
68
|
+
const storedRegistration = { ...registration };
|
|
69
|
+
if (ttlSeconds === undefined) {
|
|
70
|
+
this.store.set(streamingID, storedRegistration);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this.store.set(streamingID, storedRegistration, ttlSeconds);
|
|
74
|
+
}
|
|
75
|
+
const onClose = () => {
|
|
76
|
+
if (storedRegistration.response.writableFinished) {
|
|
77
|
+
if (!this.activeClaims.has(streamingID)) {
|
|
78
|
+
this.store.del(streamingID);
|
|
79
|
+
}
|
|
80
|
+
this.detachResponseWatchers(streamingID);
|
|
81
|
+
return;
|
|
37
82
|
}
|
|
83
|
+
this.cancelFromDestination(streamingID, new Error(`Original response ${streamingID} closed prematurely.`));
|
|
38
84
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
85
|
+
const onError = (error) => this.cancelFromDestination(streamingID, error);
|
|
86
|
+
storedRegistration.response.once('close', onClose);
|
|
87
|
+
storedRegistration.response.once('error', onError);
|
|
88
|
+
this.responseWatchers.set(streamingID, {
|
|
89
|
+
response: storedRegistration.response,
|
|
90
|
+
onClose,
|
|
91
|
+
onError,
|
|
92
|
+
});
|
|
93
|
+
return { status: 'registered' };
|
|
94
|
+
}
|
|
95
|
+
claim(streamingID, options = {}) {
|
|
96
|
+
const registration = this.store.get(streamingID);
|
|
97
|
+
if (!registration) {
|
|
98
|
+
return {
|
|
99
|
+
status: this.activeClaims.has(streamingID)
|
|
100
|
+
? 'already-claimed'
|
|
101
|
+
: 'not-found',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if ((options.enforceBrokerOwnership &&
|
|
105
|
+
(!options.brokerAppClientId ||
|
|
106
|
+
!registration.brokerAppClientId ||
|
|
107
|
+
options.brokerAppClientId !== registration.brokerAppClientId)) ||
|
|
108
|
+
(options.enforceLegacyOwnership &&
|
|
109
|
+
(!options.legacyConnectionIdentifier ||
|
|
110
|
+
!registration.legacyConnectionIdentifier ||
|
|
111
|
+
options.legacyConnectionIdentifier !==
|
|
112
|
+
registration.legacyConnectionIdentifier))) {
|
|
113
|
+
return { status: 'owner-mismatch' };
|
|
114
|
+
}
|
|
115
|
+
// NodeCache revalidates expiry in take(). Treat that result as the
|
|
116
|
+
// ownership boundary: a value observed by get() must not escape if it
|
|
117
|
+
// expires before the cache can remove it.
|
|
118
|
+
const claimedRegistration = this.store.take(streamingID);
|
|
119
|
+
if (!claimedRegistration) {
|
|
120
|
+
return {
|
|
121
|
+
status: this.activeClaims.has(streamingID)
|
|
122
|
+
? 'already-claimed'
|
|
123
|
+
: 'not-found',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const pending = new PendingResponse(streamingID, claimedRegistration.response, (settledPending, settlement) => this.settle(settledPending, settlement));
|
|
127
|
+
this.activeClaims.set(streamingID, pending);
|
|
128
|
+
return { status: 'claimed', pending };
|
|
129
|
+
}
|
|
130
|
+
cancel(streamingID, error) {
|
|
131
|
+
const active = this.activeClaims.get(streamingID);
|
|
132
|
+
if (active) {
|
|
133
|
+
return active.cancel(error);
|
|
134
|
+
}
|
|
135
|
+
const registration = this.store.get(streamingID);
|
|
136
|
+
if (!registration) {
|
|
137
|
+
this.detachResponseWatchers(streamingID);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
this.store.del(streamingID);
|
|
141
|
+
this.detachResponseWatchers(streamingID);
|
|
142
|
+
if (!registration.response.destroyed) {
|
|
143
|
+
registration.response.destroy(error);
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
dispose() {
|
|
148
|
+
this.store.off('expired', this.onExpired);
|
|
149
|
+
for (const streamingID of this.responseWatchers.keys()) {
|
|
150
|
+
this.detachResponseWatchers(streamingID);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
settle(pending, settlement, destroyDestination = true) {
|
|
154
|
+
if (this.activeClaims.get(pending.streamingID) !== pending) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
this.activeClaims.delete(pending.streamingID);
|
|
158
|
+
this.detachResponseWatchers(pending.streamingID);
|
|
159
|
+
if (settlement.type === 'complete') {
|
|
42
160
|
(0, metrics_1.observeResponseSize)({
|
|
43
|
-
bytes:
|
|
161
|
+
bytes: settlement.bodyBytes,
|
|
44
162
|
isStreaming: true,
|
|
45
163
|
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
164
|
+
}
|
|
165
|
+
else if (destroyDestination && !pending.destination.destroyed) {
|
|
166
|
+
pending.destination.destroy(settlement.error);
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
cancelFromDestination(streamingID, error) {
|
|
171
|
+
const active = this.activeClaims.get(streamingID);
|
|
172
|
+
if (active) {
|
|
173
|
+
// The destination has already failed or closed. Release registry state;
|
|
174
|
+
// pipeline owns propagation and destruction from this point.
|
|
175
|
+
this.settle(active, { type: 'cancel', error }, false);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const registration = this.store.get(streamingID);
|
|
179
|
+
if (registration) {
|
|
180
|
+
this.store.del(streamingID);
|
|
181
|
+
}
|
|
182
|
+
this.detachResponseWatchers(streamingID);
|
|
183
|
+
if (registration && !registration.response.destroyed) {
|
|
184
|
+
registration.response.destroy();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
detachResponseWatchers(streamingID) {
|
|
188
|
+
const watchers = this.responseWatchers.get(streamingID);
|
|
189
|
+
if (!watchers) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
watchers.response.off('close', watchers.onClose);
|
|
193
|
+
watchers.response.off('error', watchers.onError);
|
|
194
|
+
this.responseWatchers.delete(streamingID);
|
|
58
195
|
}
|
|
59
196
|
}
|
|
60
|
-
exports.
|
|
197
|
+
exports.PendingResponseRegistry = PendingResponseRegistry;
|
|
198
|
+
const isResponseUnavailable = (response) => response.destroyed ||
|
|
199
|
+
response.writableEnded ||
|
|
200
|
+
response.writableFinished ||
|
|
201
|
+
response.headersSent;
|
|
202
|
+
exports.pendingResponseRegistry = new PendingResponseRegistry();
|
|
61
203
|
//# sourceMappingURL=server-post-stream-handler.js.map
|