Skip to content

Commit 5cc4d25

Browse files
committed
Add a helper function for parsing Accept headers
1 parent 3aee809 commit 5cc4d25

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { acceptMatches } from "./accept";
2+
3+
describe("acceptMatches", () => {
4+
it.each([null, undefined, "*/*"])("always returns true for %s", (value) => {
5+
expect(acceptMatches(value, "text/plain")).toEqual(true);
6+
});
7+
8+
it("handles explicit matches", () => {
9+
expect(acceptMatches("text/plain", "text/plain")).toEqual(true);
10+
expect(acceptMatches("text/plain; q=5", "text/plain")).toEqual(true);
11+
});
12+
13+
it("handles wildcard subtypes", () => {
14+
expect(acceptMatches("text/*", "text/plain")).toEqual(true);
15+
expect(acceptMatches("text/*; q=5", "text/plain")).toEqual(true);
16+
});
17+
18+
it("handles multiple acceptable values", () => {
19+
expect(acceptMatches("application/json, text/plain; q=5", "text/plain")).toEqual(true);
20+
expect(acceptMatches("application/json, text/*; q=5", "text/plain")).toEqual(true);
21+
expect(acceptMatches("application/json, text/xml; q=5, */*", "text/plain")).toEqual(true);
22+
});
23+
24+
it.each(["application/*", "application/json", "application/*; q=5; text/xml"])(
25+
"does not match text/plain to %s",
26+
(value) => {
27+
expect(acceptMatches(value, "text/plain")).toEqual(false);
28+
}
29+
);
30+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* A function for matching the 'Accept' header to an explicit MIME type.
3+
*
4+
* @param acceptHeader the header as specified by the caller
5+
* @param responseContentType the content type that we expect to return
6+
* @return true if the specified content-type is acceptable given the Accept value
7+
*/
8+
export const acceptMatches = (acceptHeader: string | null | undefined, responseContentType: string): boolean => {
9+
if (acceptHeader === null || acceptHeader === undefined) {
10+
return true;
11+
}
12+
13+
// see: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2
14+
// we only care if anything in Accept matches a content-type we want to respond with,
15+
// so we disregard all of the accept-params
16+
const acceptableContentTypes = acceptHeader.split(",").map((s) => s.split(";")[0].trim());
17+
const responsePrimaryType = responseContentType.split("/")[0];
18+
19+
for (const type of acceptableContentTypes) {
20+
if (type === "*/*" || type === `${responsePrimaryType}/*` || type === responseContentType) {
21+
return true;
22+
}
23+
}
24+
25+
return false;
26+
};

smithy-typescript-ssdk-libs/server-common/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
export * as httpbinding from "./httpbinding";
17+
export * from "./accept";
1718
export * from "./errors";
1819
export * from "./validation";
1920

0 commit comments

Comments
 (0)