1
- import { ChecksumAlgorithm } from "@aws-sdk/middleware-flexible-checksums" ;
1
+ import {
2
+ ChecksumAlgorithm ,
3
+ DEFAULT_CHECKSUM_ALGORITHM ,
4
+ RequestChecksumCalculation ,
5
+ ResponseChecksumValidation ,
6
+ } from "@aws-sdk/middleware-flexible-checksums" ;
2
7
import { HttpRequest } from "@smithy/protocol-http" ;
3
8
import { BuildMiddleware } from "@smithy/types" ;
4
9
import { Readable } from "stream" ;
@@ -7,7 +12,7 @@ import { describe, expect, test as it } from "vitest";
7
12
import { ChecksumAlgorithm as Algo , S3 } from "../../src/index" ;
8
13
9
14
describe ( "Flexible Checksums" , ( ) => {
10
- const testCases = [
15
+ const testCases : [ string , string | undefined , string ] [ ] = [
11
16
[ "" , ChecksumAlgorithm . CRC32 , "AAAAAA==" ] ,
12
17
[ "abc" , ChecksumAlgorithm . CRC32 , "NSRBwg==" ] ,
13
18
[ "Hello world" , ChecksumAlgorithm . CRC32 , "i9aeUg==" ] ,
@@ -23,148 +28,204 @@ describe("Flexible Checksums", () => {
23
28
[ "" , ChecksumAlgorithm . SHA256 , "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" ] ,
24
29
[ "abc" , ChecksumAlgorithm . SHA256 , "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=" ] ,
25
30
[ "Hello world" , ChecksumAlgorithm . SHA256 , "ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=" ] ,
31
+
32
+ // Choose default checksum algorithm when explicily not provided.
33
+ [ "Hello world" , undefined , "i9aeUg==" ] ,
26
34
] ;
27
35
28
36
describe ( "putObject" , ( ) => {
29
- testCases . forEach ( ( [ body , checksumAlgorithm , checksumValue ] ) => {
30
- const checksumHeader = `x-amz-checksum-${ checksumAlgorithm . toLowerCase ( ) } ` ;
31
-
32
- describe ( `sets ${ checksumHeader } ="${ checksumValue } "" for checksum="${ checksumAlgorithm } "` , ( ) => {
33
- const getBodyAsReadableStream = ( content : string ) => {
34
- const readableStream = new Readable ( ) ;
35
- const separator = " " ;
36
- const wordsAsChunks = content . split ( separator ) ;
37
- wordsAsChunks . forEach ( ( word , index ) => {
38
- readableStream . push ( word ) ;
39
- if ( index !== wordsAsChunks . length - 1 ) {
40
- readableStream . push ( separator ) ;
41
- }
42
- } ) ;
43
- readableStream . push ( null ) ;
44
- return readableStream ;
45
- } ;
46
-
47
- it ( `when body is sent as a request` , async ( ) => {
48
- const requestChecksumValidator : BuildMiddleware < any , any > = ( next ) => async ( args ) => {
49
- // middleware intercept the request and return it early
50
- const request = args . request as HttpRequest ;
51
- const { headers } = request ;
52
- expect ( headers [ "x-amz-sdk-checksum-algorithm" ] ) . to . equal ( checksumAlgorithm ) ;
53
- expect ( headers [ checksumHeader ] ) . to . equal ( checksumValue ) ;
54
- return { output : { } as any , response : { } as any } ;
55
- } ;
56
-
57
- const client = new S3 ( {
58
- region : "us-west-2" ,
59
- credentials : {
60
- accessKeyId : "CLIENT_TEST" ,
61
- secretAccessKey : "CLIENT_TEST" ,
62
- } ,
63
- } ) ;
64
- client . middlewareStack . addRelativeTo ( requestChecksumValidator , {
65
- relation : "after" ,
66
- toMiddleware : "flexibleChecksumsMiddleware" ,
67
- } ) ;
68
-
69
- return await client . putObject ( {
70
- Bucket : "bucket" ,
71
- Key : "key" ,
72
- Body : body ,
73
- ChecksumAlgorithm : checksumAlgorithm as Algo ,
74
- } ) ;
75
- } ) ;
76
-
77
- it ( `when body is sent as a stream` , async ( ) => {
78
- const requestChecksumValidator : BuildMiddleware < any , any > = ( next ) => async ( args ) => {
79
- // middleware intercept the request and return it early
80
- const request = args . request as HttpRequest ;
81
- const { headers, body } = request ;
82
- expect ( headers [ "content-length" ] ) . to . be . undefined ;
83
- expect ( headers [ "content-encoding" ] ) . to . equal ( "aws-chunked" ) ;
84
- expect ( headers [ "transfer-encoding" ] ) . to . equal ( "chunked" ) ;
85
- expect ( headers [ "x-amz-content-sha256" ] ) . to . equal ( "STREAMING-UNSIGNED-PAYLOAD-TRAILER" ) ;
86
- expect ( headers [ "x-amz-trailer" ] ) . to . equal ( checksumHeader ) ;
87
- body . on ( "data" , ( data : any ) => {
88
- const stringValue = data . toString ( ) ;
89
- if ( stringValue . startsWith ( checksumHeader ) ) {
90
- const receivedChecksum = stringValue . replace ( "\r\n" , "" ) . split ( ":" ) [ 1 ] ;
91
- expect ( receivedChecksum ) . to . equal ( checksumValue ) ;
92
- }
37
+ describe . each ( [ undefined , RequestChecksumCalculation . WHEN_SUPPORTED , RequestChecksumCalculation . WHEN_REQUIRED ] ) (
38
+ `when requestChecksumCalculation='%s'` ,
39
+ ( requestChecksumCalculation ) => {
40
+ describe . each ( testCases ) (
41
+ `for body="%s" and checksumAlgorithm="%s", sets checksum="%s"` ,
42
+ ( body , checksumAlgorithm , checksumValue ) => {
43
+ const checksumHeader = `x-amz-checksum-${ ( checksumAlgorithm ?? DEFAULT_CHECKSUM_ALGORITHM ) . toLowerCase ( ) } ` ;
44
+ const getBodyAsReadableStream = ( content : string ) => {
45
+ const readableStream = new Readable ( ) ;
46
+ const separator = " " ;
47
+ const wordsAsChunks = content . split ( separator ) ;
48
+ wordsAsChunks . forEach ( ( word , index ) => {
49
+ readableStream . push ( word ) ;
50
+ if ( index !== wordsAsChunks . length - 1 ) {
51
+ readableStream . push ( separator ) ;
52
+ }
53
+ } ) ;
54
+ readableStream . push ( null ) ;
55
+ return readableStream ;
56
+ } ;
57
+
58
+ it ( `when body is sent as a string` , async ( ) => {
59
+ const requestChecksumValidator : BuildMiddleware < any , any > = ( next ) => async ( args ) => {
60
+ // middleware intercept the request and return it early
61
+ const request = args . request as HttpRequest ;
62
+ const { headers } = request ;
63
+
64
+ // Headers are not set when checksumAlgorithm is not provided,
65
+ // and requestChecksumCalculation is explicitly set to WHEN_SUPPORTED.
66
+ if (
67
+ checksumAlgorithm === undefined &&
68
+ requestChecksumCalculation === RequestChecksumCalculation . WHEN_REQUIRED
69
+ ) {
70
+ expect ( headers [ "x-amz-sdk-checksum-algorithm" ] ) . toBeUndefined ( ) ;
71
+ expect ( headers [ checksumHeader ] ) . toBeUndefined ( ) ;
72
+ } else {
73
+ expect ( headers [ "x-amz-sdk-checksum-algorithm" ] ) . toEqual (
74
+ checksumAlgorithm ?? DEFAULT_CHECKSUM_ALGORITHM
75
+ ) ;
76
+ expect ( headers [ checksumHeader ] ) . toEqual ( checksumValue ) ;
77
+ }
78
+
79
+ return { output : { } as any , response : { } as any } ;
80
+ } ;
81
+
82
+ const client = new S3 ( {
83
+ region : "us-west-2" ,
84
+ credentials : {
85
+ accessKeyId : "CLIENT_TEST" ,
86
+ secretAccessKey : "CLIENT_TEST" ,
87
+ } ,
88
+ requestChecksumCalculation,
89
+ } ) ;
90
+ client . middlewareStack . addRelativeTo ( requestChecksumValidator , {
91
+ relation : "after" ,
92
+ toMiddleware : "flexibleChecksumsMiddleware" ,
93
+ } ) ;
94
+
95
+ return await client . putObject ( {
96
+ Bucket : "bucket" ,
97
+ Key : "key" ,
98
+ Body : body ,
99
+ ChecksumAlgorithm : checksumAlgorithm as Algo ,
100
+ } ) ;
101
+ } ) ;
102
+
103
+ it ( `when body is sent as a stream` , async ( ) => {
104
+ const requestChecksumValidator : BuildMiddleware < any , any > = ( next ) => async ( args ) => {
105
+ // middleware intercept the request and return it early
106
+ const request = args . request as HttpRequest ;
107
+ const { headers, body } = request ;
108
+ expect ( headers [ "content-length" ] ) . toBeUndefined ( ) ;
109
+
110
+ // Headers are not set when checksumAlgorithm is not provided,
111
+ // and requestChecksumCalculation is explicitly set to WHEN_SUPPORTED.
112
+ if (
113
+ checksumAlgorithm === undefined &&
114
+ requestChecksumCalculation === RequestChecksumCalculation . WHEN_REQUIRED
115
+ ) {
116
+ expect ( headers [ "content-encoding" ] ) . toBeUndefined ( ) ;
117
+ expect ( headers [ "transfer-encoding" ] ) . toBeUndefined ( ) ;
118
+ expect ( headers [ "x-amz-content-sha256" ] ) . toBeUndefined ( ) ;
119
+ expect ( headers [ "x-amz-trailer" ] ) . toBeUndefined ( ) ;
120
+ } else {
121
+ expect ( headers [ "content-encoding" ] ) . toEqual ( "aws-chunked" ) ;
122
+ expect ( headers [ "transfer-encoding" ] ) . toEqual ( "chunked" ) ;
123
+ expect ( headers [ "x-amz-content-sha256" ] ) . toEqual ( "STREAMING-UNSIGNED-PAYLOAD-TRAILER" ) ;
124
+ expect ( headers [ "x-amz-trailer" ] ) . toEqual ( checksumHeader ) ;
125
+ }
126
+ body . on ( "data" , ( data : any ) => {
127
+ const stringValue = data . toString ( ) ;
128
+ if ( stringValue . startsWith ( checksumHeader ) ) {
129
+ const receivedChecksum = stringValue . replace ( "\r\n" , "" ) . split ( ":" ) [ 1 ] ;
130
+ expect ( receivedChecksum ) . toEqual ( checksumValue ) ;
131
+ }
132
+ } ) ;
133
+ return { output : { } as any , response : { } as any } ;
134
+ } ;
135
+
136
+ const client = new S3 ( {
137
+ region : "us-west-2" ,
138
+ credentials : {
139
+ accessKeyId : "CLIENT_TEST" ,
140
+ secretAccessKey : "CLIENT_TEST" ,
141
+ } ,
142
+ requestChecksumCalculation,
143
+ } ) ;
144
+ client . middlewareStack . addRelativeTo ( requestChecksumValidator , {
145
+ relation : "after" ,
146
+ toMiddleware : "flexibleChecksumsMiddleware" ,
147
+ } ) ;
148
+
149
+ const bodyStream = getBodyAsReadableStream ( body ) ;
150
+ await client . putObject ( {
151
+ Bucket : "bucket" ,
152
+ Key : "key" ,
153
+ Body : bodyStream ,
154
+ ChecksumAlgorithm : checksumAlgorithm as Algo ,
155
+ } ) ;
93
156
} ) ;
94
- return { output : { } as any , response : { } as any } ;
95
- } ;
96
-
97
- const client = new S3 ( {
98
- region : "us-west-2" ,
99
- credentials : {
100
- accessKeyId : "CLIENT_TEST" ,
101
- secretAccessKey : "CLIENT_TEST" ,
102
- } ,
103
- } ) ;
104
- client . middlewareStack . addRelativeTo ( requestChecksumValidator , {
105
- relation : "after" ,
106
- toMiddleware : "flexibleChecksumsMiddleware" ,
107
- } ) ;
108
-
109
- const bodyStream = getBodyAsReadableStream ( body ) ;
110
- await client . putObject ( {
111
- Bucket : "bucket" ,
112
- Key : "key" ,
113
- Body : bodyStream ,
114
- ChecksumAlgorithm : checksumAlgorithm as Algo ,
115
- } ) ;
116
- } ) ;
117
- } ) ;
118
- } ) ;
157
+ }
158
+ ) ;
159
+ }
160
+ ) ;
119
161
} ) ;
120
162
121
163
describe ( "getObject" , async ( ) => {
122
- testCases . forEach ( ( [ body , checksumAlgorithm , checksumValue ] ) => {
123
- const checksumHeader = `x-amz-checksum-${ checksumAlgorithm . toLowerCase ( ) } ` ;
124
-
125
- it ( `validates ${ checksumHeader } ="${ checksumValue } "" set for checksum="${ checksumAlgorithm } "` , async ( ) => {
126
- const responseBody = new Readable ( ) ;
127
- responseBody . push ( body ) ;
128
- responseBody . push ( null ) ;
129
- const responseChecksumValidator : BuildMiddleware < any , any > = ( next , context ) => async ( args ) => {
130
- const request = args . request as HttpRequest ;
131
- return {
132
- output : {
133
- $metadata : { attempts : 0 , httpStatusCode : 200 } ,
134
- request,
135
- context,
136
- Body : responseBody ,
137
- } as any ,
138
- response : {
139
- body : responseBody ,
140
- headers : {
141
- [ checksumHeader ] : checksumValue ,
164
+ describe . each ( [ undefined , ResponseChecksumValidation . WHEN_SUPPORTED , ResponseChecksumValidation . WHEN_REQUIRED ] ) (
165
+ `when responseChecksumValidation='%s'` ,
166
+ ( responseChecksumValidation ) => {
167
+ it . each ( testCases ) (
168
+ `for body="%s" and checksumAlgorithm="%s", validates ChecksumMode` ,
169
+ async ( body , checksumAlgorithm , checksumValue ) => {
170
+ const checksumHeader = `x-amz-checksum-${ ( checksumAlgorithm ?? DEFAULT_CHECKSUM_ALGORITHM ) . toLowerCase ( ) } ` ;
171
+
172
+ const responseBody = new Readable ( ) ;
173
+ responseBody . push ( body ) ;
174
+ responseBody . push ( null ) ;
175
+ const responseChecksumValidator : BuildMiddleware < any , any > = ( next , context ) => async ( args ) => {
176
+ // ChecksumMode is not set when checksumAlgorithm is not provided,
177
+ // and responseChecksumValidation is explicitly set to WHEN_SUPPORTED.
178
+ if (
179
+ checksumAlgorithm === undefined &&
180
+ responseChecksumValidation === ResponseChecksumValidation . WHEN_REQUIRED
181
+ ) {
182
+ expect ( args . input . ChecksumMode ) . toBeUndefined ( ) ;
183
+ } else {
184
+ expect ( args . input . ChecksumMode ) . toEqual ( "ENABLED" ) ;
185
+ }
186
+
187
+ const request = args . request as HttpRequest ;
188
+ return {
189
+ output : {
190
+ $metadata : { attempts : 0 , httpStatusCode : 200 } ,
191
+ request,
192
+ context,
193
+ Body : responseBody ,
194
+ } as any ,
195
+ response : {
196
+ body : responseBody ,
197
+ headers : {
198
+ [ checksumHeader ] : checksumValue ,
199
+ } ,
200
+ } as any ,
201
+ } ;
202
+ } ;
203
+
204
+ const client = new S3 ( {
205
+ region : "us-west-2" ,
206
+ credentials : {
207
+ accessKeyId : "CLIENT_TEST" ,
208
+ secretAccessKey : "CLIENT_TEST" ,
142
209
} ,
143
- } as any ,
144
- } ;
145
- } ;
146
-
147
- const client = new S3 ( {
148
- region : "us-west-2" ,
149
- credentials : {
150
- accessKeyId : "CLIENT_TEST" ,
151
- secretAccessKey : "CLIENT_TEST" ,
152
- } ,
153
- } ) ;
154
- client . middlewareStack . addRelativeTo ( responseChecksumValidator , {
155
- relation : "after" ,
156
- toMiddleware : "flexibleChecksumsMiddleware" ,
157
- } ) ;
158
-
159
- const { Body } = await client . getObject ( {
160
- Bucket : "bucket" ,
161
- Key : "key" ,
162
- ChecksumMode : "ENABLED" ,
163
- } ) ;
164
- ( Body as Readable ) . on ( "data" , ( chunk ) => {
165
- expect ( chunk . toString ( ) ) . to . equal ( body ) ;
166
- } ) ;
167
- } ) ;
168
- } ) ;
210
+ responseChecksumValidation,
211
+ } ) ;
212
+ client . middlewareStack . addRelativeTo ( responseChecksumValidator , {
213
+ relation : "after" ,
214
+ toMiddleware : "flexibleChecksumsMiddleware" ,
215
+ } ) ;
216
+
217
+ const { Body } = await client . getObject ( {
218
+ Bucket : "bucket" ,
219
+ Key : "key" ,
220
+ // Do not pass ChecksumMode if algorithm is not explicitly defined. It'll be set by SDK.
221
+ ChecksumMode : checksumAlgorithm ? "ENABLED" : undefined ,
222
+ } ) ;
223
+ ( Body as Readable ) . on ( "data" , ( chunk ) => {
224
+ expect ( chunk . toString ( ) ) . toEqual ( body ) ;
225
+ } ) ;
226
+ }
227
+ ) ;
228
+ }
229
+ ) ;
169
230
} ) ;
170
231
} ) ;
0 commit comments