Skip to content

Commit d8c1dc2

Browse files
authored
fix(middleware-user-agent-header): swap user agent headers for non-browser environments (#2313)
1 parent 60a4215 commit d8c1dc2

File tree

2 files changed

+61
-47
lines changed

2 files changed

+61
-47
lines changed

packages/middleware-user-agent/src/user-agent-middleware.spec.ts

+51-44
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,40 @@ describe("userAgentMiddleware", () => {
1111
jest.clearAllMocks();
1212
});
1313

14-
it("should collect user agent pair from default, custom-supplied, and handler context", async () => {
15-
const middleware = userAgentMiddleware({
16-
defaultUserAgentProvider: async () => [
17-
["default_agent", "1.0.0"],
18-
["aws-sdk-js", "1.0.0"],
19-
],
20-
customUserAgent: [["custom_ua/abc"]],
21-
runtime: "node",
22-
});
23-
const handler = middleware(mockNextHandler, { userAgent: [["cfg/retry-mode", "standard"]] });
24-
await handler({ input: {}, request: new HttpRequest({ headers: {} }) });
25-
expect(mockNextHandler.mock.calls.length).toEqual(1);
26-
const sdkUserAgent = mockNextHandler.mock.calls[0][0].request.headers[X_AMZ_USER_AGENT];
27-
expect(sdkUserAgent).toEqual(expect.stringContaining("aws-sdk-js/1.0.0"));
28-
expect(sdkUserAgent).toEqual(expect.stringContaining("default_agent/1.0.0"));
29-
expect(sdkUserAgent).toEqual(expect.stringContaining("custom_ua/abc"));
30-
expect(sdkUserAgent).toEqual(expect.stringContaining("cfg/retry-mode/standard"));
31-
expect(mockNextHandler.mock.calls[0][0].request.headers[USER_AGENT]).toEqual(
32-
expect.stringContaining("aws-sdk-js/1.0.0")
14+
describe("should collect user agent pair from default, custom-supplied, and handler context", () => {
15+
[
16+
{ runtime: "node", sdkUserAgentKey: USER_AGENT, userAgentKey: X_AMZ_USER_AGENT },
17+
{ runtime: "browser", sdkUserAgentKey: X_AMZ_USER_AGENT, userAgentKey: USER_AGENT },
18+
].forEach(({ runtime, sdkUserAgentKey, userAgentKey }) =>
19+
it(runtime, async () => {
20+
const middleware = userAgentMiddleware({
21+
defaultUserAgentProvider: async () => [
22+
["default_agent", "1.0.0"],
23+
["aws-sdk-js", "1.0.0"],
24+
],
25+
customUserAgent: [["custom_ua/abc"]],
26+
runtime,
27+
});
28+
const handler = middleware(mockNextHandler, { userAgent: [["cfg/retry-mode", "standard"]] });
29+
await handler({ input: {}, request: new HttpRequest({ headers: {} }) });
30+
expect(mockNextHandler.mock.calls.length).toEqual(1);
31+
const sdkUserAgent = mockNextHandler.mock.calls[0][0].request.headers[sdkUserAgentKey];
32+
expect(sdkUserAgent).toEqual(expect.stringContaining("aws-sdk-js/1.0.0"));
33+
expect(sdkUserAgent).toEqual(expect.stringContaining("default_agent/1.0.0"));
34+
expect(sdkUserAgent).toEqual(expect.stringContaining("custom_ua/abc"));
35+
expect(sdkUserAgent).toEqual(expect.stringContaining("cfg/retry-mode/standard"));
36+
if (userAgentKey === USER_AGENT) {
37+
expect(mockNextHandler.mock.calls[0][0].request.headers[userAgentKey]).toBeUndefined();
38+
} else {
39+
expect(mockNextHandler.mock.calls[0][0].request.headers[userAgentKey]).toEqual(
40+
expect.stringContaining("aws-sdk-js/1.0.0")
41+
);
42+
}
43+
})
3344
);
3445
});
3546

36-
it(`should not set ${USER_AGENT} header in browser`, async () => {
37-
const middleware = userAgentMiddleware({
38-
defaultUserAgentProvider: async () => [["aws-sdk-js", "1.0.0"]],
39-
runtime: "browser",
40-
});
41-
const handler = middleware(mockNextHandler, {});
42-
await handler({ input: {}, request: new HttpRequest({ headers: {} }) });
43-
expect(mockNextHandler.mock.calls.length).toEqual(1);
44-
expect(mockNextHandler.mock.calls[0][0].request.headers[USER_AGENT]).toBeUndefined();
45-
});
46-
47-
describe("should sanitize the user agent string", () => {
47+
describe("should sanitize the SDK user agent string", () => {
4848
const cases: { ua: UserAgentPair; expected: string }[] = [
4949
{ ua: ["/name", "1.0.0"], expected: "name/1.0.0" },
5050
{ ua: ["Name", "1.0.0"], expected: "Name/1.0.0" },
@@ -54,18 +54,25 @@ describe("userAgentMiddleware", () => {
5454
{ ua: ["name", "1.0.0(test_version)"], expected: "name/1.0.0_test_version" },
5555
{ ua: ["api/Service", "1.0.0"], expected: "api/service/1.0.0" },
5656
];
57-
for (const { ua, expected } of cases) {
58-
it(`should sanitize user agent ${ua} to ${expected}`, async () => {
59-
const middleware = userAgentMiddleware({
60-
defaultUserAgentProvider: async () => [ua],
61-
runtime: "browser",
62-
});
63-
const handler = middleware(mockNextHandler, {});
64-
await handler({ input: {}, request: new HttpRequest({ headers: {} }) });
65-
expect(mockNextHandler.mock.calls[0][0].request.headers[X_AMZ_USER_AGENT]).toEqual(
66-
expect.stringContaining(expected)
67-
);
68-
});
69-
}
57+
[
58+
{ runtime: "node", sdkUserAgentKey: USER_AGENT },
59+
{ runtime: "browser", sdkUserAgentKey: X_AMZ_USER_AGENT },
60+
].forEach(({ runtime, sdkUserAgentKey }) =>
61+
describe(runtime, () => {
62+
for (const { ua, expected } of cases) {
63+
it(`should sanitize user agent ${ua} to ${expected}`, async () => {
64+
const middleware = userAgentMiddleware({
65+
defaultUserAgentProvider: async () => [ua],
66+
runtime,
67+
});
68+
const handler = middleware(mockNextHandler, {});
69+
await handler({ input: {}, request: new HttpRequest({ headers: {} }) });
70+
expect(mockNextHandler.mock.calls[0][0].request.headers[sdkUserAgentKey]).toEqual(
71+
expect.stringContaining(expected)
72+
);
73+
});
74+
}
75+
})
76+
);
7077
});
7178
});

packages/middleware-user-agent/src/user-agent-middleware.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,22 @@ export const userAgentMiddleware = (options: UserAgentResolvedConfig) => <Output
3636
const userAgent = context?.userAgent?.map(escapeUserAgent) || [];
3737
const defaultUserAgent = (await options.defaultUserAgentProvider()).map(escapeUserAgent);
3838
const customUserAgent = options?.customUserAgent?.map(escapeUserAgent) || [];
39+
3940
// Set value to AWS-specific user agent header
40-
headers[X_AMZ_USER_AGENT] = [...defaultUserAgent, ...userAgent, ...customUserAgent].join(SPACE);
41+
const sdkUserAgentValue = [...defaultUserAgent, ...userAgent, ...customUserAgent].join(SPACE);
4142
// Get value to be sent with non-AWS-specific user agent header.
4243
const normalUAValue = [
4344
...defaultUserAgent.filter((section) => section.startsWith("aws-sdk-")),
4445
...customUserAgent,
4546
].join(SPACE);
46-
if (options.runtime !== "browser" && normalUAValue) {
47-
headers[USER_AGENT] = headers[USER_AGENT] ? `${headers[USER_AGENT]} ${normalUAValue}` : normalUAValue;
47+
48+
if (options.runtime !== "browser") {
49+
if (normalUAValue) {
50+
headers[X_AMZ_USER_AGENT] = headers[X_AMZ_USER_AGENT] ? `${headers[USER_AGENT]} ${normalUAValue}` : normalUAValue;
51+
}
52+
headers[USER_AGENT] = sdkUserAgentValue;
53+
} else {
54+
headers[X_AMZ_USER_AGENT] = sdkUserAgentValue;
4855
}
4956

5057
return next({

0 commit comments

Comments
 (0)