endpoints.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. package endpoints
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "github.com/aws/aws-sdk-go-v2/aws"
  7. )
  8. const (
  9. defaultProtocol = "https"
  10. defaultSigner = "v4"
  11. )
  12. var (
  13. protocolPriority = []string{"https", "http"}
  14. signerPriority = []string{"v4"}
  15. )
  16. // Options provide configuration needed to direct how endpoints are resolved.
  17. type Options struct {
  18. // Disable usage of HTTPS (TLS / SSL)
  19. DisableHTTPS bool
  20. }
  21. // Partitions is a slice of partition
  22. type Partitions []Partition
  23. // ResolveEndpoint resolves a service endpoint for the given region and options.
  24. func (ps Partitions) ResolveEndpoint(region string, opts Options) (aws.Endpoint, error) {
  25. if len(ps) == 0 {
  26. return aws.Endpoint{}, fmt.Errorf("no partitions found")
  27. }
  28. for i := 0; i < len(ps); i++ {
  29. if !ps[i].canResolveEndpoint(region) {
  30. continue
  31. }
  32. return ps[i].ResolveEndpoint(region, opts)
  33. }
  34. // fallback to first partition format to use when resolving the endpoint.
  35. return ps[0].ResolveEndpoint(region, opts)
  36. }
  37. // Partition is an AWS partition description for a service and its' region endpoints.
  38. type Partition struct {
  39. ID string
  40. RegionRegex *regexp.Regexp
  41. PartitionEndpoint string
  42. IsRegionalized bool
  43. Defaults Endpoint
  44. Endpoints Endpoints
  45. }
  46. func (p Partition) canResolveEndpoint(region string) bool {
  47. _, ok := p.Endpoints[region]
  48. return ok || p.RegionRegex.MatchString(region)
  49. }
  50. // ResolveEndpoint resolves and service endpoint for the given region and options.
  51. func (p Partition) ResolveEndpoint(region string, options Options) (resolved aws.Endpoint, err error) {
  52. if len(region) == 0 && len(p.PartitionEndpoint) != 0 {
  53. region = p.PartitionEndpoint
  54. }
  55. e, _ := p.endpointForRegion(region)
  56. return e.resolve(p.ID, region, p.Defaults, options), nil
  57. }
  58. func (p Partition) endpointForRegion(region string) (Endpoint, bool) {
  59. if e, ok := p.Endpoints[region]; ok {
  60. return e, true
  61. }
  62. if !p.IsRegionalized {
  63. return p.Endpoints[p.PartitionEndpoint], region == p.PartitionEndpoint
  64. }
  65. // Unable to find any matching endpoint, return
  66. // blank that will be used for generic endpoint creation.
  67. return Endpoint{}, false
  68. }
  69. // Endpoints is a map of service config regions to endpoints
  70. type Endpoints map[string]Endpoint
  71. // CredentialScope is the credential scope of a region and service
  72. type CredentialScope struct {
  73. Region string
  74. Service string
  75. }
  76. // Endpoint is a service endpoint description
  77. type Endpoint struct {
  78. // True if the endpoint cannot be resolved for this partition/region/service
  79. Unresolveable aws.Ternary
  80. Hostname string
  81. Protocols []string
  82. CredentialScope CredentialScope
  83. SignatureVersions []string `json:"signatureVersions"`
  84. }
  85. func (e Endpoint) resolve(partition, region string, def Endpoint, options Options) aws.Endpoint {
  86. var merged Endpoint
  87. merged.mergeIn(def)
  88. merged.mergeIn(e)
  89. e = merged
  90. var u string
  91. if e.Unresolveable != aws.TrueTernary {
  92. // Only attempt to resolve the endpoint if it can be resolved.
  93. hostname := strings.Replace(e.Hostname, "{region}", region, 1)
  94. scheme := getEndpointScheme(e.Protocols, options.DisableHTTPS)
  95. u = scheme + "://" + hostname
  96. }
  97. signingRegion := e.CredentialScope.Region
  98. if len(signingRegion) == 0 {
  99. signingRegion = region
  100. }
  101. signingName := e.CredentialScope.Service
  102. return aws.Endpoint{
  103. URL: u,
  104. PartitionID: partition,
  105. SigningRegion: signingRegion,
  106. SigningName: signingName,
  107. SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
  108. }
  109. }
  110. func (e *Endpoint) mergeIn(other Endpoint) {
  111. if other.Unresolveable != aws.UnknownTernary {
  112. e.Unresolveable = other.Unresolveable
  113. }
  114. if len(other.Hostname) > 0 {
  115. e.Hostname = other.Hostname
  116. }
  117. if len(other.Protocols) > 0 {
  118. e.Protocols = other.Protocols
  119. }
  120. if len(other.CredentialScope.Region) > 0 {
  121. e.CredentialScope.Region = other.CredentialScope.Region
  122. }
  123. if len(other.CredentialScope.Service) > 0 {
  124. e.CredentialScope.Service = other.CredentialScope.Service
  125. }
  126. if len(other.SignatureVersions) > 0 {
  127. e.SignatureVersions = other.SignatureVersions
  128. }
  129. }
  130. func getEndpointScheme(protocols []string, disableHTTPS bool) string {
  131. if disableHTTPS {
  132. return "http"
  133. }
  134. return getByPriority(protocols, protocolPriority, defaultProtocol)
  135. }
  136. func getByPriority(s []string, p []string, def string) string {
  137. if len(s) == 0 {
  138. return def
  139. }
  140. for i := 0; i < len(p); i++ {
  141. for j := 0; j < len(s); j++ {
  142. if s[j] == p[i] {
  143. return s[j]
  144. }
  145. }
  146. }
  147. return s[0]
  148. }
  149. // MapFIPSRegion extracts the intrinsic AWS region from one that may have an
  150. // embedded FIPS microformat.
  151. func MapFIPSRegion(region string) string {
  152. const fipsInfix = "-fips-"
  153. const fipsPrefix = "fips-"
  154. const fipsSuffix = "-fips"
  155. if strings.Contains(region, fipsInfix) ||
  156. strings.Contains(region, fipsPrefix) ||
  157. strings.Contains(region, fipsSuffix) {
  158. region = strings.ReplaceAll(region, fipsInfix, "-")
  159. region = strings.ReplaceAll(region, fipsPrefix, "")
  160. region = strings.ReplaceAll(region, fipsSuffix, "")
  161. }
  162. return region
  163. }