endpoint_builder.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. package s3
  2. import (
  3. "net/url"
  4. "strings"
  5. "github.com/aws/aws-sdk-go/aws"
  6. "github.com/aws/aws-sdk-go/aws/awserr"
  7. "github.com/aws/aws-sdk-go/aws/endpoints"
  8. "github.com/aws/aws-sdk-go/aws/request"
  9. "github.com/aws/aws-sdk-go/internal/s3shared"
  10. "github.com/aws/aws-sdk-go/internal/s3shared/arn"
  11. "github.com/aws/aws-sdk-go/private/protocol"
  12. )
  13. const (
  14. accessPointPrefixLabel = "accesspoint"
  15. accountIDPrefixLabel = "accountID"
  16. accessPointPrefixTemplate = "{" + accessPointPrefixLabel + "}-{" + accountIDPrefixLabel + "}."
  17. outpostPrefixLabel = "outpost"
  18. outpostAccessPointPrefixTemplate = accessPointPrefixTemplate + "{" + outpostPrefixLabel + "}."
  19. )
  20. // hasCustomEndpoint returns true if endpoint is a custom endpoint
  21. func hasCustomEndpoint(r *request.Request) bool {
  22. return len(aws.StringValue(r.Config.Endpoint)) > 0
  23. }
  24. // accessPointEndpointBuilder represents the endpoint builder for access point arn
  25. type accessPointEndpointBuilder arn.AccessPointARN
  26. // build builds the endpoint for corresponding access point arn
  27. //
  28. // For building an endpoint from access point arn, format used is:
  29. // - Access point endpoint format : {accesspointName}-{accountId}.s3-accesspoint.{region}.{dnsSuffix}
  30. // - example : myaccesspoint-012345678901.s3-accesspoint.us-west-2.amazonaws.com
  31. //
  32. // Access Point Endpoint requests are signed using "s3" as signing name.
  33. func (a accessPointEndpointBuilder) build(req *request.Request) error {
  34. resolveService := arn.AccessPointARN(a).Service
  35. resolveRegion := arn.AccessPointARN(a).Region
  36. endpoint, err := resolveRegionalEndpoint(req, resolveRegion, "", resolveService)
  37. if err != nil {
  38. return s3shared.NewFailedToResolveEndpointError(arn.AccessPointARN(a),
  39. req.ClientInfo.PartitionID, resolveRegion, err)
  40. }
  41. endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
  42. if !hasCustomEndpoint(req) {
  43. if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
  44. return err
  45. }
  46. // dual stack provided by endpoint resolver
  47. updateS3HostForS3AccessPoint(req)
  48. }
  49. protocol.HostPrefixBuilder{
  50. Prefix: accessPointPrefixTemplate,
  51. LabelsFn: a.hostPrefixLabelValues,
  52. }.Build(req)
  53. // signer redirection
  54. redirectSigner(req, endpoint.SigningName, endpoint.SigningRegion)
  55. err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
  56. if err != nil {
  57. return s3shared.NewInvalidARNError(arn.AccessPointARN(a), err)
  58. }
  59. return nil
  60. }
  61. func (a accessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
  62. return map[string]string{
  63. accessPointPrefixLabel: arn.AccessPointARN(a).AccessPointName,
  64. accountIDPrefixLabel: arn.AccessPointARN(a).AccountID,
  65. }
  66. }
  67. // s3ObjectLambdaAccessPointEndpointBuilder represents the endpoint builder for an s3 object lambda access point arn
  68. type s3ObjectLambdaAccessPointEndpointBuilder arn.S3ObjectLambdaAccessPointARN
  69. // build builds the endpoint for corresponding access point arn
  70. //
  71. // For building an endpoint from access point arn, format used is:
  72. // - Access point endpoint format : {accesspointName}-{accountId}.s3-object-lambda.{region}.{dnsSuffix}
  73. // - example : myaccesspoint-012345678901.s3-object-lambda.us-west-2.amazonaws.com
  74. //
  75. // Access Point Endpoint requests are signed using "s3-object-lambda" as signing name.
  76. func (a s3ObjectLambdaAccessPointEndpointBuilder) build(req *request.Request) error {
  77. resolveRegion := arn.S3ObjectLambdaAccessPointARN(a).Region
  78. endpoint, err := resolveRegionalEndpoint(req, resolveRegion, "", EndpointsID)
  79. if err != nil {
  80. return s3shared.NewFailedToResolveEndpointError(arn.S3ObjectLambdaAccessPointARN(a),
  81. req.ClientInfo.PartitionID, resolveRegion, err)
  82. }
  83. endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
  84. endpoint.SigningName = s3ObjectsLambdaNamespace
  85. if !hasCustomEndpoint(req) {
  86. if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
  87. return err
  88. }
  89. updateS3HostPrefixForS3ObjectLambda(req)
  90. }
  91. protocol.HostPrefixBuilder{
  92. Prefix: accessPointPrefixTemplate,
  93. LabelsFn: a.hostPrefixLabelValues,
  94. }.Build(req)
  95. // signer redirection
  96. redirectSigner(req, endpoint.SigningName, endpoint.SigningRegion)
  97. err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
  98. if err != nil {
  99. return s3shared.NewInvalidARNError(arn.S3ObjectLambdaAccessPointARN(a), err)
  100. }
  101. return nil
  102. }
  103. func (a s3ObjectLambdaAccessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
  104. return map[string]string{
  105. accessPointPrefixLabel: arn.S3ObjectLambdaAccessPointARN(a).AccessPointName,
  106. accountIDPrefixLabel: arn.S3ObjectLambdaAccessPointARN(a).AccountID,
  107. }
  108. }
  109. // outpostAccessPointEndpointBuilder represents the Endpoint builder for outpost access point arn.
  110. type outpostAccessPointEndpointBuilder arn.OutpostAccessPointARN
  111. // build builds an endpoint corresponding to the outpost access point arn.
  112. //
  113. // For building an endpoint from outpost access point arn, format used is:
  114. // - Outpost access point endpoint format : {accesspointName}-{accountId}.{outpostId}.s3-outposts.{region}.{dnsSuffix}
  115. // - example : myaccesspoint-012345678901.op-01234567890123456.s3-outposts.us-west-2.amazonaws.com
  116. //
  117. // Outpost AccessPoint Endpoint request are signed using "s3-outposts" as signing name.
  118. func (o outpostAccessPointEndpointBuilder) build(req *request.Request) error {
  119. resolveRegion := o.Region
  120. resolveService := o.Service
  121. endpointsID := resolveService
  122. if resolveService == s3OutpostsNamespace {
  123. endpointsID = "s3"
  124. }
  125. endpoint, err := resolveRegionalEndpoint(req, resolveRegion, "", endpointsID)
  126. if err != nil {
  127. return s3shared.NewFailedToResolveEndpointError(o,
  128. req.ClientInfo.PartitionID, resolveRegion, err)
  129. }
  130. endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
  131. if !hasCustomEndpoint(req) {
  132. if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
  133. return err
  134. }
  135. updateHostPrefix(req, endpointsID, resolveService)
  136. }
  137. protocol.HostPrefixBuilder{
  138. Prefix: outpostAccessPointPrefixTemplate,
  139. LabelsFn: o.hostPrefixLabelValues,
  140. }.Build(req)
  141. // set the signing region, name to resolved names from ARN
  142. redirectSigner(req, resolveService, resolveRegion)
  143. err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
  144. if err != nil {
  145. return s3shared.NewInvalidARNError(o, err)
  146. }
  147. return nil
  148. }
  149. func (o outpostAccessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
  150. return map[string]string{
  151. accessPointPrefixLabel: o.AccessPointName,
  152. accountIDPrefixLabel: o.AccountID,
  153. outpostPrefixLabel: o.OutpostID,
  154. }
  155. }
  156. func resolveRegionalEndpoint(r *request.Request, region, resolvedRegion, endpointsID string) (endpoints.ResolvedEndpoint, error) {
  157. return r.Config.EndpointResolver.EndpointFor(endpointsID, region, func(opts *endpoints.Options) {
  158. opts.DisableSSL = aws.BoolValue(r.Config.DisableSSL)
  159. opts.UseDualStack = aws.BoolValue(r.Config.UseDualStack)
  160. opts.UseDualStackEndpoint = r.Config.UseDualStackEndpoint
  161. opts.UseFIPSEndpoint = r.Config.UseFIPSEndpoint
  162. opts.S3UsEast1RegionalEndpoint = endpoints.RegionalS3UsEast1Endpoint
  163. opts.ResolvedRegion = resolvedRegion
  164. opts.Logger = r.Config.Logger
  165. opts.LogDeprecated = r.Config.LogLevel.Matches(aws.LogDebugWithDeprecated)
  166. })
  167. }
  168. func updateRequestEndpoint(r *request.Request, endpoint string) (err error) {
  169. r.HTTPRequest.URL, err = url.Parse(endpoint + r.Operation.HTTPPath)
  170. if err != nil {
  171. return awserr.New(request.ErrCodeSerialization,
  172. "failed to parse endpoint URL", err)
  173. }
  174. return nil
  175. }
  176. // redirectSigner sets signing name, signing region for a request
  177. func redirectSigner(req *request.Request, signingName string, signingRegion string) {
  178. req.ClientInfo.SigningName = signingName
  179. req.ClientInfo.SigningRegion = signingRegion
  180. }
  181. func updateS3HostForS3AccessPoint(req *request.Request) {
  182. updateHostPrefix(req, "s3", s3AccessPointNamespace)
  183. }
  184. func updateS3HostPrefixForS3ObjectLambda(req *request.Request) {
  185. updateHostPrefix(req, "s3", s3ObjectsLambdaNamespace)
  186. }
  187. func updateHostPrefix(req *request.Request, oldEndpointPrefix, newEndpointPrefix string) {
  188. host := req.HTTPRequest.URL.Host
  189. if strings.HasPrefix(host, oldEndpointPrefix) {
  190. // replace service hostlabel oldEndpointPrefix to newEndpointPrefix
  191. req.HTTPRequest.URL.Host = newEndpointPrefix + host[len(oldEndpointPrefix):]
  192. }
  193. }