@aws-sdk/middleware-websocket 3.972.4 → 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 +248 -210
- package/dist-es/{websocket-fetch-handler.js → WebSocketFetchHandler.js} +67 -33
- 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} +2 -2
- 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} +1 -7
- package/dist-types/{eventstream-payload-handler-provider.d.ts → ws-eventstream/eventStreamPayloadHandlerProvider.d.ts} +1 -1
- package/package.json +3 -1
- package/dist-es/get-event-signing-stream.js +0 -40
- 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,194 +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
|
-
const match = (headers?.authorization ?? "").match(/Signature=(\w+)$/);
|
|
68
|
-
const priorSignature = (match ?? [])[1] ?? (query && query["X-Amz-Signature"]) ?? "";
|
|
69
|
-
const signingStream = getEventSigningTransformStream(priorSignature, await this.messageSigner(), this.eventStreamCodec, this.systemClockOffsetProvider);
|
|
70
|
-
payload.pipeThrough(signingStream).pipeThrough(placeHolderStream);
|
|
71
|
-
let result;
|
|
72
|
-
try {
|
|
73
|
-
result = await next(args);
|
|
74
|
-
}
|
|
75
|
-
catch (e) {
|
|
76
|
-
const p = payload.cancel?.();
|
|
77
|
-
if (p instanceof Promise) {
|
|
78
|
-
p.catch(() => { });
|
|
79
|
-
}
|
|
80
|
-
throw e;
|
|
81
|
-
}
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const eventStreamPayloadHandlerProvider = (options) => new EventStreamPayloadHandler(options);
|
|
87
|
-
|
|
88
|
-
const injectSessionIdMiddleware = () => (next) => async (args) => {
|
|
89
|
-
const requestParams = {
|
|
90
|
-
...args.input,
|
|
91
|
-
};
|
|
92
|
-
const response = await next(args);
|
|
93
|
-
const output = response.output;
|
|
94
|
-
if (requestParams.SessionId && output.SessionId == null) {
|
|
95
|
-
output.SessionId = requestParams.SessionId;
|
|
96
|
-
}
|
|
97
|
-
return response;
|
|
98
|
-
};
|
|
99
|
-
const injectSessionIdMiddlewareOptions = {
|
|
100
|
-
step: "initialize",
|
|
101
|
-
name: "injectSessionIdMiddleware",
|
|
102
|
-
tags: ["WEBSOCKET", "EVENT_STREAM"],
|
|
103
|
-
override: true,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const websocketEndpointMiddleware = (config, options) => (next) => (args) => {
|
|
107
|
-
const { request } = args;
|
|
108
|
-
if (protocolHttp.HttpRequest.isInstance(request) &&
|
|
109
|
-
config.requestHandler.metadata?.handlerProtocol?.toLowerCase().includes("websocket")) {
|
|
110
|
-
request.protocol = "wss:";
|
|
111
|
-
request.method = "GET";
|
|
112
|
-
request.path = `${request.path}-websocket`;
|
|
113
|
-
const { headers } = request;
|
|
114
|
-
delete headers["content-type"];
|
|
115
|
-
delete headers["x-amz-content-sha256"];
|
|
116
|
-
for (const name of Object.keys(headers)) {
|
|
117
|
-
if (name.indexOf(options.headerPrefix) === 0) {
|
|
118
|
-
const chunkedName = name.replace(options.headerPrefix, "");
|
|
119
|
-
request.query[chunkedName] = headers[name];
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (headers["x-amz-user-agent"]) {
|
|
123
|
-
request.query["user-agent"] = headers["x-amz-user-agent"];
|
|
124
|
-
}
|
|
125
|
-
request.headers = { host: headers.host ?? request.hostname };
|
|
126
|
-
}
|
|
127
|
-
return next(args);
|
|
128
|
-
};
|
|
129
|
-
const websocketEndpointMiddlewareOptions = {
|
|
130
|
-
name: "websocketEndpointMiddleware",
|
|
131
|
-
tags: ["WEBSOCKET", "EVENT_STREAM"],
|
|
132
|
-
relation: "after",
|
|
133
|
-
toMiddleware: "eventStreamHeaderMiddleware",
|
|
134
|
-
override: true,
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const getWebSocketPlugin = (config, options) => ({
|
|
138
|
-
applyToStack: (clientStack) => {
|
|
139
|
-
clientStack.addRelativeTo(websocketEndpointMiddleware(config, options), websocketEndpointMiddlewareOptions);
|
|
140
|
-
clientStack.add(injectSessionIdMiddleware(), injectSessionIdMiddlewareOptions);
|
|
141
|
-
},
|
|
142
|
-
});
|
|
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');
|
|
143
10
|
|
|
144
11
|
const isWebSocketRequest = (request) => request.protocol === "ws:" || request.protocol === "wss:";
|
|
145
12
|
|
|
146
|
-
|
|
147
|
-
signer;
|
|
148
|
-
constructor(options) {
|
|
149
|
-
this.signer = options.signer;
|
|
150
|
-
}
|
|
151
|
-
presign(originalRequest, options = {}) {
|
|
152
|
-
return this.signer.presign(originalRequest, options);
|
|
153
|
-
}
|
|
154
|
-
async sign(toSign, options) {
|
|
155
|
-
if (protocolHttp.HttpRequest.isInstance(toSign) && isWebSocketRequest(toSign)) {
|
|
156
|
-
const signedRequest = await this.signer.presign({ ...toSign, body: "" }, {
|
|
157
|
-
...options,
|
|
158
|
-
expiresIn: 60,
|
|
159
|
-
unsignableHeaders: new Set(Object.keys(toSign.headers).filter((header) => header !== "host")),
|
|
160
|
-
});
|
|
161
|
-
return {
|
|
162
|
-
...signedRequest,
|
|
163
|
-
body: toSign.body,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
return this.signer.sign(toSign, options);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const resolveWebSocketConfig = (input) => {
|
|
173
|
-
const { signer } = input;
|
|
174
|
-
return Object.assign(input, {
|
|
175
|
-
signer: async (authScheme) => {
|
|
176
|
-
const signerObj = await signer(authScheme);
|
|
177
|
-
if (validateSigner(signerObj)) {
|
|
178
|
-
return new WebsocketSignatureV4({ signer: signerObj });
|
|
179
|
-
}
|
|
180
|
-
throw new Error("Expected WebsocketSignatureV4 signer, please check the client constructor.");
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
};
|
|
184
|
-
const validateSigner = (signer) => !!signer;
|
|
185
|
-
|
|
186
|
-
const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 2000;
|
|
13
|
+
const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 3000;
|
|
187
14
|
class WebSocketFetchHandler {
|
|
188
15
|
metadata = {
|
|
189
16
|
handlerProtocol: "websocket/h1.1",
|
|
190
17
|
};
|
|
191
|
-
config;
|
|
18
|
+
config = {};
|
|
192
19
|
configPromise;
|
|
193
20
|
httpHandler;
|
|
194
21
|
sockets = {};
|
|
@@ -200,13 +27,20 @@ class WebSocketFetchHandler {
|
|
|
200
27
|
}
|
|
201
28
|
constructor(options, httpHandler = new fetchHttpHandler.FetchHttpHandler()) {
|
|
202
29
|
this.httpHandler = httpHandler;
|
|
30
|
+
const setConfig = (opts) => {
|
|
31
|
+
this.config = {
|
|
32
|
+
...(opts ?? {}),
|
|
33
|
+
};
|
|
34
|
+
return this.config;
|
|
35
|
+
};
|
|
203
36
|
if (typeof options === "function") {
|
|
204
37
|
this.config = {};
|
|
205
|
-
this.configPromise = options().then((opts) =>
|
|
38
|
+
this.configPromise = options().then((opts) => {
|
|
39
|
+
return setConfig(opts);
|
|
40
|
+
});
|
|
206
41
|
}
|
|
207
42
|
else {
|
|
208
|
-
this.
|
|
209
|
-
this.configPromise = Promise.resolve(this.config);
|
|
43
|
+
this.configPromise = Promise.resolve(setConfig(options));
|
|
210
44
|
}
|
|
211
45
|
}
|
|
212
46
|
destroy() {
|
|
@@ -218,17 +52,20 @@ class WebSocketFetchHandler {
|
|
|
218
52
|
}
|
|
219
53
|
}
|
|
220
54
|
async handle(request) {
|
|
55
|
+
this.config = await this.configPromise;
|
|
56
|
+
const { logger } = this.config;
|
|
221
57
|
if (!isWebSocketRequest(request)) {
|
|
58
|
+
logger?.debug?.(`@aws-sdk - ws fetching ${request.protocol}${request.hostname}${request.path}`);
|
|
222
59
|
return this.httpHandler.handle(request);
|
|
223
60
|
}
|
|
224
61
|
const url = utilFormatUrl.formatUrl(request);
|
|
62
|
+
logger?.debug?.(`@aws-sdk - ws connecting ${url.split("?")[0]}`);
|
|
225
63
|
const socket = new WebSocket(url);
|
|
226
64
|
if (!this.sockets[url]) {
|
|
227
65
|
this.sockets[url] = [];
|
|
228
66
|
}
|
|
229
67
|
this.sockets[url].push(socket);
|
|
230
68
|
socket.binaryType = "arraybuffer";
|
|
231
|
-
this.config = await this.configPromise;
|
|
232
69
|
const { connectionTimeout = DEFAULT_WS_CONNECTION_TIMEOUT_MS } = this.config;
|
|
233
70
|
await this.waitForReady(socket, connectionTimeout);
|
|
234
71
|
const { body } = request;
|
|
@@ -261,6 +98,7 @@ class WebSocketFetchHandler {
|
|
|
261
98
|
reject({
|
|
262
99
|
$metadata: {
|
|
263
100
|
httpStatusCode: 500,
|
|
101
|
+
websocketSynthetic500Error: true,
|
|
264
102
|
},
|
|
265
103
|
});
|
|
266
104
|
}, connectionTimeout);
|
|
@@ -271,41 +109,60 @@ class WebSocketFetchHandler {
|
|
|
271
109
|
});
|
|
272
110
|
}
|
|
273
111
|
connect(socket, data) {
|
|
274
|
-
|
|
275
|
-
let
|
|
276
|
-
let
|
|
277
|
-
|
|
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
|
+
};
|
|
278
130
|
socket.onmessage = (event) => {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
+
}
|
|
283
144
|
};
|
|
284
|
-
socket.onerror = (
|
|
285
|
-
socketErrorOccurred = true;
|
|
145
|
+
socket.onerror = (event) => {
|
|
286
146
|
socket.close();
|
|
287
|
-
|
|
147
|
+
push({ done: true, error: event });
|
|
288
148
|
};
|
|
289
149
|
socket.onclose = () => {
|
|
290
150
|
this.removeNotUsableSockets(socket.url);
|
|
291
|
-
|
|
292
|
-
return;
|
|
293
|
-
if (streamError) {
|
|
294
|
-
reject(streamError);
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
resolve({
|
|
298
|
-
done: true,
|
|
299
|
-
value: undefined,
|
|
300
|
-
});
|
|
301
|
-
}
|
|
151
|
+
push({ done: true });
|
|
302
152
|
};
|
|
303
153
|
const outputStream = {
|
|
304
154
|
[Symbol.asyncIterator]: () => ({
|
|
305
|
-
next
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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;
|
|
309
166
|
});
|
|
310
167
|
},
|
|
311
168
|
}),
|
|
@@ -322,7 +179,10 @@ class WebSocketFetchHandler {
|
|
|
322
179
|
}
|
|
323
180
|
}
|
|
324
181
|
catch (err) {
|
|
325
|
-
|
|
182
|
+
push({
|
|
183
|
+
done: true,
|
|
184
|
+
error: err,
|
|
185
|
+
});
|
|
326
186
|
}
|
|
327
187
|
finally {
|
|
328
188
|
socket.close(1000);
|
|
@@ -348,6 +208,184 @@ const getIterator = (stream) => {
|
|
|
348
208
|
const toReadableStream = (asyncIterable) => typeof ReadableStream === "function" ? eventstreamSerdeBrowser.iterableToReadableStream(asyncIterable) : asyncIterable;
|
|
349
209
|
const isReadableStream = (payload) => typeof ReadableStream === "function" && payload instanceof ReadableStream;
|
|
350
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
|
+
|
|
351
389
|
exports.WebSocketFetchHandler = WebSocketFetchHandler;
|
|
352
390
|
exports.eventStreamPayloadHandlerProvider = eventStreamPayloadHandlerProvider;
|
|
353
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,41 +103,60 @@ 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
|
}),
|
|
@@ -142,7 +173,10 @@ export class WebSocketFetchHandler {
|
|
|
142
173
|
}
|
|
143
174
|
}
|
|
144
175
|
catch (err) {
|
|
145
|
-
|
|
176
|
+
push({
|
|
177
|
+
done: true,
|
|
178
|
+
error: err,
|
|
179
|
+
});
|
|
146
180
|
}
|
|
147
181
|
finally {
|
|
148
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;
|
|
@@ -19,7 +19,7 @@ export class EventStreamPayloadHandler {
|
|
|
19
19
|
request.body = placeHolderStream.readable;
|
|
20
20
|
const match = (headers?.authorization ?? "").match(/Signature=(\w+)$/);
|
|
21
21
|
const priorSignature = (match ?? [])[1] ?? (query && query["X-Amz-Signature"]) ?? "";
|
|
22
|
-
const signingStream =
|
|
22
|
+
const signingStream = new EventSigningTransformStream(priorSignature, await this.messageSigner(), this.eventStreamCodec, this.systemClockOffsetProvider);
|
|
23
23
|
payload.pipeThrough(signingStream).pipeThrough(placeHolderStream);
|
|
24
24
|
let result;
|
|
25
25
|
try {
|
|
@@ -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,4 @@
|
|
|
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
2
|
/**
|
|
3
3
|
* @internal
|
|
4
4
|
*/
|
|
@@ -9,12 +9,6 @@ export interface EventStreamPayloadHandlerOptions {
|
|
|
9
9
|
systemClockOffset?: number;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* A handler that control the eventstream payload flow:
|
|
13
|
-
* 1. Pause stream for initial request.
|
|
14
|
-
* 2. Close the stream if initial request fails.
|
|
15
|
-
* 3. Start piping payload when connection is established.
|
|
16
|
-
* 4. Sign the payload after payload stream starting to flow.
|
|
17
|
-
*
|
|
18
12
|
* @internal
|
|
19
13
|
*/
|
|
20
14
|
export declare class EventStreamPayloadHandler implements IEventStreamPayloadHandler {
|
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": {
|
|
@@ -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
|