1//go:build go1.7 2// +build go1.7 3 4package s3 5 6import ( 7 "bytes" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "strings" 13 "testing" 14 15 "github.com/aws/aws-sdk-go/aws" 16 "github.com/aws/aws-sdk-go/aws/endpoints" 17 "github.com/aws/aws-sdk-go/aws/request" 18 "github.com/aws/aws-sdk-go/awstesting/unit" 19) 20 21func 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 684func 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 782type readSeeker struct { 783 br *bytes.Reader 784} 785 786func (r *readSeeker) Read(p []byte) (int, error) { 787 return r.br.Read(p) 788} 789 790func (r *readSeeker) Seek(offset int64, whence int) (int64, error) { 791 return r.br.Seek(offset, whence) 792} 793 794type readOnlyReader struct { 795 br *bytes.Reader 796} 797 798func (r *readOnlyReader) Read(p []byte) (int, error) { 799 return r.br.Read(p) 800} 801 802type lenReader struct { 803 br *bytes.Reader 804} 805 806func (r *lenReader) Read(p []byte) (int, error) { 807 return r.br.Read(p) 808} 809 810func (r *lenReader) Len() int { 811 return r.br.Len() 812} 813 814func 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} 978