Skip to content

Commit e21ed11

Browse files
authored
Accept Provider in CompressionInputConfig (#1132)
1 parent 2b4d278 commit e21ed11

10 files changed

+136
-98
lines changed

.changeset/bright-elephants-walk.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/middleware-compression": patch
3+
---
4+
5+
Accept Provider in CompressionInputConfig

packages/middleware-compression/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"@smithy/node-config-provider": "workspace:^",
1919
"@smithy/types": "workspace:^",
2020
"@smithy/util-config-provider": "workspace:^",
21+
"@smithy/util-middleware": "workspace:^",
2122
"fflate": "0.8.1",
2223
"tslib": "^2.5.0"
2324
},

packages/middleware-compression/src/compressionMiddleware.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ describe(compressionMiddleware.name, () => {
1515
const mockBody = "body";
1616
const mockConfig = {
1717
bodyLengthChecker: jest.fn().mockReturnValue(mockBody.length),
18-
disableRequestCompression: false,
19-
requestMinCompressionSizeBytes: 0,
18+
disableRequestCompression: async () => false,
19+
requestMinCompressionSizeBytes: async () => 0,
2020
};
2121
const mockMiddlewareConfig = {
2222
encodings: [CompressionAlgorithm.GZIP],
@@ -32,20 +32,20 @@ describe(compressionMiddleware.name, () => {
3232

3333
it("skips compression if it's not an HttpRequest", async () => {
3434
const { isInstance } = HttpRequest;
35-
(isInstance as unknown as jest.Mock).mockReturnValue(false);
35+
((isInstance as unknown) as jest.Mock).mockReturnValue(false);
3636
await compressionMiddleware(mockConfig, mockMiddlewareConfig)(mockNext, mockContext)({ ...mockArgs } as any);
3737
expect(mockNext).toHaveBeenCalledWith(mockArgs);
3838
});
3939

4040
describe("HttpRequest", () => {
4141
beforeEach(() => {
4242
const { isInstance } = HttpRequest;
43-
(isInstance as unknown as jest.Mock).mockReturnValue(true);
43+
((isInstance as unknown) as jest.Mock).mockReturnValue(true);
4444
(isStreaming as jest.Mock).mockReturnValue(false);
4545
});
4646

4747
it("skips compression if disabled", async () => {
48-
await compressionMiddleware({ ...mockConfig, disableRequestCompression: true }, mockMiddlewareConfig)(
48+
await compressionMiddleware({ ...mockConfig, disableRequestCompression: async () => true }, mockMiddlewareConfig)(
4949
mockNext,
5050
mockContext
5151
)({ ...mockArgs } as any);
@@ -107,7 +107,7 @@ describe(compressionMiddleware.name, () => {
107107
describe("not streaming", () => {
108108
it("skips compression if body is smaller than min size", async () => {
109109
await compressionMiddleware(
110-
{ ...mockConfig, requestMinCompressionSizeBytes: mockBody.length + 1 },
110+
{ ...mockConfig, requestMinCompressionSizeBytes: async () => mockBody.length + 1 },
111111
mockMiddlewareConfig
112112
)(
113113
mockNext,

packages/middleware-compression/src/compressionMiddleware.ts

+60-52
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111

1212
import { compressStream } from "./compressStream";
1313
import { compressString } from "./compressString";
14-
import { CompressionResolvedConfig } from "./configurations";
14+
import { CompressionPreviouslyResolved, CompressionResolvedConfig } from "./configurations";
1515
import { CLIENT_SUPPORTED_ALGORITHMS, CompressionAlgorithm } from "./constants";
1616
import { isStreaming } from "./isStreaming";
1717

@@ -34,67 +34,75 @@ export interface CompressionMiddlewareConfig {
3434
/**
3535
* @internal
3636
*/
37-
export const compressionMiddleware =
38-
(config: CompressionResolvedConfig, middlewareConfig: CompressionMiddlewareConfig): BuildMiddleware<any, any> =>
39-
<Output extends MetadataBearer>(next: BuildHandler<any, Output>): BuildHandler<any, Output> =>
40-
async (args: BuildHandlerArguments<any>): Promise<BuildHandlerOutput<Output>> => {
41-
if (!HttpRequest.isInstance(args.request) || config.disableRequestCompression) {
42-
return next(args);
43-
}
37+
export const compressionMiddleware = (
38+
config: CompressionResolvedConfig & CompressionPreviouslyResolved,
39+
middlewareConfig: CompressionMiddlewareConfig
40+
): BuildMiddleware<any, any> => <Output extends MetadataBearer>(
41+
next: BuildHandler<any, Output>
42+
): BuildHandler<any, Output> => async (args: BuildHandlerArguments<any>): Promise<BuildHandlerOutput<Output>> => {
43+
if (!HttpRequest.isInstance(args.request)) {
44+
return next(args);
45+
}
46+
47+
const disableRequestCompression = await config.disableRequestCompression();
48+
if (disableRequestCompression) {
49+
return next(args);
50+
}
4451

45-
const { request } = args;
46-
const { body, headers } = request;
47-
const { encodings, streamRequiresLength } = middlewareConfig;
52+
const { request } = args;
53+
const { body, headers } = request;
54+
const { encodings, streamRequiresLength } = middlewareConfig;
4855

49-
let updatedBody = body;
50-
let updatedHeaders = headers;
56+
let updatedBody = body;
57+
let updatedHeaders = headers;
5158

52-
for (const algorithm of encodings) {
53-
if (CLIENT_SUPPORTED_ALGORITHMS.includes(algorithm as CompressionAlgorithm)) {
54-
let isRequestCompressed = false;
55-
if (isStreaming(body)) {
56-
if (!streamRequiresLength) {
57-
updatedBody = await compressStream(body);
58-
isRequestCompressed = true;
59-
} else {
60-
// Invalid case. We should never get here.
61-
throw new Error("Compression is not supported for streaming blobs that require a length.");
62-
}
59+
for (const algorithm of encodings) {
60+
if (CLIENT_SUPPORTED_ALGORITHMS.includes(algorithm as CompressionAlgorithm)) {
61+
let isRequestCompressed = false;
62+
if (isStreaming(body)) {
63+
if (!streamRequiresLength) {
64+
updatedBody = await compressStream(body);
65+
isRequestCompressed = true;
6366
} else {
64-
const bodyLength = config.bodyLengthChecker(body);
65-
if (bodyLength && bodyLength >= config.requestMinCompressionSizeBytes) {
66-
updatedBody = await compressString(body);
67-
isRequestCompressed = true;
68-
}
67+
// Invalid case. We should never get here.
68+
throw new Error("Compression is not supported for streaming blobs that require a length.");
6969
}
70+
} else {
71+
const bodyLength = config.bodyLengthChecker(body);
72+
const requestMinCompressionSizeBytes = await config.requestMinCompressionSizeBytes();
73+
if (bodyLength && bodyLength >= requestMinCompressionSizeBytes) {
74+
updatedBody = await compressString(body);
75+
isRequestCompressed = true;
76+
}
77+
}
7078

71-
if (isRequestCompressed) {
72-
// Either append to the header if it already exists, else set it
73-
if (headers["Content-Encoding"]) {
74-
updatedHeaders = {
75-
...headers,
76-
"Content-Encoding": `${headers["Content-Encoding"]},${algorithm}`,
77-
};
78-
} else {
79-
updatedHeaders = { ...headers, "Content-Encoding": algorithm };
80-
}
81-
82-
// We've matched on one supported algorithm in the
83-
// priority-ordered list, so we're finished.
84-
break;
79+
if (isRequestCompressed) {
80+
// Either append to the header if it already exists, else set it
81+
if (headers["Content-Encoding"]) {
82+
updatedHeaders = {
83+
...headers,
84+
"Content-Encoding": `${headers["Content-Encoding"]},${algorithm}`,
85+
};
86+
} else {
87+
updatedHeaders = { ...headers, "Content-Encoding": algorithm };
8588
}
89+
90+
// We've matched on one supported algorithm in the
91+
// priority-ordered list, so we're finished.
92+
break;
8693
}
8794
}
95+
}
8896

89-
return next({
90-
...args,
91-
request: {
92-
...request,
93-
body: updatedBody,
94-
headers: updatedHeaders,
95-
},
96-
});
97-
};
97+
return next({
98+
...args,
99+
request: {
100+
...request,
101+
body: updatedBody,
102+
headers: updatedHeaders,
103+
},
104+
});
105+
};
98106

99107
export const compressionMiddlewareOptions: BuildHandlerOptions & AbsoluteLocation = {
100108
name: "compressionMiddleware",
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,42 @@
1-
import { BodyLengthCalculator } from "@smithy/types";
1+
import { BodyLengthCalculator, Provider } from "@smithy/types";
22

33
/**
44
* @public
55
*/
66
export interface CompressionInputConfig {
7-
/**
8-
* A function that can calculate the length of a body.
9-
*/
10-
bodyLengthChecker: BodyLengthCalculator;
11-
127
/**
138
* Whether to disable request compression.
149
*/
15-
disableRequestCompression: boolean;
10+
disableRequestCompression: boolean | Provider<boolean>;
1611

1712
/**
1813
* The minimum size in bytes that a request body should be to trigger compression.
1914
* The value must be a non-negative integer value between 0 and 10485760 bytes inclusive.
2015
*/
21-
requestMinCompressionSizeBytes: number;
16+
requestMinCompressionSizeBytes: number | Provider<number>;
17+
}
18+
19+
/**
20+
* @internal
21+
*/
22+
export interface CompressionPreviouslyResolved {
23+
/**
24+
* A function that can calculate the length of a body.
25+
*/
26+
bodyLengthChecker: BodyLengthCalculator;
2227
}
2328

2429
/**
2530
* @internal
2631
*/
27-
export interface CompressionResolvedConfig extends CompressionInputConfig {}
32+
export interface CompressionResolvedConfig {
33+
/**
34+
* Resolved value for input config {@link CompressionInputConfig.disableRequestCompression}
35+
*/
36+
disableRequestCompression: Provider<boolean>;
37+
38+
/**
39+
* Resolved value for input config {@link CompressionInputConfig.requestMinCompressionSizeBytes}
40+
*/
41+
requestMinCompressionSizeBytes: Provider<number>;
42+
}

packages/middleware-compression/src/getCompressionPlugin.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ jest.mock("./compressionMiddleware");
66
describe(getCompressionPlugin.name, () => {
77
const config = {
88
bodyLengthChecker: jest.fn(),
9-
disableRequestCompression: false,
10-
requestMinCompressionSizeBytes: 0,
9+
disableRequestCompression: async () => false,
10+
requestMinCompressionSizeBytes: async () => 0,
1111
};
1212
const middlewareConfig = { encodings: [] };
1313

packages/middleware-compression/src/getCompressionPlugin.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import {
55
CompressionMiddlewareConfig,
66
compressionMiddlewareOptions,
77
} from "./compressionMiddleware";
8-
import { CompressionResolvedConfig } from "./configurations";
8+
import { CompressionPreviouslyResolved, CompressionResolvedConfig } from "./configurations";
99

1010
/**
1111
* @internal
1212
*/
1313
export const getCompressionPlugin = (
14-
config: CompressionResolvedConfig,
14+
config: CompressionResolvedConfig & CompressionPreviouslyResolved,
1515
middlewareConfig: CompressionMiddlewareConfig
1616
): Pluggable<any, any> => ({
1717
applyToStack: (clientStack) => {

packages/middleware-compression/src/resolveCompressionConfig.spec.ts

+17-15
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,41 @@ describe(resolveCompressionConfig.name, () => {
66
disableRequestCompression: false,
77
requestMinCompressionSizeBytes: 0,
88
};
9-
it("should throw an error if requestMinCompressionSizeBytes is less than 0", () => {
9+
10+
it("should throw an error if requestMinCompressionSizeBytes is less than 0", async () => {
1011
const requestMinCompressionSizeBytes = -1;
11-
expect(() => {
12-
resolveCompressionConfig({ ...mockConfig, requestMinCompressionSizeBytes });
13-
}).toThrow(
12+
const resolvedConfig = resolveCompressionConfig({ ...mockConfig, requestMinCompressionSizeBytes });
13+
await expect(resolvedConfig.requestMinCompressionSizeBytes()).rejects.toThrow(
1414
new RangeError(
1515
"The value for requestMinCompressionSizeBytes must be between 0 and 10485760 inclusive. " +
1616
`The provided value ${requestMinCompressionSizeBytes} is outside this range."`
1717
)
1818
);
1919
});
2020

21-
it("should throw an error if requestMinCompressionSizeBytes is greater than 10485760", () => {
21+
it("should throw an error if requestMinCompressionSizeBytes is greater than 10485760", async () => {
2222
const requestMinCompressionSizeBytes = 10485761;
23-
expect(() => {
24-
resolveCompressionConfig({ ...mockConfig, requestMinCompressionSizeBytes });
25-
}).toThrow(
23+
const resolvedConfig = resolveCompressionConfig({ ...mockConfig, requestMinCompressionSizeBytes });
24+
await expect(resolvedConfig.requestMinCompressionSizeBytes()).rejects.toThrow(
2625
new RangeError(
2726
"The value for requestMinCompressionSizeBytes must be between 0 and 10485760 inclusive. " +
2827
`The provided value ${requestMinCompressionSizeBytes} is outside this range."`
2928
)
3029
);
3130
});
3231

33-
it.each([0, 10240, 10485760])("returns requestMinCompressionSizeBytes value %s", (requestMinCompressionSizeBytes) => {
34-
const inputConfig = { ...mockConfig, requestMinCompressionSizeBytes };
35-
const resolvedConfig = resolveCompressionConfig(inputConfig);
36-
expect(inputConfig).toEqual(resolvedConfig);
37-
});
32+
it.each([0, 10240, 10485760])(
33+
"returns requestMinCompressionSizeBytes value %s",
34+
async (requestMinCompressionSizeBytes) => {
35+
const inputConfig = { ...mockConfig, requestMinCompressionSizeBytes };
36+
const resolvedConfig = resolveCompressionConfig(inputConfig);
37+
await expect(resolvedConfig.requestMinCompressionSizeBytes()).resolves.toEqual(requestMinCompressionSizeBytes);
38+
}
39+
);
3840

39-
it.each([false, true])("returns disableRequestCompression value %s", (disableRequestCompression) => {
41+
it.each([false, true])("returns disableRequestCompression value %s", async (disableRequestCompression) => {
4042
const inputConfig = { ...mockConfig, disableRequestCompression };
4143
const resolvedConfig = resolveCompressionConfig(inputConfig);
42-
expect(inputConfig).toEqual(resolvedConfig);
44+
await expect(resolvedConfig.disableRequestCompression()).resolves.toEqual(disableRequestCompression);
4345
});
4446
});
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1+
import { normalizeProvider } from "@smithy/util-middleware";
2+
13
import { CompressionInputConfig, CompressionResolvedConfig } from "./configurations";
24

35
/**
46
* @internal
57
*/
6-
export const resolveCompressionConfig = <T>(input: T & CompressionInputConfig): T & CompressionResolvedConfig => {
7-
const { requestMinCompressionSizeBytes } = input;
8+
export const resolveCompressionConfig = <T>(input: T & CompressionInputConfig): T & CompressionResolvedConfig => ({
9+
...input,
10+
disableRequestCompression: normalizeProvider(input.disableRequestCompression),
11+
requestMinCompressionSizeBytes: async () => {
12+
const requestMinCompressionSizeBytes = await normalizeProvider(input.requestMinCompressionSizeBytes)();
813

9-
// The requestMinCompressionSizeBytes should be less than the upper limit for API Gateway
10-
// https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-openapi-minimum-compression-size.html
11-
if (requestMinCompressionSizeBytes < 0 || requestMinCompressionSizeBytes > 10485760) {
12-
throw new RangeError(
13-
"The value for requestMinCompressionSizeBytes must be between 0 and 10485760 inclusive. " +
14-
`The provided value ${requestMinCompressionSizeBytes} is outside this range."`
15-
);
16-
}
14+
// The requestMinCompressionSizeBytes should be less than the upper limit for API Gateway
15+
// https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-openapi-minimum-compression-size.html
16+
if (requestMinCompressionSizeBytes < 0 || requestMinCompressionSizeBytes > 10485760) {
17+
throw new RangeError(
18+
"The value for requestMinCompressionSizeBytes must be between 0 and 10485760 inclusive. " +
19+
`The provided value ${requestMinCompressionSizeBytes} is outside this range."`
20+
);
21+
}
1722

18-
return input;
19-
};
23+
return requestMinCompressionSizeBytes;
24+
},
25+
});

yarn.lock

+1
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,7 @@ __metadata:
20852085
"@smithy/node-config-provider": "workspace:^"
20862086
"@smithy/types": "workspace:^"
20872087
"@smithy/util-config-provider": "workspace:^"
2088+
"@smithy/util-middleware": "workspace:^"
20882089
"@tsconfig/recommended": 1.0.1
20892090
concurrently: 7.0.0
20902091
downlevel-dts: 0.10.1

0 commit comments

Comments
 (0)