...

Source file src/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/endpoints.go

Documentation: github.com/aws/aws-sdk-go-v2/internal/endpoints/v2

     1  package endpoints
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/aws/smithy-go/logging"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/aws/aws-sdk-go-v2/aws"
    10  )
    11  
    12  // DefaultKey is a compound map key of a variant and other values.
    13  type DefaultKey struct {
    14  	Variant        EndpointVariant
    15  	ServiceVariant ServiceVariant
    16  }
    17  
    18  // EndpointKey is a compound map key of a region and associated variant value.
    19  type EndpointKey struct {
    20  	Region         string
    21  	Variant        EndpointVariant
    22  	ServiceVariant ServiceVariant
    23  }
    24  
    25  // EndpointVariant is a bit field to describe the endpoints attributes.
    26  type EndpointVariant uint64
    27  
    28  const (
    29  	// FIPSVariant indicates that the endpoint is FIPS capable.
    30  	FIPSVariant EndpointVariant = 1 << (64 - 1 - iota)
    31  
    32  	// DualStackVariant indicates that the endpoint is DualStack capable.
    33  	DualStackVariant
    34  )
    35  
    36  // ServiceVariant is a bit field to describe the service endpoint attributes.
    37  type ServiceVariant uint64
    38  
    39  const (
    40  	defaultProtocol = "https"
    41  	defaultSigner   = "v4"
    42  )
    43  
    44  var (
    45  	protocolPriority = []string{"https", "http"}
    46  	signerPriority   = []string{"v4", "s3v4"}
    47  )
    48  
    49  // Options provide configuration needed to direct how endpoints are resolved.
    50  type Options struct {
    51  	// Logger is a logging implementation that log events should be sent to.
    52  	Logger logging.Logger
    53  
    54  	// LogDeprecated indicates that deprecated endpoints should be logged to the provided logger.
    55  	LogDeprecated bool
    56  
    57  	// ResolvedRegion is the resolved region string. If provided (non-zero length) it takes priority
    58  	// over the region name passed to the ResolveEndpoint call.
    59  	ResolvedRegion string
    60  
    61  	// Disable usage of HTTPS (TLS / SSL)
    62  	DisableHTTPS bool
    63  
    64  	// Instruct the resolver to use a service endpoint that supports dual-stack.
    65  	// If a service does not have a dual-stack endpoint an error will be returned by the resolver.
    66  	UseDualStackEndpoint aws.DualStackEndpointState
    67  
    68  	// Instruct the resolver to use a service endpoint that supports FIPS.
    69  	// If a service does not have a FIPS endpoint an error will be returned by the resolver.
    70  	UseFIPSEndpoint aws.FIPSEndpointState
    71  
    72  	// ServiceVariant is a bitfield of service specified endpoint variant data.
    73  	ServiceVariant ServiceVariant
    74  }
    75  
    76  // GetEndpointVariant returns the EndpointVariant for the variant associated options.
    77  func (o Options) GetEndpointVariant() (v EndpointVariant) {
    78  	if o.UseDualStackEndpoint == aws.DualStackEndpointStateEnabled {
    79  		v |= DualStackVariant
    80  	}
    81  	if o.UseFIPSEndpoint == aws.FIPSEndpointStateEnabled {
    82  		v |= FIPSVariant
    83  	}
    84  	return v
    85  }
    86  
    87  // Partitions is a slice of partition
    88  type Partitions []Partition
    89  
    90  // ResolveEndpoint resolves a service endpoint for the given region and options.
    91  func (ps Partitions) ResolveEndpoint(region string, opts Options) (aws.Endpoint, error) {
    92  	if len(ps) == 0 {
    93  		return aws.Endpoint{}, fmt.Errorf("no partitions found")
    94  	}
    95  
    96  	if opts.Logger == nil {
    97  		opts.Logger = logging.Nop{}
    98  	}
    99  
   100  	if len(opts.ResolvedRegion) > 0 {
   101  		region = opts.ResolvedRegion
   102  	}
   103  
   104  	for i := 0; i < len(ps); i++ {
   105  		if !ps[i].canResolveEndpoint(region, opts) {
   106  			continue
   107  		}
   108  
   109  		return ps[i].ResolveEndpoint(region, opts)
   110  	}
   111  
   112  	// fallback to first partition format to use when resolving the endpoint.
   113  	return ps[0].ResolveEndpoint(region, opts)
   114  }
   115  
   116  // Partition is an AWS partition description for a service and its' region endpoints.
   117  type Partition struct {
   118  	ID                string
   119  	RegionRegex       *regexp.Regexp
   120  	PartitionEndpoint string
   121  	IsRegionalized    bool
   122  	Defaults          map[DefaultKey]Endpoint
   123  	Endpoints         Endpoints
   124  }
   125  
   126  func (p Partition) canResolveEndpoint(region string, opts Options) bool {
   127  	_, ok := p.Endpoints[EndpointKey{
   128  		Region:  region,
   129  		Variant: opts.GetEndpointVariant(),
   130  	}]
   131  	return ok || p.RegionRegex.MatchString(region)
   132  }
   133  
   134  // ResolveEndpoint resolves and service endpoint for the given region and options.
   135  func (p Partition) ResolveEndpoint(region string, options Options) (resolved aws.Endpoint, err error) {
   136  	if len(region) == 0 && len(p.PartitionEndpoint) != 0 {
   137  		region = p.PartitionEndpoint
   138  	}
   139  
   140  	endpoints := p.Endpoints
   141  
   142  	variant := options.GetEndpointVariant()
   143  	serviceVariant := options.ServiceVariant
   144  
   145  	defaults := p.Defaults[DefaultKey{
   146  		Variant:        variant,
   147  		ServiceVariant: serviceVariant,
   148  	}]
   149  
   150  	return p.endpointForRegion(region, variant, serviceVariant, endpoints).resolve(p.ID, region, defaults, options)
   151  }
   152  
   153  func (p Partition) endpointForRegion(region string, variant EndpointVariant, serviceVariant ServiceVariant, endpoints Endpoints) Endpoint {
   154  	key := EndpointKey{
   155  		Region:  region,
   156  		Variant: variant,
   157  	}
   158  
   159  	if e, ok := endpoints[key]; ok {
   160  		return e
   161  	}
   162  
   163  	if !p.IsRegionalized {
   164  		return endpoints[EndpointKey{
   165  			Region:         p.PartitionEndpoint,
   166  			Variant:        variant,
   167  			ServiceVariant: serviceVariant,
   168  		}]
   169  	}
   170  
   171  	// Unable to find any matching endpoint, return
   172  	// blank that will be used for generic endpoint creation.
   173  	return Endpoint{}
   174  }
   175  
   176  // Endpoints is a map of service config regions to endpoints
   177  type Endpoints map[EndpointKey]Endpoint
   178  
   179  // CredentialScope is the credential scope of a region and service
   180  type CredentialScope struct {
   181  	Region  string
   182  	Service string
   183  }
   184  
   185  // Endpoint is a service endpoint description
   186  type Endpoint struct {
   187  	// True if the endpoint cannot be resolved for this partition/region/service
   188  	Unresolveable aws.Ternary
   189  
   190  	Hostname  string
   191  	Protocols []string
   192  
   193  	CredentialScope CredentialScope
   194  
   195  	SignatureVersions []string
   196  
   197  	// Indicates that this endpoint is deprecated.
   198  	Deprecated aws.Ternary
   199  }
   200  
   201  // IsZero returns whether the endpoint structure is an empty (zero) value.
   202  func (e Endpoint) IsZero() bool {
   203  	switch {
   204  	case e.Unresolveable != aws.UnknownTernary:
   205  		return false
   206  	case len(e.Hostname) != 0:
   207  		return false
   208  	case len(e.Protocols) != 0:
   209  		return false
   210  	case e.CredentialScope != (CredentialScope{}):
   211  		return false
   212  	case len(e.SignatureVersions) != 0:
   213  		return false
   214  	}
   215  	return true
   216  }
   217  
   218  func (e Endpoint) resolve(partition, region string, def Endpoint, options Options) (aws.Endpoint, error) {
   219  	var merged Endpoint
   220  	merged.mergeIn(def)
   221  	merged.mergeIn(e)
   222  	e = merged
   223  
   224  	if e.IsZero() {
   225  		return aws.Endpoint{}, fmt.Errorf("unable to resolve endpoint for region: %v", region)
   226  	}
   227  
   228  	var u string
   229  	if e.Unresolveable != aws.TrueTernary {
   230  		// Only attempt to resolve the endpoint if it can be resolved.
   231  		hostname := strings.Replace(e.Hostname, "{region}", region, 1)
   232  
   233  		scheme := getEndpointScheme(e.Protocols, options.DisableHTTPS)
   234  		u = scheme + "://" + hostname
   235  	}
   236  
   237  	signingRegion := e.CredentialScope.Region
   238  	if len(signingRegion) == 0 {
   239  		signingRegion = region
   240  	}
   241  	signingName := e.CredentialScope.Service
   242  
   243  	if e.Deprecated == aws.TrueTernary && options.LogDeprecated {
   244  		options.Logger.Logf(logging.Warn, "endpoint identifier %q, url %q marked as deprecated", region, u)
   245  	}
   246  
   247  	return aws.Endpoint{
   248  		URL:           u,
   249  		PartitionID:   partition,
   250  		SigningRegion: signingRegion,
   251  		SigningName:   signingName,
   252  		SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
   253  	}, nil
   254  }
   255  
   256  func (e *Endpoint) mergeIn(other Endpoint) {
   257  	if other.Unresolveable != aws.UnknownTernary {
   258  		e.Unresolveable = other.Unresolveable
   259  	}
   260  	if len(other.Hostname) > 0 {
   261  		e.Hostname = other.Hostname
   262  	}
   263  	if len(other.Protocols) > 0 {
   264  		e.Protocols = other.Protocols
   265  	}
   266  	if len(other.CredentialScope.Region) > 0 {
   267  		e.CredentialScope.Region = other.CredentialScope.Region
   268  	}
   269  	if len(other.CredentialScope.Service) > 0 {
   270  		e.CredentialScope.Service = other.CredentialScope.Service
   271  	}
   272  	if len(other.SignatureVersions) > 0 {
   273  		e.SignatureVersions = other.SignatureVersions
   274  	}
   275  	if other.Deprecated != aws.UnknownTernary {
   276  		e.Deprecated = other.Deprecated
   277  	}
   278  }
   279  
   280  func getEndpointScheme(protocols []string, disableHTTPS bool) string {
   281  	if disableHTTPS {
   282  		return "http"
   283  	}
   284  
   285  	return getByPriority(protocols, protocolPriority, defaultProtocol)
   286  }
   287  
   288  func getByPriority(s []string, p []string, def string) string {
   289  	if len(s) == 0 {
   290  		return def
   291  	}
   292  
   293  	for i := 0; i < len(p); i++ {
   294  		for j := 0; j < len(s); j++ {
   295  			if s[j] == p[i] {
   296  				return s[j]
   297  			}
   298  		}
   299  	}
   300  
   301  	return s[0]
   302  }
   303  

View as plain text