/
middleware.ts
400 lines (346 loc) · 14.8 KB
/
middleware.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
import { Logger } from "./logger";
import { UserAgent } from "./util";
export interface InitializeHandlerArguments<Input extends object> {
/**
* User input to a command. Reflects the userland representation of the
* union of data types the command can effectively handle.
*/
input: Input;
}
export interface InitializeHandlerOutput<Output extends object> extends DeserializeHandlerOutput<Output> {
output: Output;
}
export interface SerializeHandlerArguments<Input extends object> extends InitializeHandlerArguments<Input> {
/**
* The user input serialized as a request object. The request object is unknown,
* so you cannot modify it directly. When work with request, you need to guard its
* type to e.g. HttpRequest with 'instanceof' operand
*
* During the build phase of the execution of a middleware stack, a built
* request may or may not be available.
*/
request?: unknown;
}
export interface SerializeHandlerOutput<Output extends object> extends InitializeHandlerOutput<Output> {}
export interface BuildHandlerArguments<Input extends object> extends FinalizeHandlerArguments<Input> {}
export interface BuildHandlerOutput<Output extends object> extends InitializeHandlerOutput<Output> {}
export interface FinalizeHandlerArguments<Input extends object> extends SerializeHandlerArguments<Input> {
/**
* The user input serialized as a request.
*/
request: unknown;
}
export interface FinalizeHandlerOutput<Output extends object> extends InitializeHandlerOutput<Output> {}
export interface DeserializeHandlerArguments<Input extends object> extends FinalizeHandlerArguments<Input> {}
export interface DeserializeHandlerOutput<Output extends object> {
/**
* The raw response object from runtime is deserialized to structured output object.
* The response object is unknown so you cannot modify it directly. When work with
* response, you need to guard its type to e.g. HttpResponse with 'instanceof' operand.
*
* During the deserialize phase of the execution of a middleware stack, a deserialized
* response may or may not be available
*/
response: unknown;
output?: Output;
}
export interface InitializeHandler<Input extends object, Output extends object> {
/**
* Asynchronously converts an input object into an output object.
*
* @param args An object containing a input to the command as well as any
* associated or previously generated execution artifacts.
*/
(args: InitializeHandlerArguments<Input>): Promise<InitializeHandlerOutput<Output>>;
}
export type Handler<Input extends object, Output extends object> = InitializeHandler<Input, Output>;
export interface SerializeHandler<Input extends object, Output extends object> {
/**
* Asynchronously converts an input object into an output object.
*
* @param args An object containing a input to the command as well as any
* associated or previously generated execution artifacts.
*/
(args: SerializeHandlerArguments<Input>): Promise<SerializeHandlerOutput<Output>>;
}
export interface FinalizeHandler<Input extends object, Output extends object> {
/**
* Asynchronously converts an input object into an output object.
*
* @param args An object containing a input to the command as well as any
* associated or previously generated execution artifacts.
*/
(args: FinalizeHandlerArguments<Input>): Promise<FinalizeHandlerOutput<Output>>;
}
export interface BuildHandler<Input extends object, Output extends object> {
(args: BuildHandlerArguments<Input>): Promise<BuildHandlerOutput<Output>>;
}
export interface DeserializeHandler<Input extends object, Output extends object> {
(args: DeserializeHandlerArguments<Input>): Promise<DeserializeHandlerOutput<Output>>;
}
/**
* A factory function that creates functions implementing the {Handler}
* interface.
*/
export interface InitializeMiddleware<Input extends object, Output extends object> {
/**
* @param next The handler to invoke after this middleware has operated on
* the user input and before this middleware operates on the output.
*
* @param context Invariant data and functions for use by the handler.
*/
(next: InitializeHandler<Input, Output>, context: HandlerExecutionContext): InitializeHandler<Input, Output>;
}
/**
* A factory function that creates functions implementing the {BuildHandler}
* interface.
*/
export interface SerializeMiddleware<Input extends object, Output extends object> {
/**
* @param next The handler to invoke after this middleware has operated on
* the user input and before this middleware operates on the output.
*
* @param context Invariant data and functions for use by the handler.
*/
(next: SerializeHandler<Input, Output>, context: HandlerExecutionContext): SerializeHandler<Input, Output>;
}
/**
* A factory function that creates functions implementing the {FinalizeHandler}
* interface.
*/
export interface FinalizeRequestMiddleware<Input extends object, Output extends object> {
/**
* @param next The handler to invoke after this middleware has operated on
* the user input and before this middleware operates on the output.
*
* @param context Invariant data and functions for use by the handler.
*/
(next: FinalizeHandler<Input, Output>, context: HandlerExecutionContext): FinalizeHandler<Input, Output>;
}
export interface BuildMiddleware<Input extends object, Output extends object> {
(next: BuildHandler<Input, Output>, context: HandlerExecutionContext): BuildHandler<Input, Output>;
}
export interface DeserializeMiddleware<Input extends object, Output extends object> {
(next: DeserializeHandler<Input, Output>, context: HandlerExecutionContext): DeserializeHandler<Input, Output>;
}
export type MiddlewareType<Input extends object, Output extends object> =
| InitializeMiddleware<Input, Output>
| SerializeMiddleware<Input, Output>
| BuildMiddleware<Input, Output>
| FinalizeRequestMiddleware<Input, Output>
| DeserializeMiddleware<Input, Output>;
/**
* A factory function that creates the terminal handler atop which a middleware
* stack sits.
*/
export interface Terminalware {
<Input extends object, Output extends object>(context: HandlerExecutionContext): DeserializeHandler<Input, Output>;
}
export type Step = "initialize" | "serialize" | "build" | "finalizeRequest" | "deserialize";
export type Priority = "high" | "normal" | "low";
export interface HandlerOptions {
/**
* Handlers are ordered using a "step" that describes the stage of command
* execution at which the handler will be executed. The available steps are:
*
* - initialize: The input is being prepared. Examples of typical
* initialization tasks include injecting default options computing
* derived parameters.
* - serialize: The input is complete and ready to be serialized. Examples
* of typical serialization tasks include input validation and building
* an HTTP request from user input.
* - build: The input has been serialized into an HTTP request, but that
* request may require further modification. Any request alterations
* will be applied to all retries. Examples of typical build tasks
* include injecting HTTP headers that describe a stable aspect of the
* request, such as `Content-Length` or a body checksum.
* - finalizeRequest: The request is being prepared to be sent over the wire. The
* request in this stage should already be semantically complete and
* should therefore only be altered as match the recipient's
* expectations. Examples of typical finalization tasks include request
* signing and injecting hop-by-hop headers.
* - deserialize: The response has arrived, the middleware here will deserialize
* the raw response object to structured response
*
* Unlike initialization and build handlers, which are executed once
* per operation execution, finalization and deserialize handlers will be
* executed foreach HTTP request sent.
*
* @default 'initialize'
*/
step?: Step;
/**
* A list of strings to any that identify the general purpose or important
* characteristics of a given handler.
*/
tags?: Array<string>;
/**
* A unique name to refer to a middleware
*/
name?: string;
/**
* A flag to override the existing middleware with the same name. Without
* setting it, adding middleware with duplicated name will throw an exception.
* @internal
*/
override?: boolean;
}
export interface AbsoluteLocation {
/**
* By default middleware will be added to individual step in un-guaranteed order.
* In the case that
*
* @default 'normal'
*/
priority?: Priority;
}
export type Relation = "before" | "after";
export interface RelativeLocation {
/**
* Specify the relation to be before or after a know middleware.
*/
relation: Relation;
/**
* A known middleware name to indicate inserting middleware's location.
*/
toMiddleware: string;
}
export type RelativeMiddlewareOptions = RelativeLocation & Omit<HandlerOptions, "step">;
export interface InitializeHandlerOptions extends HandlerOptions {
step?: "initialize";
}
export interface SerializeHandlerOptions extends HandlerOptions {
step: "serialize";
}
export interface BuildHandlerOptions extends HandlerOptions {
step: "build";
}
export interface FinalizeRequestHandlerOptions extends HandlerOptions {
step: "finalizeRequest";
}
export interface DeserializeHandlerOptions extends HandlerOptions {
step: "deserialize";
}
/**
* A stack storing middleware. It can be resolved into a handler. It supports 2
* approaches for adding middleware:
* 1. Adding middleware to specific step with `add()`. The order of middleware
* added into same step is determined by order of adding them. If one middleware
* needs to be executed at the front of the step or at the end of step, set
* `priority` options to `high` or `low`.
* 2. Adding middleware to location relative to known middleware with `addRelativeTo()`.
* This is useful when given middleware must be executed before or after specific
* middleware(`toMiddleware`). You can add a middleware relatively to another
* middleware which also added relatively. But eventually, this relative middleware
* chain **must** be 'anchored' by a middleware that added using `add()` API
* with absolute `step` and `priority`. This mothod will throw if specified
* `toMiddleware` is not found.
*/
export interface MiddlewareStack<Input extends object, Output extends object> extends Pluggable<Input, Output> {
/**
* Add middleware to the stack to be executed during the "initialize" step,
* optionally specifying a priority, tags and name
*/
add(middleware: InitializeMiddleware<Input, Output>, options?: InitializeHandlerOptions & AbsoluteLocation): void;
/**
* Add middleware to the stack to be executed during the "serialize" step,
* optionally specifying a priority, tags and name
*/
add(middleware: SerializeMiddleware<Input, Output>, options: SerializeHandlerOptions & AbsoluteLocation): void;
/**
* Add middleware to the stack to be executed during the "build" step,
* optionally specifying a priority, tags and name
*/
add(middleware: BuildMiddleware<Input, Output>, options: BuildHandlerOptions & AbsoluteLocation): void;
/**
* Add middleware to the stack to be executed during the "finalizeRequest" step,
* optionally specifying a priority, tags and name
*/
add(
middleware: FinalizeRequestMiddleware<Input, Output>,
options: FinalizeRequestHandlerOptions & AbsoluteLocation
): void;
/**
* Add middleware to the stack to be executed during the "deserialize" step,
* optionally specifying a priority, tags and name
*/
add(middleware: DeserializeMiddleware<Input, Output>, options: DeserializeHandlerOptions & AbsoluteLocation): void;
/**
* Add middleware to a stack position before or after a known middleware,optionally
* specifying name and tags.
*/
addRelativeTo(middleware: MiddlewareType<Input, Output>, options: RelativeMiddlewareOptions): void;
/**
* Apply a customization function to mutate the middleware stack, often
* used for customizations that requires mutating multiple middleware.
*/
use(pluggable: Pluggable<Input, Output>): void;
/**
* Create a shallow clone of this stack. Step bindings and handler priorities
* and tags are preserved in the copy.
*/
clone(): MiddlewareStack<Input, Output>;
/**
* Removes middleware from the stack.
*
* If a string is provided, it will be treated as middleware name. If a middleware
* is inserted with the given name, it will be removed.
*
* If a middleware class is provided, all usages thereof will be removed.
*/
remove(toRemove: MiddlewareType<Input, Output> | string): boolean;
/**
* Removes middleware that contains given tag
*
* Multiple middleware will potentially be removed
*/
removeByTag(toRemove: string): boolean;
/**
* Create a stack containing the middlewares in this stack as well as the
* middlewares in the `from` stack. Neither source is modified, and step
* bindings and handler priorities and tags are preserved in the copy.
*/
concat<InputType extends Input, OutputType extends Output>(
from: MiddlewareStack<InputType, OutputType>
): MiddlewareStack<InputType, OutputType>;
/**
* Builds a single handler function from zero or more middleware classes and
* a core handler. The core handler is meant to send command objects to AWS
* services and return promises that will resolve with the operation result
* or be rejected with an error.
*
* When a composed handler is invoked, the arguments will pass through all
* middleware in a defined order, and the return from the innermost handler
* will pass through all middleware in the reverse of that order.
*/
resolve<InputType extends Input, OutputType extends Output>(
handler: DeserializeHandler<InputType, OutputType>,
context: HandlerExecutionContext
): InitializeHandler<InputType, OutputType>;
}
/**
* Data and helper objects that are not expected to change from one execution of
* a composed handler to another.
*/
export interface HandlerExecutionContext {
/**
* A logger that may be invoked by any handler during execution of an
* operation.
*/
logger?: Logger;
/**
* Additional user agent that inferred by middleware. It can be used to save
* the internal user agent sections without overriding the `customUserAgent`
* config in clients.
*/
userAgent?: UserAgent;
[key: string]: any;
}
export interface Pluggable<Input extends object, Output extends object> {
/**
* A function that mutate the passed in middleware stack. Functions implementing
* this interface can add, remove, modify existing middleware stack from clients
* or commands
*/
applyToStack: (stack: MiddlewareStack<Input, Output>) => void;
}