1package session
2
3import (
4	"crypto/tls"
5	"crypto/x509"
6	"fmt"
7	"io"
8	"io/ioutil"
9	"net/http"
10	"os"
11	"time"
12
13	"github.com/aws/aws-sdk-go/aws"
14	"github.com/aws/aws-sdk-go/aws/awserr"
15	"github.com/aws/aws-sdk-go/aws/client"
16	"github.com/aws/aws-sdk-go/aws/corehandlers"
17	"github.com/aws/aws-sdk-go/aws/credentials"
18	"github.com/aws/aws-sdk-go/aws/csm"
19	"github.com/aws/aws-sdk-go/aws/defaults"
20	"github.com/aws/aws-sdk-go/aws/endpoints"
21	"github.com/aws/aws-sdk-go/aws/request"
22)
23
24const (
25	// ErrCodeSharedConfig represents an error that occurs in the shared
26	// configuration logic
27	ErrCodeSharedConfig = "SharedConfigErr"
28)
29
30// ErrSharedConfigSourceCollision will be returned if a section contains both
31// source_profile and credential_source
32var ErrSharedConfigSourceCollision = awserr.New(ErrCodeSharedConfig, "only source profile or credential source can be specified, not both", nil)
33
34// ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment
35// variables are empty and Environment was set as the credential source
36var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil)
37
38// ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided
39var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil)
40
41// A Session provides a central location to create service clients from and
42// store configurations and request handlers for those services.
43//
44// Sessions are safe to create service clients concurrently, but it is not safe
45// to mutate the Session concurrently.
46//
47// The Session satisfies the service client's client.ConfigProvider.
48type Session struct {
49	Config   *aws.Config
50	Handlers request.Handlers
51
52	options Options
53}
54
55// New creates a new instance of the handlers merging in the provided configs
56// on top of the SDK's default configurations. Once the Session is created it
57// can be mutated to modify the Config or Handlers. The Session is safe to be
58// read concurrently, but it should not be written to concurrently.
59//
60// If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New
61// method could now encounter an error when loading the configuration. When
62// The environment variable is set, and an error occurs, New will return a
63// session that will fail all requests reporting the error that occurred while
64// loading the session. Use NewSession to get the error when creating the
65// session.
66//
67// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
68// the shared config file (~/.aws/config) will also be loaded, in addition to
69// the shared credentials file (~/.aws/credentials). Values set in both the
70// shared config, and shared credentials will be taken from the shared
71// credentials file.
72//
73// Deprecated: Use NewSession functions to create sessions instead. NewSession
74// has the same functionality as New except an error can be returned when the
75// func is called instead of waiting to receive an error until a request is made.
76func New(cfgs ...*aws.Config) *Session {
77	// load initial config from environment
78	envCfg, envErr := loadEnvConfig()
79
80	if envCfg.EnableSharedConfig {
81		var cfg aws.Config
82		cfg.MergeIn(cfgs...)
83		s, err := NewSessionWithOptions(Options{
84			Config:            cfg,
85			SharedConfigState: SharedConfigEnable,
86		})
87		if err != nil {
88			// Old session.New expected all errors to be discovered when
89			// a request is made, and would report the errors then. This
90			// needs to be replicated if an error occurs while creating
91			// the session.
92			msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " +
93				"Use session.NewSession to handle errors occurring during session creation."
94
95			// Session creation failed, need to report the error and prevent
96			// any requests from succeeding.
97			s = &Session{Config: defaults.Config()}
98			s.logDeprecatedNewSessionError(msg, err, cfgs)
99		}
100
101		return s
102	}
103
104	s := deprecatedNewSession(envCfg, cfgs...)
105	if envErr != nil {
106		msg := "failed to load env config"
107		s.logDeprecatedNewSessionError(msg, envErr, cfgs)
108	}
109
110	if csmCfg, err := loadCSMConfig(envCfg, []string{}); err != nil {
111		if l := s.Config.Logger; l != nil {
112			l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err))
113		}
114	} else if csmCfg.Enabled {
115		err := enableCSM(&s.Handlers, csmCfg, s.Config.Logger)
116		if err != nil {
117			msg := "failed to enable CSM"
118			s.logDeprecatedNewSessionError(msg, err, cfgs)
119		}
120	}
121
122	return s
123}
124
125// NewSession returns a new Session created from SDK defaults, config files,
126// environment, and user provided config files. Once the Session is created
127// it can be mutated to modify the Config or Handlers. The Session is safe to
128// be read concurrently, but it should not be written to concurrently.
129//
130// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
131// the shared config file (~/.aws/config) will also be loaded in addition to
132// the shared credentials file (~/.aws/credentials). Values set in both the
133// shared config, and shared credentials will be taken from the shared
134// credentials file. Enabling the Shared Config will also allow the Session
135// to be built with retrieving credentials with AssumeRole set in the config.
136//
137// See the NewSessionWithOptions func for information on how to override or
138// control through code how the Session will be created, such as specifying the
139// config profile, and controlling if shared config is enabled or not.
140func NewSession(cfgs ...*aws.Config) (*Session, error) {
141	opts := Options{}
142	opts.Config.MergeIn(cfgs...)
143
144	return NewSessionWithOptions(opts)
145}
146
147// SharedConfigState provides the ability to optionally override the state
148// of the session's creation based on the shared config being enabled or
149// disabled.
150type SharedConfigState int
151
152const (
153	// SharedConfigStateFromEnv does not override any state of the
154	// AWS_SDK_LOAD_CONFIG env var. It is the default value of the
155	// SharedConfigState type.
156	SharedConfigStateFromEnv SharedConfigState = iota
157
158	// SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value
159	// and disables the shared config functionality.
160	SharedConfigDisable
161
162	// SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value
163	// and enables the shared config functionality.
164	SharedConfigEnable
165)
166
167// Options provides the means to control how a Session is created and what
168// configuration values will be loaded.
169//
170type Options struct {
171	// Provides config values for the SDK to use when creating service clients
172	// and making API requests to services. Any value set in with this field
173	// will override the associated value provided by the SDK defaults,
174	// environment or config files where relevant.
175	//
176	// If not set, configuration values from from SDK defaults, environment,
177	// config will be used.
178	Config aws.Config
179
180	// Overrides the config profile the Session should be created from. If not
181	// set the value of the environment variable will be loaded (AWS_PROFILE,
182	// or AWS_DEFAULT_PROFILE if the Shared Config is enabled).
183	//
184	// If not set and environment variables are not set the "default"
185	// (DefaultSharedConfigProfile) will be used as the profile to load the
186	// session config from.
187	Profile string
188
189	// Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG
190	// environment variable. By default a Session will be created using the
191	// value provided by the AWS_SDK_LOAD_CONFIG environment variable.
192	//
193	// Setting this value to SharedConfigEnable or SharedConfigDisable
194	// will allow you to override the AWS_SDK_LOAD_CONFIG environment variable
195	// and enable or disable the shared config functionality.
196	SharedConfigState SharedConfigState
197
198	// Ordered list of files the session will load configuration from.
199	// It will override environment variable AWS_SHARED_CREDENTIALS_FILE, AWS_CONFIG_FILE.
200	SharedConfigFiles []string
201
202	// When the SDK's shared config is configured to assume a role with MFA
203	// this option is required in order to provide the mechanism that will
204	// retrieve the MFA token. There is no default value for this field. If
205	// it is not set an error will be returned when creating the session.
206	//
207	// This token provider will be called when ever the assumed role's
208	// credentials need to be refreshed. Within the context of service clients
209	// all sharing the same session the SDK will ensure calls to the token
210	// provider are atomic. When sharing a token provider across multiple
211	// sessions additional synchronization logic is needed to ensure the
212	// token providers do not introduce race conditions. It is recommend to
213	// share the session where possible.
214	//
215	// stscreds.StdinTokenProvider is a basic implementation that will prompt
216	// from stdin for the MFA token code.
217	//
218	// This field is only used if the shared configuration is enabled, and
219	// the config enables assume role wit MFA via the mfa_serial field.
220	AssumeRoleTokenProvider func() (string, error)
221
222	// When the SDK's shared config is configured to assume a role this option
223	// may be provided to set the expiry duration of the STS credentials.
224	// Defaults to 15 minutes if not set as documented in the
225	// stscreds.AssumeRoleProvider.
226	AssumeRoleDuration time.Duration
227
228	// Reader for a custom Credentials Authority (CA) bundle in PEM format that
229	// the SDK will use instead of the default system's root CA bundle. Use this
230	// only if you want to replace the CA bundle the SDK uses for TLS requests.
231	//
232	// Enabling this option will attempt to merge the Transport into the SDK's HTTP
233	// client. If the client's Transport is not a http.Transport an error will be
234	// returned. If the Transport's TLS config is set this option will cause the SDK
235	// to overwrite the Transport's TLS config's  RootCAs value. If the CA
236	// bundle reader contains multiple certificates all of them will be loaded.
237	//
238	// The Session option CustomCABundle is also available when creating sessions
239	// to also enable this feature. CustomCABundle session option field has priority
240	// over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
241	CustomCABundle io.Reader
242
243	// The handlers that the session and all API clients will be created with.
244	// This must be a complete set of handlers. Use the defaults.Handlers()
245	// function to initialize this value before changing the handlers to be
246	// used by the SDK.
247	Handlers request.Handlers
248
249	// Allows specifying a custom endpoint to be used by the EC2 IMDS client
250	// when making requests to the EC2 IMDS API. The must endpoint value must
251	// include protocol prefix.
252	//
253	// If unset, will the EC2 IMDS client will use its default endpoint.
254	//
255	// Can also be specified via the environment variable,
256	// AWS_EC2_METADATA_SERVICE_ENDPOINT.
257	//
258	//   AWS_EC2_METADATA_SERVICE_ENDPOINT=http://169.254.169.254
259	//
260	// If using an URL with an IPv6 address literal, the IPv6 address
261	// component must be enclosed in square brackets.
262	//
263	//   AWS_EC2_METADATA_SERVICE_ENDPOINT=http://[::1]
264	EC2IMDSEndpoint string
265}
266
267// NewSessionWithOptions returns a new Session created from SDK defaults, config files,
268// environment, and user provided config files. This func uses the Options
269// values to configure how the Session is created.
270//
271// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
272// the shared config file (~/.aws/config) will also be loaded in addition to
273// the shared credentials file (~/.aws/credentials). Values set in both the
274// shared config, and shared credentials will be taken from the shared
275// credentials file. Enabling the Shared Config will also allow the Session
276// to be built with retrieving credentials with AssumeRole set in the config.
277//
278//     // Equivalent to session.New
279//     sess := session.Must(session.NewSessionWithOptions(session.Options{}))
280//
281//     // Specify profile to load for the session's config
282//     sess := session.Must(session.NewSessionWithOptions(session.Options{
283//          Profile: "profile_name",
284//     }))
285//
286//     // Specify profile for config and region for requests
287//     sess := session.Must(session.NewSessionWithOptions(session.Options{
288//          Config: aws.Config{Region: aws.String("us-east-1")},
289//          Profile: "profile_name",
290//     }))
291//
292//     // Force enable Shared Config support
293//     sess := session.Must(session.NewSessionWithOptions(session.Options{
294//         SharedConfigState: session.SharedConfigEnable,
295//     }))
296func NewSessionWithOptions(opts Options) (*Session, error) {
297	var envCfg envConfig
298	var err error
299	if opts.SharedConfigState == SharedConfigEnable {
300		envCfg, err = loadSharedEnvConfig()
301		if err != nil {
302			return nil, fmt.Errorf("failed to load shared config, %v", err)
303		}
304	} else {
305		envCfg, err = loadEnvConfig()
306		if err != nil {
307			return nil, fmt.Errorf("failed to load environment config, %v", err)
308		}
309	}
310
311	if len(opts.Profile) != 0 {
312		envCfg.Profile = opts.Profile
313	}
314
315	switch opts.SharedConfigState {
316	case SharedConfigDisable:
317		envCfg.EnableSharedConfig = false
318	case SharedConfigEnable:
319		envCfg.EnableSharedConfig = true
320	}
321
322	// Only use AWS_CA_BUNDLE if session option is not provided.
323	if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil {
324		f, err := os.Open(envCfg.CustomCABundle)
325		if err != nil {
326			return nil, awserr.New("LoadCustomCABundleError",
327				"failed to open custom CA bundle PEM file", err)
328		}
329		defer f.Close()
330		opts.CustomCABundle = f
331	}
332
333	return newSession(opts, envCfg, &opts.Config)
334}
335
336// Must is a helper function to ensure the Session is valid and there was no
337// error when calling a NewSession function.
338//
339// This helper is intended to be used in variable initialization to load the
340// Session and configuration at startup. Such as:
341//
342//     var sess = session.Must(session.NewSession())
343func Must(sess *Session, err error) *Session {
344	if err != nil {
345		panic(err)
346	}
347
348	return sess
349}
350
351// Wraps the endpoint resolver with a resolver that will return a custom
352// endpoint for EC2 IMDS.
353func wrapEC2IMDSEndpoint(resolver endpoints.Resolver, endpoint string) endpoints.Resolver {
354	return endpoints.ResolverFunc(
355		func(service, region string, opts ...func(*endpoints.Options)) (
356			endpoints.ResolvedEndpoint, error,
357		) {
358			if service == ec2MetadataServiceID {
359				return endpoints.ResolvedEndpoint{
360					URL:           endpoint,
361					SigningName:   ec2MetadataServiceID,
362					SigningRegion: region,
363				}, nil
364			}
365			return resolver.EndpointFor(service, region)
366		})
367}
368
369func deprecatedNewSession(envCfg envConfig, cfgs ...*aws.Config) *Session {
370	cfg := defaults.Config()
371	handlers := defaults.Handlers()
372
373	// Apply the passed in configs so the configuration can be applied to the
374	// default credential chain
375	cfg.MergeIn(cfgs...)
376	if cfg.EndpointResolver == nil {
377		// An endpoint resolver is required for a session to be able to provide
378		// endpoints for service client configurations.
379		cfg.EndpointResolver = endpoints.DefaultResolver()
380	}
381
382	if len(envCfg.EC2IMDSEndpoint) != 0 {
383		cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, envCfg.EC2IMDSEndpoint)
384	}
385
386	cfg.Credentials = defaults.CredChain(cfg, handlers)
387
388	// Reapply any passed in configs to override credentials if set
389	cfg.MergeIn(cfgs...)
390
391	s := &Session{
392		Config:   cfg,
393		Handlers: handlers,
394		options: Options{
395			EC2IMDSEndpoint: envCfg.EC2IMDSEndpoint,
396		},
397	}
398
399	initHandlers(s)
400	return s
401}
402
403func enableCSM(handlers *request.Handlers, cfg csmConfig, logger aws.Logger) error {
404	if logger != nil {
405		logger.Log("Enabling CSM")
406	}
407
408	r, err := csm.Start(cfg.ClientID, csm.AddressWithDefaults(cfg.Host, cfg.Port))
409	if err != nil {
410		return err
411	}
412	r.InjectHandlers(handlers)
413
414	return nil
415}
416
417func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
418	cfg := defaults.Config()
419
420	handlers := opts.Handlers
421	if handlers.IsEmpty() {
422		handlers = defaults.Handlers()
423	}
424
425	// Get a merged version of the user provided config to determine if
426	// credentials were.
427	userCfg := &aws.Config{}
428	userCfg.MergeIn(cfgs...)
429	cfg.MergeIn(userCfg)
430
431	// Ordered config files will be loaded in with later files overwriting
432	// previous config file values.
433	var cfgFiles []string
434	if opts.SharedConfigFiles != nil {
435		cfgFiles = opts.SharedConfigFiles
436	} else {
437		cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
438		if !envCfg.EnableSharedConfig {
439			// The shared config file (~/.aws/config) is only loaded if instructed
440			// to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
441			cfgFiles = cfgFiles[1:]
442		}
443	}
444
445	// Load additional config from file(s)
446	sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig)
447	if err != nil {
448		if len(envCfg.Profile) == 0 && !envCfg.EnableSharedConfig && (envCfg.Creds.HasKeys() || userCfg.Credentials != nil) {
449			// Special case where the user has not explicitly specified an AWS_PROFILE,
450			// or session.Options.profile, shared config is not enabled, and the
451			// environment has credentials, allow the shared config file to fail to
452			// load since the user has already provided credentials, and nothing else
453			// is required to be read file. Github(aws/aws-sdk-go#2455)
454		} else if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
455			return nil, err
456		}
457	}
458
459	if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil {
460		return nil, err
461	}
462
463	s := &Session{
464		Config:   cfg,
465		Handlers: handlers,
466		options:  opts,
467	}
468
469	initHandlers(s)
470
471	if csmCfg, err := loadCSMConfig(envCfg, cfgFiles); err != nil {
472		if l := s.Config.Logger; l != nil {
473			l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err))
474		}
475	} else if csmCfg.Enabled {
476		err = enableCSM(&s.Handlers, csmCfg, s.Config.Logger)
477		if err != nil {
478			return nil, err
479		}
480	}
481
482	// Setup HTTP client with custom cert bundle if enabled
483	if opts.CustomCABundle != nil {
484		if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil {
485			return nil, err
486		}
487	}
488
489	return s, nil
490}
491
492type csmConfig struct {
493	Enabled  bool
494	Host     string
495	Port     string
496	ClientID string
497}
498
499var csmProfileName = "aws_csm"
500
501func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) {
502	if envCfg.CSMEnabled != nil {
503		if *envCfg.CSMEnabled {
504			return csmConfig{
505				Enabled:  true,
506				ClientID: envCfg.CSMClientID,
507				Host:     envCfg.CSMHost,
508				Port:     envCfg.CSMPort,
509			}, nil
510		}
511		return csmConfig{}, nil
512	}
513
514	sharedCfg, err := loadSharedConfig(csmProfileName, cfgFiles, false)
515	if err != nil {
516		if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
517			return csmConfig{}, err
518		}
519	}
520	if sharedCfg.CSMEnabled != nil && *sharedCfg.CSMEnabled == true {
521		return csmConfig{
522			Enabled:  true,
523			ClientID: sharedCfg.CSMClientID,
524			Host:     sharedCfg.CSMHost,
525			Port:     sharedCfg.CSMPort,
526		}, nil
527	}
528
529	return csmConfig{}, nil
530}
531
532func loadCustomCABundle(s *Session, bundle io.Reader) error {
533	var t *http.Transport
534	switch v := s.Config.HTTPClient.Transport.(type) {
535	case *http.Transport:
536		t = v
537	default:
538		if s.Config.HTTPClient.Transport != nil {
539			return awserr.New("LoadCustomCABundleError",
540				"unable to load custom CA bundle, HTTPClient's transport unsupported type", nil)
541		}
542	}
543	if t == nil {
544		// Nil transport implies `http.DefaultTransport` should be used. Since
545		// the SDK cannot modify, nor copy the `DefaultTransport` specifying
546		// the values the next closest behavior.
547		t = getCABundleTransport()
548	}
549
550	p, err := loadCertPool(bundle)
551	if err != nil {
552		return err
553	}
554	if t.TLSClientConfig == nil {
555		t.TLSClientConfig = &tls.Config{}
556	}
557	t.TLSClientConfig.RootCAs = p
558
559	s.Config.HTTPClient.Transport = t
560
561	return nil
562}
563
564func loadCertPool(r io.Reader) (*x509.CertPool, error) {
565	b, err := ioutil.ReadAll(r)
566	if err != nil {
567		return nil, awserr.New("LoadCustomCABundleError",
568			"failed to read custom CA bundle PEM file", err)
569	}
570
571	p := x509.NewCertPool()
572	if !p.AppendCertsFromPEM(b) {
573		return nil, awserr.New("LoadCustomCABundleError",
574			"failed to load custom CA bundle PEM file", err)
575	}
576
577	return p, nil
578}
579
580func mergeConfigSrcs(cfg, userCfg *aws.Config,
581	envCfg envConfig, sharedCfg sharedConfig,
582	handlers request.Handlers,
583	sessOpts Options,
584) error {
585
586	// Region if not already set by user
587	if len(aws.StringValue(cfg.Region)) == 0 {
588		if len(envCfg.Region) > 0 {
589			cfg.WithRegion(envCfg.Region)
590		} else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
591			cfg.WithRegion(sharedCfg.Region)
592		}
593	}
594
595	if cfg.EnableEndpointDiscovery == nil {
596		if envCfg.EnableEndpointDiscovery != nil {
597			cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery)
598		} else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil {
599			cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery)
600		}
601	}
602
603	// Regional Endpoint flag for STS endpoint resolving
604	mergeSTSRegionalEndpointConfig(cfg, []endpoints.STSRegionalEndpoint{
605		userCfg.STSRegionalEndpoint,
606		envCfg.STSRegionalEndpoint,
607		sharedCfg.STSRegionalEndpoint,
608		endpoints.LegacySTSEndpoint,
609	})
610
611	// Regional Endpoint flag for S3 endpoint resolving
612	mergeS3UsEast1RegionalEndpointConfig(cfg, []endpoints.S3UsEast1RegionalEndpoint{
613		userCfg.S3UsEast1RegionalEndpoint,
614		envCfg.S3UsEast1RegionalEndpoint,
615		sharedCfg.S3UsEast1RegionalEndpoint,
616		endpoints.LegacyS3UsEast1Endpoint,
617	})
618
619	ec2IMDSEndpoint := sessOpts.EC2IMDSEndpoint
620	if len(ec2IMDSEndpoint) == 0 {
621		ec2IMDSEndpoint = envCfg.EC2IMDSEndpoint
622	}
623	if len(ec2IMDSEndpoint) != 0 {
624		cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, ec2IMDSEndpoint)
625	}
626
627	// Configure credentials if not already set by the user when creating the
628	// Session.
629	if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
630		creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts)
631		if err != nil {
632			return err
633		}
634		cfg.Credentials = creds
635	}
636
637	cfg.S3UseARNRegion = userCfg.S3UseARNRegion
638	if cfg.S3UseARNRegion == nil {
639		cfg.S3UseARNRegion = &envCfg.S3UseARNRegion
640	}
641	if cfg.S3UseARNRegion == nil {
642		cfg.S3UseARNRegion = &sharedCfg.S3UseARNRegion
643	}
644
645	return nil
646}
647
648func mergeSTSRegionalEndpointConfig(cfg *aws.Config, values []endpoints.STSRegionalEndpoint) {
649	for _, v := range values {
650		if v != endpoints.UnsetSTSEndpoint {
651			cfg.STSRegionalEndpoint = v
652			break
653		}
654	}
655}
656
657func mergeS3UsEast1RegionalEndpointConfig(cfg *aws.Config, values []endpoints.S3UsEast1RegionalEndpoint) {
658	for _, v := range values {
659		if v != endpoints.UnsetS3UsEast1Endpoint {
660			cfg.S3UsEast1RegionalEndpoint = v
661			break
662		}
663	}
664}
665
666func initHandlers(s *Session) {
667	// Add the Validate parameter handler if it is not disabled.
668	s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
669	if !aws.BoolValue(s.Config.DisableParamValidation) {
670		s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
671	}
672}
673
674// Copy creates and returns a copy of the current Session, copying the config
675// and handlers. If any additional configs are provided they will be merged
676// on top of the Session's copied config.
677//
678//     // Create a copy of the current Session, configured for the us-west-2 region.
679//     sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
680func (s *Session) Copy(cfgs ...*aws.Config) *Session {
681	newSession := &Session{
682		Config:   s.Config.Copy(cfgs...),
683		Handlers: s.Handlers.Copy(),
684		options:  s.options,
685	}
686
687	initHandlers(newSession)
688
689	return newSession
690}
691
692// ClientConfig satisfies the client.ConfigProvider interface and is used to
693// configure the service client instances. Passing the Session to the service
694// client's constructor (New) will use this method to configure the client.
695func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Config {
696	s = s.Copy(cfgs...)
697
698	region := aws.StringValue(s.Config.Region)
699	resolved, err := s.resolveEndpoint(service, region, s.Config)
700	if err != nil {
701		s.Handlers.Validate.PushBack(func(r *request.Request) {
702			if len(r.ClientInfo.Endpoint) != 0 {
703				// Error occurred while resolving endpoint, but the request
704				// being invoked has had an endpoint specified after the client
705				// was created.
706				return
707			}
708			r.Error = err
709		})
710	}
711
712	return client.Config{
713		Config:             s.Config,
714		Handlers:           s.Handlers,
715		PartitionID:        resolved.PartitionID,
716		Endpoint:           resolved.URL,
717		SigningRegion:      resolved.SigningRegion,
718		SigningNameDerived: resolved.SigningNameDerived,
719		SigningName:        resolved.SigningName,
720	}
721}
722
723const ec2MetadataServiceID = "ec2metadata"
724
725func (s *Session) resolveEndpoint(service, region string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) {
726
727	if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 {
728		return endpoints.ResolvedEndpoint{
729			URL:           endpoints.AddScheme(ep, aws.BoolValue(cfg.DisableSSL)),
730			SigningRegion: region,
731		}, nil
732	}
733
734	resolved, err := cfg.EndpointResolver.EndpointFor(service, region,
735		func(opt *endpoints.Options) {
736			opt.DisableSSL = aws.BoolValue(cfg.DisableSSL)
737			opt.UseDualStack = aws.BoolValue(cfg.UseDualStack)
738			// Support for STSRegionalEndpoint where the STSRegionalEndpoint is
739			// provided in envConfig or sharedConfig with envConfig getting
740			// precedence.
741			opt.STSRegionalEndpoint = cfg.STSRegionalEndpoint
742
743			// Support for S3UsEast1RegionalEndpoint where the S3UsEast1RegionalEndpoint is
744			// provided in envConfig or sharedConfig with envConfig getting
745			// precedence.
746			opt.S3UsEast1RegionalEndpoint = cfg.S3UsEast1RegionalEndpoint
747
748			// Support the condition where the service is modeled but its
749			// endpoint metadata is not available.
750			opt.ResolveUnknownService = true
751		},
752	)
753	if err != nil {
754		return endpoints.ResolvedEndpoint{}, err
755	}
756
757	return resolved, nil
758}
759
760// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception
761// that the EndpointResolver will not be used to resolve the endpoint. The only
762// endpoint set must come from the aws.Config.Endpoint field.
763func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config {
764	s = s.Copy(cfgs...)
765
766	var resolved endpoints.ResolvedEndpoint
767	if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 {
768		resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL))
769		resolved.SigningRegion = aws.StringValue(s.Config.Region)
770	}
771
772	return client.Config{
773		Config:             s.Config,
774		Handlers:           s.Handlers,
775		Endpoint:           resolved.URL,
776		SigningRegion:      resolved.SigningRegion,
777		SigningNameDerived: resolved.SigningNameDerived,
778		SigningName:        resolved.SigningName,
779	}
780}
781
782// logDeprecatedNewSessionError function enables error handling for session
783func (s *Session) logDeprecatedNewSessionError(msg string, err error, cfgs []*aws.Config) {
784	// Session creation failed, need to report the error and prevent
785	// any requests from succeeding.
786	s.Config.MergeIn(cfgs...)
787	s.Config.Logger.Log("ERROR:", msg, "Error:", err)
788	s.Handlers.Validate.PushBack(func(r *request.Request) {
789		r.Error = err
790	})
791}
792