Skip to content

Commit 1a181ee

Browse files
AllanZhengYPsrchase
authored andcommitted
feat(client-s3): support generating endpoints from multi-region access point (#2796)
* Revert "fix(client-s3): revert MRAP customizations (#2759)" This reverts commit 9f04714. * chore: update mrap packages dependencies * feat(middleware-sdk-s3): dynamically import aws-crt package optional dependencies * feat(middleware-user-agent): track if aws-crt is available at runtime * chore: update crt signer loading error message * chore(middleware-sdk-s3): add crt package to dev dependency and track usage This makes sure the type of CRT package is available and the Lerna builds packages in right order--build crt signer package before buiding s3 middleware package. * docs(client-s3): add docs to Bucket members on using MRAP * chore(signature-v4-crt): update crt version to ^1.9.7 * fix(client-s3): address PR feedbacks
1 parent 93e47fc commit 1a181ee

15 files changed

+106
-72
lines changed

packages/signature-v4/src/SignatureV4.ts

+2-19
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ import { createScope, getSigningKey } from "./credentialDerivation";
3737
import { getCanonicalHeaders } from "./getCanonicalHeaders";
3838
import { getCanonicalQuery } from "./getCanonicalQuery";
3939
import { getPayloadHash } from "./getPayloadHash";
40-
import { hasHeader } from "./hasHeader";
40+
import { hasHeader } from "./headerUtil";
4141
import { moveHeadersToQuery } from "./moveHeadersToQuery";
42+
import { normalizeCredentialsProvider, normalizeRegionProvider } from "./normalizeProvider";
4243
import { prepareRequest } from "./prepareRequest";
4344
import { iso8601 } from "./utilDate";
4445

@@ -317,21 +318,3 @@ const formatDate = (now: DateInput): { longDate: string; shortDate: string } =>
317318
};
318319

319320
const getCanonicalHeaderList = (headers: object): string => Object.keys(headers).sort().join(";");
320-
321-
const normalizeRegionProvider = (region: string | Provider<string>): Provider<string> => {
322-
if (typeof region === "string") {
323-
const promisified = Promise.resolve(region);
324-
return () => promisified;
325-
} else {
326-
return region;
327-
}
328-
};
329-
330-
const normalizeCredentialsProvider = (credentials: Credentials | Provider<Credentials>): Provider<Credentials> => {
331-
if (typeof credentials === "object") {
332-
const promisified = Promise.resolve(credentials);
333-
return () => promisified;
334-
} else {
335-
return credentials;
336-
}
337-
};

packages/signature-v4/src/cloneRequest.ts

+7-10
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,17 @@ import { HttpRequest, QueryParameterBag } from "@aws-sdk/types";
33
/**
44
* @internal
55
*/
6-
export function cloneRequest({ headers, query, ...rest }: HttpRequest): HttpRequest {
7-
return {
8-
...rest,
9-
headers: { ...headers },
10-
query: query ? cloneQuery(query) : undefined,
11-
};
12-
}
6+
export const cloneRequest = ({ headers, query, ...rest }: HttpRequest): HttpRequest => ({
7+
...rest,
8+
headers: { ...headers },
9+
query: query ? cloneQuery(query) : undefined,
10+
});
1311

14-
function cloneQuery(query: QueryParameterBag): QueryParameterBag {
15-
return Object.keys(query).reduce((carry: QueryParameterBag, paramName: string) => {
12+
export const cloneQuery = (query: QueryParameterBag): QueryParameterBag =>
13+
Object.keys(query).reduce((carry: QueryParameterBag, paramName: string) => {
1614
const param = query[paramName];
1715
return {
1816
...carry,
1917
[paramName]: Array.isArray(param) ? [...param] : param,
2018
};
2119
}, {});
22-
}

packages/signature-v4/src/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const SIGNED_HEADERS_QUERY_PARAM = "X-Amz-SignedHeaders";
55
export const EXPIRES_QUERY_PARAM = "X-Amz-Expires";
66
export const SIGNATURE_QUERY_PARAM = "X-Amz-Signature";
77
export const TOKEN_QUERY_PARAM = "X-Amz-Security-Token";
8+
export const REGION_SET_PARAM = "X-Amz-Region-Set";
89

910
export const AUTH_HEADER = "authorization";
1011
export const AMZ_DATE_HEADER = AMZ_DATE_QUERY_PARAM.toLowerCase();
@@ -40,6 +41,7 @@ export const SEC_HEADER_PATTERN = /^sec-/;
4041
export const UNSIGNABLE_PATTERNS = [/^proxy-/i, /^sec-/i];
4142

4243
export const ALGORITHM_IDENTIFIER = "AWS4-HMAC-SHA256";
44+
export const ALGORITHM_IDENTIFIER_V4A = "AWS4-ECDSA-P256-SHA256";
4345

4446
export const EVENT_ALGORITHM_IDENTIFIER = "AWS4-HMAC-SHA256-PAYLOAD";
4547

packages/signature-v4/src/credentialDerivation.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ const cacheQueue: Array<string> = [];
1313
* @param region The AWS region in which the service resides.
1414
* @param service The service to which the signed request is being sent.
1515
*/
16-
export function createScope(shortDate: string, region: string, service: string): string {
17-
return `${shortDate}/${region}/${service}/${KEY_TYPE_IDENTIFIER}`;
18-
}
16+
export const createScope = (shortDate: string, region: string, service: string): string =>
17+
`${shortDate}/${region}/${service}/${KEY_TYPE_IDENTIFIER}`;
1918

2019
/**
2120
* Derive a signing key from its composite parts
@@ -57,15 +56,15 @@ export const getSigningKey = async (
5756
/**
5857
* @internal
5958
*/
60-
export function clearCredentialCache(): void {
59+
export const clearCredentialCache = (): void => {
6160
cacheQueue.length = 0;
6261
Object.keys(signingKeyCache).forEach((cacheKey) => {
6362
delete signingKeyCache[cacheKey];
6463
});
65-
}
64+
};
6665

67-
function hmac(ctor: HashConstructor, secret: SourceData, data: SourceData): Promise<Uint8Array> {
66+
const hmac = (ctor: HashConstructor, secret: SourceData, data: SourceData): Promise<Uint8Array> => {
6867
const hash = new ctor(secret);
6968
hash.update(data);
7069
return hash.digest();
71-
}
70+
};

packages/signature-v4/src/getCanonicalHeaders.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import { HeaderBag, HttpRequest } from "@aws-sdk/types";
33
import { ALWAYS_UNSIGNABLE_HEADERS, PROXY_HEADER_PATTERN, SEC_HEADER_PATTERN } from "./constants";
44

55
/**
6-
* @internal
6+
* @private
77
*/
8-
export function getCanonicalHeaders(
8+
export const getCanonicalHeaders = (
99
{ headers }: HttpRequest,
1010
unsignableHeaders?: Set<string>,
1111
signableHeaders?: Set<string>
12-
): HeaderBag {
12+
): HeaderBag => {
1313
const canonical: HeaderBag = {};
1414
for (const headerName of Object.keys(headers).sort()) {
1515
const canonicalHeaderName = headerName.toLowerCase();
@@ -28,4 +28,4 @@ export function getCanonicalHeaders(
2828
}
2929

3030
return canonical;
31-
}
31+
};

packages/signature-v4/src/getCanonicalQuery.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { escapeUri } from "@aws-sdk/util-uri-escape";
44
import { SIGNATURE_HEADER } from "./constants";
55

66
/**
7-
* @internal
7+
* @private
88
*/
9-
export function getCanonicalQuery({ query = {} }: HttpRequest): string {
9+
export const getCanonicalQuery = ({ query = {} }: HttpRequest): string => {
1010
const keys: Array<string> = [];
1111
const serialized: { [key: string]: string } = {};
1212
for (const key of Object.keys(query).sort()) {
@@ -34,4 +34,4 @@ export function getCanonicalQuery({ query = {} }: HttpRequest): string {
3434
.map((key) => serialized[key])
3535
.filter((serialized) => serialized) // omit any falsy values
3636
.join("&");
37-
}
37+
};

packages/signature-v4/src/getPayloadHash.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { toHex } from "@aws-sdk/util-hex-encoding";
55
import { SHA256_HEADER, UNSIGNED_PAYLOAD } from "./constants";
66

77
/**
8-
* @internal
8+
* @private
99
*/
10-
export async function getPayloadHash(
10+
export const getPayloadHash = async (
1111
{ headers, body }: HttpRequest,
1212
hashConstructor: HashConstructor
13-
): Promise<string> {
13+
): Promise<string> => {
1414
for (const headerName of Object.keys(headers)) {
1515
if (headerName.toLowerCase() === SHA256_HEADER) {
1616
return headers[headerName];
@@ -29,4 +29,4 @@ export async function getPayloadHash(
2929
// body is unsignable. Attempt to send the request with an unsigned payload,
3030
// which may or may not be accepted by the service.
3131
return UNSIGNED_PAYLOAD;
32-
}
32+
};

packages/signature-v4/src/hasHeader.ts

-12
This file was deleted.
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { HeaderBag } from "@aws-sdk/types";
2+
3+
export const hasHeader = (soughtHeader: string, headers: HeaderBag): boolean => {
4+
soughtHeader = soughtHeader.toLowerCase();
5+
for (const headerName of Object.keys(headers)) {
6+
if (soughtHeader === headerName.toLowerCase()) {
7+
return true;
8+
}
9+
}
10+
11+
return false;
12+
};
13+
14+
/* Get the value of one request header, ignore the case. Return string if header is in the headers, else return undefined */
15+
export const getHeaderValue = (soughtHeader: string, headers: HeaderBag): string | undefined => {
16+
soughtHeader = soughtHeader.toLowerCase();
17+
for (const headerName of Object.keys(headers)) {
18+
if (soughtHeader === headerName.toLowerCase()) {
19+
return headers[headerName];
20+
}
21+
}
22+
23+
return undefined;
24+
};
25+
26+
/* Delete the one request header, ignore the case. Do nothing if it's not there */
27+
export const deleteHeader = (soughtHeader: string, headers: HeaderBag) => {
28+
soughtHeader = soughtHeader.toLowerCase();
29+
for (const headerName of Object.keys(headers)) {
30+
if (soughtHeader === headerName.toLowerCase()) {
31+
delete headers[headerName];
32+
}
33+
}
34+
};

packages/signature-v4/src/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
export * from "./credentialDerivation";
2+
export { getCanonicalHeaders } from "./getCanonicalHeaders";
3+
export { getCanonicalQuery } from "./getCanonicalQuery";
4+
export { getPayloadHash } from "./getPayloadHash";
5+
export { moveHeadersToQuery } from "./moveHeadersToQuery";
6+
export { prepareRequest } from "./prepareRequest";
7+
export { normalizeCredentialsProvider, normalizeRegionProvider } from "./normalizeProvider";
28
export * from "./SignatureV4";

packages/signature-v4/src/moveHeadersToQuery.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { HttpRequest, QueryParameterBag } from "@aws-sdk/types";
33
import { cloneRequest } from "./cloneRequest";
44

55
/**
6-
* @internal
6+
* @private
77
*/
8-
export function moveHeadersToQuery(
8+
export const moveHeadersToQuery = (
99
request: HttpRequest,
1010
options: { unhoistableHeaders?: Set<string> } = {}
11-
): HttpRequest & { query: QueryParameterBag } {
11+
): HttpRequest & { query: QueryParameterBag } => {
1212
const { headers, query = {} as QueryParameterBag } =
1313
typeof (request as any).clone === "function" ? (request as any).clone() : cloneRequest(request);
1414
for (const name of Object.keys(headers)) {
@@ -24,4 +24,4 @@ export function moveHeadersToQuery(
2424
headers,
2525
query,
2626
};
27-
}
27+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Credentials, Provider } from "@aws-sdk/types";
2+
3+
/**
4+
* @private
5+
*/
6+
export const normalizeRegionProvider = (region: string | Provider<string>): Provider<string> => {
7+
if (typeof region === "string") {
8+
const promisified = Promise.resolve(region);
9+
return () => promisified;
10+
} else {
11+
return region;
12+
}
13+
};
14+
15+
/**
16+
* @private
17+
*/
18+
export const normalizeCredentialsProvider = (
19+
credentials: Credentials | Provider<Credentials>
20+
): Provider<Credentials> => {
21+
if (typeof credentials === "object") {
22+
const promisified = Promise.resolve(credentials);
23+
return () => promisified;
24+
} else {
25+
return credentials;
26+
}
27+
};

packages/signature-v4/src/prepareRequest.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { cloneRequest } from "./cloneRequest";
44
import { GENERATED_HEADERS } from "./constants";
55

66
/**
7-
* @internal
7+
* @private
88
*/
9-
export function prepareRequest(request: HttpRequest): HttpRequest {
9+
export const prepareRequest = (request: HttpRequest): HttpRequest => {
1010
// Create a clone of the request object that does not clone the body
1111
request = typeof (request as any).clone === "function" ? (request as any).clone() : cloneRequest(request);
1212

@@ -17,4 +17,4 @@ export function prepareRequest(request: HttpRequest): HttpRequest {
1717
}
1818

1919
return request;
20-
}
20+
};

packages/signature-v4/src/utilDate.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
export function iso8601(time: number | string | Date): string {
2-
return toDate(time)
1+
export const iso8601 = (time: number | string | Date): string =>
2+
toDate(time)
33
.toISOString()
44
.replace(/\.\d{3}Z$/, "Z");
5-
}
65

7-
export function toDate(time: number | string | Date): Date {
6+
export const toDate = (time: number | string | Date): Date => {
87
if (typeof time === "number") {
98
return new Date(time * 1000);
109
}
@@ -17,4 +16,4 @@ export function toDate(time: number | string | Date): Date {
1716
}
1817

1918
return time;
20-
}
19+
};

packages/signature-v4/tsconfig.cjs.json

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"declarationDir": "./dist/types",
55
"rootDir": "./src",
66
"outDir": "./dist/cjs",
7-
"noUnusedLocals": true,
87
"baseUrl": "."
98
},
109
"extends": "../../tsconfig.cjs.json",

0 commit comments

Comments
 (0)