-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathnetstorage.go
293 lines (254 loc) · 8.26 KB
/
netstorage.go
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
// Package netstorage provides interfacing the Akamai Netstorage(File/Object Store) API http(s) call
package netstorage
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"path"
"strings"
"time"
)
// Netstorage struct provides all the necessary fields to
// create authorization headers.
// They are on the Akamai Netstorage account page.
// Hostname format should be "-nsu.akamaihd.net" and
// Note that don't expose Key on public repository.
// "Ssl" element is decided by "NetNetstorage" function - string "s" means https and "" does http.
type Netstorage struct {
Hostname string
Keyname string
Key string
Ssl string
Client *http.Client
}
// NewNetstorage func creates and initiates Netstorage struct.
// ssl parameter decides https(true) and http(false) which means "s" and "".
func NewNetstorage(hostname, keyname, key string, ssl bool) *Netstorage {
if hostname == "" || keyname == "" || key == "" {
panic("[NetstorageError] You should input netstorage hostname, keyname and key all")
}
s := ""
if ssl {
s = "s"
}
return &Netstorage{hostname, keyname, key, s, http.DefaultClient}
}
// Only for upload action. (Used by _request func)
func _ifUploadAction(kwargs map[string]string) (*io.Reader, error) {
var data io.Reader
if kwargs["action"] == "upload" {
bArr, err := ioutil.ReadFile(kwargs["source"])
if err != nil {
return nil, err
}
data = bytes.NewReader(bArr)
}
return &data, nil
}
// Reads http body from response, closes response.Body and
// returns that string. (Used by _request func)
func _getBody(kwargs map[string]string, response *http.Response) (string, error) {
var body []byte
var err error
if kwargs["action"] == "download" && response.StatusCode == 200 {
localDestination := kwargs["destination"]
if localDestination == "" {
localDestination = path.Base(kwargs["path"])
} else if s, err := os.Stat(localDestination); err == nil && s.IsDir() {
localDestination = path.Join(localDestination, path.Base(kwargs["path"]))
}
out, err := os.Create(localDestination)
if err != nil {
return "", err
}
defer out.Close()
if _, err := io.Copy(out, response.Body); err != nil {
return "", err
}
body = []byte("Download done")
} else {
body, err = ioutil.ReadAll(response.Body)
if err != nil {
return "", err
}
}
return string(body), nil
}
// Create the authorization headers with Netstorage struct values then
// request to the Netstorage hostname, and return the response,
// the body string and the error.
func (ns *Netstorage) _request(kwargs map[string]string) (*http.Response, string, error) {
var err error
nsPath := kwargs["path"]
if u, err := url.Parse(nsPath); strings.HasPrefix(nsPath, "/") && err == nil {
nsPath = u.RequestURI()
} else {
return nil, "", fmt.Errorf("[Netstorage Error] Invalid netstorage path: %s", nsPath)
}
acsAction := fmt.Sprintf("version=1&action=%s", kwargs["action"])
acsAuthData := fmt.Sprintf("5, 0.0.0.0, 0.0.0.0, %d, %d, %s",
time.Now().Unix(),
rand.Intn(100000),
ns.Keyname)
signString := fmt.Sprintf("%s\nx-akamai-acs-action:%s\n", nsPath, acsAction)
mac := hmac.New(sha256.New, []byte(ns.Key))
mac.Write([]byte(acsAuthData + signString))
acsAuthSign := base64.StdEncoding.EncodeToString(mac.Sum(nil))
data, err := _ifUploadAction(kwargs)
if err != nil {
return nil, "", err
}
request, err := http.NewRequest(kwargs["method"],
fmt.Sprintf("http%s://%s%s", ns.Ssl, ns.Hostname, nsPath), *data)
if err != nil {
return nil, "", err
}
request.Header.Add("X-Akamai-ACS-Action", acsAction)
request.Header.Add("X-Akamai-ACS-Auth-Data", acsAuthData)
request.Header.Add("X-Akamai-ACS-Auth-Sign", acsAuthSign)
request.Header.Add("Accept-Encoding", "identity")
request.Header.Add("User-Agent", "NetStorageKit-Golang")
response, err := ns.Client.Do(request)
if err != nil {
return nil, "", err
}
defer response.Body.Close()
body, err := _getBody(kwargs, response)
return response, body, err
}
// Dir returns the directory structure
func (ns *Netstorage) Dir(nsPath string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "dir&format=xml",
"method": "GET",
"path": nsPath,
})
}
// Download returns the string "Download done" when the download completes.
// The first parameter is Netstorage source path and
// the second is Local destination path. If you put only the first parameter,
// it downloads to current local path with the first parameter's file name.
// From the third parameters will be ignored.
// Note that you can downlad only a file, not a directory.
func (ns *Netstorage) Download(path ...string) (*http.Response, string, error) {
nsSource := path[0]
if strings.HasSuffix(nsSource, "/") {
return nil, "", fmt.Errorf("[NetstorageError] Nestorage download path shouldn't be a directory: %s", nsSource)
}
localDestination := ""
if len(path) >= 2 {
localDestination = path[1]
}
return ns._request(map[string]string{
"action": "download",
"method": "GET",
"path": nsSource,
"destination": localDestination,
})
}
// Du returns the disk usage information for a directory
func (ns *Netstorage) Du(nsPath string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "du&format=xml",
"method": "GET",
"path": nsPath,
})
}
// Stat returns the information about an object structure
func (ns *Netstorage) Stat(nsPath string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "stat&format=xml",
"method": "GET",
"path": nsPath,
})
}
// Mkdir creates an empty directory
func (ns *Netstorage) Mkdir(nsPath string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "mkdir",
"method": "POST",
"path": nsPath,
})
}
// Rmdir deletes an empty directory
func (ns *Netstorage) Rmdir(nsPath string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "rmdir",
"method": "POST",
"path": nsPath,
})
}
// Mtime changes a file’s mtime
func (ns *Netstorage) Mtime(nsPath string, mtime int64) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": fmt.Sprintf("mtime&format=xml&mtime=%d", mtime),
"method": "POST",
"path": nsPath,
})
}
// Delete deletes an object/symbolic link
func (ns *Netstorage) Delete(nsPath string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "delete",
"method": "POST",
"path": nsPath,
})
}
// QuickDelete deletes a directory (i.e., recursively delete a directory tree)
// In order to use this func, you need to the privilege on the CP Code.
func (ns *Netstorage) QuickDelete(nsPath string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "quick-delete&quick-delete=imreallyreallysure",
"method": "POST",
"path": nsPath,
})
}
// Rename renames a file or symbolic link.
func (ns *Netstorage) Rename(nsTarget, nsDestination string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "rename&destination=" + url.QueryEscape(nsDestination),
"method": "POST",
"path": nsTarget,
})
}
// Symlink creates a symbolic link.
func (ns *Netstorage) Symlink(nsTarget, nsDestination string) (*http.Response, string, error) {
return ns._request(map[string]string{
"action": "symlink&target=" + url.QueryEscape(nsTarget),
"method": "POST",
"path": nsDestination,
})
}
// Upload uploads an object.
// The first parameter is the local source path and the second is
// the Netstorage destination path.
// If you put the directory path on "nsDestination" parameter, that filename
// will be the "localSource" parameter filename.
// Note that you can upload only a file, not a directory.
func (ns *Netstorage) Upload(localSource, nsDestination string) (*http.Response, string, error) {
s, err := os.Stat(localSource)
if err != nil {
return nil, "", err
}
if s.Mode().IsRegular() {
if strings.HasSuffix(nsDestination, "/") {
nsDestination = nsDestination + path.Base(localSource)
}
} else {
return nil, "", fmt.Errorf("[NetstorageError] You should upload a file, not %s", localSource)
}
return ns._request(map[string]string{
"action": "upload",
"method": "PUT",
"source": localSource,
"path": nsDestination,
})
}