1package s3_test
2
3import (
4	"encoding/json"
5	"net/url"
6	"os"
7	"path/filepath"
8	"strings"
9	"testing"
10
11	"github.com/aws/aws-sdk-go/aws"
12	"github.com/aws/aws-sdk-go/aws/awserr"
13	"github.com/aws/aws-sdk-go/aws/endpoints"
14	"github.com/aws/aws-sdk-go/awstesting/unit"
15	"github.com/aws/aws-sdk-go/service/s3"
16)
17
18type s3BucketTest struct {
19	bucket  string
20	url     string
21	errCode string
22}
23
24var (
25	sslTests = []s3BucketTest{
26		{"abc", "https://abc.s3.mock-region.amazonaws.com/", ""},
27		{"a$b$c", "https://s3.mock-region.amazonaws.com/a%24b%24c", ""},
28		{"a.b.c", "https://s3.mock-region.amazonaws.com/a.b.c", ""},
29		{"a..bc", "https://s3.mock-region.amazonaws.com/a..bc", ""},
30	}
31
32	nosslTests = []s3BucketTest{
33		{"a.b.c", "http://a.b.c.s3.mock-region.amazonaws.com/", ""},
34		{"a..bc", "http://s3.mock-region.amazonaws.com/a..bc", ""},
35	}
36
37	forcepathTests = []s3BucketTest{
38		{"abc", "https://s3.mock-region.amazonaws.com/abc", ""},
39		{"a$b$c", "https://s3.mock-region.amazonaws.com/a%24b%24c", ""},
40		{"a.b.c", "https://s3.mock-region.amazonaws.com/a.b.c", ""},
41		{"a..bc", "https://s3.mock-region.amazonaws.com/a..bc", ""},
42	}
43
44	accelerateTests = []s3BucketTest{
45		{"abc", "https://abc.s3-accelerate.amazonaws.com/", ""},
46		{"a.b.c", "https://s3.mock-region.amazonaws.com/%7BBucket%7D", "InvalidParameterException"},
47		{"a$b$c", "https://s3.mock-region.amazonaws.com/%7BBucket%7D", "InvalidParameterException"},
48	}
49
50	accelerateNoSSLTests = []s3BucketTest{
51		{"abc", "http://abc.s3-accelerate.amazonaws.com/", ""},
52		{"a.b.c", "http://a.b.c.s3-accelerate.amazonaws.com/", ""},
53		{"a$b$c", "http://s3.mock-region.amazonaws.com/%7BBucket%7D", "InvalidParameterException"},
54	}
55
56	accelerateDualstack = []s3BucketTest{
57		{"abc", "https://abc.s3-accelerate.dualstack.amazonaws.com/", ""},
58		{"a.b.c", "https://s3.dualstack.mock-region.amazonaws.com/%7BBucket%7D", "InvalidParameterException"},
59		{"a$b$c", "https://s3.dualstack.mock-region.amazonaws.com/%7BBucket%7D", "InvalidParameterException"},
60	}
61)
62
63func runTests(t *testing.T, svc *s3.S3, tests []s3BucketTest) {
64	for i, test := range tests {
65		req, _ := svc.ListObjectsRequest(&s3.ListObjectsInput{Bucket: &test.bucket})
66		req.Build()
67		if e, a := test.url, req.HTTPRequest.URL.String(); e != a {
68			t.Errorf("%d, expect url %s, got %s", i, e, a)
69		}
70		if test.errCode != "" {
71			if err := req.Error; err == nil {
72				t.Fatalf("%d, expect no error", i)
73			}
74			if a, e := req.Error.(awserr.Error).Code(), test.errCode; !strings.Contains(a, e) {
75				t.Errorf("%d, expect error code to contain %q, got %q", i, e, a)
76			}
77		}
78	}
79}
80
81func TestAccelerateBucketBuild(t *testing.T) {
82	s := s3.New(unit.Session, &aws.Config{S3UseAccelerate: aws.Bool(true)})
83	runTests(t, s, accelerateTests)
84}
85
86func TestAccelerateNoSSLBucketBuild(t *testing.T) {
87	s := s3.New(unit.Session, &aws.Config{S3UseAccelerate: aws.Bool(true), DisableSSL: aws.Bool(true)})
88	runTests(t, s, accelerateNoSSLTests)
89}
90
91func TestAccelerateDualstackBucketBuild(t *testing.T) {
92	s := s3.New(unit.Session, &aws.Config{
93		S3UseAccelerate: aws.Bool(true),
94		UseDualStack:    aws.Bool(true),
95	})
96	runTests(t, s, accelerateDualstack)
97}
98
99func TestHostStyleBucketBuild(t *testing.T) {
100	s := s3.New(unit.Session)
101	runTests(t, s, sslTests)
102}
103
104func TestHostStyleBucketBuildNoSSL(t *testing.T) {
105	s := s3.New(unit.Session, &aws.Config{DisableSSL: aws.Bool(true)})
106	runTests(t, s, nosslTests)
107}
108
109func TestPathStyleBucketBuild(t *testing.T) {
110	s := s3.New(unit.Session, &aws.Config{S3ForcePathStyle: aws.Bool(true)})
111	runTests(t, s, forcepathTests)
112}
113
114func TestHostStyleBucketGetBucketLocation(t *testing.T) {
115	s := s3.New(unit.Session)
116	req, _ := s.GetBucketLocationRequest(&s3.GetBucketLocationInput{
117		Bucket: aws.String("bucket"),
118	})
119
120	req.Build()
121	if req.Error != nil {
122		t.Fatalf("expect no error, got %v", req.Error)
123	}
124	u, _ := url.Parse(req.HTTPRequest.URL.String())
125	if e, a := "bucket", u.Host; strings.Contains(a, e) {
126		t.Errorf("expect %s to not be in %s", e, a)
127	}
128	if e, a := "bucket", u.Path; !strings.Contains(a, e) {
129		t.Errorf("expect %s to be in %s", e, a)
130	}
131}
132
133func TestVirtualHostStyleSuite(t *testing.T) {
134	f, err := os.Open(filepath.Join("testdata", "virtual_host.json"))
135	if err != nil {
136		t.Fatalf("expect no error, %v", err)
137	}
138
139	cases := []struct {
140		Bucket                    string
141		Region                    string
142		UseDualStack              bool
143		UseS3Accelerate           bool
144		S3UsEast1RegionalEndpoint string
145		ConfiguredAddressingStyle string
146
147		ExpectedURI string
148	}{}
149
150	decoder := json.NewDecoder(f)
151	if err := decoder.Decode(&cases); err != nil {
152		t.Fatalf("expect no error, %v", err)
153	}
154
155	const testPathStyle = "path"
156	for i, c := range cases {
157		svc := s3.New(unit.Session, &aws.Config{
158			Region:           &c.Region,
159			UseDualStack:     &c.UseDualStack,
160			S3UseAccelerate:  &c.UseS3Accelerate,
161			S3ForcePathStyle: aws.Bool(c.ConfiguredAddressingStyle == testPathStyle),
162			S3UsEast1RegionalEndpoint: func() endpoints.S3UsEast1RegionalEndpoint {
163				if len(c.S3UsEast1RegionalEndpoint) == 0 {
164					return endpoints.UnsetS3UsEast1Endpoint
165				}
166				v, err := endpoints.GetS3UsEast1RegionalEndpoint(c.S3UsEast1RegionalEndpoint)
167				if err != nil {
168					t.Fatalf("unexpected error, %v", err)
169				}
170				return v
171			}(),
172		})
173
174		req, _ := svc.HeadBucketRequest(&s3.HeadBucketInput{
175			Bucket: &c.Bucket,
176		})
177		req.Build()
178		if req.Error != nil {
179			t.Fatalf("expect no error, got %v", req.Error)
180		}
181
182		// Trim trailing '/' that are added by the SDK but not in the tests.
183		actualURI := strings.TrimRightFunc(
184			req.HTTPRequest.URL.String(),
185			func(r rune) bool { return r == '/' },
186		)
187		if e, a := c.ExpectedURI, actualURI; e != a {
188			t.Errorf("%d URLs do not match\nexpect: %s\nactual: %s", i, e, a)
189		}
190	}
191}
192