github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/endpoint_builder.go (about)

     1  package s3
     2  
     3  import (
     4  	"net/url"
     5  	"strings"
     6  
     7  	"github.com/aavshr/aws-sdk-go/aws"
     8  	"github.com/aavshr/aws-sdk-go/aws/awserr"
     9  	"github.com/aavshr/aws-sdk-go/aws/endpoints"
    10  	"github.com/aavshr/aws-sdk-go/aws/request"
    11  	"github.com/aavshr/aws-sdk-go/internal/s3shared"
    12  	"github.com/aavshr/aws-sdk-go/internal/s3shared/arn"
    13  	"github.com/aavshr/aws-sdk-go/private/protocol"
    14  )
    15  
    16  const (
    17  	accessPointPrefixLabel    = "accesspoint"
    18  	accountIDPrefixLabel      = "accountID"
    19  	accessPointPrefixTemplate = "{" + accessPointPrefixLabel + "}-{" + accountIDPrefixLabel + "}."
    20  
    21  	outpostPrefixLabel               = "outpost"
    22  	outpostAccessPointPrefixTemplate = accessPointPrefixTemplate + "{" + outpostPrefixLabel + "}."
    23  )
    24  
    25  // hasCustomEndpoint returns true if endpoint is a custom endpoint
    26  func hasCustomEndpoint(r *request.Request) bool {
    27  	return len(aws.StringValue(r.Config.Endpoint)) > 0
    28  }
    29  
    30  // accessPointEndpointBuilder represents the endpoint builder for access point arn
    31  type accessPointEndpointBuilder arn.AccessPointARN
    32  
    33  // build builds the endpoint for corresponding access point arn
    34  //
    35  // For building an endpoint from access point arn, format used is:
    36  // - Access point endpoint format : {accesspointName}-{accountId}.s3-accesspoint.{region}.{dnsSuffix}
    37  // - example : myaccesspoint-012345678901.s3-accesspoint.us-west-2.amazonaws.com
    38  //
    39  // Access Point Endpoint requests are signed using "s3" as signing name.
    40  //
    41  func (a accessPointEndpointBuilder) build(req *request.Request) error {
    42  	resolveService := arn.AccessPointARN(a).Service
    43  	resolveRegion := arn.AccessPointARN(a).Region
    44  	cfgRegion := aws.StringValue(req.Config.Region)
    45  
    46  	if s3shared.IsFIPS(cfgRegion) {
    47  		if aws.BoolValue(req.Config.S3UseARNRegion) && s3shared.IsCrossRegion(req, resolveRegion) {
    48  			// FIPS with cross region is not supported, the SDK must fail
    49  			// because there is no well defined method for SDK to construct a
    50  			// correct FIPS endpoint.
    51  			return s3shared.NewClientConfiguredForCrossRegionFIPSError(arn.AccessPointARN(a),
    52  				req.ClientInfo.PartitionID, cfgRegion, nil)
    53  		}
    54  		resolveRegion = cfgRegion
    55  	}
    56  
    57  	endpoint, err := resolveRegionalEndpoint(req, resolveRegion, resolveService)
    58  	if err != nil {
    59  		return s3shared.NewFailedToResolveEndpointError(arn.AccessPointARN(a),
    60  			req.ClientInfo.PartitionID, cfgRegion, err)
    61  	}
    62  
    63  	endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
    64  
    65  	if !hasCustomEndpoint(req) {
    66  		if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
    67  			return err
    68  		}
    69  
    70  		// dual stack provided by endpoint resolver
    71  		updateS3HostForS3AccessPoint(req)
    72  	}
    73  
    74  	protocol.HostPrefixBuilder{
    75  		Prefix:   accessPointPrefixTemplate,
    76  		LabelsFn: a.hostPrefixLabelValues,
    77  	}.Build(req)
    78  
    79  	// signer redirection
    80  	redirectSigner(req, endpoint.SigningName, endpoint.SigningRegion)
    81  
    82  	err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
    83  	if err != nil {
    84  		return s3shared.NewInvalidARNError(arn.AccessPointARN(a), err)
    85  	}
    86  
    87  	return nil
    88  }
    89  
    90  func (a accessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
    91  	return map[string]string{
    92  		accessPointPrefixLabel: arn.AccessPointARN(a).AccessPointName,
    93  		accountIDPrefixLabel:   arn.AccessPointARN(a).AccountID,
    94  	}
    95  }
    96  
    97  // s3ObjectLambdaAccessPointEndpointBuilder represents the endpoint builder for an s3 object lambda access point arn
    98  type s3ObjectLambdaAccessPointEndpointBuilder arn.S3ObjectLambdaAccessPointARN
    99  
   100  // build builds the endpoint for corresponding access point arn
   101  //
   102  // For building an endpoint from access point arn, format used is:
   103  // - Access point endpoint format : {accesspointName}-{accountId}.s3-object-lambda.{region}.{dnsSuffix}
   104  // - example : myaccesspoint-012345678901.s3-object-lambda.us-west-2.amazonaws.com
   105  //
   106  // Access Point Endpoint requests are signed using "s3-object-lambda" as signing name.
   107  //
   108  func (a s3ObjectLambdaAccessPointEndpointBuilder) build(req *request.Request) error {
   109  	resolveRegion := arn.S3ObjectLambdaAccessPointARN(a).Region
   110  	cfgRegion := aws.StringValue(req.Config.Region)
   111  
   112  	if s3shared.IsFIPS(cfgRegion) {
   113  		if aws.BoolValue(req.Config.S3UseARNRegion) && s3shared.IsCrossRegion(req, resolveRegion) {
   114  			// FIPS with cross region is not supported, the SDK must fail
   115  			// because there is no well defined method for SDK to construct a
   116  			// correct FIPS endpoint.
   117  			return s3shared.NewClientConfiguredForCrossRegionFIPSError(arn.S3ObjectLambdaAccessPointARN(a),
   118  				req.ClientInfo.PartitionID, cfgRegion, nil)
   119  		}
   120  		resolveRegion = cfgRegion
   121  	}
   122  
   123  	endpoint, err := resolveRegionalEndpoint(req, resolveRegion, EndpointsID)
   124  	if err != nil {
   125  		return s3shared.NewFailedToResolveEndpointError(arn.S3ObjectLambdaAccessPointARN(a),
   126  			req.ClientInfo.PartitionID, cfgRegion, err)
   127  	}
   128  
   129  	endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
   130  
   131  	endpoint.SigningName = s3ObjectsLambdaNamespace
   132  
   133  	if !hasCustomEndpoint(req) {
   134  		if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
   135  			return err
   136  		}
   137  
   138  		updateS3HostPrefixForS3ObjectLambda(req)
   139  	}
   140  
   141  	protocol.HostPrefixBuilder{
   142  		Prefix:   accessPointPrefixTemplate,
   143  		LabelsFn: a.hostPrefixLabelValues,
   144  	}.Build(req)
   145  
   146  	// signer redirection
   147  	redirectSigner(req, endpoint.SigningName, endpoint.SigningRegion)
   148  
   149  	err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
   150  	if err != nil {
   151  		return s3shared.NewInvalidARNError(arn.S3ObjectLambdaAccessPointARN(a), err)
   152  	}
   153  
   154  	return nil
   155  }
   156  
   157  func (a s3ObjectLambdaAccessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
   158  	return map[string]string{
   159  		accessPointPrefixLabel: arn.S3ObjectLambdaAccessPointARN(a).AccessPointName,
   160  		accountIDPrefixLabel:   arn.S3ObjectLambdaAccessPointARN(a).AccountID,
   161  	}
   162  }
   163  
   164  // outpostAccessPointEndpointBuilder represents the Endpoint builder for outpost access point arn.
   165  type outpostAccessPointEndpointBuilder arn.OutpostAccessPointARN
   166  
   167  // build builds an endpoint corresponding to the outpost access point arn.
   168  //
   169  // For building an endpoint from outpost access point arn, format used is:
   170  // - Outpost access point endpoint format : {accesspointName}-{accountId}.{outpostId}.s3-outposts.{region}.{dnsSuffix}
   171  // - example : myaccesspoint-012345678901.op-01234567890123456.s3-outposts.us-west-2.amazonaws.com
   172  //
   173  // Outpost AccessPoint Endpoint request are signed using "s3-outposts" as signing name.
   174  //
   175  func (o outpostAccessPointEndpointBuilder) build(req *request.Request) error {
   176  	resolveRegion := o.Region
   177  	resolveService := o.Service
   178  
   179  	endpointsID := resolveService
   180  	if resolveService == s3OutpostsNamespace {
   181  		endpointsID = "s3"
   182  	}
   183  
   184  	endpoint, err := resolveRegionalEndpoint(req, resolveRegion, endpointsID)
   185  	if err != nil {
   186  		return s3shared.NewFailedToResolveEndpointError(o,
   187  			req.ClientInfo.PartitionID, resolveRegion, err)
   188  	}
   189  
   190  	endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
   191  
   192  	if !hasCustomEndpoint(req) {
   193  		if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
   194  			return err
   195  		}
   196  		updateHostPrefix(req, endpointsID, resolveService)
   197  	}
   198  
   199  	protocol.HostPrefixBuilder{
   200  		Prefix:   outpostAccessPointPrefixTemplate,
   201  		LabelsFn: o.hostPrefixLabelValues,
   202  	}.Build(req)
   203  
   204  	// set the signing region, name to resolved names from ARN
   205  	redirectSigner(req, resolveService, resolveRegion)
   206  
   207  	err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
   208  	if err != nil {
   209  		return s3shared.NewInvalidARNError(o, err)
   210  	}
   211  
   212  	return nil
   213  }
   214  
   215  func (o outpostAccessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
   216  	return map[string]string{
   217  		accessPointPrefixLabel: o.AccessPointName,
   218  		accountIDPrefixLabel:   o.AccountID,
   219  		outpostPrefixLabel:     o.OutpostID,
   220  	}
   221  }
   222  
   223  func resolveRegionalEndpoint(r *request.Request, region string, endpointsID string) (endpoints.ResolvedEndpoint, error) {
   224  	return r.Config.EndpointResolver.EndpointFor(endpointsID, region, func(opts *endpoints.Options) {
   225  		opts.DisableSSL = aws.BoolValue(r.Config.DisableSSL)
   226  		opts.UseDualStack = aws.BoolValue(r.Config.UseDualStack)
   227  		opts.S3UsEast1RegionalEndpoint = endpoints.RegionalS3UsEast1Endpoint
   228  	})
   229  }
   230  
   231  func updateRequestEndpoint(r *request.Request, endpoint string) (err error) {
   232  	r.HTTPRequest.URL, err = url.Parse(endpoint + r.Operation.HTTPPath)
   233  	if err != nil {
   234  		return awserr.New(request.ErrCodeSerialization,
   235  			"failed to parse endpoint URL", err)
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  // redirectSigner sets signing name, signing region for a request
   242  func redirectSigner(req *request.Request, signingName string, signingRegion string) {
   243  	req.ClientInfo.SigningName = signingName
   244  	req.ClientInfo.SigningRegion = signingRegion
   245  }
   246  
   247  func updateS3HostForS3AccessPoint(req *request.Request) {
   248  	updateHostPrefix(req, "s3", s3AccessPointNamespace)
   249  }
   250  
   251  func updateS3HostPrefixForS3ObjectLambda(req *request.Request) {
   252  	updateHostPrefix(req, "s3", s3ObjectsLambdaNamespace)
   253  }
   254  
   255  func updateHostPrefix(req *request.Request, oldEndpointPrefix, newEndpointPrefix string) {
   256  	host := req.HTTPRequest.URL.Host
   257  	if strings.HasPrefix(host, oldEndpointPrefix) {
   258  		// replace service hostlabel oldEndpointPrefix to newEndpointPrefix
   259  		req.HTTPRequest.URL.Host = newEndpointPrefix + host[len(oldEndpointPrefix):]
   260  	}
   261  }