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