Skip to content

Commit ef38358

Browse files
authored
private : add support for httpChecksumRequired trait (#570)
1 parent 40a22fc commit ef38358

File tree

5 files changed

+353
-113
lines changed

5 files changed

+353
-113
lines changed

service/s3/content_md5.go renamed to private/checksum/content_md5.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1-
package s3
1+
package checksum
22

33
import (
44
"crypto/md5"
55
"encoding/base64"
66
"io"
77

8-
request "github.com/aws/aws-sdk-go-v2/aws"
8+
"github.com/aws/aws-sdk-go-v2/aws"
99
"github.com/aws/aws-sdk-go-v2/aws/awserr"
1010
)
1111

12-
// contentMD5 computes and sets the HTTP Content-MD5 header for requests that
12+
const contentMD5Header = "Content-Md5"
13+
14+
// AddBodyContentMD5Handler computes and sets the HTTP Content-MD5 header for requests that
1315
// require it.
14-
func contentMD5(r *request.Request) {
16+
func AddBodyContentMD5Handler(r *aws.Request) {
17+
// if Content-MD5 header is already present, return
18+
if v := r.HTTPRequest.Header.Get(contentMD5Header); len(v) != 0 {
19+
return
20+
}
21+
1522
h := md5.New()
1623

1724
// hash the body. seek back to the first position after reading to reset

private/model/api/operation.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ type Operation struct {
2929
Endpoint *EndpointTrait `json:"endpoint"`
3030
HasEndpointARN bool `json:"-"`
3131

32-
IsEndpointDiscoveryOp bool `json:"endpointoperation"`
33-
EndpointDiscovery *EndpointDiscovery `json:"endpointdiscovery"`
32+
IsEndpointDiscoveryOp bool `json:"endpointoperation"`
33+
EndpointDiscovery *EndpointDiscovery `json:"endpointdiscovery"`
34+
IsHttpChecksumRequired bool `json:"httpChecksumRequired"`
3435
}
3536

3637
// EndpointTrait provides the structure of the modeled enpdoint trait, and its
@@ -205,6 +206,15 @@ func (c *{{ .API.StructName }}) {{ $reqType }}(input {{ .InputRef.GoType }}) ({{
205206
}
206207
{{- end }}
207208
{{ end -}}
209+
210+
{{- if .IsHttpChecksumRequired }}
211+
{{- $_ := .API.AddSDKImport "private/checksum" }}
212+
req.Handlers.Build.PushBackNamed(aws.NamedHandler{
213+
Name: "contentMd5Handler",
214+
Fn: checksum.AddBodyContentMD5Handler,
215+
})
216+
{{- end }}
217+
208218
return {{ $reqType }}{Request: req, Input: input, Copy: c.{{ $reqType }} }
209219
}
210220

service/s3/content_md5_test.go

Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
package s3_test
2+
3+
import (
4+
"crypto/md5"
5+
"encoding/base64"
6+
"io/ioutil"
7+
"testing"
8+
9+
"github.com/aws/aws-sdk-go-v2/aws"
10+
"github.com/aws/aws-sdk-go-v2/internal/awstesting/unit"
11+
"github.com/aws/aws-sdk-go-v2/service/s3"
12+
)
13+
14+
func assertMD5(t *testing.T, req *aws.Request) {
15+
err := req.Build()
16+
if err != nil {
17+
t.Errorf("expected no error, but received %v", err)
18+
}
19+
20+
b, _ := ioutil.ReadAll(req.HTTPRequest.Body)
21+
out := md5.Sum(b)
22+
if len(b) == 0 {
23+
t.Error("expected non-empty value")
24+
}
25+
if a := req.HTTPRequest.Header.Get("Content-MD5"); len(a) == 0 {
26+
t.Fatal("Expected Content-MD5 header to be present in the operation request, was not")
27+
} else if e := base64.StdEncoding.EncodeToString(out[:]); e != a {
28+
t.Errorf("expected %s, but received %s", e, a)
29+
}
30+
}
31+
32+
func TestMD5InPutBucketCors(t *testing.T) {
33+
svc := s3.New(unit.Config())
34+
req := svc.PutBucketCorsRequest(&s3.PutBucketCorsInput{
35+
Bucket: aws.String("bucketname"),
36+
CORSConfiguration: &s3.CORSConfiguration{
37+
CORSRules: []s3.CORSRule{
38+
{
39+
AllowedMethods: []string{"GET"},
40+
AllowedOrigins: []string{"*"},
41+
},
42+
},
43+
},
44+
})
45+
46+
assertMD5(t, req.Request)
47+
}
48+
49+
func TestMD5InPutBucketLifecycle(t *testing.T) {
50+
svc := s3.New(unit.Config())
51+
req := svc.PutBucketLifecycleRequest(&s3.PutBucketLifecycleInput{
52+
Bucket: aws.String("bucketname"),
53+
LifecycleConfiguration: &s3.LifecycleConfiguration{
54+
Rules: []s3.Rule{
55+
{
56+
ID: aws.String("ID"),
57+
Prefix: aws.String("Prefix"),
58+
Status: s3.ExpirationStatusEnabled,
59+
},
60+
},
61+
},
62+
})
63+
assertMD5(t, req.Request)
64+
}
65+
66+
func TestMD5InPutBucketPolicy(t *testing.T) {
67+
svc := s3.New(unit.Config())
68+
req := svc.PutBucketPolicyRequest(&s3.PutBucketPolicyInput{
69+
Bucket: aws.String("bucketname"),
70+
Policy: aws.String("{}"),
71+
})
72+
assertMD5(t, req.Request)
73+
}
74+
75+
func TestMD5InPutBucketTagging(t *testing.T) {
76+
svc := s3.New(unit.Config())
77+
req := svc.PutBucketTaggingRequest(&s3.PutBucketTaggingInput{
78+
Bucket: aws.String("bucketname"),
79+
Tagging: &s3.Tagging{
80+
TagSet: []s3.Tag{
81+
{Key: aws.String("KEY"), Value: aws.String("VALUE")},
82+
},
83+
},
84+
})
85+
assertMD5(t, req.Request)
86+
}
87+
88+
func TestMD5InDeleteObjects(t *testing.T) {
89+
svc := s3.New(unit.Config())
90+
req := svc.DeleteObjectsRequest(&s3.DeleteObjectsInput{
91+
Bucket: aws.String("bucketname"),
92+
Delete: &s3.Delete{
93+
Objects: []s3.ObjectIdentifier{
94+
{Key: aws.String("key")},
95+
},
96+
},
97+
})
98+
assertMD5(t, req.Request)
99+
}
100+
101+
func TestMD5InPutBucketLifecycleConfiguration(t *testing.T) {
102+
svc := s3.New(unit.Config())
103+
req := svc.PutBucketLifecycleConfigurationRequest(&s3.PutBucketLifecycleConfigurationInput{
104+
Bucket: aws.String("bucketname"),
105+
LifecycleConfiguration: &s3.BucketLifecycleConfiguration{
106+
Rules: []s3.LifecycleRule{
107+
{Prefix: aws.String("prefix"), Status: s3.ExpirationStatusEnabled},
108+
},
109+
},
110+
})
111+
assertMD5(t, req.Request)
112+
}
113+
114+
func TestMD5InPutBucketReplication(t *testing.T) {
115+
svc := s3.New(unit.Config())
116+
req := svc.PutBucketReplicationRequest(&s3.PutBucketReplicationInput{
117+
Bucket: aws.String("bucketname"),
118+
ReplicationConfiguration: &s3.ReplicationConfiguration{
119+
Role: aws.String("Role"),
120+
Rules: []s3.ReplicationRule{
121+
{
122+
Destination: &s3.Destination{
123+
Bucket: aws.String("mock bucket"),
124+
},
125+
},
126+
},
127+
},
128+
Token: aws.String("token"),
129+
})
130+
131+
assertMD5(t, req.Request)
132+
}
133+
134+
func TestMD5InPutBucketAcl(t *testing.T) {
135+
svc := s3.New(unit.Config())
136+
req := svc.PutBucketAclRequest(&s3.PutBucketAclInput{
137+
Bucket: aws.String("bucketname"),
138+
AccessControlPolicy: &s3.AccessControlPolicy{
139+
Grants: []s3.Grant{{
140+
Grantee: &s3.Grantee{
141+
ID: aws.String("mock id"),
142+
Type: s3.Type("type"),
143+
},
144+
Permission: s3.PermissionFullControl,
145+
}},
146+
Owner: &s3.Owner{
147+
DisplayName: aws.String("mock name"),
148+
},
149+
},
150+
})
151+
assertMD5(t, req.Request)
152+
}
153+
154+
func TestMD5InPutBucketEncryption(t *testing.T) {
155+
svc := s3.New(unit.Config())
156+
req := svc.PutBucketEncryptionRequest(&s3.PutBucketEncryptionInput{
157+
Bucket: aws.String("bucketname"),
158+
ServerSideEncryptionConfiguration: &s3.ServerSideEncryptionConfiguration{
159+
Rules: []s3.ServerSideEncryptionRule{
160+
{
161+
ApplyServerSideEncryptionByDefault: &s3.ServerSideEncryptionByDefault{
162+
KMSMasterKeyID: aws.String("mock KMS master key id"),
163+
SSEAlgorithm: "mock SSE algorithm",
164+
},
165+
},
166+
},
167+
},
168+
})
169+
170+
assertMD5(t, req.Request)
171+
}
172+
173+
func TestMD5InPutBucketLogging(t *testing.T) {
174+
svc := s3.New(unit.Config())
175+
req := svc.PutBucketLoggingRequest(&s3.PutBucketLoggingInput{
176+
Bucket: aws.String("bucket name"),
177+
BucketLoggingStatus: &s3.BucketLoggingStatus{LoggingEnabled: &s3.LoggingEnabled{
178+
TargetBucket: aws.String("target bucket"),
179+
TargetPrefix: aws.String("target prefix"),
180+
}},
181+
})
182+
183+
assertMD5(t, req.Request)
184+
}
185+
186+
func TestMD5InPutBucketNotification(t *testing.T) {
187+
svc := s3.New(unit.Config())
188+
req := svc.PutBucketNotificationRequest(&s3.PutBucketNotificationInput{
189+
Bucket: aws.String("bucket name"),
190+
NotificationConfiguration: &s3.NotificationConfigurationDeprecated{
191+
TopicConfiguration: &s3.TopicConfigurationDeprecated{
192+
Id: aws.String("id"),
193+
},
194+
},
195+
})
196+
197+
assertMD5(t, req.Request)
198+
}
199+
200+
func TestMD5InPutBucketRequestPayment(t *testing.T) {
201+
svc := s3.New(unit.Config())
202+
req := svc.PutBucketRequestPaymentRequest(&s3.PutBucketRequestPaymentInput{
203+
Bucket: aws.String("bucketname"),
204+
RequestPaymentConfiguration: &s3.RequestPaymentConfiguration{
205+
Payer: s3.Payer("payer"),
206+
},
207+
})
208+
209+
assertMD5(t, req.Request)
210+
}
211+
212+
func TestMD5InPutBucketVersioning(t *testing.T) {
213+
svc := s3.New(unit.Config())
214+
req := svc.PutBucketVersioningRequest(&s3.PutBucketVersioningInput{
215+
Bucket: aws.String("bucketname"),
216+
VersioningConfiguration: &s3.VersioningConfiguration{
217+
MFADelete: s3.MFADeleteDisabled,
218+
Status: s3.BucketVersioningStatusSuspended,
219+
},
220+
})
221+
222+
assertMD5(t, req.Request)
223+
}
224+
225+
func TestMD5InPutBucketWebsite(t *testing.T) {
226+
svc := s3.New(unit.Config())
227+
req := svc.PutBucketWebsiteRequest(&s3.PutBucketWebsiteInput{
228+
Bucket: aws.String("bucket name"),
229+
WebsiteConfiguration: &s3.WebsiteConfiguration{
230+
ErrorDocument: &s3.ErrorDocument{
231+
Key: aws.String("error"),
232+
},
233+
},
234+
})
235+
236+
assertMD5(t, req.Request)
237+
}
238+
239+
func TestMD5InPutObjectLegalHold(t *testing.T) {
240+
svc := s3.New(unit.Config())
241+
req := svc.PutObjectLegalHoldRequest(&s3.PutObjectLegalHoldInput{
242+
Bucket: aws.String("bucketname"),
243+
Key: aws.String("key"),
244+
LegalHold: &s3.ObjectLockLegalHold{
245+
Status: s3.ObjectLockLegalHoldStatusOff,
246+
},
247+
})
248+
249+
assertMD5(t, req.Request)
250+
}
251+
252+
func TestMD5InPutObjectRetention(t *testing.T) {
253+
svc := s3.New(unit.Config())
254+
req := svc.PutObjectRetentionRequest(&s3.PutObjectRetentionInput{
255+
Bucket: aws.String("bucket name"),
256+
BypassGovernanceRetention: nil,
257+
Key: aws.String("key"),
258+
Retention: &s3.ObjectLockRetention{
259+
Mode: s3.ObjectLockRetentionMode("mode"),
260+
},
261+
VersionId: nil,
262+
})
263+
264+
assertMD5(t, req.Request)
265+
}
266+
267+
func TestMD5InPutObjectLockConfiguration(t *testing.T) {
268+
svc := s3.New(unit.Config())
269+
req := svc.PutObjectLockConfigurationRequest(&s3.PutObjectLockConfigurationInput{
270+
Bucket: aws.String("bucket name"),
271+
ObjectLockConfiguration: &s3.ObjectLockConfiguration{
272+
ObjectLockEnabled: s3.ObjectLockEnabledEnabled,
273+
},
274+
})
275+
276+
assertMD5(t, req.Request)
277+
}
278+
279+
func TestMD5InPutObjectAcl(t *testing.T) {
280+
svc := s3.New(unit.Config())
281+
req := svc.PutObjectAclRequest(&s3.PutObjectAclInput{
282+
AccessControlPolicy: &s3.AccessControlPolicy{
283+
Grants: []s3.Grant{{
284+
Grantee: &s3.Grantee{
285+
ID: aws.String("mock id"),
286+
Type: s3.Type("type"),
287+
},
288+
Permission: s3.PermissionFullControl,
289+
}},
290+
Owner: &s3.Owner{
291+
DisplayName: aws.String("mock name"),
292+
},
293+
},
294+
Bucket: aws.String("bucket name"),
295+
Key: aws.String("key"),
296+
})
297+
298+
assertMD5(t, req.Request)
299+
}
300+
301+
func TestMD5InPutObjectTagging(t *testing.T) {
302+
svc := s3.New(unit.Config())
303+
req := svc.PutObjectTaggingRequest(&s3.PutObjectTaggingInput{
304+
Bucket: aws.String("bucket name"),
305+
Key: aws.String("key"),
306+
Tagging: &s3.Tagging{TagSet: []s3.Tag{
307+
{
308+
Key: aws.String("key"),
309+
Value: aws.String("value"),
310+
},
311+
}},
312+
})
313+
314+
assertMD5(t, req.Request)
315+
}
316+
317+
func TestMD5InPutPublicAccessBlock(t *testing.T) {
318+
svc := s3.New(unit.Config())
319+
req := svc.PutPublicAccessBlockRequest(&s3.PutPublicAccessBlockInput{
320+
Bucket: aws.String("bucket name"),
321+
PublicAccessBlockConfiguration: &s3.PublicAccessBlockConfiguration{
322+
BlockPublicAcls: aws.Bool(true),
323+
BlockPublicPolicy: aws.Bool(true),
324+
IgnorePublicAcls: aws.Bool(true),
325+
RestrictPublicBuckets: aws.Bool(true),
326+
},
327+
})
328+
329+
assertMD5(t, req.Request)
330+
}

0 commit comments

Comments
 (0)