forked from rhnvrm/simples3
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimples3.go
More file actions
156 lines (131 loc) · 3.66 KB
/
simples3.go
File metadata and controls
156 lines (131 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// LICENSE BSD-2-Clause-FreeBSD
// Copyright (c) 2018, Rohan Verma <hello@rohanverma.net>
package simples3
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"fmt"
"net/http"
"strings"
"sync"
"time"
)
const (
// AMZMetaPrefix to prefix metadata key.
AMZMetaPrefix = "x-amz-meta-"
)
// S3 provides a wrapper around your S3 credentials.
type S3 struct {
AccessKey string
SecretKey string
Region string
Client *http.Client
Token string
Endpoint string
URIFormat string
UsePathStyle bool // Use path-style URLs (default: true)
initMode string
expiry time.Time
mu sync.Mutex
}
// New returns an instance of S3.
func New(region, accessKey, secretKey string) *S3 {
return &S3{
Region: region,
AccessKey: accessKey,
SecretKey: secretKey,
URIFormat: "https://s3.%s.amazonaws.com/%s",
UsePathStyle: true,
}
}
func (s3 *S3) getClient() *http.Client {
if s3.Client == nil {
return http.DefaultClient
}
return s3.Client
}
// SetEndpoint can be used to the set a custom endpoint for
// using an alternate instance compatible with the s3 API.
// If no protocol is included in the URI, defaults to HTTPS.
func (s3 *S3) SetEndpoint(uri string) *S3 {
if len(uri) > 0 {
if !strings.HasPrefix(uri, "http") {
uri = "https://" + uri
}
// make sure there is no trailing slash
uri = strings.TrimRight(uri, "/")
s3.Endpoint = uri
}
return s3
}
// SetToken can be used to set a Temporary Security Credential token obtained from
// using an IAM role or AWS STS.
func (s3 *S3) SetToken(token string) *S3 {
if token != "" {
s3.Token = token
}
return s3
}
// SetClient can be used to set the http client to be
// used by the package. If client passed is nil,
// http.DefaultClient is used.
func (s3 *S3) SetClient(client *http.Client) *S3 {
if client != nil {
s3.Client = client
} else {
s3.Client = http.DefaultClient
}
return s3
}
// SetPathStyle enables path-style URLs (e.g., https://s3.region.amazonaws.com/bucket/key).
// This is the default behavior.
func (s3 *S3) SetPathStyle() *S3 {
s3.UsePathStyle = true
return s3
}
// SetVirtualHostedStyle enables virtual-hosted style URLs (e.g., https://bucket.s3.region.amazonaws.com/key).
// When using virtual-hosted style, the bucket name should be included in the Endpoint,
// and the Bucket parameter in API calls can be empty.
func (s3 *S3) SetVirtualHostedStyle() *S3 {
s3.UsePathStyle = false
return s3
}
func (s3 *S3) signRequest(req *http.Request) error {
var (
err error
date = req.Header.Get("Date")
t = time.Now().UTC()
)
if date != "" {
t, err = time.Parse(http.TimeFormat, date)
if err != nil {
return err
}
}
req.Header.Set("Date", t.Format(amzDateISO8601TimeFormat))
req.Header.Set("X-Amz-Date", t.Format(amzDateISO8601TimeFormat))
if s3.Token != "" {
req.Header.Set("X-Amz-Security-Token", s3.Token)
}
// The x-amz-content-sha256 header is required for all AWS
// Signature Version 4 requests. It provides a hash of the
// request payload. If there is no payload, you must provide
// the hash of an empty string.
if req.Header.Get("x-amz-content-sha256") == "" {
emptyhash := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
req.Header.Set("x-amz-content-sha256", emptyhash)
}
k := s3.signKeys(t)
h := hmac.New(sha256.New, k)
s3.writeStringToSign(h, t, req)
auth := bytes.NewBufferString(algorithm)
auth.Write([]byte(" Credential=" + s3.AccessKey + "/" + s3.creds(t)))
auth.Write([]byte{',', ' '})
auth.Write([]byte("SignedHeaders="))
writeHeaderList(auth, req)
auth.Write([]byte{',', ' '})
auth.Write([]byte("Signature=" + fmt.Sprintf("%x", h.Sum(nil))))
req.Header.Set("Authorization", auth.String())
return nil
}