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

     1  //go:build go1.7
     2  // +build go1.7
     3  
     4  package s3
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/aavshr/aws-sdk-go/aws"
    16  	"github.com/aavshr/aws-sdk-go/aws/endpoints"
    17  	"github.com/aavshr/aws-sdk-go/aws/request"
    18  	"github.com/aavshr/aws-sdk-go/awstesting/unit"
    19  )
    20  
    21  func TestEndpoint(t *testing.T) {
    22  	cases := map[string]struct {
    23  		bucket                string
    24  		config                *aws.Config
    25  		req                   func(svc *S3) *request.Request
    26  		expectedEndpoint      string
    27  		expectedSigningName   string
    28  		expectedSigningRegion string
    29  		expectedErr           string
    30  	}{
    31  		"standard custom endpoint url": {
    32  			bucket: "bucketname",
    33  			config: &aws.Config{
    34  				Region:   aws.String("us-west-2"),
    35  				Endpoint: aws.String("beta.example.com"),
    36  			},
    37  			expectedEndpoint:      "https://bucketname.beta.example.com",
    38  			expectedSigningName:   "s3",
    39  			expectedSigningRegion: "us-west-2",
    40  		},
    41  		"Object Lambda with no UseARNRegion flag set": {
    42  			bucket: "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint/myap",
    43  			config: &aws.Config{
    44  				Region: aws.String("us-west-2"),
    45  			},
    46  			expectedEndpoint:      "https://myap-123456789012.s3-object-lambda.us-west-2.amazonaws.com",
    47  			expectedSigningName:   "s3-object-lambda",
    48  			expectedSigningRegion: "us-west-2",
    49  		},
    50  		"Object Lambda with UseARNRegion flag set": {
    51  			bucket: "arn:aws:s3-object-lambda:us-east-1:123456789012:accesspoint/myap",
    52  			config: &aws.Config{
    53  				Region:         aws.String("us-west-2"),
    54  				S3UseARNRegion: aws.Bool(true),
    55  			},
    56  			expectedEndpoint:      "https://myap-123456789012.s3-object-lambda.us-east-1.amazonaws.com",
    57  			expectedSigningName:   "s3-object-lambda",
    58  			expectedSigningRegion: "us-east-1",
    59  		},
    60  		"Object Lambda with Cross-Region error": {
    61  			bucket: "arn:aws:s3-object-lambda:us-east-1:123456789012:accesspoint/myap",
    62  			config: &aws.Config{
    63  				Region: aws.String("us-west-2"),
    64  			},
    65  			expectedErr: "client region does not match provided ARN region",
    66  		},
    67  		"Object Lambda Pseudo-Region with UseARNRegion flag set": {
    68  			bucket: "arn:aws:s3-object-lambda:us-east-1:123456789012:accesspoint/myap",
    69  			config: &aws.Config{
    70  				Region:         aws.String("aws-global"),
    71  				S3UseARNRegion: aws.Bool(true),
    72  			},
    73  			expectedEndpoint:      "https://myap-123456789012.s3-object-lambda.us-east-1.amazonaws.com",
    74  			expectedSigningRegion: "us-east-1",
    75  			expectedSigningName:   "s3-object-lambda",
    76  		},
    77  		"Object Lambda Cross-Region DualStack error": {
    78  			bucket: "arn:aws:s3-object-lambda:us-east-1:123456789012:accesspoint/myap",
    79  			config: &aws.Config{
    80  				Region:         aws.String("us-west-2"),
    81  				UseDualStack:   aws.Bool(true),
    82  				S3UseARNRegion: aws.Bool(true),
    83  			},
    84  			expectedErr: "client configured for S3 Dual-stack but is not supported with resource ARN",
    85  		},
    86  		"Object Lambda Cross-Partition error": {
    87  			bucket: "arn:aws-cn:s3-object-lambda:cn-north-1:123456789012:accesspoint/myap",
    88  			config: &aws.Config{
    89  				Region:         aws.String("us-west-2"),
    90  				S3UseARNRegion: aws.Bool(true),
    91  			},
    92  			expectedErr: "client partition does not match provided ARN partition",
    93  		},
    94  		"Object Lambda FIPS Pseudo-Region": {
    95  			bucket: "arn:aws-us-gov:s3-object-lambda:us-gov-west-1:123456789012:accesspoint/myap",
    96  			config: &aws.Config{
    97  				Region: aws.String("fips-us-gov-west-1"),
    98  			},
    99  			expectedEndpoint:      "https://myap-123456789012.s3-object-lambda-fips.us-gov-west-1.amazonaws.com",
   100  			expectedSigningRegion: "us-gov-west-1",
   101  			expectedSigningName:   "s3-object-lambda",
   102  		},
   103  		"Object Lambda FIPS Pseudo-Region with UseARNRegion flag set": {
   104  			bucket: "arn:aws-us-gov:s3-object-lambda:us-gov-west-1:123456789012:accesspoint/myap",
   105  			config: &aws.Config{
   106  				Region:         aws.String("fips-us-gov-west-1"),
   107  				S3UseARNRegion: aws.Bool(true),
   108  			},
   109  			expectedEndpoint:      "https://myap-123456789012.s3-object-lambda-fips.us-gov-west-1.amazonaws.com",
   110  			expectedSigningRegion: "us-gov-west-1",
   111  			expectedSigningName:   "s3-object-lambda",
   112  		},
   113  		"Object Lambda with Accelerate": {
   114  			bucket: "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:myendpoint",
   115  			config: &aws.Config{
   116  				Region:          aws.String("us-west-2"),
   117  				S3UseAccelerate: aws.Bool(true),
   118  			},
   119  			expectedErr: "client configured for S3 Accelerate but is not supported with resource ARN",
   120  		},
   121  		"Object Lambda with Custom Endpoint": {
   122  			bucket: "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:myendpoint",
   123  			config: &aws.Config{
   124  				Region:   aws.String("us-west-2"),
   125  				Endpoint: aws.String("my-domain.com"),
   126  			},
   127  			expectedEndpoint:      "https://myendpoint-123456789012.my-domain.com",
   128  			expectedSigningName:   "s3-object-lambda",
   129  			expectedSigningRegion: "us-west-2",
   130  		},
   131  		"AccessPoint with custom endpoint url": {
   132  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
   133  			config: &aws.Config{
   134  				Region:   aws.String("us-west-2"),
   135  				Endpoint: aws.String("beta.example.com"),
   136  			},
   137  			expectedEndpoint:      "https://myendpoint-123456789012.beta.example.com",
   138  			expectedSigningName:   "s3",
   139  			expectedSigningRegion: "us-west-2",
   140  		},
   141  		"Outpost AccessPoint with custom endpoint url": {
   142  			bucket: "arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   143  			config: &aws.Config{
   144  				Region:   aws.String("us-west-2"),
   145  				Endpoint: aws.String("beta.example.com"),
   146  			},
   147  			expectedEndpoint:      "https://myaccesspoint-123456789012.op-01234567890123456.beta.example.com",
   148  			expectedSigningName:   "s3-outposts",
   149  			expectedSigningRegion: "us-west-2",
   150  		},
   151  		"ListBucket with custom endpoint url": {
   152  			config: &aws.Config{
   153  				Region:   aws.String("us-west-2"),
   154  				Endpoint: aws.String("bucket.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com"),
   155  			},
   156  			req: func(svc *S3) *request.Request {
   157  				req, _ := svc.ListBucketsRequest(&ListBucketsInput{})
   158  				return req
   159  			},
   160  			expectedEndpoint:      "https://bucket.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com",
   161  			expectedSigningName:   "s3",
   162  			expectedSigningRegion: "us-west-2",
   163  		},
   164  		"Path-style addressing with custom endpoint url": {
   165  			bucket: "bucketname",
   166  			config: &aws.Config{
   167  				Region:           aws.String("us-west-2"),
   168  				Endpoint:         aws.String("bucket.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com"),
   169  				S3ForcePathStyle: aws.Bool(true),
   170  			},
   171  			expectedEndpoint:      "https://bucket.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com",
   172  			expectedSigningName:   "s3",
   173  			expectedSigningRegion: "us-west-2",
   174  		},
   175  		"Virtual host addressing with custom endpoint url": {
   176  			bucket: "bucketname",
   177  			config: &aws.Config{
   178  				Region:   aws.String("us-west-2"),
   179  				Endpoint: aws.String("bucket.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com"),
   180  			},
   181  			expectedEndpoint:      "https://bucketname.bucket.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com",
   182  			expectedSigningName:   "s3",
   183  			expectedSigningRegion: "us-west-2",
   184  		},
   185  		"Access-point with custom endpoint url and use_arn_region set": {
   186  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
   187  			config: &aws.Config{
   188  				Region:         aws.String("eu-west-1"),
   189  				Endpoint:       aws.String("accesspoint.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com"),
   190  				S3UseARNRegion: aws.Bool(true),
   191  			},
   192  			expectedEndpoint:      "https://myendpoint-123456789012.accesspoint.vpce-123-abc.s3.us-west-2.vpce.amazonaws.com",
   193  			expectedSigningName:   "s3",
   194  			expectedSigningRegion: "us-west-2",
   195  		},
   196  		"Custom endpoint url with Dualstack": {
   197  			bucket: "bucketname",
   198  			config: &aws.Config{
   199  				Region:       aws.String("us-west-2"),
   200  				Endpoint:     aws.String("beta.example.com"),
   201  				UseDualStack: aws.Bool(true),
   202  			},
   203  			expectedEndpoint:      "https://bucketname.beta.example.com",
   204  			expectedSigningName:   "s3",
   205  			expectedSigningRegion: "us-west-2",
   206  		},
   207  		"Outpost with custom endpoint url and Dualstack": {
   208  			bucket: "arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   209  			config: &aws.Config{
   210  				Region:       aws.String("us-west-2"),
   211  				Endpoint:     aws.String("beta.example.com"),
   212  				UseDualStack: aws.Bool(true),
   213  			},
   214  			expectedErr: "client configured for S3 Dual-stack but is not supported with resource ARN",
   215  		},
   216  		"Outpost AccessPoint with no S3UseARNRegion flag set": {
   217  			bucket: "arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   218  			config: &aws.Config{
   219  				Region: aws.String("us-west-2"),
   220  			},
   221  			expectedEndpoint:      "https://myaccesspoint-123456789012.op-01234567890123456.s3-outposts.us-west-2.amazonaws.com",
   222  			expectedSigningName:   "s3-outposts",
   223  			expectedSigningRegion: "us-west-2",
   224  		},
   225  		"Outpost AccessPoint Cross-Region Enabled": {
   226  			bucket: "arn:aws:s3-outposts:us-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   227  			config: &aws.Config{
   228  				Region:         aws.String("us-west-2"),
   229  				S3UseARNRegion: aws.Bool(true),
   230  			},
   231  			expectedEndpoint:      "https://myaccesspoint-123456789012.op-01234567890123456.s3-outposts.us-east-1.amazonaws.com",
   232  			expectedSigningName:   "s3-outposts",
   233  			expectedSigningRegion: "us-east-1",
   234  		},
   235  		"Outpost AccessPoint Cross-Region Disabled": {
   236  			bucket: "arn:aws:s3-outposts:us-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   237  			config: &aws.Config{
   238  				Region: aws.String("us-west-2"),
   239  			},
   240  			expectedErr: "client region does not match provided ARN region",
   241  		},
   242  		"Outpost AccessPoint other partition": {
   243  			bucket: "arn:aws-cn:s3-outposts:cn-north-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   244  			config: &aws.Config{
   245  				Region:         aws.String("us-west-2"),
   246  				S3UseARNRegion: aws.Bool(true),
   247  			},
   248  			expectedErr: "ConfigurationError: client partition does not match provided ARN partition",
   249  		},
   250  		"Outpost AccessPoint cn partition": {
   251  			bucket: "arn:aws-cn:s3-outposts:cn-north-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   252  			config: &aws.Config{
   253  				Region: aws.String("cn-north-1"),
   254  			},
   255  			expectedEndpoint:      "https://myaccesspoint-123456789012.op-01234567890123456.s3-outposts.cn-north-1.amazonaws.com.cn",
   256  			expectedSigningName:   "s3-outposts",
   257  			expectedSigningRegion: "cn-north-1",
   258  		},
   259  		"Outpost AccessPoint us-gov region": {
   260  			bucket: "arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   261  			config: &aws.Config{
   262  				Region:         aws.String("us-gov-east-1"),
   263  				S3UseARNRegion: aws.Bool(true),
   264  			},
   265  			expectedEndpoint:      "https://myaccesspoint-123456789012.op-01234567890123456.s3-outposts.us-gov-east-1.amazonaws.com",
   266  			expectedSigningName:   "s3-outposts",
   267  			expectedSigningRegion: "us-gov-east-1",
   268  		},
   269  		"Outpost AccessPoint FIPS client region, resolved signing region does not match ARN region": {
   270  			bucket: "arn:aws-us-gov:s3-outposts:us-gov-unknown-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   271  			config: &aws.Config{
   272  				EndpointResolver: endpoints.AwsUsGovPartition(),
   273  				Region:           aws.String("fips-us-gov-unknown-1"),
   274  			},
   275  			expectedErr: "ConfigurationError: client region does not match provided ARN region",
   276  		},
   277  		"Outpost AccessPoint FIPS client region, resolved signing region does match ARN region": {
   278  			bucket: "arn:aws-us-gov:s3-outposts:us-gov-west-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   279  			config: &aws.Config{
   280  				EndpointResolver: endpoints.AwsUsGovPartition(),
   281  				Region:           aws.String("fips-us-gov-west-1"),
   282  			},
   283  			expectedErr: "use of ARN is not supported when client or request is configured for FIPS",
   284  		},
   285  		"Outpost AccessPoint FIPS client region with matching ARN region": {
   286  			bucket: "arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   287  			config: &aws.Config{
   288  				EndpointResolver: endpoints.AwsUsGovPartition(),
   289  				Region:           aws.String("fips-us-gov-east-1"),
   290  				S3UseARNRegion:   aws.Bool(true),
   291  			},
   292  			expectedErr: "use of ARN is not supported when client or request is configured for FIPS",
   293  		},
   294  		"Outpost AccessPoint FIPS client region with cross-region ARN": {
   295  			bucket: "arn:aws-us-gov:s3-outposts:us-gov-west-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   296  			config: &aws.Config{
   297  				EndpointResolver: endpoints.AwsUsGovPartition(),
   298  				Region:           aws.String("fips-us-gov-east-1"),
   299  				S3UseARNRegion:   aws.Bool(true),
   300  			},
   301  			expectedErr: "use of ARN is not supported when client or request is configured for FIPS",
   302  		},
   303  		"Outpost AccessPoint with DualStack": {
   304  			bucket: "arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   305  			config: &aws.Config{
   306  				Region:       aws.String("us-west-2"),
   307  				UseDualStack: aws.Bool(true),
   308  			},
   309  			expectedErr: "ConfigurationError: client configured for S3 Dual-stack but is not supported with resource ARN",
   310  		},
   311  		"Outpost AccessPoint with Accelerate": {
   312  			bucket: "arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   313  			config: &aws.Config{
   314  				Region:          aws.String("us-west-2"),
   315  				S3UseAccelerate: aws.Bool(true),
   316  			},
   317  			expectedErr: "ConfigurationError: client configured for S3 Accelerate but is not supported with resource ARN",
   318  		},
   319  		"AccessPoint": {
   320  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
   321  			config: &aws.Config{
   322  				Region: aws.String("us-west-2"),
   323  			},
   324  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.us-west-2.amazonaws.com",
   325  			expectedSigningName:   "s3",
   326  			expectedSigningRegion: "us-west-2",
   327  		},
   328  		"AccessPoint slash delimiter": {
   329  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint/myendpoint",
   330  			config: &aws.Config{
   331  				Region: aws.String("us-west-2"),
   332  			},
   333  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.us-west-2.amazonaws.com",
   334  			expectedSigningName:   "s3",
   335  			expectedSigningRegion: "us-west-2",
   336  		},
   337  		"AccessPoint other partition": {
   338  			bucket: "arn:aws-cn:s3:cn-north-1:123456789012:accesspoint:myendpoint",
   339  			config: &aws.Config{
   340  				Region: aws.String("cn-north-1"),
   341  			},
   342  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.cn-north-1.amazonaws.com.cn",
   343  			expectedSigningName:   "s3",
   344  			expectedSigningRegion: "cn-north-1",
   345  		},
   346  		"AccessPoint Cross-Region Disabled": {
   347  			bucket: "arn:aws:s3:ap-south-1:123456789012:accesspoint:myendpoint",
   348  			config: &aws.Config{
   349  				Region: aws.String("us-west-2"),
   350  			},
   351  			expectedErr: "client region does not match provided ARN region",
   352  		},
   353  		"AccessPoint Cross-Region Enabled": {
   354  			bucket: "arn:aws:s3:ap-south-1:123456789012:accesspoint:myendpoint",
   355  			config: &aws.Config{
   356  				Region:         aws.String("us-west-2"),
   357  				S3UseARNRegion: aws.Bool(true),
   358  			},
   359  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.ap-south-1.amazonaws.com",
   360  			expectedSigningName:   "s3",
   361  			expectedSigningRegion: "ap-south-1",
   362  		},
   363  		"AccessPoint us-east-1": {
   364  			bucket: "arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint",
   365  			config: &aws.Config{
   366  				Region:         aws.String("us-east-1"),
   367  				S3UseARNRegion: aws.Bool(true),
   368  			},
   369  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.us-east-1.amazonaws.com",
   370  			expectedSigningName:   "s3",
   371  			expectedSigningRegion: "us-east-1",
   372  		},
   373  		"AccessPoint us-east-1 cross region": {
   374  			bucket: "arn:aws:s3:us-east-1:123456789012:accesspoint:myendpoint",
   375  			config: &aws.Config{
   376  				Region:         aws.String("us-west-2"),
   377  				S3UseARNRegion: aws.Bool(true),
   378  			},
   379  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.us-east-1.amazonaws.com",
   380  			expectedSigningName:   "s3",
   381  			expectedSigningRegion: "us-east-1",
   382  		},
   383  		"AccessPoint Cross-Partition not supported": {
   384  			bucket: "arn:aws-cn:s3:cn-north-1:123456789012:accesspoint:myendpoint",
   385  			config: &aws.Config{
   386  				Region:         aws.String("us-west-2"),
   387  				UseDualStack:   aws.Bool(true),
   388  				S3UseARNRegion: aws.Bool(true),
   389  			},
   390  			expectedErr: "client partition does not match provided ARN partition",
   391  		},
   392  		"AccessPoint DualStack": {
   393  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
   394  			config: &aws.Config{
   395  				Region:       aws.String("us-west-2"),
   396  				UseDualStack: aws.Bool(true),
   397  			},
   398  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.dualstack.us-west-2.amazonaws.com",
   399  			expectedSigningName:   "s3",
   400  			expectedSigningRegion: "us-west-2",
   401  		},
   402  		"AccessPoint FIPS same region with cross region disabled": {
   403  			bucket: "arn:aws-us-gov:s3:us-gov-west-1:123456789012:accesspoint:myendpoint",
   404  			config: &aws.Config{
   405  				Region: aws.String("fips-us-gov-west-1"),
   406  				EndpointResolver: endpoints.ResolverFunc(
   407  					func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
   408  						switch region {
   409  						case "fips-us-gov-west-1":
   410  							return endpoints.ResolvedEndpoint{
   411  								URL:           "s3-fips.us-gov-west-1.amazonaws.com",
   412  								PartitionID:   "aws-us-gov",
   413  								SigningRegion: "us-gov-west-1",
   414  								SigningName:   service,
   415  								SigningMethod: "s3v4",
   416  							}, nil
   417  						}
   418  						return endpoints.ResolvedEndpoint{}, nil
   419  					}),
   420  			},
   421  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint-fips.us-gov-west-1.amazonaws.com",
   422  			expectedSigningName:   "s3",
   423  			expectedSigningRegion: "us-gov-west-1",
   424  		},
   425  		"AccessPoint FIPS same region with cross region enabled": {
   426  			bucket: "arn:aws-us-gov:s3:us-gov-west-1:123456789012:accesspoint:myendpoint",
   427  			config: &aws.Config{
   428  				Region: aws.String("fips-us-gov-west-1"),
   429  				EndpointResolver: endpoints.ResolverFunc(
   430  					func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
   431  						switch region {
   432  						case "fips-us-gov-west-1":
   433  							return endpoints.ResolvedEndpoint{
   434  								URL:           "s3-fips.us-gov-west-1.amazonaws.com",
   435  								PartitionID:   "aws-us-gov",
   436  								SigningRegion: "us-gov-west-1",
   437  								SigningName:   service,
   438  								SigningMethod: "s3v4",
   439  							}, nil
   440  						}
   441  						return endpoints.ResolvedEndpoint{}, nil
   442  					}),
   443  				S3UseARNRegion: aws.Bool(true),
   444  			},
   445  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint-fips.us-gov-west-1.amazonaws.com",
   446  			expectedSigningName:   "s3",
   447  			expectedSigningRegion: "us-gov-west-1",
   448  		},
   449  		"AccessPoint FIPS cross region not supported": {
   450  			bucket: "arn:aws-us-gov:s3:us-gov-east-1:123456789012:accesspoint:myendpoint",
   451  			config: &aws.Config{
   452  				Region:         aws.String("fips-us-gov-west-1"),
   453  				S3UseARNRegion: aws.Bool(true),
   454  			},
   455  			expectedErr: "client configured for FIPS",
   456  		},
   457  		"AccessPoint Accelerate not supported": {
   458  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
   459  			config: &aws.Config{
   460  				Region:          aws.String("us-west-2"),
   461  				S3UseAccelerate: aws.Bool(true),
   462  			},
   463  			expectedErr: "client configured for S3 Accelerate",
   464  		},
   465  		"Custom Resolver Without PartitionID in ClientInfo": {
   466  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
   467  			config: &aws.Config{
   468  				Region: aws.String("us-west-2"),
   469  				EndpointResolver: endpoints.ResolverFunc(
   470  					func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
   471  						switch region {
   472  						case "us-west-2":
   473  							return endpoints.ResolvedEndpoint{
   474  								URL:           "s3.us-west-2.amazonaws.com",
   475  								SigningRegion: "us-west-2",
   476  								SigningName:   service,
   477  								SigningMethod: "s3v4",
   478  							}, nil
   479  						}
   480  						return endpoints.ResolvedEndpoint{}, nil
   481  					}),
   482  			},
   483  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.us-west-2.amazonaws.com",
   484  			expectedSigningRegion: "us-west-2",
   485  			expectedSigningName:   "s3",
   486  		},
   487  		"Custom Resolver Without PartitionID in Cross-Region Target": {
   488  			bucket: "arn:aws:s3:us-west-2:123456789012:accesspoint:myendpoint",
   489  			config: &aws.Config{
   490  				Region:         aws.String("us-east-1"),
   491  				S3UseARNRegion: aws.Bool(true),
   492  				EndpointResolver: endpoints.ResolverFunc(
   493  					func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
   494  						switch region {
   495  						case "us-west-2":
   496  							return endpoints.ResolvedEndpoint{
   497  								URL:           "s3.us-west-2.amazonaws.com",
   498  								PartitionID:   "aws",
   499  								SigningRegion: "us-west-2",
   500  								SigningName:   service,
   501  								SigningMethod: "s3v4",
   502  							}, nil
   503  						case "us-east-1":
   504  							return endpoints.ResolvedEndpoint{
   505  								URL:           "s3.us-east-1.amazonaws.com",
   506  								SigningRegion: "us-east-1",
   507  								SigningName:   service,
   508  								SigningMethod: "s3v4",
   509  							}, nil
   510  						}
   511  
   512  						return endpoints.ResolvedEndpoint{}, nil
   513  					}),
   514  			},
   515  			expectedEndpoint:      "https://myendpoint-123456789012.s3-accesspoint.us-west-2.amazonaws.com",
   516  			expectedSigningRegion: "us-west-2",
   517  			expectedSigningName:   "s3",
   518  		},
   519  		"bucket host-style": {
   520  			bucket:                "mock-bucket",
   521  			config:                &aws.Config{Region: aws.String("us-west-2")},
   522  			expectedEndpoint:      "https://mock-bucket.s3.us-west-2.amazonaws.com",
   523  			expectedSigningName:   "s3",
   524  			expectedSigningRegion: "us-west-2",
   525  		},
   526  		"bucket path-style": {
   527  			bucket: "mock-bucket",
   528  			config: &aws.Config{
   529  				Region:           aws.String("us-west-2"),
   530  				S3ForcePathStyle: aws.Bool(true),
   531  			},
   532  			expectedEndpoint:      "https://s3.us-west-2.amazonaws.com",
   533  			expectedSigningName:   "s3",
   534  			expectedSigningRegion: "us-west-2",
   535  		},
   536  		"bucket host-style endpoint with default port": {
   537  			bucket: "mock-bucket",
   538  			config: &aws.Config{
   539  				Region:   aws.String("us-west-2"),
   540  				Endpoint: aws.String("https://s3.us-west-2.amazonaws.com:443"),
   541  			},
   542  			expectedEndpoint:      "https://mock-bucket.s3.us-west-2.amazonaws.com:443",
   543  			expectedSigningName:   "s3",
   544  			expectedSigningRegion: "us-west-2",
   545  		},
   546  		"bucket host-style endpoint with non-default port": {
   547  			bucket: "mock-bucket",
   548  			config: &aws.Config{
   549  				Region:   aws.String("us-west-2"),
   550  				Endpoint: aws.String("https://s3.us-west-2.amazonaws.com:8443"),
   551  			},
   552  			expectedEndpoint:      "https://mock-bucket.s3.us-west-2.amazonaws.com:8443",
   553  			expectedSigningName:   "s3",
   554  			expectedSigningRegion: "us-west-2",
   555  		},
   556  		"bucket path-style endpoint with default port": {
   557  			bucket: "mock-bucket",
   558  			config: &aws.Config{
   559  				Region:           aws.String("us-west-2"),
   560  				Endpoint:         aws.String("https://s3.us-west-2.amazonaws.com:443"),
   561  				S3ForcePathStyle: aws.Bool(true),
   562  			},
   563  			expectedEndpoint:      "https://s3.us-west-2.amazonaws.com:443",
   564  			expectedSigningName:   "s3",
   565  			expectedSigningRegion: "us-west-2",
   566  		},
   567  		"bucket path-style endpoint with non-default port": {
   568  			bucket: "mock-bucket",
   569  			config: &aws.Config{
   570  				Region:           aws.String("us-west-2"),
   571  				Endpoint:         aws.String("https://s3.us-west-2.amazonaws.com:8443"),
   572  				S3ForcePathStyle: aws.Bool(true),
   573  			},
   574  			expectedEndpoint:      "https://s3.us-west-2.amazonaws.com:8443",
   575  			expectedSigningName:   "s3",
   576  			expectedSigningRegion: "us-west-2",
   577  		},
   578  		"Invalid AccessPoint ARN with FIPS pseudo-region (prefix)": {
   579  			bucket: "arn:aws:s3:fips-us-east-1:123456789012:accesspoint:myendpoint",
   580  			config: &aws.Config{
   581  				Region:         aws.String("us-west-2"),
   582  				S3UseARNRegion: aws.Bool(true),
   583  			},
   584  			expectedErr: "FIPS region not allowed in ARN",
   585  		},
   586  		"Invalid AccessPoint ARN with FIPS pseudo-region (suffix)": {
   587  			bucket: "arn:aws:s3:us-east-1-fips:123456789012:accesspoint:myendpoint",
   588  			config: &aws.Config{
   589  				Region:         aws.String("us-west-2"),
   590  				S3UseARNRegion: aws.Bool(true),
   591  			},
   592  			expectedErr: "FIPS region not allowed in ARN",
   593  		},
   594  		"Invalid Outpost AccessPoint ARN with FIPS pseudo-region (prefix)": {
   595  			bucket: "arn:aws:s3-outposts:fips-us-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   596  			config: &aws.Config{
   597  				Region:         aws.String("us-west-2"),
   598  				S3UseARNRegion: aws.Bool(true),
   599  			},
   600  			expectedErr: "FIPS region not allowed in ARN",
   601  		},
   602  		"Invalid Outpost AccessPoint ARN with FIPS pseudo-region (suffix)": {
   603  			bucket: "arn:aws:s3-outposts:us-east-1-fips:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint",
   604  			config: &aws.Config{
   605  				Region:         aws.String("us-west-2"),
   606  				S3UseARNRegion: aws.Bool(true),
   607  			},
   608  			expectedErr: "FIPS region not allowed in ARN",
   609  		},
   610  		"Invalid Object Lambda ARN with FIPS pseudo-region (prefix)": {
   611  			bucket: "arn:aws:s3-object-lambda:fips-us-east-1:123456789012:accesspoint/myap",
   612  			config: &aws.Config{
   613  				Region:         aws.String("us-west-2"),
   614  				S3UseARNRegion: aws.Bool(true),
   615  			},
   616  			expectedErr: "FIPS region not allowed in ARN",
   617  		},
   618  		"Invalid Object Lambda ARN with FIPS pseudo-region (suffix)": {
   619  			bucket: "arn:aws:s3-object-lambda:us-east-1-fips:123456789012:accesspoint/myap",
   620  			config: &aws.Config{
   621  				Region:         aws.String("us-west-2"),
   622  				S3UseARNRegion: aws.Bool(true),
   623  			},
   624  			expectedErr: "FIPS region not allowed in ARN",
   625  		},
   626  	}
   627  
   628  	for name, c := range cases {
   629  		t.Run(name, func(t *testing.T) {
   630  			if strings.EqualFold("az", name) {
   631  				fmt.Print()
   632  			}
   633  
   634  			sess := unit.Session.Copy(c.config)
   635  
   636  			svc := New(sess)
   637  
   638  			var req *request.Request
   639  			if c.req == nil {
   640  				req, _ = svc.ListObjectsRequest(&ListObjectsInput{
   641  					Bucket: &c.bucket,
   642  				})
   643  			} else {
   644  				req = c.req(svc)
   645  			}
   646  
   647  			req.Handlers.Send.Clear()
   648  			req.Handlers.Send.PushBack(func(r *request.Request) {
   649  				defer func() {
   650  					r.HTTPResponse = &http.Response{
   651  						StatusCode:    200,
   652  						ContentLength: 0,
   653  						Body:          ioutil.NopCloser(bytes.NewReader(nil)),
   654  					}
   655  				}()
   656  				if len(c.expectedErr) != 0 {
   657  					return
   658  				}
   659  
   660  				endpoint := fmt.Sprintf("%s://%s", r.HTTPRequest.URL.Scheme, r.HTTPRequest.URL.Host)
   661  				if e, a := c.expectedEndpoint, endpoint; e != a {
   662  					t.Errorf("expected %v, got %v", e, a)
   663  				}
   664  
   665  				if e, a := c.expectedSigningName, r.ClientInfo.SigningName; e != a {
   666  					t.Errorf("expected %v, got %v", e, a)
   667  				}
   668  				if e, a := c.expectedSigningRegion, r.ClientInfo.SigningRegion; e != a {
   669  					t.Errorf("expected %v, got %v", e, a)
   670  				}
   671  			})
   672  			err := req.Send()
   673  			if len(c.expectedErr) == 0 && err != nil {
   674  				t.Errorf("expected no error but got: %v", err)
   675  			} else if len(c.expectedErr) != 0 && err == nil {
   676  				t.Errorf("expected err %q, but got nil", c.expectedErr)
   677  			} else if len(c.expectedErr) != 0 && err != nil && !strings.Contains(err.Error(), c.expectedErr) {
   678  				t.Errorf("expected %v, got %v", c.expectedErr, err.Error())
   679  			}
   680  		})
   681  	}
   682  }
   683  
   684  func TestWriteGetObjectResponse_UpdateEndpoint(t *testing.T) {
   685  	cases := map[string]struct {
   686  		config                *aws.Config
   687  		expectedEndpoint      string
   688  		expectedSigningRegion string
   689  		expectedSigningName   string
   690  		expectedErr           string
   691  	}{
   692  		"standard endpoint": {
   693  			config: &aws.Config{
   694  				Region: aws.String("us-west-2"),
   695  			},
   696  			expectedEndpoint:      "https://test-route.s3-object-lambda.us-west-2.amazonaws.com",
   697  			expectedSigningRegion: "us-west-2",
   698  			expectedSigningName:   "s3-object-lambda",
   699  		},
   700  		"fips endpoint": {
   701  			config: &aws.Config{
   702  				Region: aws.String("fips-us-gov-west-1"),
   703  			},
   704  			expectedEndpoint:      "https://test-route.s3-object-lambda-fips.us-gov-west-1.amazonaws.com",
   705  			expectedSigningRegion: "us-gov-west-1",
   706  			expectedSigningName:   "s3-object-lambda",
   707  		},
   708  		"duakstack endpoint": {
   709  			config: &aws.Config{
   710  				Region:       aws.String("us-west-2"),
   711  				UseDualStack: aws.Bool(true),
   712  			},
   713  			expectedErr: "client configured for dualstack but not supported for operation",
   714  		},
   715  		"accelerate endpoint": {
   716  			config: &aws.Config{
   717  				Region:          aws.String("us-west-2"),
   718  				S3UseAccelerate: aws.Bool(true),
   719  			},
   720  			expectedErr: "client configured for accelerate but not supported for operation",
   721  		},
   722  		"custom endpoint": {
   723  			config: &aws.Config{
   724  				Region:   aws.String("us-west-2"),
   725  				Endpoint: aws.String("https://my-domain.com"),
   726  			},
   727  			expectedEndpoint:      "https://test-route.my-domain.com",
   728  			expectedSigningRegion: "us-west-2",
   729  			expectedSigningName:   "s3-object-lambda",
   730  		},
   731  	}
   732  
   733  	for name, c := range cases {
   734  		t.Run(name, func(t *testing.T) {
   735  			sess := unit.Session.Copy(c.config)
   736  
   737  			svc := New(sess)
   738  
   739  			var req *request.Request
   740  			req, _ = svc.WriteGetObjectResponseRequest(&WriteGetObjectResponseInput{
   741  				RequestRoute: aws.String("test-route"),
   742  				RequestToken: aws.String("test-token"),
   743  			})
   744  
   745  			req.Handlers.Send.Clear()
   746  			req.Handlers.Send.PushBack(func(r *request.Request) {
   747  				defer func() {
   748  					r.HTTPResponse = &http.Response{
   749  						StatusCode:    200,
   750  						ContentLength: 0,
   751  						Body:          ioutil.NopCloser(bytes.NewReader(nil)),
   752  					}
   753  				}()
   754  				if len(c.expectedErr) != 0 {
   755  					return
   756  				}
   757  
   758  				endpoint := fmt.Sprintf("%s://%s", r.HTTPRequest.URL.Scheme, r.HTTPRequest.URL.Host)
   759  				if e, a := c.expectedEndpoint, endpoint; e != a {
   760  					t.Errorf("expected %v, got %v", e, a)
   761  				}
   762  
   763  				if e, a := c.expectedSigningName, r.ClientInfo.SigningName; e != a {
   764  					t.Errorf("expected %v, got %v", e, a)
   765  				}
   766  				if e, a := c.expectedSigningRegion, r.ClientInfo.SigningRegion; e != a {
   767  					t.Errorf("expected %v, got %v", e, a)
   768  				}
   769  			})
   770  			err := req.Send()
   771  			if len(c.expectedErr) == 0 && err != nil {
   772  				t.Errorf("expected no error but got: %v", err)
   773  			} else if len(c.expectedErr) != 0 && err == nil {
   774  				t.Errorf("expected err %q, but got nil", c.expectedErr)
   775  			} else if len(c.expectedErr) != 0 && err != nil && !strings.Contains(err.Error(), c.expectedErr) {
   776  				t.Errorf("expected %v, got %v", c.expectedErr, err.Error())
   777  			}
   778  		})
   779  	}
   780  }
   781  
   782  type readSeeker struct {
   783  	br *bytes.Reader
   784  }
   785  
   786  func (r *readSeeker) Read(p []byte) (int, error) {
   787  	return r.br.Read(p)
   788  }
   789  
   790  func (r *readSeeker) Seek(offset int64, whence int) (int64, error) {
   791  	return r.br.Seek(offset, whence)
   792  }
   793  
   794  type readOnlyReader struct {
   795  	br *bytes.Reader
   796  }
   797  
   798  func (r *readOnlyReader) Read(p []byte) (int, error) {
   799  	return r.br.Read(p)
   800  }
   801  
   802  type lenReader struct {
   803  	br *bytes.Reader
   804  }
   805  
   806  func (r *lenReader) Read(p []byte) (int, error) {
   807  	return r.br.Read(p)
   808  }
   809  
   810  func (r *lenReader) Len() int {
   811  	return r.br.Len()
   812  }
   813  
   814  func TestWriteGetObjectResponse(t *testing.T) {
   815  	cases := map[string]struct {
   816  		Handler func(*testing.T) http.Handler
   817  		Input   WriteGetObjectResponseInput
   818  	}{
   819  		"Content-Length seekable": {
   820  			Handler: func(t *testing.T) http.Handler {
   821  				return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
   822  					expectedInput := []byte("test input")
   823  
   824  					if len(request.TransferEncoding) != 0 {
   825  						t.Errorf("expect no transfer-encoding")
   826  					}
   827  
   828  					if e, a := fmt.Sprintf("%d", len(expectedInput)), request.Header.Get("Content-Length"); e != a {
   829  						t.Errorf("expect %v, got %v", e, a)
   830  					}
   831  
   832  					if e, a := "UNSIGNED-PAYLOAD", request.Header.Get("X-Amz-Content-Sha256"); e != a {
   833  						t.Errorf("expect %v, got %v", e, a)
   834  					}
   835  
   836  					all, err := ioutil.ReadAll(request.Body)
   837  					if err != nil {
   838  						t.Errorf("expect no error, got %v", err)
   839  					}
   840  					if !bytes.Equal(all, expectedInput) {
   841  						t.Error("input did not match expected")
   842  					}
   843  					writer.WriteHeader(200)
   844  				})
   845  			},
   846  			Input: WriteGetObjectResponseInput{
   847  				RequestRoute: aws.String("route"),
   848  				RequestToken: aws.String("token"),
   849  				Body:         &readSeeker{br: bytes.NewReader([]byte("test input"))},
   850  			},
   851  		},
   852  		"Content-Length Len Interface": {
   853  			Handler: func(t *testing.T) http.Handler {
   854  				return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
   855  					expectedInput := []byte("test input")
   856  
   857  					if len(request.TransferEncoding) != 0 {
   858  						t.Errorf("expect no transfer-encoding")
   859  					}
   860  
   861  					if e, a := fmt.Sprintf("%d", len(expectedInput)), request.Header.Get("Content-Length"); e != a {
   862  						t.Errorf("expect %v, got %v", e, a)
   863  					}
   864  
   865  					if e, a := "UNSIGNED-PAYLOAD", request.Header.Get("X-Amz-Content-Sha256"); e != a {
   866  						t.Errorf("expect %v, got %v", e, a)
   867  					}
   868  
   869  					all, err := ioutil.ReadAll(request.Body)
   870  					if err != nil {
   871  						t.Errorf("expect no error, got %v", err)
   872  					}
   873  					if !bytes.Equal(all, expectedInput) {
   874  						t.Error("input did not match expected")
   875  					}
   876  					writer.WriteHeader(200)
   877  				})
   878  			},
   879  			Input: WriteGetObjectResponseInput{
   880  				RequestRoute: aws.String("route"),
   881  				RequestToken: aws.String("token"),
   882  				Body:         aws.ReadSeekCloser(&lenReader{bytes.NewReader([]byte("test input"))}),
   883  			},
   884  		},
   885  		"Content-Length Input Parameter": {
   886  			Handler: func(t *testing.T) http.Handler {
   887  				return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
   888  					expectedInput := []byte("test input")
   889  
   890  					if len(request.TransferEncoding) != 0 {
   891  						t.Errorf("expect no transfer-encoding")
   892  					}
   893  
   894  					if e, a := fmt.Sprintf("%d", len(expectedInput)), request.Header.Get("Content-Length"); e != a {
   895  						t.Errorf("expect %v, got %v", e, a)
   896  					}
   897  
   898  					if e, a := "UNSIGNED-PAYLOAD", request.Header.Get("X-Amz-Content-Sha256"); e != a {
   899  						t.Errorf("expect %v, got %v", e, a)
   900  					}
   901  
   902  					all, err := ioutil.ReadAll(request.Body)
   903  					if err != nil {
   904  						t.Errorf("expect no error, got %v", err)
   905  					}
   906  					if !bytes.Equal(all, expectedInput) {
   907  						t.Error("input did not match expected")
   908  					}
   909  					writer.WriteHeader(200)
   910  				})
   911  			},
   912  			Input: WriteGetObjectResponseInput{
   913  				RequestRoute:  aws.String("route"),
   914  				RequestToken:  aws.String("token"),
   915  				Body:          aws.ReadSeekCloser(&readOnlyReader{bytes.NewReader([]byte("test input"))}),
   916  				ContentLength: aws.Int64(10),
   917  			},
   918  		},
   919  		"Content-Length Not Provided": {
   920  			Handler: func(t *testing.T) http.Handler {
   921  				return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
   922  					expectedInput := []byte("test input")
   923  
   924  					encoding := ""
   925  					if len(request.TransferEncoding) == 1 {
   926  						encoding = request.TransferEncoding[0]
   927  					}
   928  					if encoding != "chunked" {
   929  						t.Errorf("expect transfer-encoding chunked, got %v", encoding)
   930  					}
   931  
   932  					if e, a := "", request.Header.Get("Content-Length"); e != a {
   933  						t.Errorf("expect %v, got %v", e, a)
   934  					}
   935  
   936  					if e, a := "UNSIGNED-PAYLOAD", request.Header.Get("X-Amz-Content-Sha256"); e != a {
   937  						t.Errorf("expect %v, got %v", e, a)
   938  					}
   939  
   940  					all, err := ioutil.ReadAll(request.Body)
   941  					if err != nil {
   942  						t.Errorf("expect no error, got %v", err)
   943  					}
   944  					if !bytes.Equal(all, expectedInput) {
   945  						t.Error("input did not match expected")
   946  					}
   947  					writer.WriteHeader(200)
   948  				})
   949  			},
   950  			Input: WriteGetObjectResponseInput{
   951  				RequestRoute: aws.String("route"),
   952  				RequestToken: aws.String("token"),
   953  				Body:         aws.ReadSeekCloser(&readOnlyReader{bytes.NewReader([]byte("test input"))}),
   954  			},
   955  		},
   956  	}
   957  
   958  	for name, tt := range cases {
   959  		t.Run(name, func(t *testing.T) {
   960  			server := httptest.NewServer(tt.Handler(t))
   961  			defer server.Close()
   962  
   963  			sess := unit.Session.Copy(&aws.Config{
   964  				Region:                    aws.String("us-west-2"),
   965  				Endpoint:                  &server.URL,
   966  				DisableEndpointHostPrefix: aws.Bool(true),
   967  			})
   968  
   969  			client := New(sess)
   970  
   971  			_, err := client.WriteGetObjectResponse(&tt.Input)
   972  			if err != nil {
   973  				t.Fatalf("expect no error, got %v", err)
   974  			}
   975  		})
   976  	}
   977  }