240 lines
8.2 KiB
Go
240 lines
8.2 KiB
Go
|
package s3
|
||
|
|
||
|
import (
|
||
|
"net/url"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/aws/aws-sdk-go/aws"
|
||
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||
|
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||
|
"github.com/aws/aws-sdk-go/aws/request"
|
||
|
"github.com/aws/aws-sdk-go/internal/s3shared"
|
||
|
"github.com/aws/aws-sdk-go/internal/s3shared/arn"
|
||
|
"github.com/aws/aws-sdk-go/private/protocol"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
accessPointPrefixLabel = "accesspoint"
|
||
|
accountIDPrefixLabel = "accountID"
|
||
|
accessPointPrefixTemplate = "{" + accessPointPrefixLabel + "}-{" + accountIDPrefixLabel + "}."
|
||
|
|
||
|
outpostPrefixLabel = "outpost"
|
||
|
outpostAccessPointPrefixTemplate = accessPointPrefixTemplate + "{" + outpostPrefixLabel + "}."
|
||
|
)
|
||
|
|
||
|
// hasCustomEndpoint returns true if endpoint is a custom endpoint
|
||
|
func hasCustomEndpoint(r *request.Request) bool {
|
||
|
return len(aws.StringValue(r.Config.Endpoint)) > 0
|
||
|
}
|
||
|
|
||
|
// accessPointEndpointBuilder represents the endpoint builder for access point arn
|
||
|
type accessPointEndpointBuilder arn.AccessPointARN
|
||
|
|
||
|
// build builds the endpoint for corresponding access point arn
|
||
|
//
|
||
|
// For building an endpoint from access point arn, format used is:
|
||
|
// - Access point endpoint format : {accesspointName}-{accountId}.s3-accesspoint.{region}.{dnsSuffix}
|
||
|
// - example : myaccesspoint-012345678901.s3-accesspoint.us-west-2.amazonaws.com
|
||
|
//
|
||
|
// Access Point Endpoint requests are signed using "s3" as signing name.
|
||
|
func (a accessPointEndpointBuilder) build(req *request.Request) error {
|
||
|
resolveService := arn.AccessPointARN(a).Service
|
||
|
resolveRegion := arn.AccessPointARN(a).Region
|
||
|
|
||
|
endpoint, err := resolveRegionalEndpoint(req, resolveRegion, "", resolveService)
|
||
|
if err != nil {
|
||
|
return s3shared.NewFailedToResolveEndpointError(arn.AccessPointARN(a),
|
||
|
req.ClientInfo.PartitionID, resolveRegion, err)
|
||
|
}
|
||
|
|
||
|
endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
|
||
|
|
||
|
if !hasCustomEndpoint(req) {
|
||
|
if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// dual stack provided by endpoint resolver
|
||
|
updateS3HostForS3AccessPoint(req)
|
||
|
}
|
||
|
|
||
|
protocol.HostPrefixBuilder{
|
||
|
Prefix: accessPointPrefixTemplate,
|
||
|
LabelsFn: a.hostPrefixLabelValues,
|
||
|
}.Build(req)
|
||
|
|
||
|
// signer redirection
|
||
|
redirectSigner(req, endpoint.SigningName, endpoint.SigningRegion)
|
||
|
|
||
|
err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
|
||
|
if err != nil {
|
||
|
return s3shared.NewInvalidARNError(arn.AccessPointARN(a), err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a accessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
|
||
|
return map[string]string{
|
||
|
accessPointPrefixLabel: arn.AccessPointARN(a).AccessPointName,
|
||
|
accountIDPrefixLabel: arn.AccessPointARN(a).AccountID,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// s3ObjectLambdaAccessPointEndpointBuilder represents the endpoint builder for an s3 object lambda access point arn
|
||
|
type s3ObjectLambdaAccessPointEndpointBuilder arn.S3ObjectLambdaAccessPointARN
|
||
|
|
||
|
// build builds the endpoint for corresponding access point arn
|
||
|
//
|
||
|
// For building an endpoint from access point arn, format used is:
|
||
|
// - Access point endpoint format : {accesspointName}-{accountId}.s3-object-lambda.{region}.{dnsSuffix}
|
||
|
// - example : myaccesspoint-012345678901.s3-object-lambda.us-west-2.amazonaws.com
|
||
|
//
|
||
|
// Access Point Endpoint requests are signed using "s3-object-lambda" as signing name.
|
||
|
func (a s3ObjectLambdaAccessPointEndpointBuilder) build(req *request.Request) error {
|
||
|
resolveRegion := arn.S3ObjectLambdaAccessPointARN(a).Region
|
||
|
|
||
|
endpoint, err := resolveRegionalEndpoint(req, resolveRegion, "", EndpointsID)
|
||
|
if err != nil {
|
||
|
return s3shared.NewFailedToResolveEndpointError(arn.S3ObjectLambdaAccessPointARN(a),
|
||
|
req.ClientInfo.PartitionID, resolveRegion, err)
|
||
|
}
|
||
|
|
||
|
endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
|
||
|
|
||
|
endpoint.SigningName = s3ObjectsLambdaNamespace
|
||
|
|
||
|
if !hasCustomEndpoint(req) {
|
||
|
if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
updateS3HostPrefixForS3ObjectLambda(req)
|
||
|
}
|
||
|
|
||
|
protocol.HostPrefixBuilder{
|
||
|
Prefix: accessPointPrefixTemplate,
|
||
|
LabelsFn: a.hostPrefixLabelValues,
|
||
|
}.Build(req)
|
||
|
|
||
|
// signer redirection
|
||
|
redirectSigner(req, endpoint.SigningName, endpoint.SigningRegion)
|
||
|
|
||
|
err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
|
||
|
if err != nil {
|
||
|
return s3shared.NewInvalidARNError(arn.S3ObjectLambdaAccessPointARN(a), err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a s3ObjectLambdaAccessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
|
||
|
return map[string]string{
|
||
|
accessPointPrefixLabel: arn.S3ObjectLambdaAccessPointARN(a).AccessPointName,
|
||
|
accountIDPrefixLabel: arn.S3ObjectLambdaAccessPointARN(a).AccountID,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// outpostAccessPointEndpointBuilder represents the Endpoint builder for outpost access point arn.
|
||
|
type outpostAccessPointEndpointBuilder arn.OutpostAccessPointARN
|
||
|
|
||
|
// build builds an endpoint corresponding to the outpost access point arn.
|
||
|
//
|
||
|
// For building an endpoint from outpost access point arn, format used is:
|
||
|
// - Outpost access point endpoint format : {accesspointName}-{accountId}.{outpostId}.s3-outposts.{region}.{dnsSuffix}
|
||
|
// - example : myaccesspoint-012345678901.op-01234567890123456.s3-outposts.us-west-2.amazonaws.com
|
||
|
//
|
||
|
// Outpost AccessPoint Endpoint request are signed using "s3-outposts" as signing name.
|
||
|
func (o outpostAccessPointEndpointBuilder) build(req *request.Request) error {
|
||
|
resolveRegion := o.Region
|
||
|
resolveService := o.Service
|
||
|
|
||
|
endpointsID := resolveService
|
||
|
if resolveService == s3OutpostsNamespace {
|
||
|
endpointsID = "s3"
|
||
|
}
|
||
|
|
||
|
endpoint, err := resolveRegionalEndpoint(req, resolveRegion, "", endpointsID)
|
||
|
if err != nil {
|
||
|
return s3shared.NewFailedToResolveEndpointError(o,
|
||
|
req.ClientInfo.PartitionID, resolveRegion, err)
|
||
|
}
|
||
|
|
||
|
endpoint.URL = endpoints.AddScheme(endpoint.URL, aws.BoolValue(req.Config.DisableSSL))
|
||
|
|
||
|
if !hasCustomEndpoint(req) {
|
||
|
if err = updateRequestEndpoint(req, endpoint.URL); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
updateHostPrefix(req, endpointsID, resolveService)
|
||
|
}
|
||
|
|
||
|
protocol.HostPrefixBuilder{
|
||
|
Prefix: outpostAccessPointPrefixTemplate,
|
||
|
LabelsFn: o.hostPrefixLabelValues,
|
||
|
}.Build(req)
|
||
|
|
||
|
// set the signing region, name to resolved names from ARN
|
||
|
redirectSigner(req, resolveService, resolveRegion)
|
||
|
|
||
|
err = protocol.ValidateEndpointHost(req.Operation.Name, req.HTTPRequest.URL.Host)
|
||
|
if err != nil {
|
||
|
return s3shared.NewInvalidARNError(o, err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (o outpostAccessPointEndpointBuilder) hostPrefixLabelValues() map[string]string {
|
||
|
return map[string]string{
|
||
|
accessPointPrefixLabel: o.AccessPointName,
|
||
|
accountIDPrefixLabel: o.AccountID,
|
||
|
outpostPrefixLabel: o.OutpostID,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func resolveRegionalEndpoint(r *request.Request, region, resolvedRegion, endpointsID string) (endpoints.ResolvedEndpoint, error) {
|
||
|
return r.Config.EndpointResolver.EndpointFor(endpointsID, region, func(opts *endpoints.Options) {
|
||
|
opts.DisableSSL = aws.BoolValue(r.Config.DisableSSL)
|
||
|
opts.UseDualStack = aws.BoolValue(r.Config.UseDualStack)
|
||
|
opts.UseDualStackEndpoint = r.Config.UseDualStackEndpoint
|
||
|
opts.UseFIPSEndpoint = r.Config.UseFIPSEndpoint
|
||
|
opts.S3UsEast1RegionalEndpoint = endpoints.RegionalS3UsEast1Endpoint
|
||
|
opts.ResolvedRegion = resolvedRegion
|
||
|
opts.Logger = r.Config.Logger
|
||
|
opts.LogDeprecated = r.Config.LogLevel.Matches(aws.LogDebugWithDeprecated)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func updateRequestEndpoint(r *request.Request, endpoint string) (err error) {
|
||
|
r.HTTPRequest.URL, err = url.Parse(endpoint + r.Operation.HTTPPath)
|
||
|
if err != nil {
|
||
|
return awserr.New(request.ErrCodeSerialization,
|
||
|
"failed to parse endpoint URL", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// redirectSigner sets signing name, signing region for a request
|
||
|
func redirectSigner(req *request.Request, signingName string, signingRegion string) {
|
||
|
req.ClientInfo.SigningName = signingName
|
||
|
req.ClientInfo.SigningRegion = signingRegion
|
||
|
}
|
||
|
|
||
|
func updateS3HostForS3AccessPoint(req *request.Request) {
|
||
|
updateHostPrefix(req, "s3", s3AccessPointNamespace)
|
||
|
}
|
||
|
|
||
|
func updateS3HostPrefixForS3ObjectLambda(req *request.Request) {
|
||
|
updateHostPrefix(req, "s3", s3ObjectsLambdaNamespace)
|
||
|
}
|
||
|
|
||
|
func updateHostPrefix(req *request.Request, oldEndpointPrefix, newEndpointPrefix string) {
|
||
|
host := req.HTTPRequest.URL.Host
|
||
|
if strings.HasPrefix(host, oldEndpointPrefix) {
|
||
|
// replace service hostlabel oldEndpointPrefix to newEndpointPrefix
|
||
|
req.HTTPRequest.URL.Host = newEndpointPrefix + host[len(oldEndpointPrefix):]
|
||
|
}
|
||
|
}
|