598 lines
18 KiB
Go
598 lines
18 KiB
Go
package service
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/hmac"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"mime"
|
|
"net/url"
|
|
"path"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/alibabacloud-go/tea/tea"
|
|
)
|
|
|
|
var characters = map[string]bool{"-": true, "_": true, ".": true, "~": true}
|
|
|
|
// signKeyList is a list about params without value
|
|
var signKeyList = []string{"location", "cors", "objectMeta",
|
|
"uploadId", "partNumber", "security-token",
|
|
"position", "img", "style", "styleName",
|
|
"replication", "replicationProgress",
|
|
"replicationLocation", "cname", "qos",
|
|
"startTime", "endTime", "symlink",
|
|
"x-oss-process", "response-content-type",
|
|
"response-content-language", "response-expires",
|
|
"response-cache-control", "response-content-disposition",
|
|
"response-content-encoding", "udf", "udfName", "udfImage",
|
|
"udfId", "udfImageDesc", "udfApplication",
|
|
"udfApplicationLog", "restore", "callback", "callback-var",
|
|
"policy", "encryption", "versions", "versioning", "versionId"}
|
|
|
|
var extToMimeType = map[string]string{
|
|
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
|
".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
|
|
".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
|
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
|
|
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
|
".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
|
|
".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
|
".apk": "application/vnd.android.package-archive",
|
|
".hqx": "application/mac-binhex40",
|
|
".cpt": "application/mac-compactpro",
|
|
".doc": "application/msword",
|
|
".ogg": "application/ogg",
|
|
".pdf": "application/pdf",
|
|
".rtf": "text/rtf",
|
|
".mif": "application/vnd.mif",
|
|
".xls": "application/vnd.ms-excel",
|
|
".ppt": "application/vnd.ms-powerpoint",
|
|
".odc": "application/vnd.oasis.opendocument.chart",
|
|
".odb": "application/vnd.oasis.opendocument.database",
|
|
".odf": "application/vnd.oasis.opendocument.formula",
|
|
".odg": "application/vnd.oasis.opendocument.graphics",
|
|
".otg": "application/vnd.oasis.opendocument.graphics-template",
|
|
".odi": "application/vnd.oasis.opendocument.image",
|
|
".odp": "application/vnd.oasis.opendocument.presentation",
|
|
".otp": "application/vnd.oasis.opendocument.presentation-template",
|
|
".ods": "application/vnd.oasis.opendocument.spreadsheet",
|
|
".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
|
|
".odt": "application/vnd.oasis.opendocument.text",
|
|
".odm": "application/vnd.oasis.opendocument.text-master",
|
|
".ott": "application/vnd.oasis.opendocument.text-template",
|
|
".oth": "application/vnd.oasis.opendocument.text-web",
|
|
".sxw": "application/vnd.sun.xml.writer",
|
|
".stw": "application/vnd.sun.xml.writer.template",
|
|
".sxc": "application/vnd.sun.xml.calc",
|
|
".stc": "application/vnd.sun.xml.calc.template",
|
|
".sxd": "application/vnd.sun.xml.draw",
|
|
".std": "application/vnd.sun.xml.draw.template",
|
|
".sxi": "application/vnd.sun.xml.impress",
|
|
".sti": "application/vnd.sun.xml.impress.template",
|
|
".sxg": "application/vnd.sun.xml.writer.global",
|
|
".sxm": "application/vnd.sun.xml.math",
|
|
".sis": "application/vnd.symbian.install",
|
|
".wbxml": "application/vnd.wap.wbxml",
|
|
".wmlc": "application/vnd.wap.wmlc",
|
|
".wmlsc": "application/vnd.wap.wmlscriptc",
|
|
".bcpio": "application/x-bcpio",
|
|
".torrent": "application/x-bittorrent",
|
|
".bz2": "application/x-bzip2",
|
|
".vcd": "application/x-cdlink",
|
|
".pgn": "application/x-chess-pgn",
|
|
".cpio": "application/x-cpio",
|
|
".csh": "application/x-csh",
|
|
".dvi": "application/x-dvi",
|
|
".spl": "application/x-futuresplash",
|
|
".gtar": "application/x-gtar",
|
|
".hdf": "application/x-hdf",
|
|
".jar": "application/x-java-archive",
|
|
".jnlp": "application/x-java-jnlp-file",
|
|
".js": "application/x-javascript",
|
|
".ksp": "application/x-kspread",
|
|
".chrt": "application/x-kchart",
|
|
".kil": "application/x-killustrator",
|
|
".latex": "application/x-latex",
|
|
".rpm": "application/x-rpm",
|
|
".sh": "application/x-sh",
|
|
".shar": "application/x-shar",
|
|
".swf": "application/x-shockwave-flash",
|
|
".sit": "application/x-stuffit",
|
|
".sv4cpio": "application/x-sv4cpio",
|
|
".sv4crc": "application/x-sv4crc",
|
|
".tar": "application/x-tar",
|
|
".tcl": "application/x-tcl",
|
|
".tex": "application/x-tex",
|
|
".man": "application/x-troff-man",
|
|
".me": "application/x-troff-me",
|
|
".ms": "application/x-troff-ms",
|
|
".ustar": "application/x-ustar",
|
|
".src": "application/x-wais-source",
|
|
".zip": "application/zip",
|
|
".m3u": "audio/x-mpegurl",
|
|
".ra": "audio/x-pn-realaudio",
|
|
".wav": "audio/x-wav",
|
|
".wma": "audio/x-ms-wma",
|
|
".wax": "audio/x-ms-wax",
|
|
".pdb": "chemical/x-pdb",
|
|
".xyz": "chemical/x-xyz",
|
|
".bmp": "image/bmp",
|
|
".gif": "image/gif",
|
|
".ief": "image/ief",
|
|
".png": "image/png",
|
|
".wbmp": "image/vnd.wap.wbmp",
|
|
".ras": "image/x-cmu-raster",
|
|
".pnm": "image/x-portable-anymap",
|
|
".pbm": "image/x-portable-bitmap",
|
|
".pgm": "image/x-portable-graymap",
|
|
".ppm": "image/x-portable-pixmap",
|
|
".rgb": "image/x-rgb",
|
|
".xbm": "image/x-xbitmap",
|
|
".xpm": "image/x-xpixmap",
|
|
".xwd": "image/x-xwindowdump",
|
|
".css": "text/css",
|
|
".rtx": "text/richtext",
|
|
".tsv": "text/tab-separated-values",
|
|
".jad": "text/vnd.sun.j2me.app-descriptor",
|
|
".wml": "text/vnd.wap.wml",
|
|
".wmls": "text/vnd.wap.wmlscript",
|
|
".etx": "text/x-setext",
|
|
".mxu": "video/vnd.mpegurl",
|
|
".flv": "video/x-flv",
|
|
".wm": "video/x-ms-wm",
|
|
".wmv": "video/x-ms-wmv",
|
|
".wmx": "video/x-ms-wmx",
|
|
".wvx": "video/x-ms-wvx",
|
|
".avi": "video/x-msvideo",
|
|
".movie": "video/x-sgi-movie",
|
|
".ice": "x-conference/x-cooltalk",
|
|
".3gp": "video/3gpp",
|
|
".ai": "application/postscript",
|
|
".aif": "audio/x-aiff",
|
|
".aifc": "audio/x-aiff",
|
|
".aiff": "audio/x-aiff",
|
|
".asc": "text/plain",
|
|
".atom": "application/atom+xml",
|
|
".au": "audio/basic",
|
|
".bin": "application/octet-stream",
|
|
".cdf": "application/x-netcdf",
|
|
".cgm": "image/cgm",
|
|
".class": "application/octet-stream",
|
|
".dcr": "application/x-director",
|
|
".dif": "video/x-dv",
|
|
".dir": "application/x-director",
|
|
".djv": "image/vnd.djvu",
|
|
".djvu": "image/vnd.djvu",
|
|
".dll": "application/octet-stream",
|
|
".dmg": "application/octet-stream",
|
|
".dms": "application/octet-stream",
|
|
".dtd": "application/xml-dtd",
|
|
".dv": "video/x-dv",
|
|
".dxr": "application/x-director",
|
|
".eps": "application/postscript",
|
|
".exe": "application/octet-stream",
|
|
".ez": "application/andrew-inset",
|
|
".gram": "application/srgs",
|
|
".grxml": "application/srgs+xml",
|
|
".gz": "application/x-gzip",
|
|
".htm": "text/html",
|
|
".html": "text/html",
|
|
".ico": "image/x-icon",
|
|
".ics": "text/calendar",
|
|
".ifb": "text/calendar",
|
|
".iges": "model/iges",
|
|
".igs": "model/iges",
|
|
".jp2": "image/jp2",
|
|
".jpe": "image/jpeg",
|
|
".jpeg": "image/jpeg",
|
|
".jpg": "image/jpeg",
|
|
".kar": "audio/midi",
|
|
".lha": "application/octet-stream",
|
|
".lzh": "application/octet-stream",
|
|
".m4a": "audio/mp4a-latm",
|
|
".m4p": "audio/mp4a-latm",
|
|
".m4u": "video/vnd.mpegurl",
|
|
".m4v": "video/x-m4v",
|
|
".mac": "image/x-macpaint",
|
|
".mathml": "application/mathml+xml",
|
|
".mesh": "model/mesh",
|
|
".mid": "audio/midi",
|
|
".midi": "audio/midi",
|
|
".mov": "video/quicktime",
|
|
".mp2": "audio/mpeg",
|
|
".mp3": "audio/mpeg",
|
|
".mp4": "video/mp4",
|
|
".mpe": "video/mpeg",
|
|
".mpeg": "video/mpeg",
|
|
".mpg": "video/mpeg",
|
|
".mpga": "audio/mpeg",
|
|
".msh": "model/mesh",
|
|
".nc": "application/x-netcdf",
|
|
".oda": "application/oda",
|
|
".ogv": "video/ogv",
|
|
".pct": "image/pict",
|
|
".pic": "image/pict",
|
|
".pict": "image/pict",
|
|
".pnt": "image/x-macpaint",
|
|
".pntg": "image/x-macpaint",
|
|
".ps": "application/postscript",
|
|
".qt": "video/quicktime",
|
|
".qti": "image/x-quicktime",
|
|
".qtif": "image/x-quicktime",
|
|
".ram": "audio/x-pn-realaudio",
|
|
".rdf": "application/rdf+xml",
|
|
".rm": "application/vnd.rn-realmedia",
|
|
".roff": "application/x-troff",
|
|
".sgm": "text/sgml",
|
|
".sgml": "text/sgml",
|
|
".silo": "model/mesh",
|
|
".skd": "application/x-koan",
|
|
".skm": "application/x-koan",
|
|
".skp": "application/x-koan",
|
|
".skt": "application/x-koan",
|
|
".smi": "application/smil",
|
|
".smil": "application/smil",
|
|
".snd": "audio/basic",
|
|
".so": "application/octet-stream",
|
|
".svg": "image/svg+xml",
|
|
".t": "application/x-troff",
|
|
".texi": "application/x-texinfo",
|
|
".texinfo": "application/x-texinfo",
|
|
".tif": "image/tiff",
|
|
".tiff": "image/tiff",
|
|
".tr": "application/x-troff",
|
|
".txt": "text/plain",
|
|
".vrml": "model/vrml",
|
|
".vxml": "application/voicexml+xml",
|
|
".webm": "video/webm",
|
|
".wrl": "model/vrml",
|
|
".xht": "application/xhtml+xml",
|
|
".xhtml": "application/xhtml+xml",
|
|
".xml": "application/xml",
|
|
".xsl": "application/xml",
|
|
".xslt": "application/xslt+xml",
|
|
".xul": "application/vnd.mozilla.xul+xml",
|
|
".webp": "image/webp",
|
|
}
|
|
|
|
func typeByExtension(filePath string) string {
|
|
typ := mime.TypeByExtension(path.Ext(filePath))
|
|
if typ == "" {
|
|
typ = extToMimeType[strings.ToLower(path.Ext(filePath))]
|
|
}
|
|
if typ == "" {
|
|
typ = "text/plain"
|
|
}
|
|
return typ
|
|
}
|
|
|
|
// Sorter defines the key-value structure for storing the sorted data in signHeader.
|
|
type Sorter struct {
|
|
Keys []string
|
|
Vals []string
|
|
}
|
|
|
|
// newSorter is an additional function for function Sign.
|
|
func newSorter(m map[string]string) *Sorter {
|
|
hs := &Sorter{
|
|
Keys: make([]string, 0, len(m)),
|
|
Vals: make([]string, 0, len(m)),
|
|
}
|
|
|
|
for k, v := range m {
|
|
hs.Keys = append(hs.Keys, k)
|
|
hs.Vals = append(hs.Vals, v)
|
|
}
|
|
return hs
|
|
}
|
|
|
|
// Sort is an additional function for function SignHeader.
|
|
func (hs *Sorter) Sort() {
|
|
sort.Sort(hs)
|
|
}
|
|
|
|
// Len is an additional function for function SignHeader.
|
|
func (hs *Sorter) Len() int {
|
|
return len(hs.Vals)
|
|
}
|
|
|
|
// Less is an additional function for function SignHeader.
|
|
func (hs *Sorter) Less(i, j int) bool {
|
|
return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
|
|
}
|
|
|
|
// Swap is an additional function for function SignHeader.
|
|
func (hs *Sorter) Swap(i, j int) {
|
|
hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i]
|
|
hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i]
|
|
}
|
|
|
|
// Determine whether the parameters are in signKeyList
|
|
// signKeyList is a list about params without value
|
|
func isParamSign(paramKey string) bool {
|
|
for _, k := range signKeyList {
|
|
if paramKey == k {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Fill in the values in dataValue for result
|
|
func flatRepeatedList(dataValue reflect.Value, result map[string]string, prefix string) {
|
|
if !dataValue.IsValid() {
|
|
return
|
|
}
|
|
|
|
dataType := dataValue.Type()
|
|
if dataType.Kind().String() == "slice" {
|
|
handleRepeatedParams(dataValue, result, prefix)
|
|
} else if dataType.Kind().String() == "map" {
|
|
handleMap(dataValue, result, prefix)
|
|
} else {
|
|
result[prefix] = fmt.Sprintf("%v", dataValue.Interface())
|
|
}
|
|
}
|
|
|
|
func handleRepeatedParams(repeatedFieldValue reflect.Value, result map[string]string, prefix string) {
|
|
if repeatedFieldValue.IsValid() && !repeatedFieldValue.IsNil() {
|
|
for m := 0; m < repeatedFieldValue.Len(); m++ {
|
|
elementValue := repeatedFieldValue.Index(m)
|
|
key := prefix + "." + strconv.Itoa(m+1)
|
|
fieldValue := reflect.ValueOf(elementValue.Interface())
|
|
if fieldValue.Kind().String() == "map" {
|
|
handleMap(fieldValue, result, key)
|
|
} else {
|
|
result[key] = fmt.Sprintf("%v", fieldValue.Interface())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func handleMap(valueField reflect.Value, result map[string]string, prefix string) {
|
|
if valueField.IsValid() && valueField.String() != "" {
|
|
valueFieldType := valueField.Type()
|
|
if valueFieldType.Kind().String() == "map" {
|
|
var byt []byte
|
|
byt, _ = json.Marshal(valueField.Interface())
|
|
cache := make(map[string]interface{})
|
|
_ = json.Unmarshal(byt, &cache)
|
|
for key, value := range cache {
|
|
pre := ""
|
|
if prefix != "" {
|
|
pre = prefix + "." + key
|
|
} else {
|
|
pre = key
|
|
}
|
|
fieldValue := reflect.ValueOf(value)
|
|
flatRepeatedList(fieldValue, result, pre)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get XMl StartElement
|
|
func getStartElement(body []byte) string {
|
|
d := xml.NewDecoder(bytes.NewReader(body))
|
|
for {
|
|
tok, err := d.Token()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if t, ok := tok.(xml.StartElement); ok {
|
|
return t.Name.Local
|
|
}
|
|
}
|
|
}
|
|
|
|
func getSignedStrV1(req *tea.Request, canonicalizedResource, accessKeySecret string) string {
|
|
// Find out the "x-oss-"'s address in header of the request
|
|
temp := make(map[string]string)
|
|
|
|
for k, v := range req.Headers {
|
|
if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
|
|
temp[strings.ToLower(k)] = tea.StringValue(v)
|
|
}
|
|
}
|
|
hs := newSorter(temp)
|
|
|
|
// Sort the temp by the ascending order
|
|
hs.Sort()
|
|
|
|
// Get the canonicalizedOSSHeaders
|
|
canonicalizedOSSHeaders := ""
|
|
for i := range hs.Keys {
|
|
canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
|
|
}
|
|
|
|
// Give other parameters values
|
|
// when sign URL, date is expires
|
|
date := tea.StringValue(req.Headers["date"])
|
|
contentType := tea.StringValue(req.Headers["content-type"])
|
|
contentMd5 := tea.StringValue(req.Headers["content-md5"])
|
|
|
|
signStr := tea.StringValue(req.Method) + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
|
|
h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(accessKeySecret))
|
|
io.WriteString(h, signStr)
|
|
signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
|
|
|
return signedStr
|
|
}
|
|
|
|
func getSignedStrV2(req *tea.Request, canonicalizedResource, accessKeySecret string, additionalHeaders []string) string {
|
|
// Find out the "x-oss-"'s address in header of the request
|
|
temp := make(map[string]string)
|
|
for _, value := range additionalHeaders {
|
|
if req.Headers[value] != nil {
|
|
temp[strings.ToLower(value)] = tea.StringValue(req.Headers[value])
|
|
}
|
|
}
|
|
for k, v := range req.Headers {
|
|
if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
|
|
temp[strings.ToLower(k)] = tea.StringValue(v)
|
|
}
|
|
}
|
|
hs := newSorter(temp)
|
|
|
|
// Sort the temp by the ascending order
|
|
hs.Sort()
|
|
|
|
// Get the canonicalizedOSSHeaders
|
|
canonicalizedOSSHeaders := ""
|
|
for i := range hs.Keys {
|
|
canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
|
|
}
|
|
// Give other parameters values
|
|
// when sign URL, date is expires
|
|
date := tea.StringValue(req.Headers["date"])
|
|
contentType := tea.StringValue(req.Headers["content-type"])
|
|
contentMd5 := tea.StringValue(req.Headers["content-md5"])
|
|
signStr := tea.StringValue(req.Method) + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + strings.Join(additionalHeaders, ";") + "\n" + canonicalizedResource
|
|
h := hmac.New(func() hash.Hash { return sha256.New() }, []byte(accessKeySecret))
|
|
io.WriteString(h, signStr)
|
|
signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
|
return signedStr
|
|
}
|
|
|
|
func uriEncode(rawStr string, encodeSlash bool) string {
|
|
res := ""
|
|
for i := 0; i < len(rawStr); i++ {
|
|
tmp := string(rawStr[i])
|
|
if (tmp >= "a" && tmp <= "z") || (tmp >= "A" && tmp <= "Z") ||
|
|
(tmp >= "0" && tmp <= "9") || characters[tmp] {
|
|
res = res + tmp
|
|
} else if tmp == "/" {
|
|
if encodeSlash {
|
|
res = res + "%2F"
|
|
} else {
|
|
res = res + tmp
|
|
}
|
|
} else {
|
|
res = res + "%" + fmt.Sprintf("%02x", tmp)
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func XmlUnmarshal(body []byte, result interface{}) (interface{}, error) {
|
|
start := getStartElement(body)
|
|
dataValue := reflect.ValueOf(result).Elem()
|
|
dataType := dataValue.Type()
|
|
for i := 0; i < dataType.NumField(); i++ {
|
|
field := dataType.Field(i)
|
|
name, containsNameTag := field.Tag.Lookup("xml")
|
|
if containsNameTag {
|
|
if name == start {
|
|
realType := dataValue.Field(i).Type()
|
|
realValue := reflect.New(realType).Interface()
|
|
err := xml.Unmarshal(body, realValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return realValue, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func getSignatureV1(request *tea.Request, bucketName, accessKeySecret string) string {
|
|
resource := ""
|
|
if bucketName != "" {
|
|
resource = "/" + bucketName
|
|
}
|
|
resource = resource + tea.StringValue(request.Pathname)
|
|
if !strings.Contains(resource, "?") && len(request.Query) > 0 {
|
|
resource += "?"
|
|
}
|
|
|
|
for key, value := range request.Query {
|
|
if isParamSign(key) {
|
|
if value != nil {
|
|
if strings.HasSuffix(resource, "?") {
|
|
resource = resource + key + "=" + tea.StringValue(value)
|
|
} else {
|
|
resource = resource + "&" + key + "=" + tea.StringValue(value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return getSignedStrV1(request, resource, accessKeySecret)
|
|
}
|
|
|
|
func getSignatureV2(request *tea.Request, bucketName, accessKeySecret string, additionalHeaders []string) string {
|
|
resource := ""
|
|
pathName := tea.StringValue(request.Pathname)
|
|
if bucketName != "" {
|
|
pathName = "/" + bucketName + tea.StringValue(request.Pathname)
|
|
}
|
|
|
|
strs := strings.Split(pathName, "?")
|
|
resource += uriEncode(strs[0], true)
|
|
tmp := make(map[string]string)
|
|
for k, v := range request.Query {
|
|
tmp[k] = tea.StringValue(v)
|
|
}
|
|
hs := newSorter(tmp)
|
|
if strings.Contains(pathName, "?") {
|
|
hs.Keys = append(hs.Keys, strs[1])
|
|
hs.Vals = append(hs.Vals, "")
|
|
}
|
|
|
|
// Sort the temp by the ascending order
|
|
hs.Sort()
|
|
if len(hs.Keys) > 0 {
|
|
resource += "?"
|
|
}
|
|
for i := range hs.Keys {
|
|
if !strings.HasSuffix(resource, "?") {
|
|
resource += "&"
|
|
}
|
|
if hs.Vals[i] == "" {
|
|
resource += uriEncode(hs.Keys[i], true)
|
|
} else {
|
|
resource += uriEncode(hs.Keys[i], true) + "=" + uriEncode(hs.Vals[i], true)
|
|
}
|
|
}
|
|
return getSignedStrV2(request, resource, accessKeySecret, additionalHeaders)
|
|
}
|
|
|
|
// Decryption
|
|
func base64Decode(value string) string {
|
|
strs := strings.Split(value, "/")
|
|
result, err := base64.StdEncoding.DecodeString(strs[len(strs)-1])
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
strs[len(strs)-1] = string(result)
|
|
return strings.Join(strs, "/")
|
|
}
|
|
|
|
// Decryption
|
|
func urlDecode(value string) string {
|
|
strs := strings.Split(value, "/")
|
|
result, err := url.QueryUnescape(strs[len(strs)-1])
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
strs[len(strs)-1] = result
|
|
return strings.Join(strs, "/")
|
|
}
|
|
|
|
func listToString(a []string, sep string) string {
|
|
return strings.Join(a, sep)
|
|
}
|