Skip to content

Commit

Permalink
support httpChecksumRequired trait (#3799)
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP authored Jun 16, 2021
1 parent bad49e3 commit 7872257
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 125 deletions.
5 changes: 5 additions & 0 deletions .changes/next-release/feature-Model-750c473e.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "feature",
"category": "Model",
"description": "Add support for httpChecksumRequired trait. SDK will insert Content-MD5 header value for operations with this trait. To disable the behavior, users can unset computeChecksums config"
}
23 changes: 23 additions & 0 deletions lib/event_listeners.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,29 @@ AWS.EventListeners = {
new AWS.ParamValidator(validation).validate(rules, req.params);
});

add('COMPUTE_CHECKSUM', 'afterBuild', function COMPUTE_CHECKSUM(req) {
if (!req.service.api.operations) {
return;
}
var operation = req.service.api.operations[req.operation];
if (!operation) {
return;
}
var body = req.httpRequest.body;
var isNonStreamingPayload = body && (AWS.util.Buffer.isBuffer(body) || typeof body === 'string');
var headers = req.httpRequest.headers;
if (
operation.httpChecksumRequired &&
req.service.config.computeChecksums &&
isNonStreamingPayload &&
req.service.getSignerClass(req) === AWS.Signers.V4 &&
!headers['Content-MD5']
) {
var md5 = AWS.util.crypto.md5(body, 'base64');
headers['Content-MD5'] = md5;
}
});

addAsync('COMPUTE_SHA256', 'afterBuild', function COMPUTE_SHA256(req, done) {
req.haltHandlersOnError();
if (!req.service.api.operations) {
Expand Down
1 change: 1 addition & 0 deletions lib/model/operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function Operation(name, operation, options) {
(operation.endpointdiscovery.required ? 'REQUIRED' : 'OPTIONAL') :
'NULL'
);
property(this, 'httpChecksumRequired', operation.httpChecksumRequired, false);

memoizedProperty(this, 'input', function() {
if (!operation.input) {
Expand Down
40 changes: 18 additions & 22 deletions lib/services/s3.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,45 +446,41 @@ AWS.util.update(AWS.S3.prototype, {
},

/**
* Checks whether checksums should be computed for the request.
* If the request requires checksums to be computed, this will always
* return true, otherwise it depends on whether {AWS.Config.computeChecksums}
* is set.
* Checks whether checksums should be computed for the request if it's not
* already set by {AWS.EventListeners.Core.COMPUTE_CHECKSUM}. It depends on
* whether {AWS.Config.computeChecksums} is set.
*
* @param req [AWS.Request] the request to check against
* @return [Boolean] whether to compute checksums for a request.
* @api private
*/
willComputeChecksums: function willComputeChecksums(req) {
if (this.computableChecksumOperations[req.operation]) return true;
if (!this.config.computeChecksums) return false;

// TODO: compute checksums for Stream objects
if (!AWS.util.Buffer.isBuffer(req.httpRequest.body) &&
typeof req.httpRequest.body !== 'string') {
return false;
}

var rules = req.service.api.operations[req.operation].input.members;
var body = req.httpRequest.body;
var needsContentMD5 = rules.ContentMD5 &&
!req.params.ContentMD5 &&
body &&
(AWS.util.Buffer.isBuffer(req.httpRequest.body) || typeof req.httpRequest.body === 'string');

// Sha256 signing disabled, and not a presigned url
if (req.service.shouldDisableBodySigning(req) && !Object.prototype.hasOwnProperty.call(req.httpRequest.headers, 'presigned-expires')) {
if (rules.ContentMD5 && !req.params.ContentMD5) {
return true;
}
if (needsContentMD5 && req.service.shouldDisableBodySigning(req) && !req.isPresigned()) {
return true;
}

// V4 signer uses SHA256 signatures so only compute MD5 if it is required
if (req.service.getSignerClass(req) === AWS.Signers.V4) {
if (rules.ContentMD5 && !rules.ContentMD5.required) return false;
// SigV2 and presign, for backwards compatibility purpose.
if (needsContentMD5 && this.getSignatureVersion(req) === 's3' && req.isPresigned()) {
return true;
}

if (rules.ContentMD5 && !req.params.ContentMD5) return true;
return false;
},

/**
* A listener that computes the Content-MD5 and sets it in the header.
* @see AWS.S3.willComputeChecksums
* This listener is to support S3-specific features like
* s3DisableBodySigning and SigV2 presign. Content MD5 logic for SigV4 is
* handled in AWS.EventListeners.Core.COMPUTE_CHECKSUM
*
* @api private
*/
computeContentMd5: function computeContentMd5(req) {
Expand Down
14 changes: 7 additions & 7 deletions scripts/region-checker/allowlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ var allowlist = {
254,
267,
273,
648,
650,
769,
780,
781,
782,
787
644,
646,
765,
776,
777,
778,
783
]
};

Expand Down
51 changes: 48 additions & 3 deletions test/event_listeners.spec.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions test/foo-service.fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ var model = {
},
},
},
'PutWithChecksum': {
'http': {
'method': 'PUT',
'requestUri': '/'
},
'input': {
'type': 'structure',
'members': {
'Body': {
'shape': 'StringShape'
}
},
},
'httpChecksumRequired': true,
'payload': 'Body'
},
},
'shapes': {
'StreamingBody': {
Expand Down
93 changes: 0 additions & 93 deletions test/services/s3.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2532,19 +2532,6 @@ describe('AWS.S3', function() {
});
});

AWS.util.each(AWS.S3.prototype.computableChecksumOperations, function(operation) {
describe(operation, function() {
it('forces Content-MD5 header parameter', function() {
var req = s3[operation]({
Bucket: 'bucket',
ContentMD5: '000'
}).build();
var hash = AWS.util.crypto.md5(req.httpRequest.body, 'base64');
expect(req.httpRequest.headers['Content-MD5']).to.equal(hash);
});
});
});

describe('willComputeChecksums', function() {
var willCompute = function(operation, opts) {
var compute = opts.computeChecksums;
Expand All @@ -2569,86 +2556,6 @@ describe('AWS.S3', function() {
}
};

it('computes checksums if the operation requires it', function() {
willCompute('deleteObjects', {
computeChecksums: true
});
willCompute('putBucketCors', {
computeChecksums: true
});
willCompute('putBucketLifecycle', {
computeChecksums: true
});
willCompute('putBucketLifecycleConfiguration', {
computeChecksums: true
});
willCompute('putBucketTagging', {
computeChecksums: true
});
willCompute('putBucketReplication', {
computeChecksums: true
});
willCompute('putObjectLegalHold', {
computeChecksums: true
});
willCompute('putObjectRetention', {
computeChecksums: true
});
willCompute('putObjectLockConfiguration', {
computeChecksums: true
});
});

it('computes checksums if computeChecksums is off and operation requires it', function() {
willCompute('deleteObjects', {
computeChecksums: false
});
willCompute('putBucketCors', {
computeChecksums: false
});
willCompute('putBucketLifecycle', {
computeChecksums: false
});
willCompute('putBucketLifecycleConfiguration', {
computeChecksums: false
});
willCompute('putBucketTagging', {
computeChecksums: false
});
willCompute('putBucketReplication', {
computeChecksums: false
});
willCompute('putObjectLegalHold', {
computeChecksums: false
});
willCompute('putObjectRetention', {
computeChecksums: false
});
willCompute('putObjectLockConfiguration', {
computeChecksums: false
});
});

it('does not compute checksums if computeChecksums is off', function() {
willCompute('putObject', {
computeChecksums: false,
hash: null
});
});

it('does not compute checksums if computeChecksums is on and ContentMD5 is provided', function() {
willCompute('putBucketAcl', {
computeChecksums: true,
hash: '000'
});
});

it('computes checksums if computeChecksums is on and ContentMD5 is not provided', function() {
willCompute('putBucketAcl', {
computeChecksums: true
});
});

if (AWS.util.isNode()) {
it('does not compute checksums for Stream objects', function() {
s3 = new AWS.S3({
Expand Down

0 comments on commit 7872257

Please sign in to comment.