You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
4.6 KiB
235 lines
4.6 KiB
package upyun
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/md5"
|
|
"crypto/sha1"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
escape []uint32 = []uint32{
|
|
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
|
|
|
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
|
|
0xfc001fff, /* 1111 1100 0000 0000 0001 1111 1111 1111 */
|
|
|
|
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
|
|
0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */
|
|
|
|
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
|
|
0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */
|
|
|
|
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
|
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
|
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
|
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
|
}
|
|
hexMap = "0123456789ABCDEF"
|
|
)
|
|
|
|
func makeRFC1123Date(d time.Time) string {
|
|
utc := d.UTC().Format(time.RFC1123)
|
|
return strings.ReplaceAll(utc, "UTC", "GMT")
|
|
}
|
|
|
|
func makeUserAgent(version string) string {
|
|
return fmt.Sprintf("UPYUN Go SDK V2/%s", version)
|
|
}
|
|
|
|
func md5Str(s string) string {
|
|
return fmt.Sprintf("%x", md5.Sum([]byte(s)))
|
|
}
|
|
|
|
func base64ToStr(b []byte) string {
|
|
return base64.StdEncoding.EncodeToString(b)
|
|
}
|
|
|
|
func hmacSha1(key string, data []byte) []byte {
|
|
hm := hmac.New(sha1.New, []byte(key))
|
|
hm.Write(data)
|
|
return hm.Sum(nil)
|
|
}
|
|
|
|
func ishex(c byte) bool {
|
|
switch {
|
|
case '0' <= c && c <= '9':
|
|
return true
|
|
case 'a' <= c && c <= 'f':
|
|
return true
|
|
case 'A' <= c && c <= 'F':
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func unhex(c byte) byte {
|
|
switch {
|
|
case '0' <= c && c <= '9':
|
|
return c - '0'
|
|
case 'a' <= c && c <= 'f':
|
|
return c - 'a' + 10
|
|
case 'A' <= c && c <= 'F':
|
|
return c - 'A' + 10
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func escapeUri(s string) string {
|
|
size := 0
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
if escape[c>>5]&(1<<(c&0x1f)) > 0 {
|
|
size += 3
|
|
} else {
|
|
size++
|
|
}
|
|
}
|
|
|
|
ret := make([]byte, size)
|
|
j := 0
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
if escape[c>>5]&(1<<(c&0x1f)) > 0 {
|
|
ret[j] = '%'
|
|
ret[j+1] = hexMap[c>>4]
|
|
ret[j+2] = hexMap[c&0xf]
|
|
j += 3
|
|
} else {
|
|
ret[j] = c
|
|
j += 1
|
|
}
|
|
}
|
|
return string(ret)
|
|
}
|
|
|
|
func unescapeUri(s string) string {
|
|
n := 0
|
|
for i := 0; i < len(s); n++ {
|
|
switch s[i] {
|
|
case '%':
|
|
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
|
|
// if not correct, return original string
|
|
return s
|
|
}
|
|
i += 3
|
|
default:
|
|
i++
|
|
}
|
|
}
|
|
|
|
t := make([]byte, n)
|
|
j := 0
|
|
for i := 0; i < len(s); j++ {
|
|
switch s[i] {
|
|
case '%':
|
|
t[j] = unhex(s[i+1])<<4 | unhex(s[i+2])
|
|
i += 3
|
|
default:
|
|
t[j] = s[i]
|
|
i++
|
|
}
|
|
}
|
|
return string(t)
|
|
}
|
|
|
|
var readHTTPBody = ioutil.ReadAll
|
|
|
|
func readHTTPBodyToStr(resp *http.Response) (string, error) {
|
|
b, err := readHTTPBody(resp.Body)
|
|
resp.Body.Close()
|
|
if err != nil {
|
|
return "", errorOperation("read body", err)
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
func addQueryToUri(rawurl string, kwargs map[string]string) string {
|
|
u, _ := url.ParseRequestURI(rawurl)
|
|
q := u.Query()
|
|
for k, v := range kwargs {
|
|
q.Add(k, v)
|
|
}
|
|
u.RawQuery = q.Encode()
|
|
return u.String()
|
|
}
|
|
|
|
func encodeQueryToPayload(kwargs map[string]string) string {
|
|
payload := url.Values{}
|
|
for k, v := range kwargs {
|
|
payload.Set(k, v)
|
|
}
|
|
return payload.Encode()
|
|
}
|
|
|
|
func readHTTPBodyToInt(resp *http.Response) (int64, error) {
|
|
b, err := readHTTPBody(resp.Body)
|
|
resp.Body.Close()
|
|
if err != nil {
|
|
return 0, errorOperation("read body", err)
|
|
}
|
|
|
|
n, err := strconv.ParseInt(string(b), 10, 64)
|
|
if err != nil {
|
|
return 0, errorOperation("parse int", err)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func parseStrToInt(s string) int64 {
|
|
n, _ := strconv.ParseInt(s, 10, 64)
|
|
return n
|
|
}
|
|
|
|
func md5File(f io.ReadSeeker) (string, error) {
|
|
offset, _ := f.Seek(0, 0)
|
|
defer f.Seek(offset, 0)
|
|
hash := md5.New()
|
|
if _, err := io.Copy(hash, f); err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
|
}
|
|
|
|
type JsonFileInfo struct {
|
|
ContentType string `json:"type"`
|
|
Name string `json:"name"`
|
|
Length int64 `json:"length"`
|
|
LastModified int64 `json:"last_modified"`
|
|
}
|
|
type JsonFiles struct {
|
|
Files []*JsonFileInfo `json:"files"`
|
|
Iter string `json:"iter"`
|
|
}
|
|
|
|
func parseBodyToFileInfos(b []byte) (iter string, fInfos []*FileInfo, err error) {
|
|
files := &JsonFiles{}
|
|
err = json.Unmarshal(b, files)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
iter = files.Iter
|
|
fInfos = make([]*FileInfo, len(files.Files))
|
|
|
|
for i, f := range files.Files {
|
|
fInfos[i] = &FileInfo{
|
|
Name: f.Name,
|
|
IsDir: f.ContentType == "folder",
|
|
ContentType: f.ContentType,
|
|
Size: f.Length,
|
|
Time: time.Unix(f.LastModified, 0),
|
|
}
|
|
}
|
|
return
|
|
}
|