From 2f3826c7a059a788da41473cefb8049968e2001c Mon Sep 17 00:00:00 2001 From: AllanFly120 Date: Wed, 27 Nov 2019 15:36:49 -0800 Subject: [PATCH] feat: add opt-in support for S3 us-east-1 regional endpoint (#2960) * generalize resolveRegionalEndpointsFlag function; update STS customization --- .../next-release/feature-Region-e1805cab.json | 5 + lib/config.js | 13 +- lib/config_regional_endpoint.js | 67 +++++++++ lib/services/s3.js | 30 ++++ lib/services/sts.js | 79 +++------- scripts/region-checker/whitelist.js | 28 ++-- test/services/s3.spec.js | 138 ++++++++++++++++++ test/services/sts.spec.js | 66 +++++---- 8 files changed, 328 insertions(+), 98 deletions(-) create mode 100644 .changes/next-release/feature-Region-e1805cab.json create mode 100644 lib/config_regional_endpoint.js diff --git a/.changes/next-release/feature-Region-e1805cab.json b/.changes/next-release/feature-Region-e1805cab.json new file mode 100644 index 0000000000..886a139b91 --- /dev/null +++ b/.changes/next-release/feature-Region-e1805cab.json @@ -0,0 +1,5 @@ +{ + "type": "feature", + "category": "Region", + "description": "s3 client now support sending request to us-east-1 regional endpoint with `s3UsEast1RegionalEndpoint` client configuration set to `regional`" +} \ No newline at end of file diff --git a/lib/config.js b/lib/config.js index b808260548..3939d2a965 100644 --- a/lib/config.js +++ b/lib/config.js @@ -82,6 +82,12 @@ var PromisesDependency; * @return [Boolean] whether to disable S3 body signing when using signature version `v4`. * Body signing can only be disabled when using https. Defaults to `true`. * + * @!attribute s3UsEast1RegionalEndpoint + * @return ['legacy'|'regional'] when region is set to 'us-east-1', whether to send s3 + * request to global endpoints or 'us-east-1' regional endpoints. This config is only + * applicable to S3 client; + * Defaults to 'legacy' + * * @!attribute useAccelerateEndpoint * @note This configuration option is only compatible with S3 while accessing * dns-compatible buckets. @@ -238,6 +244,10 @@ AWS.Config = AWS.util.inherit({ * @option options s3DisableBodySigning [Boolean] whether S3 body signing * should be disabled when using signature version `v4`. Body signing * can only be disabled when using https. Defaults to `true`. + * @option options s3UsEast1RegionalEndpoint ['legacy'|'regional'] when region + * is set to 'us-east-1', whether to send s3 request to global endpoints or + * 'us-east-1' regional endpoints. This config is only applicable to S3 client. + * Defaults to `legacy` * * @option options retryDelayOptions [map] A set of options to configure * the retry delay on retryable errors. Currently supported options are: @@ -523,6 +533,7 @@ AWS.Config = AWS.util.inherit({ s3ForcePathStyle: false, s3BucketEndpoint: false, s3DisableBodySigning: true, + s3UsEast1RegionalEndpoint: 'legacy', computeChecksums: true, convertResponseTypes: true, correctClockSkew: false, @@ -537,7 +548,7 @@ AWS.Config = AWS.util.inherit({ endpointDiscoveryEnabled: false, endpointCacheSize: 1000, hostPrefixEnabled: true, - stsRegionalEndpoints: null + stsRegionalEndpoints: 'legacy' }, /** diff --git a/lib/config_regional_endpoint.js b/lib/config_regional_endpoint.js new file mode 100644 index 0000000000..3c053beaac --- /dev/null +++ b/lib/config_regional_endpoint.js @@ -0,0 +1,67 @@ +var AWS = require('./core'); +/** + * @api private + */ +function validateRegionalEndpointsFlagValue(configValue, errorOptions) { + if (typeof configValue !== 'string') return undefined; + else if (['legacy', 'regional'].indexOf(configValue.toLowerCase()) >= 0) { + return configValue.toLowerCase(); + } else { + throw AWS.util.error(new Error(), errorOptions); + } +} + +/** + * Resolve the configuration value for regional endpoint from difference sources: client + * config, environmental variable, shared config file. Value can be case-insensitive + * 'legacy' or 'reginal'. + * @param originalConfig user-supplied config object to resolve + * @param options a map of config property names from individual configuration source + * - env: name of environmental variable that refers to the config + * - sharedConfig: name of shared configuration file property that refers to the config + * - clientConfig: name of client configuration property that refers to the config + * + * @api private + */ +function resolveRegionalEndpointsFlag(originalConfig, options) { + originalConfig = originalConfig || {}; + //validate config value + var resolved; + if (originalConfig[options.clientConfig]) { + resolved = validateRegionalEndpointsFlagValue(originalConfig[options.clientConfig], { + code: 'InvalidConfiguration', + message: 'invalid "' + options.clientConfig + '" configuration. Expect "legacy" ' + + ' or "regional". Got "' + originalConfig[options.clientConfig] + '".' + }); + if (resolved) return resolved; + } + if (!AWS.util.isNode()) return resolved; + //validate environmental variable + if (Object.prototype.hasOwnProperty.call(process.env, options.env)) { + var envFlag = process.env[options.env]; + resolved = validateRegionalEndpointsFlagValue(envFlag, { + code: 'InvalidEnvironmentalVariable', + message: 'invalid ' + options.env + ' environmental variable. Expect "legacy" ' + + ' or "regional". Got "' + process.env[options.env] + '".' + }); + if (resolved) return resolved; + } + //validate shared config file + var profile = {}; + try { + var profiles = AWS.util.getProfilesFromSharedConfig(AWS.util.iniLoader); + profile = profiles[process.env.AWS_PROFILE || AWS.util.defaultProfile]; + } catch (e) {}; + if (profile && Object.prototype.hasOwnProperty.call(profile, options.sharedConfig)) { + var fileFlag = profile[options.sharedConfig]; + resolved = validateRegionalEndpointsFlagValue(fileFlag, { + code: 'InvalidConfiguration', + message: 'invalid ' + options.sharedConfig + ' profile config. Expect "legacy" ' + + ' or "regional". Got "' + profile[options.sharedConfig] + '".' + }); + if (resolved) return resolved; + } + return resolved; +} + +module.exports = resolveRegionalEndpointsFlag; diff --git a/lib/services/s3.js b/lib/services/s3.js index f89b9aff6c..5f745d7c5c 100644 --- a/lib/services/s3.js +++ b/lib/services/s3.js @@ -1,5 +1,6 @@ var AWS = require('../core'); var v4Credentials = require('../signers/v4_credentials'); +var resolveRegionalEndpointsFlag = require('../config_regional_endpoint'); // Pull in managed upload extension require('../s3/managed_upload'); @@ -105,6 +106,7 @@ AWS.util.update(AWS.S3.prototype, { request.addListener('validate', this.validateBucketEndpoint); request.addListener('validate', this.correctBucketRegionFromCache); request.addListener('validate', this.validateBucketName, prependListener); + request.addListener('validate', this.optInUsEast1RegionalEndpoint, prependListener); request.addListener('build', this.addContentType); request.addListener('build', this.populateURI); @@ -189,6 +191,34 @@ AWS.util.update(AWS.S3.prototype, { return invalidOperations.indexOf(operation) === -1; }, + /** + * When us-east-1 region endpoint configuration is set, in stead of sending request to + * global endpoint(e.g. 's3.amazonaws.com'), we will send request to + * 's3.us-east-1.amazonaws.com'. + * @api private + */ + optInUsEast1RegionalEndpoint: function optInUsEast1RegionalEndpoint(req) { + var service = req.service; + var config = service.config; + config.s3UsEast1RegionalEndpoint = resolveRegionalEndpointsFlag(service._originalConfig, { + env: 'AWS_S3_US_EAST_1_REGIONAL_ENDPOINT', + sharedConfig: 's3_us_east_1_regional_endpoint', + clientConfig: 's3UsEast1RegionalEndpoint' + }); + if ( + !(service._originalConfig || {}).endpoint && + req.httpRequest.region === 'us-east-1' && + config.s3UsEast1RegionalEndpoint === 'regional' && + req.httpRequest.endpoint.hostname.indexOf('s3.amazonaws.com') >= 0 + ) { + var insertPoint = config.endpoint.indexOf('.amazonaws.com'); + regionalEndpoint = config.endpoint.substring(0, insertPoint) + + '.us-east-1' + config.endpoint.substring(insertPoint); + var endpoint = req.httpRequest.endpoint; + endpoint.hostname = regionalEndpoint; + endpoint.host = regionalEndpoint; + } + }, /** * S3 prefers dns-compatible bucket names to be moved from the uri path diff --git a/lib/services/sts.js b/lib/services/sts.js index c774de4441..4a86294587 100644 --- a/lib/services/sts.js +++ b/lib/services/sts.js @@ -1,5 +1,5 @@ var AWS = require('../core'); -var regionConfig = require('../region_config'); +var resolveRegionalEndpointsFlag = require('../config_regional_endpoint'); var ENV_REGIONAL_ENDPOINT_ENABLED = 'AWS_STS_REGIONAL_ENDPOINTS'; var CONFIG_REGIONAL_ENDPOINT_ENABLED = 'sts_regional_endpoints'; @@ -51,77 +51,38 @@ AWS.util.update(AWS.STS.prototype, { /** * @api private */ - validateRegionalEndpointsFlagValue: function validateRegionalEndpointsFlagValue(configValue, errorOptions) { - if (typeof configValue === 'string' && ['legacy', 'regional'].indexOf(configValue.toLowerCase()) >= 0) { - this.config.stsRegionalEndpoints = configValue.toLowerCase(); - return; - } else { - throw AWS.util.error(new Error(), errorOptions); - } + setupRequestListeners: function setupRequestListeners(request) { + request.addListener('validate', this.optInRegionalEndpoint, true); }, /** * @api private */ - validateRegionalEndpointsFlag: function validateRegionalEndpointsFlag() { - //validate config value - var config = this.config; - if (config.stsRegionalEndpoints) { - this.validateRegionalEndpointsFlagValue(config.stsRegionalEndpoints, { - code: 'InvalidConfiguration', - message: 'invalid "stsRegionalEndpoints" configuration. Expect "legacy" ' + - ' or "regional". Got "' + config.stsRegionalEndpoints + '".' - }); - } - if (!AWS.util.isNode()) return; - //validate environmental variable - if (Object.prototype.hasOwnProperty.call(process.env, ENV_REGIONAL_ENDPOINT_ENABLED)) { - var envFlag = process.env[ENV_REGIONAL_ENDPOINT_ENABLED]; - this.validateRegionalEndpointsFlagValue(envFlag, { - code: 'InvalidEnvironmentalVariable', - message: 'invalid ' + ENV_REGIONAL_ENDPOINT_ENABLED + ' environmental variable. Expect "legacy" ' + - ' or "regional". Got "' + process.env[ENV_REGIONAL_ENDPOINT_ENABLED] + '".' - }); - } - //validate shared config file - var profile = {}; - try { - var profiles = AWS.util.getProfilesFromSharedConfig(AWS.util.iniLoader); - profile = profiles[process.env.AWS_PROFILE || AWS.util.defaultProfile]; - } catch (e) {}; - if (profile && Object.prototype.hasOwnProperty.call(profile, CONFIG_REGIONAL_ENDPOINT_ENABLED)) { - var fileFlag = profile[CONFIG_REGIONAL_ENDPOINT_ENABLED]; - this.validateRegionalEndpointsFlagValue(fileFlag, { - code: 'InvalidConfiguration', - message: 'invalid '+CONFIG_REGIONAL_ENDPOINT_ENABLED+' profile config. Expect "legacy" ' + - ' or "regional". Got "' + profile[CONFIG_REGIONAL_ENDPOINT_ENABLED] + '".' - }); - } - }, - - /** - * @api private - */ - optInRegionalEndpoint: function optInRegionalEndpoint() { - this.validateRegionalEndpointsFlag(); - var config = this.config; - if (config.stsRegionalEndpoints === 'regional') { - regionConfig(this); - if (!this.isGlobalEndpoint) return; - this.isGlobalEndpoint = false; + optInRegionalEndpoint: function optInRegionalEndpoint(req) { + var service = req.service; + var config = service.config; + config.stsRegionalEndpoints = resolveRegionalEndpointsFlag(service._originalConfig, { + env: ENV_REGIONAL_ENDPOINT_ENABLED, + sharedConfig: CONFIG_REGIONAL_ENDPOINT_ENABLED, + clientConfig: 'stsRegionalEndpoints' + }); + if ( + config.stsRegionalEndpoints === 'regional' && + service.isGlobalEndpoint + ) { //client will throw if region is not supplied; request will be signed with specified region if (!config.region) { throw AWS.util.error(new Error(), {code: 'ConfigError', message: 'Missing region in config'}); } var insertPoint = config.endpoint.indexOf('.amazonaws.com'); - config.endpoint = config.endpoint.substring(0, insertPoint) + + regionalEndpoint = config.endpoint.substring(0, insertPoint) + '.' + config.region + config.endpoint.substring(insertPoint); + var endpoint = req.httpRequest.endpoint; + endpoint.hostname = regionalEndpoint; + endpoint.host = regionalEndpoint; + req.httpRequest.region = config.region; } - }, - - validateService: function validateService() { - this.optInRegionalEndpoint(); } }); diff --git a/scripts/region-checker/whitelist.js b/scripts/region-checker/whitelist.js index 8d5ae197ae..2d542b7f03 100644 --- a/scripts/region-checker/whitelist.js +++ b/scripts/region-checker/whitelist.js @@ -2,7 +2,11 @@ var whitelist = { '/config.js': [ 24, 25, - 187 + 85, + 86, + 193, + 247, + 248 ], '/credentials/cognito_identity_credentials.js': [ 78, @@ -25,16 +29,20 @@ var whitelist = { 316 ], '/services/s3.js': [ - 68, 69, - 515, - 517, - 516, - 636, - 647, - 648, - 649, - 654 + 70, + 194, + 196, + 209, + 215, + 545, + 546, + 547, + 666, + 677, + 678, + 684, + 679 ] }; diff --git a/test/services/s3.spec.js b/test/services/s3.spec.js index 63212e4512..95ac99748b 100644 --- a/test/services/s3.spec.js +++ b/test/services/s3.spec.js @@ -3194,4 +3194,142 @@ describe('AWS.S3', function() { }); }); }); + + describe('s3UsEast1RegionalEndpoint config', function() { + it('should set the config from client', function() { + helpers.mockHttpResponse(200, {}, '{}'); + var values = ['regional', 'RegionaL', 'legacy', 'LegacY']; + for (var i = 0; i < values.length; i++) { + var s3 = new AWS.S3({s3UsEast1RegionalEndpoint: values[i]}); + var request = s3.listBuckets().build(function() {}); + expect(request.service.config.s3UsEast1RegionalEndpoint).to.equal( + values[i].toLowerCase() + ); + } + }); + + it('should throw if the config is set to invalid values', function() { + helpers.mockHttpResponse(200, {}, '{}'); + var values = ['foo', 'bar', 'region']; + var errors = []; + for (var i = 0; i < values.length; i++) { + var s3 = new AWS.S3({s3UsEast1RegionalEndpoint: values[i]}); + s3.listBuckets().build(function(err) { + errors.push(err); + }); + } + expect(errors.length).to.equal(values.length); + for (var i = 0; i < errors.length; i++) { + expect(errors[i].code).to.equal('InvalidConfiguration'); + } + }); + + if (AWS.util.isNode()) { + describe('should set the config from AWS_S3_US_EAST_1_REGIONAL_ENDPOINT environmental variable', function() { + var originalEnv; + beforeEach(function() { + originalEnv = process.env; + process.env = {}; + }); + afterEach(function() { + process.env = originalEnv; + }); + it('should be used if client config is not set', function() { + process.env.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT = 'Regional'; + var s3 = new AWS.S3(); + s3.listBuckets().build(function() {}); + expect(s3.config.s3UsEast1RegionalEndpoint).to.equal('regional'); + process.env.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT = 'LegacY'; + s3 = new AWS.S3(); + s3.listBuckets().build(function() {}); + expect(s3.config.s3UsEast1RegionalEndpoint).to.equal('legacy'); + }); + + it('should throw if the config is set to invalid values', function() { + var values = ['foo', 'bar', 'region']; + var errors = []; + for (var i = 0; i < values.length; i++) { + process.env.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT = values[i]; + s3 = new AWS.S3(); + s3.listBuckets().build(function(err) { + errors.push(err); + }); + } + expect(errors.length).to.equal(values.length); + for (var i = 0; i < errors.length; i++) { + expect(errors[i].code).to.equal('InvalidEnvironmentalVariable'); + } + }); + }); + + describe('should set config from s3_us_east_1_regional_endpoint config file entry', function() { + it('should be used if environmental variable is not set', function() { + helpers.spyOn(AWS.util, 'getProfilesFromSharedConfig').andReturn({ + default: { + s3_us_east_1_regional_endpoint: 'RegionaL' + } + }); + var s3 = new AWS.S3(); + s3.listBuckets().build(function() {}); + expect(s3.config.s3UsEast1RegionalEndpoint).to.equal('regional'); + helpers.spyOn(AWS.util, 'getProfilesFromSharedConfig').andReturn({ + default: { + s3_us_east_1_regional_endpoint: 'LegaCy' + } + }); + var s3 = new AWS.S3(); + s3.listBuckets().build(function() {}); + expect(s3.config.s3UsEast1RegionalEndpoint).to.equal('legacy'); + }); + + it('should throw if the config is set to invalid values', function() { + var values = ['foo', 'bar', 'region']; + var errors = []; + for (var i = 0; i < values.length; i++) { + helpers.spyOn(AWS.util, 'getProfilesFromSharedConfig').andReturn({ + default: { + s3_us_east_1_regional_endpoint: values[i] + } + }); + s3 = new AWS.S3(); + s3.listBuckets().build(function(err) { + errors.push(err); + }); + } + expect(errors.length).to.equal(values.length); + for (var i = 0; i < errors.length; i++) { + expect(errors[i].code).to.equal('InvalidConfiguration'); + } + }); + }); + + describe('should construct regional endpoint correctly', function() { + it('according to config settings', function() { + var s3 = new AWS.S3({region: 'us-west-2'}); + var request = s3.listBuckets().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('s3.us-west-2.amazonaws.com'); + s3 = new AWS.S3({region: 'us-east-1'}); + var request = s3.listBuckets().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('s3.amazonaws.com'); + s3 = new AWS.S3({region: 'us-east-1', s3UsEast1RegionalEndpoint: 'regional'}); + request = s3.listBuckets().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('s3.us-east-1.amazonaws.com'); + }); + it('should use global endpoints for when config is set to legacy', function() { + s3 = new AWS.S3({region: 'us-east-1', s3UsEast1RegionalEndpoint: 'legacy'}); + request = s3.listBuckets().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('s3.amazonaws.com'); + }); + it('should not update endpoint if supplied a custom endpoint', function() { + s3 = new AWS.S3({ + region: 'us-east-1', + s3UsEast1RegionalEndpoint: 'regional', + endpoint: 's3.amazonaws.com' + }); + request = s3.listBuckets().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('s3.amazonaws.com'); + }); + }); + } + }); }); diff --git a/test/services/sts.spec.js b/test/services/sts.spec.js index 37c09de6e8..3150d7ca12 100644 --- a/test/services/sts.spec.js +++ b/test/services/sts.spec.js @@ -125,22 +125,24 @@ describe('regional endpoints', function() { describe('stsRegionalConfig client config', function() { it ('should set the service client stsRegionalConfig config', function() { + helpers.mockHttpResponse(200, {}, '{}'); var values = ['regional', 'RegionaL', 'legacy', 'LegacY']; for (var i = 0; i < values.length; i++) { var sts = new AWS.STS({stsRegionalEndpoints: values[i]}); - expect(['regional', 'legacy'].indexOf(sts.config.stsRegionalEndpoints) >= 0).to.equal(true); + var request = sts.getCallerIdentity().build(function() {}); + expect(['regional', 'legacy'].indexOf(request.service.config.stsRegionalEndpoints) >= 0).to.equal(true); } }); it('should throw if the config is set to invalid values', function() { + helpers.mockHttpResponse(200, {}, '{}'); var values = ['foo', 'bar', 'region']; var errors = []; for (var i = 0; i < values.length; i++) { - try { - new AWS.STS({stsRegionalEndpoints: values[i]}); - } catch (e) { - errors.push(e); - } + var sts = new AWS.STS({stsRegionalEndpoints: values[i]}); + sts.getCallerIdentity().build(function(err) { + errors.push(err); + }); } expect(errors.length).to.equal(values.length); for (var i = 0; i < errors.length; i++) { @@ -162,9 +164,11 @@ it('should be used if client config is not set', function() { process.env.AWS_STS_REGIONAL_ENDPOINTS = 'Regional'; var sts = new AWS.STS(); + sts.getCallerIdentity().build(function(err) {}); expect(sts.config.stsRegionalEndpoints).to.equal('regional'); process.env.AWS_STS_REGIONAL_ENDPOINTS = 'LegacY'; sts = new AWS.STS(); + sts.getCallerIdentity().build(function(err) {}); expect(sts.config.stsRegionalEndpoints).to.equal('legacy'); }); @@ -172,12 +176,11 @@ var values = ['foo', 'bar', 'region']; var errors = []; for (var i = 0; i < values.length; i++) { - try { - process.env.AWS_STS_REGIONAL_ENDPOINTS = values[i]; - new AWS.STS(); - } catch (e) { - errors.push(e); - } + process.env.AWS_STS_REGIONAL_ENDPOINTS = values[i]; + sts = new AWS.STS(); + sts.getCallerIdentity().build(function(err) { + errors.push(err); + }); } expect(errors.length).to.equal(values.length); for (var i = 0; i < errors.length; i++) { @@ -194,6 +197,7 @@ } }); var sts = new AWS.STS(); + sts.getCallerIdentity().build(function() {}); expect(sts.config.stsRegionalEndpoints).to.equal('regional'); helpers.spyOn(AWS.util, 'getProfilesFromSharedConfig').andReturn({ default: { @@ -201,6 +205,7 @@ } }); var sts = new AWS.STS(); + sts.getCallerIdentity().build(function() {}); expect(sts.config.stsRegionalEndpoints).to.equal('legacy'); }); it('should throw if the config is set to invalid values', function() { @@ -212,11 +217,10 @@ sts_regional_endpoints: values[i] } }); - try { - new AWS.STS(); - } catch (e) { - errors.push(e); - } + sts = new AWS.STS(); + sts.getCallerIdentity().build(function(err) { + errors.push(err); + }); } expect(errors.length).to.equal(values.length); for (var i = 0; i < errors.length; i++) { @@ -225,6 +229,7 @@ }); }); } + describe('service client stsRegionalConfig config', function() { var originalRegion; var originalEnv; @@ -247,36 +252,41 @@ var regions = ['us-west-2', 'ap-east-1']; for (var i = 0; i < regions.length; i++) { var sts = new AWS.STS({region: regions[i]}); - expect(sts.config.endpoint).to.contain('sts.amazonaws.com'); + var request = sts.getCallerIdentity().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('sts.amazonaws.com'); } var sts = new AWS.STS({region: 'cn-north-1'}); - expect(sts.config.endpoint).to.contain('sts.cn-north-1.amazonaws.com.cn'); + request = sts.getCallerIdentity().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('sts.cn-north-1.amazonaws.com.cn'); }); it('should use global endpoints for when config is set to legacy', function() { var regions = ['us-west-2', 'ap-east-1']; for (var i = 0; i < regions.length; i++) { var sts = new AWS.STS({region: regions[i], stsRegionalEndpoints: 'legacy'}); - expect(sts.config.endpoint).to.contain('sts.amazonaws.com'); + var request = sts.getCallerIdentity().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('sts.amazonaws.com'); } var sts = new AWS.STS({region: 'cn-north-1', stsRegionalEndpoints: 'legacy'}); - expect(sts.config.endpoint).to.contain('sts.cn-north-1.amazonaws.com.cn'); + request = sts.getCallerIdentity().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('sts.cn-north-1.amazonaws.com.cn'); }); it('should use regional endpoints for when config is set to regional', function() { var regions = ['us-west-2', 'ap-east-1']; for (var i = 0; i < regions.length; i++) { var sts = new AWS.STS({region: regions[i], stsRegionalEndpoints: 'regional'}); - expect(sts.config.endpoint).to.contain('sts.' + regions[i] + '.amazonaws.com'); + var request = sts.getCallerIdentity().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('sts.' + regions[i] + '.amazonaws.com'); } var sts = new AWS.STS({region: 'cn-north-1', stsRegionalEndpoints: 'regional'}); - expect(sts.config.endpoint).to.contain('sts.cn-north-1.amazonaws.com.cn'); + request = sts.getCallerIdentity().build(function() {}); + expect(request.httpRequest.endpoint.hostname).to.contain('sts.cn-north-1.amazonaws.com.cn'); }); it('should ask for region if stsRegionalEndpoints is set', function() { var error; - try { - new AWS.STS({stsRegionalEndpoints: 'regional'}); - } catch (e) { - error = e; - } + sts = new AWS.STS({stsRegionalEndpoints: 'regional'}); + sts.getCallerIdentity().build(function(err) { + error = err; + }); expect(error.code).to.equal('ConfigError'); expect(error.message).to.equal('Missing region in config'); });