Skip to content

Commit

Permalink
Merge pull request #13 from mozillazg/sign-escape
Browse files Browse the repository at this point in the history
修复当 headers 相关参数的值中包含空格时会出现服务端返回签名不匹配的问题
  • Loading branch information
mozillazg authored Apr 14, 2019
2 parents 8b52d53 + 8afcc1e commit 6d744d7
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 13 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ go:
- 1.7
- 1.8
- 1.9
- '1.10'
- '1.11'
- '1.10.x'
- '1.11.x'
- '1.12.x'
- master

sudo: false
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Changelog


## [0.11.1] (2019-xx-yy)

### Bugfix

* 修复当 url 或 headers 相关参数的值中包含空格时会出现服务端返回签名不匹配的问题。(via [#13])


## [0.11.0] (2018-12-08)

### 不兼容旧版的变更
Expand Down Expand Up @@ -132,6 +139,7 @@
* 完成大部分 Bucket API(还剩一个 Put Bucket Lifecycle)


[0.11.1]: https://github.com/mozillazg/go-cos/compare/v0.11.0...v0.11.1
[0.11.0]: https://github.com/mozillazg/go-cos/compare/v0.10.0...v0.11.0
[0.10.0]: https://github.com/mozillazg/go-cos/compare/v0.9.0...v0.10.0
[0.9.0]: https://github.com/mozillazg/go-cos/compare/v0.8.0...v0.9.0
Expand All @@ -147,3 +155,4 @@
[7dcd701]: https://github.com/mozillazg/go-cos/commit/7dcd701975f483d57525b292ab31d0f9a6c8866c
[#7]: https://github.com/mozillazg/go-cos/pull/7
[@jojohappy]: https://github.com/jojohappy
[#13]: https://github.com/mozillazg/go-cos/pull/13
11 changes: 10 additions & 1 deletion _example/bucket/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,21 @@ func main() {
Prefix: "test",
MaxKeys: 3,
}
v, _, err := c.Bucket.Get(context.Background(), opt)
v, resp, err := c.Bucket.Get(context.Background(), opt)
if err != nil {
panic(err)
}
resp.Body.Close()

for _, c := range v.Contents {
fmt.Printf("%s, %d\n", c.Key, c.Size)
}

// 测试特殊字符
opt.Prefix = "test/put_ + !'()* option"
_, resp, err = c.Bucket.Get(context.Background(), opt)
if err != nil {
panic(err)
}
resp.Body.Close()
}
23 changes: 19 additions & 4 deletions _example/object/put.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"net/http"
"os"
"strings"
Expand Down Expand Up @@ -33,19 +34,33 @@ func main() {
panic(err)
}

name = "test/put_option.go"
// 测试上传以及特殊字符
name = "test/put_ + !'()* option.go"
contentDisposition := "attachment; filename=Hello - world!(+)'*.go"
f = strings.NewReader("test xxx")
opt := &cos.ObjectPutOptions{
ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
ContentType: "text/html",
ContentType: "text/html",
ContentDisposition: contentDisposition,
},
ACLHeaderOptions: &cos.ACLHeaderOptions{
//XCosACL: "public-read",
// XCosACL: "public-read",
XCosACL: "private",
},
}
_, err = c.Object.Put(context.Background(), name, f, opt)
resp, err := c.Object.Put(context.Background(), name, f, opt)
if err != nil {
panic(err)
}
resp.Body.Close()

// 测试特殊字符
resp, err = c.Object.Get(context.Background(), name, nil)
if err != nil {
panic(err)
}
resp.Body.Close()
if resp.Header.Get("Content-Disposition") != contentDisposition {
panic(errors.New("wong Content-Disposition"))
}
}
55 changes: 50 additions & 5 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ func (a *AuthTime) keyString() string {
}

// newAuthorization 通过一系列步骤生成最终需要的 Authorization 字符串
//
// https://cloud.tencent.com/document/product/436/7778
func newAuthorization(auth Auth, req *http.Request, authTime AuthTime) string {
secretKey := auth.SecretKey
secretID := auth.SecretID
Expand Down Expand Up @@ -156,34 +158,77 @@ func genFormatString(method string, uri url.URL, formatParameters, formatHeaders
)
}

// https://github.com/tencentyun/cos-nodejs-sdk-v5/blob/a1dad3e9e3776cd24c97975f3aa47631e5001ff0/sdk/util.js#L11
func camSafeURLEncode(s string) string {
s = encodeURIComponent(s)
s = strings.Replace(s, "!", "%21", -1)
s = strings.Replace(s, "'", "%27", -1)
s = strings.Replace(s, "(", "%28", -1)
s = strings.Replace(s, ")", "%29", -1)
s = strings.Replace(s, "*", "%2A", -1)
return s
}

type valuesForSign map[string][]string

func (vs valuesForSign) Add(key, value string) {
key = strings.ToLower(key)
vs[key] = append(vs[key], value)
}

// https://cloud.tencent.com/document/product/436/7778
// https://github.com/tencentyun/cos-nodejs-sdk-v5/blob/a1dad3e9e3776cd24c97975f3aa47631e5001ff0/sdk/util.js#L42-L69
func (vs valuesForSign) Encode() string {
var keys []string
for k := range vs {
keys = append(keys, k)
}
// 字典序排序
sort.Strings(keys)

var pairs []string
for _, k := range keys {
items := vs[k]
sort.Strings(items)
for _, v := range items {
pairs = append(
pairs,
fmt.Sprintf("%s=%s", camSafeURLEncode(k), camSafeURLEncode(v)))
}
}
// <key1>=<value1>&<key2>=<value2>
return strings.Join(pairs, "&")
}

// genFormatParameters 生成 FormatParameters 和 SignedParameterList
func genFormatParameters(parameters url.Values) (formatParameters string, signedParameterList []string) {
ps := url.Values{}
ps := valuesForSign{}
for key, values := range parameters {
key = strings.ToLower(key)
for _, value := range values {
key = strings.ToLower(key)
ps.Add(key, value)
signedParameterList = append(signedParameterList, key)
}
}
//formatParameters = strings.ToLower(ps.Encode())

formatParameters = ps.Encode()
sort.Strings(signedParameterList)
return
}

// genFormatHeaders 生成 FormatHeaders 和 SignedHeaderList
func genFormatHeaders(headers http.Header) (formatHeaders string, signedHeaderList []string) {
hs := url.Values{}
hs := valuesForSign{}
for key, values := range headers {
key = strings.ToLower(key)
for _, value := range values {
key = strings.ToLower(key)
if isSignHeader(key) {
hs.Add(key, value)
signedHeaderList = append(signedHeaderList, key)
}
}
}

formatHeaders = hs.Encode()
sort.Strings(signedHeaderList)
return
Expand Down
Loading

0 comments on commit 6d744d7

Please sign in to comment.