@aws-sdk/middleware-websocket 3.972.3 → 3.972.5
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-cjs/index.js +255 -210
- package/dist-es/{websocket-fetch-handler.js → WebSocketFetchHandler.js} +74 -35
- package/dist-es/WebsocketSignatureV4.js +3 -0
- package/dist-es/getWebSocketPlugin.js +2 -2
- package/dist-es/index.js +3 -3
- package/dist-es/ws-eventstream/EventSigningTransformStream.js +41 -0
- package/dist-es/{EventStreamPayloadHandler.js → ws-eventstream/EventStreamPayloadHandler.js} +9 -7
- package/dist-types/{websocket-fetch-handler.d.ts → WebSocketFetchHandler.d.ts} +7 -2
- package/dist-types/WebsocketSignatureV4.d.ts +14 -4
- package/dist-types/getWebSocketPlugin.d.ts +1 -1
- package/dist-types/index.d.ts +3 -3
- package/dist-types/{middleware-websocket-endpoint.d.ts → middlewares/websocketEndpointMiddleware.d.ts} +1 -1
- package/dist-types/{middleware-session-id.d.ts → middlewares/websocketInjectSessionIdMiddleware.d.ts} +1 -1
- package/dist-types/{websocket-configuration.d.ts → resolveWebSocketConfig.d.ts} +1 -1
- package/dist-types/ts3.4/{websocket-fetch-handler.d.ts → WebSocketFetchHandler.d.ts} +4 -1
- package/dist-types/ts3.4/WebsocketSignatureV4.d.ts +25 -2
- package/dist-types/ts3.4/index.d.ts +3 -3
- package/dist-types/ts3.4/ws-eventstream/EventSigningTransformStream.d.ts +15 -0
- package/dist-types/utils.d.ts +1 -1
- package/dist-types/ws-eventstream/EventSigningTransformStream.d.ts +21 -0
- package/dist-types/{EventStreamPayloadHandler.d.ts → ws-eventstream/EventStreamPayloadHandler.d.ts} +5 -6
- package/dist-types/ws-eventstream/eventStreamPayloadHandlerProvider.d.ts +5 -0
- package/package.json +4 -3
- package/dist-es/get-event-signing-stream.js +0 -40
- package/dist-types/eventstream-payload-handler-provider.d.ts +0 -3
- package/dist-types/get-event-signing-stream.d.ts +0 -10
- package/dist-types/ts3.4/get-event-signing-stream.d.ts +0 -8
- /package/dist-es/{middleware-websocket-endpoint.js → middlewares/websocketEndpointMiddleware.js} +0 -0
- /package/dist-es/{middleware-session-id.js → middlewares/websocketInjectSessionIdMiddleware.js} +0 -0
- /package/dist-es/{websocket-configuration.js → resolveWebSocketConfig.js} +0 -0
- /package/dist-es/{eventstream-payload-handler-provider.js → ws-eventstream/eventStreamPayloadHandlerProvider.js} +0 -0
- /package/dist-types/ts3.4/{middleware-websocket-endpoint.d.ts → middlewares/websocketEndpointMiddleware.d.ts} +0 -0
- /package/dist-types/ts3.4/{middleware-session-id.d.ts → middlewares/websocketInjectSessionIdMiddleware.d.ts} +0 -0
- /package/dist-types/ts3.4/{websocket-configuration.d.ts → resolveWebSocketConfig.d.ts} +0 -0
- /package/dist-types/ts3.4/{EventStreamPayloadHandler.d.ts → ws-eventstream/EventStreamPayloadHandler.d.ts} +0 -0
- /package/dist-types/ts3.4/{eventstream-payload-handler-provider.d.ts → ws-eventstream/eventStreamPayloadHandlerProvider.d.ts} +0 -0
package/dist-cjs/index.js
CHANGED
|
@@ -1,192 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var eventstreamCodec = require('@smithy/eventstream-codec');
|
|
4
|
-
var utilHexEncoding = require('@smithy/util-hex-encoding');
|
|
5
|
-
var protocolHttp = require('@smithy/protocol-http');
|
|
6
3
|
var utilFormatUrl = require('@aws-sdk/util-format-url');
|
|
7
4
|
var eventstreamSerdeBrowser = require('@smithy/eventstream-serde-browser');
|
|
8
5
|
var fetchHttpHandler = require('@smithy/fetch-http-handler');
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
start() { },
|
|
14
|
-
async transform(chunk, controller) {
|
|
15
|
-
try {
|
|
16
|
-
const now = new Date(Date.now() + (await systemClockOffsetProvider()));
|
|
17
|
-
const dateHeader = {
|
|
18
|
-
":date": { type: "timestamp", value: now },
|
|
19
|
-
};
|
|
20
|
-
const signedMessage = await messageSigner.sign({
|
|
21
|
-
message: {
|
|
22
|
-
body: chunk,
|
|
23
|
-
headers: dateHeader,
|
|
24
|
-
},
|
|
25
|
-
priorSignature: priorSignature,
|
|
26
|
-
}, {
|
|
27
|
-
signingDate: now,
|
|
28
|
-
});
|
|
29
|
-
priorSignature = signedMessage.signature;
|
|
30
|
-
const serializedSigned = eventStreamCodec.encode({
|
|
31
|
-
headers: {
|
|
32
|
-
...dateHeader,
|
|
33
|
-
":chunk-signature": {
|
|
34
|
-
type: "binary",
|
|
35
|
-
value: utilHexEncoding.fromHex(signedMessage.signature),
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
body: chunk,
|
|
39
|
-
});
|
|
40
|
-
controller.enqueue(serializedSigned);
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
controller.error(error);
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
return new TransformStream({ ...transformer });
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
class EventStreamPayloadHandler {
|
|
51
|
-
messageSigner;
|
|
52
|
-
eventStreamCodec;
|
|
53
|
-
systemClockOffsetProvider;
|
|
54
|
-
constructor(options) {
|
|
55
|
-
this.messageSigner = options.messageSigner;
|
|
56
|
-
this.eventStreamCodec = new eventstreamCodec.EventStreamCodec(options.utf8Encoder, options.utf8Decoder);
|
|
57
|
-
this.systemClockOffsetProvider = async () => options.systemClockOffset ?? 0;
|
|
58
|
-
}
|
|
59
|
-
async handle(next, args, context = {}) {
|
|
60
|
-
const request = args.request;
|
|
61
|
-
const { body: payload, headers, query } = request;
|
|
62
|
-
if (!(payload instanceof ReadableStream)) {
|
|
63
|
-
throw new Error("Eventstream payload must be a ReadableStream.");
|
|
64
|
-
}
|
|
65
|
-
const placeHolderStream = new TransformStream();
|
|
66
|
-
request.body = placeHolderStream.readable;
|
|
67
|
-
let result;
|
|
68
|
-
try {
|
|
69
|
-
result = await next(args);
|
|
70
|
-
}
|
|
71
|
-
catch (e) {
|
|
72
|
-
request.body.cancel();
|
|
73
|
-
throw e;
|
|
74
|
-
}
|
|
75
|
-
const match = (headers["authorization"] || "").match(/Signature=([\w]+)$/);
|
|
76
|
-
const priorSignature = (match || [])[1] || (query && query["X-Amz-Signature"]) || "";
|
|
77
|
-
const signingStream = getEventSigningTransformStream(priorSignature, await this.messageSigner(), this.eventStreamCodec, this.systemClockOffsetProvider);
|
|
78
|
-
const signedPayload = payload.pipeThrough(signingStream);
|
|
79
|
-
signedPayload.pipeThrough(placeHolderStream);
|
|
80
|
-
return result;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const eventStreamPayloadHandlerProvider = (options) => new EventStreamPayloadHandler(options);
|
|
85
|
-
|
|
86
|
-
const injectSessionIdMiddleware = () => (next) => async (args) => {
|
|
87
|
-
const requestParams = {
|
|
88
|
-
...args.input,
|
|
89
|
-
};
|
|
90
|
-
const response = await next(args);
|
|
91
|
-
const output = response.output;
|
|
92
|
-
if (requestParams.SessionId && output.SessionId == null) {
|
|
93
|
-
output.SessionId = requestParams.SessionId;
|
|
94
|
-
}
|
|
95
|
-
return response;
|
|
96
|
-
};
|
|
97
|
-
const injectSessionIdMiddlewareOptions = {
|
|
98
|
-
step: "initialize",
|
|
99
|
-
name: "injectSessionIdMiddleware",
|
|
100
|
-
tags: ["WEBSOCKET", "EVENT_STREAM"],
|
|
101
|
-
override: true,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const websocketEndpointMiddleware = (config, options) => (next) => (args) => {
|
|
105
|
-
const { request } = args;
|
|
106
|
-
if (protocolHttp.HttpRequest.isInstance(request) &&
|
|
107
|
-
config.requestHandler.metadata?.handlerProtocol?.toLowerCase().includes("websocket")) {
|
|
108
|
-
request.protocol = "wss:";
|
|
109
|
-
request.method = "GET";
|
|
110
|
-
request.path = `${request.path}-websocket`;
|
|
111
|
-
const { headers } = request;
|
|
112
|
-
delete headers["content-type"];
|
|
113
|
-
delete headers["x-amz-content-sha256"];
|
|
114
|
-
for (const name of Object.keys(headers)) {
|
|
115
|
-
if (name.indexOf(options.headerPrefix) === 0) {
|
|
116
|
-
const chunkedName = name.replace(options.headerPrefix, "");
|
|
117
|
-
request.query[chunkedName] = headers[name];
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (headers["x-amz-user-agent"]) {
|
|
121
|
-
request.query["user-agent"] = headers["x-amz-user-agent"];
|
|
122
|
-
}
|
|
123
|
-
request.headers = { host: headers.host ?? request.hostname };
|
|
124
|
-
}
|
|
125
|
-
return next(args);
|
|
126
|
-
};
|
|
127
|
-
const websocketEndpointMiddlewareOptions = {
|
|
128
|
-
name: "websocketEndpointMiddleware",
|
|
129
|
-
tags: ["WEBSOCKET", "EVENT_STREAM"],
|
|
130
|
-
relation: "after",
|
|
131
|
-
toMiddleware: "eventStreamHeaderMiddleware",
|
|
132
|
-
override: true,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const getWebSocketPlugin = (config, options) => ({
|
|
136
|
-
applyToStack: (clientStack) => {
|
|
137
|
-
clientStack.addRelativeTo(websocketEndpointMiddleware(config, options), websocketEndpointMiddlewareOptions);
|
|
138
|
-
clientStack.add(injectSessionIdMiddleware(), injectSessionIdMiddlewareOptions);
|
|
139
|
-
},
|
|
140
|
-
});
|
|
6
|
+
var protocolHttp = require('@smithy/protocol-http');
|
|
7
|
+
var utilBase64 = require('@smithy/util-base64');
|
|
8
|
+
var eventstreamCodec = require('@smithy/eventstream-codec');
|
|
9
|
+
var utilHexEncoding = require('@smithy/util-hex-encoding');
|
|
141
10
|
|
|
142
11
|
const isWebSocketRequest = (request) => request.protocol === "ws:" || request.protocol === "wss:";
|
|
143
12
|
|
|
144
|
-
|
|
145
|
-
signer;
|
|
146
|
-
constructor(options) {
|
|
147
|
-
this.signer = options.signer;
|
|
148
|
-
}
|
|
149
|
-
presign(originalRequest, options = {}) {
|
|
150
|
-
return this.signer.presign(originalRequest, options);
|
|
151
|
-
}
|
|
152
|
-
async sign(toSign, options) {
|
|
153
|
-
if (protocolHttp.HttpRequest.isInstance(toSign) && isWebSocketRequest(toSign)) {
|
|
154
|
-
const signedRequest = await this.signer.presign({ ...toSign, body: "" }, {
|
|
155
|
-
...options,
|
|
156
|
-
expiresIn: 60,
|
|
157
|
-
unsignableHeaders: new Set(Object.keys(toSign.headers).filter((header) => header !== "host")),
|
|
158
|
-
});
|
|
159
|
-
return {
|
|
160
|
-
...signedRequest,
|
|
161
|
-
body: toSign.body,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
return this.signer.sign(toSign, options);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const resolveWebSocketConfig = (input) => {
|
|
171
|
-
const { signer } = input;
|
|
172
|
-
return Object.assign(input, {
|
|
173
|
-
signer: async (authScheme) => {
|
|
174
|
-
const signerObj = await signer(authScheme);
|
|
175
|
-
if (validateSigner(signerObj)) {
|
|
176
|
-
return new WebsocketSignatureV4({ signer: signerObj });
|
|
177
|
-
}
|
|
178
|
-
throw new Error("Expected WebsocketSignatureV4 signer, please check the client constructor.");
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
};
|
|
182
|
-
const validateSigner = (signer) => !!signer;
|
|
183
|
-
|
|
184
|
-
const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 2000;
|
|
13
|
+
const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 3000;
|
|
185
14
|
class WebSocketFetchHandler {
|
|
186
15
|
metadata = {
|
|
187
16
|
handlerProtocol: "websocket/h1.1",
|
|
188
17
|
};
|
|
189
|
-
config;
|
|
18
|
+
config = {};
|
|
190
19
|
configPromise;
|
|
191
20
|
httpHandler;
|
|
192
21
|
sockets = {};
|
|
@@ -198,13 +27,20 @@ class WebSocketFetchHandler {
|
|
|
198
27
|
}
|
|
199
28
|
constructor(options, httpHandler = new fetchHttpHandler.FetchHttpHandler()) {
|
|
200
29
|
this.httpHandler = httpHandler;
|
|
30
|
+
const setConfig = (opts) => {
|
|
31
|
+
this.config = {
|
|
32
|
+
...(opts ?? {}),
|
|
33
|
+
};
|
|
34
|
+
return this.config;
|
|
35
|
+
};
|
|
201
36
|
if (typeof options === "function") {
|
|
202
37
|
this.config = {};
|
|
203
|
-
this.configPromise = options().then((opts) =>
|
|
38
|
+
this.configPromise = options().then((opts) => {
|
|
39
|
+
return setConfig(opts);
|
|
40
|
+
});
|
|
204
41
|
}
|
|
205
42
|
else {
|
|
206
|
-
this.
|
|
207
|
-
this.configPromise = Promise.resolve(this.config);
|
|
43
|
+
this.configPromise = Promise.resolve(setConfig(options));
|
|
208
44
|
}
|
|
209
45
|
}
|
|
210
46
|
destroy() {
|
|
@@ -216,17 +52,20 @@ class WebSocketFetchHandler {
|
|
|
216
52
|
}
|
|
217
53
|
}
|
|
218
54
|
async handle(request) {
|
|
55
|
+
this.config = await this.configPromise;
|
|
56
|
+
const { logger } = this.config;
|
|
219
57
|
if (!isWebSocketRequest(request)) {
|
|
58
|
+
logger?.debug?.(`@aws-sdk - ws fetching ${request.protocol}${request.hostname}${request.path}`);
|
|
220
59
|
return this.httpHandler.handle(request);
|
|
221
60
|
}
|
|
222
61
|
const url = utilFormatUrl.formatUrl(request);
|
|
62
|
+
logger?.debug?.(`@aws-sdk - ws connecting ${url.split("?")[0]}`);
|
|
223
63
|
const socket = new WebSocket(url);
|
|
224
64
|
if (!this.sockets[url]) {
|
|
225
65
|
this.sockets[url] = [];
|
|
226
66
|
}
|
|
227
67
|
this.sockets[url].push(socket);
|
|
228
68
|
socket.binaryType = "arraybuffer";
|
|
229
|
-
this.config = await this.configPromise;
|
|
230
69
|
const { connectionTimeout = DEFAULT_WS_CONNECTION_TIMEOUT_MS } = this.config;
|
|
231
70
|
await this.waitForReady(socket, connectionTimeout);
|
|
232
71
|
const { body } = request;
|
|
@@ -259,6 +98,7 @@ class WebSocketFetchHandler {
|
|
|
259
98
|
reject({
|
|
260
99
|
$metadata: {
|
|
261
100
|
httpStatusCode: 500,
|
|
101
|
+
websocketSynthetic500Error: true,
|
|
262
102
|
},
|
|
263
103
|
});
|
|
264
104
|
}, connectionTimeout);
|
|
@@ -269,53 +109,80 @@ class WebSocketFetchHandler {
|
|
|
269
109
|
});
|
|
270
110
|
}
|
|
271
111
|
connect(socket, data) {
|
|
272
|
-
|
|
273
|
-
let
|
|
274
|
-
let
|
|
275
|
-
|
|
112
|
+
const messageQueue = [];
|
|
113
|
+
let pendingResolve = null;
|
|
114
|
+
let pendingReject = null;
|
|
115
|
+
const push = (item) => {
|
|
116
|
+
if (pendingResolve) {
|
|
117
|
+
if (item.error) {
|
|
118
|
+
pendingReject(item.error);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
pendingResolve({ done: item.done, value: item.value });
|
|
122
|
+
}
|
|
123
|
+
pendingResolve = null;
|
|
124
|
+
pendingReject = null;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
messageQueue.push(item);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
276
130
|
socket.onmessage = (event) => {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
131
|
+
const { data } = event;
|
|
132
|
+
if (typeof data === "string") {
|
|
133
|
+
push({
|
|
134
|
+
done: false,
|
|
135
|
+
value: utilBase64.fromBase64(data),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
push({
|
|
140
|
+
done: false,
|
|
141
|
+
value: new Uint8Array(data),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
281
144
|
};
|
|
282
|
-
socket.onerror = (
|
|
283
|
-
socketErrorOccurred = true;
|
|
145
|
+
socket.onerror = (event) => {
|
|
284
146
|
socket.close();
|
|
285
|
-
|
|
147
|
+
push({ done: true, error: event });
|
|
286
148
|
};
|
|
287
149
|
socket.onclose = () => {
|
|
288
150
|
this.removeNotUsableSockets(socket.url);
|
|
289
|
-
|
|
290
|
-
return;
|
|
291
|
-
if (streamError) {
|
|
292
|
-
reject(streamError);
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
resolve({
|
|
296
|
-
done: true,
|
|
297
|
-
value: undefined,
|
|
298
|
-
});
|
|
299
|
-
}
|
|
151
|
+
push({ done: true });
|
|
300
152
|
};
|
|
301
153
|
const outputStream = {
|
|
302
154
|
[Symbol.asyncIterator]: () => ({
|
|
303
|
-
next
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
155
|
+
async next() {
|
|
156
|
+
if (messageQueue.length > 0) {
|
|
157
|
+
const item = messageQueue.shift();
|
|
158
|
+
if (item.error) {
|
|
159
|
+
throw item.error;
|
|
160
|
+
}
|
|
161
|
+
return { done: item.done, value: item.value };
|
|
162
|
+
}
|
|
163
|
+
return new Promise((resolve, reject) => {
|
|
164
|
+
pendingResolve = resolve;
|
|
165
|
+
pendingReject = reject;
|
|
307
166
|
});
|
|
308
167
|
},
|
|
309
168
|
}),
|
|
310
169
|
};
|
|
311
170
|
const send = async () => {
|
|
312
171
|
try {
|
|
313
|
-
for await (const
|
|
314
|
-
socket.
|
|
172
|
+
for await (const chunk of data) {
|
|
173
|
+
if (socket.readyState >= WebSocket.CLOSING) {
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
socket.send(chunk);
|
|
178
|
+
}
|
|
315
179
|
}
|
|
316
180
|
}
|
|
317
181
|
catch (err) {
|
|
318
|
-
|
|
182
|
+
push({
|
|
183
|
+
done: true,
|
|
184
|
+
error: err,
|
|
185
|
+
});
|
|
319
186
|
}
|
|
320
187
|
finally {
|
|
321
188
|
socket.close(1000);
|
|
@@ -341,6 +208,184 @@ const getIterator = (stream) => {
|
|
|
341
208
|
const toReadableStream = (asyncIterable) => typeof ReadableStream === "function" ? eventstreamSerdeBrowser.iterableToReadableStream(asyncIterable) : asyncIterable;
|
|
342
209
|
const isReadableStream = (payload) => typeof ReadableStream === "function" && payload instanceof ReadableStream;
|
|
343
210
|
|
|
211
|
+
const websocketEndpointMiddleware = (config, options) => (next) => (args) => {
|
|
212
|
+
const { request } = args;
|
|
213
|
+
if (protocolHttp.HttpRequest.isInstance(request) &&
|
|
214
|
+
config.requestHandler.metadata?.handlerProtocol?.toLowerCase().includes("websocket")) {
|
|
215
|
+
request.protocol = "wss:";
|
|
216
|
+
request.method = "GET";
|
|
217
|
+
request.path = `${request.path}-websocket`;
|
|
218
|
+
const { headers } = request;
|
|
219
|
+
delete headers["content-type"];
|
|
220
|
+
delete headers["x-amz-content-sha256"];
|
|
221
|
+
for (const name of Object.keys(headers)) {
|
|
222
|
+
if (name.indexOf(options.headerPrefix) === 0) {
|
|
223
|
+
const chunkedName = name.replace(options.headerPrefix, "");
|
|
224
|
+
request.query[chunkedName] = headers[name];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (headers["x-amz-user-agent"]) {
|
|
228
|
+
request.query["user-agent"] = headers["x-amz-user-agent"];
|
|
229
|
+
}
|
|
230
|
+
request.headers = { host: headers.host ?? request.hostname };
|
|
231
|
+
}
|
|
232
|
+
return next(args);
|
|
233
|
+
};
|
|
234
|
+
const websocketEndpointMiddlewareOptions = {
|
|
235
|
+
name: "websocketEndpointMiddleware",
|
|
236
|
+
tags: ["WEBSOCKET", "EVENT_STREAM"],
|
|
237
|
+
relation: "after",
|
|
238
|
+
toMiddleware: "eventStreamHeaderMiddleware",
|
|
239
|
+
override: true,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const injectSessionIdMiddleware = () => (next) => async (args) => {
|
|
243
|
+
const requestParams = {
|
|
244
|
+
...args.input,
|
|
245
|
+
};
|
|
246
|
+
const response = await next(args);
|
|
247
|
+
const output = response.output;
|
|
248
|
+
if (requestParams.SessionId && output.SessionId == null) {
|
|
249
|
+
output.SessionId = requestParams.SessionId;
|
|
250
|
+
}
|
|
251
|
+
return response;
|
|
252
|
+
};
|
|
253
|
+
const injectSessionIdMiddlewareOptions = {
|
|
254
|
+
step: "initialize",
|
|
255
|
+
name: "injectSessionIdMiddleware",
|
|
256
|
+
tags: ["WEBSOCKET", "EVENT_STREAM"],
|
|
257
|
+
override: true,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const getWebSocketPlugin = (config, options) => ({
|
|
261
|
+
applyToStack: (clientStack) => {
|
|
262
|
+
clientStack.addRelativeTo(websocketEndpointMiddleware(config, options), websocketEndpointMiddlewareOptions);
|
|
263
|
+
clientStack.add(injectSessionIdMiddleware(), injectSessionIdMiddlewareOptions);
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
class WebsocketSignatureV4 {
|
|
268
|
+
signer;
|
|
269
|
+
constructor(options) {
|
|
270
|
+
this.signer = options.signer;
|
|
271
|
+
}
|
|
272
|
+
presign(originalRequest, options = {}) {
|
|
273
|
+
return this.signer.presign(originalRequest, options);
|
|
274
|
+
}
|
|
275
|
+
async sign(toSign, options) {
|
|
276
|
+
if (protocolHttp.HttpRequest.isInstance(toSign) && isWebSocketRequest(toSign)) {
|
|
277
|
+
const signedRequest = await this.signer.presign({ ...toSign, body: "" }, {
|
|
278
|
+
...options,
|
|
279
|
+
expiresIn: 60,
|
|
280
|
+
unsignableHeaders: new Set(Object.keys(toSign.headers).filter((header) => header !== "host")),
|
|
281
|
+
});
|
|
282
|
+
return {
|
|
283
|
+
...signedRequest,
|
|
284
|
+
body: toSign.body,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
return this.signer.sign(toSign, options);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
signMessage(message, args) {
|
|
292
|
+
return this.signer.signMessage(message, args);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const resolveWebSocketConfig = (input) => {
|
|
297
|
+
const { signer } = input;
|
|
298
|
+
return Object.assign(input, {
|
|
299
|
+
signer: async (authScheme) => {
|
|
300
|
+
const signerObj = await signer(authScheme);
|
|
301
|
+
if (validateSigner(signerObj)) {
|
|
302
|
+
return new WebsocketSignatureV4({ signer: signerObj });
|
|
303
|
+
}
|
|
304
|
+
throw new Error("Expected WebsocketSignatureV4 signer, please check the client constructor.");
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
const validateSigner = (signer) => !!signer;
|
|
309
|
+
|
|
310
|
+
class EventSigningTransformStream extends TransformStream {
|
|
311
|
+
constructor(initialSignature, messageSigner, eventStreamCodec, systemClockOffsetProvider) {
|
|
312
|
+
let priorSignature = initialSignature;
|
|
313
|
+
super({
|
|
314
|
+
start() { },
|
|
315
|
+
async transform(chunk, controller) {
|
|
316
|
+
try {
|
|
317
|
+
const now = new Date(Date.now() + (await systemClockOffsetProvider()));
|
|
318
|
+
const dateHeader = {
|
|
319
|
+
":date": { type: "timestamp", value: now },
|
|
320
|
+
};
|
|
321
|
+
const signedMessage = await messageSigner.sign({
|
|
322
|
+
message: {
|
|
323
|
+
body: chunk,
|
|
324
|
+
headers: dateHeader,
|
|
325
|
+
},
|
|
326
|
+
priorSignature: priorSignature,
|
|
327
|
+
}, {
|
|
328
|
+
signingDate: now,
|
|
329
|
+
});
|
|
330
|
+
priorSignature = signedMessage.signature;
|
|
331
|
+
const serializedSigned = eventStreamCodec.encode({
|
|
332
|
+
headers: {
|
|
333
|
+
...dateHeader,
|
|
334
|
+
":chunk-signature": {
|
|
335
|
+
type: "binary",
|
|
336
|
+
value: utilHexEncoding.fromHex(signedMessage.signature),
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
body: chunk,
|
|
340
|
+
});
|
|
341
|
+
controller.enqueue(serializedSigned);
|
|
342
|
+
}
|
|
343
|
+
catch (error) {
|
|
344
|
+
controller.error(error);
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
class EventStreamPayloadHandler {
|
|
352
|
+
messageSigner;
|
|
353
|
+
eventStreamCodec;
|
|
354
|
+
systemClockOffsetProvider;
|
|
355
|
+
constructor(options) {
|
|
356
|
+
this.messageSigner = options.messageSigner;
|
|
357
|
+
this.eventStreamCodec = new eventstreamCodec.EventStreamCodec(options.utf8Encoder, options.utf8Decoder);
|
|
358
|
+
this.systemClockOffsetProvider = async () => options.systemClockOffset ?? 0;
|
|
359
|
+
}
|
|
360
|
+
async handle(next, args, context = {}) {
|
|
361
|
+
const request = args.request;
|
|
362
|
+
const { body: payload, headers, query } = request;
|
|
363
|
+
if (!(payload instanceof ReadableStream)) {
|
|
364
|
+
throw new Error("Eventstream payload must be a ReadableStream.");
|
|
365
|
+
}
|
|
366
|
+
const placeHolderStream = new TransformStream();
|
|
367
|
+
request.body = placeHolderStream.readable;
|
|
368
|
+
const match = (headers?.authorization ?? "").match(/Signature=(\w+)$/);
|
|
369
|
+
const priorSignature = (match ?? [])[1] ?? (query && query["X-Amz-Signature"]) ?? "";
|
|
370
|
+
const signingStream = new EventSigningTransformStream(priorSignature, await this.messageSigner(), this.eventStreamCodec, this.systemClockOffsetProvider);
|
|
371
|
+
payload.pipeThrough(signingStream).pipeThrough(placeHolderStream);
|
|
372
|
+
let result;
|
|
373
|
+
try {
|
|
374
|
+
result = await next(args);
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
const p = payload.cancel?.();
|
|
378
|
+
if (p instanceof Promise) {
|
|
379
|
+
p.catch(() => { });
|
|
380
|
+
}
|
|
381
|
+
throw e;
|
|
382
|
+
}
|
|
383
|
+
return result;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const eventStreamPayloadHandlerProvider = (options) => new EventStreamPayloadHandler(options);
|
|
388
|
+
|
|
344
389
|
exports.WebSocketFetchHandler = WebSocketFetchHandler;
|
|
345
390
|
exports.eventStreamPayloadHandlerProvider = eventStreamPayloadHandlerProvider;
|
|
346
391
|
exports.getWebSocketPlugin = getWebSocketPlugin;
|
|
@@ -2,13 +2,14 @@ import { formatUrl } from "@aws-sdk/util-format-url";
|
|
|
2
2
|
import { iterableToReadableStream, readableStreamtoIterable } from "@smithy/eventstream-serde-browser";
|
|
3
3
|
import { FetchHttpHandler } from "@smithy/fetch-http-handler";
|
|
4
4
|
import { HttpResponse } from "@smithy/protocol-http";
|
|
5
|
+
import { fromBase64 } from "@smithy/util-base64";
|
|
5
6
|
import { isWebSocketRequest } from "./utils";
|
|
6
|
-
const DEFAULT_WS_CONNECTION_TIMEOUT_MS =
|
|
7
|
+
const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 3000;
|
|
7
8
|
export class WebSocketFetchHandler {
|
|
8
9
|
metadata = {
|
|
9
10
|
handlerProtocol: "websocket/h1.1",
|
|
10
11
|
};
|
|
11
|
-
config;
|
|
12
|
+
config = {};
|
|
12
13
|
configPromise;
|
|
13
14
|
httpHandler;
|
|
14
15
|
sockets = {};
|
|
@@ -20,13 +21,20 @@ export class WebSocketFetchHandler {
|
|
|
20
21
|
}
|
|
21
22
|
constructor(options, httpHandler = new FetchHttpHandler()) {
|
|
22
23
|
this.httpHandler = httpHandler;
|
|
24
|
+
const setConfig = (opts) => {
|
|
25
|
+
this.config = {
|
|
26
|
+
...(opts ?? {}),
|
|
27
|
+
};
|
|
28
|
+
return this.config;
|
|
29
|
+
};
|
|
23
30
|
if (typeof options === "function") {
|
|
24
31
|
this.config = {};
|
|
25
|
-
this.configPromise = options().then((opts) =>
|
|
32
|
+
this.configPromise = options().then((opts) => {
|
|
33
|
+
return setConfig(opts);
|
|
34
|
+
});
|
|
26
35
|
}
|
|
27
36
|
else {
|
|
28
|
-
this.
|
|
29
|
-
this.configPromise = Promise.resolve(this.config);
|
|
37
|
+
this.configPromise = Promise.resolve(setConfig(options));
|
|
30
38
|
}
|
|
31
39
|
}
|
|
32
40
|
destroy() {
|
|
@@ -38,17 +46,20 @@ export class WebSocketFetchHandler {
|
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
async handle(request) {
|
|
49
|
+
this.config = await this.configPromise;
|
|
50
|
+
const { logger } = this.config;
|
|
41
51
|
if (!isWebSocketRequest(request)) {
|
|
52
|
+
logger?.debug?.(`@aws-sdk - ws fetching ${request.protocol}${request.hostname}${request.path}`);
|
|
42
53
|
return this.httpHandler.handle(request);
|
|
43
54
|
}
|
|
44
55
|
const url = formatUrl(request);
|
|
56
|
+
logger?.debug?.(`@aws-sdk - ws connecting ${url.split("?")[0]}`);
|
|
45
57
|
const socket = new WebSocket(url);
|
|
46
58
|
if (!this.sockets[url]) {
|
|
47
59
|
this.sockets[url] = [];
|
|
48
60
|
}
|
|
49
61
|
this.sockets[url].push(socket);
|
|
50
62
|
socket.binaryType = "arraybuffer";
|
|
51
|
-
this.config = await this.configPromise;
|
|
52
63
|
const { connectionTimeout = DEFAULT_WS_CONNECTION_TIMEOUT_MS } = this.config;
|
|
53
64
|
await this.waitForReady(socket, connectionTimeout);
|
|
54
65
|
const { body } = request;
|
|
@@ -81,6 +92,7 @@ export class WebSocketFetchHandler {
|
|
|
81
92
|
reject({
|
|
82
93
|
$metadata: {
|
|
83
94
|
httpStatusCode: 500,
|
|
95
|
+
websocketSynthetic500Error: true,
|
|
84
96
|
},
|
|
85
97
|
});
|
|
86
98
|
}, connectionTimeout);
|
|
@@ -91,53 +103,80 @@ export class WebSocketFetchHandler {
|
|
|
91
103
|
});
|
|
92
104
|
}
|
|
93
105
|
connect(socket, data) {
|
|
94
|
-
|
|
95
|
-
let
|
|
96
|
-
let
|
|
97
|
-
|
|
106
|
+
const messageQueue = [];
|
|
107
|
+
let pendingResolve = null;
|
|
108
|
+
let pendingReject = null;
|
|
109
|
+
const push = (item) => {
|
|
110
|
+
if (pendingResolve) {
|
|
111
|
+
if (item.error) {
|
|
112
|
+
pendingReject(item.error);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
pendingResolve({ done: item.done, value: item.value });
|
|
116
|
+
}
|
|
117
|
+
pendingResolve = null;
|
|
118
|
+
pendingReject = null;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
messageQueue.push(item);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
98
124
|
socket.onmessage = (event) => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
125
|
+
const { data } = event;
|
|
126
|
+
if (typeof data === "string") {
|
|
127
|
+
push({
|
|
128
|
+
done: false,
|
|
129
|
+
value: fromBase64(data),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
push({
|
|
134
|
+
done: false,
|
|
135
|
+
value: new Uint8Array(data),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
103
138
|
};
|
|
104
|
-
socket.onerror = (
|
|
105
|
-
socketErrorOccurred = true;
|
|
139
|
+
socket.onerror = (event) => {
|
|
106
140
|
socket.close();
|
|
107
|
-
|
|
141
|
+
push({ done: true, error: event });
|
|
108
142
|
};
|
|
109
143
|
socket.onclose = () => {
|
|
110
144
|
this.removeNotUsableSockets(socket.url);
|
|
111
|
-
|
|
112
|
-
return;
|
|
113
|
-
if (streamError) {
|
|
114
|
-
reject(streamError);
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
resolve({
|
|
118
|
-
done: true,
|
|
119
|
-
value: undefined,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
145
|
+
push({ done: true });
|
|
122
146
|
};
|
|
123
147
|
const outputStream = {
|
|
124
148
|
[Symbol.asyncIterator]: () => ({
|
|
125
|
-
next
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
149
|
+
async next() {
|
|
150
|
+
if (messageQueue.length > 0) {
|
|
151
|
+
const item = messageQueue.shift();
|
|
152
|
+
if (item.error) {
|
|
153
|
+
throw item.error;
|
|
154
|
+
}
|
|
155
|
+
return { done: item.done, value: item.value };
|
|
156
|
+
}
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
pendingResolve = resolve;
|
|
159
|
+
pendingReject = reject;
|
|
129
160
|
});
|
|
130
161
|
},
|
|
131
162
|
}),
|
|
132
163
|
};
|
|
133
164
|
const send = async () => {
|
|
134
165
|
try {
|
|
135
|
-
for await (const
|
|
136
|
-
socket.
|
|
166
|
+
for await (const chunk of data) {
|
|
167
|
+
if (socket.readyState >= WebSocket.CLOSING) {
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
socket.send(chunk);
|
|
172
|
+
}
|
|
137
173
|
}
|
|
138
174
|
}
|
|
139
175
|
catch (err) {
|
|
140
|
-
|
|
176
|
+
push({
|
|
177
|
+
done: true,
|
|
178
|
+
error: err,
|
|
179
|
+
});
|
|
141
180
|
}
|
|
142
181
|
finally {
|
|
143
182
|
socket.close(1000);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { websocketEndpointMiddleware, websocketEndpointMiddlewareOptions, } from "./middlewares/websocketEndpointMiddleware";
|
|
2
|
+
import { injectSessionIdMiddleware, injectSessionIdMiddlewareOptions, } from "./middlewares/websocketInjectSessionIdMiddleware";
|
|
3
3
|
export const getWebSocketPlugin = (config, options) => ({
|
|
4
4
|
applyToStack: (clientStack) => {
|
|
5
5
|
clientStack.addRelativeTo(websocketEndpointMiddleware(config, options), websocketEndpointMiddlewareOptions);
|
package/dist-es/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./WebSocketFetchHandler";
|
|
2
2
|
export * from "./getWebSocketPlugin";
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
3
|
+
export * from "./resolveWebSocketConfig";
|
|
4
|
+
export * from "./ws-eventstream/eventStreamPayloadHandlerProvider";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { fromHex } from "@smithy/util-hex-encoding";
|
|
2
|
+
export class EventSigningTransformStream extends TransformStream {
|
|
3
|
+
constructor(initialSignature, messageSigner, eventStreamCodec, systemClockOffsetProvider) {
|
|
4
|
+
let priorSignature = initialSignature;
|
|
5
|
+
super({
|
|
6
|
+
start() { },
|
|
7
|
+
async transform(chunk, controller) {
|
|
8
|
+
try {
|
|
9
|
+
const now = new Date(Date.now() + (await systemClockOffsetProvider()));
|
|
10
|
+
const dateHeader = {
|
|
11
|
+
":date": { type: "timestamp", value: now },
|
|
12
|
+
};
|
|
13
|
+
const signedMessage = await messageSigner.sign({
|
|
14
|
+
message: {
|
|
15
|
+
body: chunk,
|
|
16
|
+
headers: dateHeader,
|
|
17
|
+
},
|
|
18
|
+
priorSignature: priorSignature,
|
|
19
|
+
}, {
|
|
20
|
+
signingDate: now,
|
|
21
|
+
});
|
|
22
|
+
priorSignature = signedMessage.signature;
|
|
23
|
+
const serializedSigned = eventStreamCodec.encode({
|
|
24
|
+
headers: {
|
|
25
|
+
...dateHeader,
|
|
26
|
+
":chunk-signature": {
|
|
27
|
+
type: "binary",
|
|
28
|
+
value: fromHex(signedMessage.signature),
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
body: chunk,
|
|
32
|
+
});
|
|
33
|
+
controller.enqueue(serializedSigned);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
controller.error(error);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist-es/{EventStreamPayloadHandler.js → ws-eventstream/EventStreamPayloadHandler.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventStreamCodec } from "@smithy/eventstream-codec";
|
|
2
|
-
import {
|
|
2
|
+
import { EventSigningTransformStream } from "./EventSigningTransformStream";
|
|
3
3
|
export class EventStreamPayloadHandler {
|
|
4
4
|
messageSigner;
|
|
5
5
|
eventStreamCodec;
|
|
@@ -17,19 +17,21 @@ export class EventStreamPayloadHandler {
|
|
|
17
17
|
}
|
|
18
18
|
const placeHolderStream = new TransformStream();
|
|
19
19
|
request.body = placeHolderStream.readable;
|
|
20
|
+
const match = (headers?.authorization ?? "").match(/Signature=(\w+)$/);
|
|
21
|
+
const priorSignature = (match ?? [])[1] ?? (query && query["X-Amz-Signature"]) ?? "";
|
|
22
|
+
const signingStream = new EventSigningTransformStream(priorSignature, await this.messageSigner(), this.eventStreamCodec, this.systemClockOffsetProvider);
|
|
23
|
+
payload.pipeThrough(signingStream).pipeThrough(placeHolderStream);
|
|
20
24
|
let result;
|
|
21
25
|
try {
|
|
22
26
|
result = await next(args);
|
|
23
27
|
}
|
|
24
28
|
catch (e) {
|
|
25
|
-
|
|
29
|
+
const p = payload.cancel?.();
|
|
30
|
+
if (p instanceof Promise) {
|
|
31
|
+
p.catch(() => { });
|
|
32
|
+
}
|
|
26
33
|
throw e;
|
|
27
34
|
}
|
|
28
|
-
const match = (headers["authorization"] || "").match(/Signature=([\w]+)$/);
|
|
29
|
-
const priorSignature = (match || [])[1] || (query && query["X-Amz-Signature"]) || "";
|
|
30
|
-
const signingStream = getEventSigningTransformStream(priorSignature, await this.messageSigner(), this.eventStreamCodec, this.systemClockOffsetProvider);
|
|
31
|
-
const signedPayload = payload.pipeThrough(signingStream);
|
|
32
|
-
signedPayload.pipeThrough(placeHolderStream);
|
|
33
35
|
return result;
|
|
34
36
|
}
|
|
35
37
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { HttpRequest
|
|
2
|
-
import {
|
|
1
|
+
import type { HttpRequest } from "@smithy/protocol-http";
|
|
2
|
+
import { HttpResponse } from "@smithy/protocol-http";
|
|
3
|
+
import type { Logger, Provider, RequestHandler, RequestHandlerMetadata } from "@smithy/types";
|
|
3
4
|
export interface WebSocketFetchHandlerOptions {
|
|
4
5
|
/**
|
|
5
6
|
* The maximum time in milliseconds that the connection phase of a request
|
|
6
7
|
* may take before the connection attempt is abandoned.
|
|
7
8
|
*/
|
|
8
9
|
connectionTimeout?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Optional logger.
|
|
12
|
+
*/
|
|
13
|
+
logger?: Logger;
|
|
9
14
|
}
|
|
10
15
|
/**
|
|
11
16
|
* Base handler for websocket requests and HTTP request. By default, the request input and output
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import { SignatureV4 as BaseSignatureV4 } from "@smithy/signature-v4";
|
|
2
|
-
import { HttpRequest as IHttpRequest, RequestPresigner, RequestPresigningArguments, RequestSigner, RequestSigningArguments } from "@smithy/types";
|
|
3
|
-
|
|
1
|
+
import type { SignatureV4 as BaseSignatureV4 } from "@smithy/signature-v4";
|
|
2
|
+
import type { EventSigner, EventSigningArguments, FormattedEvent, HttpRequest as IHttpRequest, MessageSigner, RequestPresigner, RequestPresigningArguments, RequestSigner, RequestSigningArguments, SignableMessage, SignedMessage, SigningArguments, StringSigner } from "@smithy/types";
|
|
3
|
+
/**
|
|
4
|
+
* Because this signer defers to sigv4, it implements all signing interfaces that
|
|
5
|
+
* the sigv4 signer does, including message signing.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare class WebsocketSignatureV4 implements RequestPresigner, RequestSigner, StringSigner, EventSigner, MessageSigner {
|
|
4
10
|
private readonly signer;
|
|
5
11
|
constructor(options: {
|
|
6
12
|
signer: BaseSignatureV4;
|
|
7
13
|
});
|
|
8
14
|
presign(originalRequest: IHttpRequest, options?: RequestPresigningArguments): Promise<IHttpRequest>;
|
|
9
|
-
sign(
|
|
15
|
+
sign(stringToSign: string, options?: SigningArguments): Promise<string>;
|
|
16
|
+
sign(event: FormattedEvent, options: EventSigningArguments): Promise<string>;
|
|
17
|
+
sign(event: SignableMessage, options: SigningArguments): Promise<SignedMessage>;
|
|
18
|
+
sign(requestToSign: IHttpRequest, options?: RequestSigningArguments): Promise<IHttpRequest>;
|
|
19
|
+
signMessage(message: SignableMessage, args: SigningArguments): Promise<SignedMessage>;
|
|
10
20
|
}
|
package/dist-types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./WebSocketFetchHandler";
|
|
2
2
|
export * from "./getWebSocketPlugin";
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
3
|
+
export * from "./resolveWebSocketConfig";
|
|
4
|
+
export * from "./ws-eventstream/eventStreamPayloadHandlerProvider";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BuildMiddleware, RelativeMiddlewareOptions, RequestHandler } from "@smithy/types";
|
|
1
|
+
import type { BuildMiddleware, RelativeMiddlewareOptions, RequestHandler } from "@smithy/types";
|
|
2
2
|
/**
|
|
3
3
|
* Middleware that modify the request to from http to WebSocket
|
|
4
4
|
* This middleware can only be applied to commands that supports bi-directional event streaming via WebSocket.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InitializeHandlerOptions, InitializeMiddleware } from "@smithy/types";
|
|
1
|
+
import type { InitializeHandlerOptions, InitializeMiddleware } from "@smithy/types";
|
|
2
2
|
/**
|
|
3
3
|
* Most WebSocket operations contains `SessionId` parameter in both input and
|
|
4
4
|
* output, with the same value. This middleware populates the `SessionId`
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { HttpRequest
|
|
1
|
+
import { HttpRequest } from "@smithy/protocol-http";
|
|
2
|
+
import { HttpResponse } from "@smithy/protocol-http";
|
|
2
3
|
import {
|
|
4
|
+
Logger,
|
|
3
5
|
Provider,
|
|
4
6
|
RequestHandler,
|
|
5
7
|
RequestHandlerMetadata,
|
|
6
8
|
} from "@smithy/types";
|
|
7
9
|
export interface WebSocketFetchHandlerOptions {
|
|
8
10
|
connectionTimeout?: number;
|
|
11
|
+
logger?: Logger;
|
|
9
12
|
}
|
|
10
13
|
export declare class WebSocketFetchHandler {
|
|
11
14
|
readonly metadata: RequestHandlerMetadata;
|
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { SignatureV4 as BaseSignatureV4 } from "@smithy/signature-v4";
|
|
2
2
|
import {
|
|
3
|
+
EventSigner,
|
|
4
|
+
EventSigningArguments,
|
|
5
|
+
FormattedEvent,
|
|
3
6
|
HttpRequest as IHttpRequest,
|
|
7
|
+
MessageSigner,
|
|
4
8
|
RequestPresigner,
|
|
5
9
|
RequestPresigningArguments,
|
|
6
10
|
RequestSigner,
|
|
7
11
|
RequestSigningArguments,
|
|
12
|
+
SignableMessage,
|
|
13
|
+
SignedMessage,
|
|
14
|
+
SigningArguments,
|
|
15
|
+
StringSigner,
|
|
8
16
|
} from "@smithy/types";
|
|
9
17
|
export declare class WebsocketSignatureV4
|
|
10
|
-
implements
|
|
18
|
+
implements
|
|
19
|
+
RequestPresigner,
|
|
20
|
+
RequestSigner,
|
|
21
|
+
StringSigner,
|
|
22
|
+
EventSigner,
|
|
23
|
+
MessageSigner
|
|
11
24
|
{
|
|
12
25
|
private readonly signer;
|
|
13
26
|
constructor(options: { signer: BaseSignatureV4 });
|
|
@@ -15,8 +28,18 @@ export declare class WebsocketSignatureV4
|
|
|
15
28
|
originalRequest: IHttpRequest,
|
|
16
29
|
options?: RequestPresigningArguments
|
|
17
30
|
): Promise<IHttpRequest>;
|
|
31
|
+
sign(stringToSign: string, options?: SigningArguments): Promise<string>;
|
|
32
|
+
sign(event: FormattedEvent, options: EventSigningArguments): Promise<string>;
|
|
18
33
|
sign(
|
|
19
|
-
|
|
34
|
+
event: SignableMessage,
|
|
35
|
+
options: SigningArguments
|
|
36
|
+
): Promise<SignedMessage>;
|
|
37
|
+
sign(
|
|
38
|
+
requestToSign: IHttpRequest,
|
|
20
39
|
options?: RequestSigningArguments
|
|
21
40
|
): Promise<IHttpRequest>;
|
|
41
|
+
signMessage(
|
|
42
|
+
message: SignableMessage,
|
|
43
|
+
args: SigningArguments
|
|
44
|
+
): Promise<SignedMessage>;
|
|
22
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./WebSocketFetchHandler";
|
|
2
2
|
export * from "./getWebSocketPlugin";
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
3
|
+
export * from "./resolveWebSocketConfig";
|
|
4
|
+
export * from "./ws-eventstream/eventStreamPayloadHandlerProvider";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { EventStreamCodec } from "@smithy/eventstream-codec";
|
|
2
|
+
import { MessageSigner, Provider } from "@smithy/types";
|
|
3
|
+
export declare class EventSigningTransformStream extends TransformStream<
|
|
4
|
+
Uint8Array,
|
|
5
|
+
Uint8Array
|
|
6
|
+
> {
|
|
7
|
+
readable: ReadableStream<Uint8Array>;
|
|
8
|
+
writable: WritableStream<Uint8Array>;
|
|
9
|
+
constructor(
|
|
10
|
+
initialSignature: string,
|
|
11
|
+
messageSigner: MessageSigner,
|
|
12
|
+
eventStreamCodec: EventStreamCodec,
|
|
13
|
+
systemClockOffsetProvider: Provider<number>
|
|
14
|
+
);
|
|
15
|
+
}
|
package/dist-types/utils.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { HttpRequest } from "@smithy/types";
|
|
1
|
+
import type { HttpRequest } from "@smithy/types";
|
|
2
2
|
export declare const isWebSocketRequest: (request: HttpRequest) => boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EventStreamCodec } from "@smithy/eventstream-codec";
|
|
2
|
+
import type { MessageSigner, Provider } from "@smithy/types";
|
|
3
|
+
/**
|
|
4
|
+
* A transform stream that signs the eventstream.
|
|
5
|
+
*
|
|
6
|
+
* Implementation replicated from @aws-sdk/eventstream-handler-node::EventSigningStream
|
|
7
|
+
* but modified to be compatible with web stream interface.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare class EventSigningTransformStream extends TransformStream<Uint8Array, Uint8Array> {
|
|
12
|
+
/**
|
|
13
|
+
* @override
|
|
14
|
+
*/
|
|
15
|
+
readable: ReadableStream<Uint8Array>;
|
|
16
|
+
/**
|
|
17
|
+
* @override
|
|
18
|
+
*/
|
|
19
|
+
writable: WritableStream<Uint8Array>;
|
|
20
|
+
constructor(initialSignature: string, messageSigner: MessageSigner, eventStreamCodec: EventStreamCodec, systemClockOffsetProvider: Provider<number>);
|
|
21
|
+
}
|
package/dist-types/{EventStreamPayloadHandler.d.ts → ws-eventstream/EventStreamPayloadHandler.d.ts}
RENAMED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { Decoder, Encoder, EventStreamPayloadHandler as IEventStreamPayloadHandler, FinalizeHandler, FinalizeHandlerArguments, FinalizeHandlerOutput, HandlerExecutionContext, MessageSigner, MetadataBearer, Provider } from "@smithy/types";
|
|
1
|
+
import type { Decoder, Encoder, EventStreamPayloadHandler as IEventStreamPayloadHandler, FinalizeHandler, FinalizeHandlerArguments, FinalizeHandlerOutput, HandlerExecutionContext, MessageSigner, MetadataBearer, Provider } from "@smithy/types";
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
2
5
|
export interface EventStreamPayloadHandlerOptions {
|
|
3
6
|
messageSigner: Provider<MessageSigner>;
|
|
4
7
|
utf8Encoder: Encoder;
|
|
@@ -6,11 +9,7 @@ export interface EventStreamPayloadHandlerOptions {
|
|
|
6
9
|
systemClockOffset?: number;
|
|
7
10
|
}
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
10
|
-
* 1. Pause stream for initial request.
|
|
11
|
-
* 2. Close the stream if initial request fails.
|
|
12
|
-
* 3. Start piping payload when connection is established.
|
|
13
|
-
* 4. Sign the payload after payload stream starting to flow.
|
|
12
|
+
* @internal
|
|
14
13
|
*/
|
|
15
14
|
export declare class EventStreamPayloadHandler implements IEventStreamPayloadHandler {
|
|
16
15
|
private readonly messageSigner;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-sdk/middleware-websocket",
|
|
3
|
-
"version": "3.972.
|
|
3
|
+
"version": "3.972.5",
|
|
4
4
|
"main": "./dist-cjs/index.js",
|
|
5
5
|
"module": "./dist-es/index.js",
|
|
6
6
|
"types": "./dist-types/index.d.ts",
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"@smithy/protocol-http": "^5.3.8",
|
|
33
33
|
"@smithy/signature-v4": "^5.3.8",
|
|
34
34
|
"@smithy/types": "^4.12.0",
|
|
35
|
+
"@smithy/util-base64": "^4.3.0",
|
|
35
36
|
"@smithy/util-hex-encoding": "^4.2.0",
|
|
37
|
+
"@smithy/util-utf8": "^4.2.0",
|
|
36
38
|
"tslib": "^2.6.2"
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
@@ -42,8 +44,7 @@
|
|
|
42
44
|
"mock-socket": "9.1.5",
|
|
43
45
|
"premove": "4.0.0",
|
|
44
46
|
"typescript": "~5.8.3",
|
|
45
|
-
"vitest-websocket-mock": "0.2.3"
|
|
46
|
-
"web-streams-polyfill": "3.2.1"
|
|
47
|
+
"vitest-websocket-mock": "0.2.3"
|
|
47
48
|
},
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">= 14.0.0"
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { fromHex } from "@smithy/util-hex-encoding";
|
|
2
|
-
export const getEventSigningTransformStream = (initialSignature, messageSigner, eventStreamCodec, systemClockOffsetProvider) => {
|
|
3
|
-
let priorSignature = initialSignature;
|
|
4
|
-
const transformer = {
|
|
5
|
-
start() { },
|
|
6
|
-
async transform(chunk, controller) {
|
|
7
|
-
try {
|
|
8
|
-
const now = new Date(Date.now() + (await systemClockOffsetProvider()));
|
|
9
|
-
const dateHeader = {
|
|
10
|
-
":date": { type: "timestamp", value: now },
|
|
11
|
-
};
|
|
12
|
-
const signedMessage = await messageSigner.sign({
|
|
13
|
-
message: {
|
|
14
|
-
body: chunk,
|
|
15
|
-
headers: dateHeader,
|
|
16
|
-
},
|
|
17
|
-
priorSignature: priorSignature,
|
|
18
|
-
}, {
|
|
19
|
-
signingDate: now,
|
|
20
|
-
});
|
|
21
|
-
priorSignature = signedMessage.signature;
|
|
22
|
-
const serializedSigned = eventStreamCodec.encode({
|
|
23
|
-
headers: {
|
|
24
|
-
...dateHeader,
|
|
25
|
-
":chunk-signature": {
|
|
26
|
-
type: "binary",
|
|
27
|
-
value: fromHex(signedMessage.signature),
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
body: chunk,
|
|
31
|
-
});
|
|
32
|
-
controller.enqueue(serializedSigned);
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
controller.error(error);
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
return new TransformStream({ ...transformer });
|
|
40
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { EventStreamCodec } from "@smithy/eventstream-codec";
|
|
2
|
-
import { MessageSigner, Provider } from "@smithy/types";
|
|
3
|
-
/**
|
|
4
|
-
* Get a transform stream that signs the eventstream
|
|
5
|
-
* Implementation replicated from @aws-sdk/eventstream-handler-node::EventSigningStream
|
|
6
|
-
* but modified to be compatible with WHATWG stream interface
|
|
7
|
-
*
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
export declare const getEventSigningTransformStream: (initialSignature: string, messageSigner: MessageSigner, eventStreamCodec: EventStreamCodec, systemClockOffsetProvider: Provider<number>) => TransformStream<Uint8Array, Uint8Array>;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { EventStreamCodec } from "@smithy/eventstream-codec";
|
|
2
|
-
import { MessageSigner, Provider } from "@smithy/types";
|
|
3
|
-
export declare const getEventSigningTransformStream: (
|
|
4
|
-
initialSignature: string,
|
|
5
|
-
messageSigner: MessageSigner,
|
|
6
|
-
eventStreamCodec: EventStreamCodec,
|
|
7
|
-
systemClockOffsetProvider: Provider<number>
|
|
8
|
-
) => TransformStream<Uint8Array, Uint8Array>;
|
/package/dist-es/{middleware-websocket-endpoint.js → middlewares/websocketEndpointMiddleware.js}
RENAMED
|
File without changes
|
/package/dist-es/{middleware-session-id.js → middlewares/websocketInjectSessionIdMiddleware.js}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|