Explorar o código

support oss v2 signature

taowei.wtw %!s(int64=5) %!d(string=hai) anos
pai
achega
2f35b153ba
Modificáronse 9 ficheiros con 448 adicións e 104 borrados
  1. 56 10
      oss/auth.go
  2. 193 42
      oss/bucket_test.go
  3. 18 0
      oss/client.go
  4. 60 11
      oss/client_test.go
  5. 4 0
      oss/conf.go
  6. 53 26
      oss/conn.go
  7. 12 7
      oss/conn_test.go
  8. 17 1
      oss/const.go
  9. 35 7
      oss/progress_test.go

+ 56 - 10
oss/auth.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"crypto/hmac"
 	"crypto/sha1"
+	"crypto/sha256"
 	"encoding/base64"
 	"fmt"
 	"hash"
@@ -20,13 +21,47 @@ type headerSorter struct {
 	Vals []string
 }
 
+// getAdditionalHeaderKeys get exist key in http header
+func (conn Conn) getAdditionalHeaderKeys(req *http.Request) ([]string, map[string]string) {
+	var keysList []string
+	keysMap := make(map[string]string)
+	srcKeys := make(map[string]string)
+
+	for k := range req.Header {
+		srcKeys[strings.ToLower(k)] = ""
+	}
+
+	for _, v := range conn.config.AdditionalHeaders {
+		if _, ok := srcKeys[strings.ToLower(v)]; ok {
+			keysMap[strings.ToLower(v)] = ""
+		}
+	}
+
+	for k := range keysMap {
+		keysList = append(keysList, k)
+	}
+	sort.Strings(keysList)
+	return keysList, keysMap
+}
+
 // signHeader signs the header and sets it as the authorization header.
 func (conn Conn) signHeader(req *http.Request, canonicalizedResource string) {
-
 	akIf := conn.config.GetCredentials()
-
-	// Get the final authorization string
-	authorizationStr := "OSS " + akIf.GetAccessKeyID() + ":" + conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret())
+	authorizationStr := ""
+	if conn.config.AuthVersion == AuthV2 {
+		additionalList, _ := conn.getAdditionalHeaderKeys(req)
+		if len(additionalList) > 0 {
+			authorizationFmt := "OSS2 AccessKeyId:%v,AdditionalHeaders:%v,Signature:%v"
+			additionnalHeadersStr := strings.Join(additionalList, ";")
+			authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), additionnalHeadersStr, conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret()))
+		} else {
+			authorizationFmt := "OSS2 AccessKeyId:%v,Signature:%v"
+			authorizationStr = fmt.Sprintf(authorizationFmt, akIf.GetAccessKeyID(), conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret()))
+		}
+	} else {
+		// Get the final authorization string
+		authorizationStr = "OSS " + akIf.GetAccessKeyID() + ":" + conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret())
+	}
 
 	// Give the parameter "Authorization" value
 	req.Header.Set(HTTPHeaderAuthorization, authorizationStr)
@@ -34,16 +69,20 @@ func (conn Conn) signHeader(req *http.Request, canonicalizedResource string) {
 
 func (conn Conn) getSignedStr(req *http.Request, canonicalizedResource string, keySecret string) string {
 	// Find out the "x-oss-"'s address in header of the request
-	temp := make(map[string]string)
-
+	ossHeadersMap := make(map[string]string)
+	additionalList, additionalMap := conn.getAdditionalHeaderKeys(req)
 	for k, v := range req.Header {
 		if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
-			temp[strings.ToLower(k)] = v[0]
+			ossHeadersMap[strings.ToLower(k)] = v[0]
+		} else if conn.config.AuthVersion == AuthV2 {
+			if _, ok := additionalMap[strings.ToLower(k)]; ok {
+				ossHeadersMap[strings.ToLower(k)] = v[0]
+			}
 		}
 	}
-	hs := newHeaderSorter(temp)
+	hs := newHeaderSorter(ossHeadersMap)
 
-	// Sort the temp by the ascending order
+	// Sort the ossHeadersMap by the ascending order
 	hs.Sort()
 
 	// Get the canonicalizedOSSHeaders
@@ -58,7 +97,15 @@ func (conn Conn) getSignedStr(req *http.Request, canonicalizedResource string, k
 	contentType := req.Header.Get(HTTPHeaderContentType)
 	contentMd5 := req.Header.Get(HTTPHeaderContentMD5)
 
+	// default is v1 signature
 	signStr := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
+	h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
+
+	// v2 signature
+	if conn.config.AuthVersion == AuthV2 {
+		signStr = req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + strings.Join(additionalList, ";") + "\n" + canonicalizedResource
+		h = hmac.New(func() hash.Hash { return sha256.New() }, []byte(keySecret))
+	}
 
 	// convert sign to log for easy to view
 	if conn.config.LogLevel >= Debug {
@@ -73,7 +120,6 @@ func (conn Conn) getSignedStr(req *http.Request, canonicalizedResource string, k
 		conn.config.WriteLog(Debug, "[Req:%p]signStr:%s\n", req, signBuf.String())
 	}
 
-	h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
 	io.WriteString(h, signStr)
 	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
 

+ 193 - 42
oss/bucket_test.go

@@ -11,6 +11,7 @@ import (
 	"io/ioutil"
 	"math/rand"
 	"net/http"
+	"net/url"
 	"os"
 	"path/filepath"
 	"strconv"
@@ -216,7 +217,7 @@ func (s *OssBucketSuite) TestPutObjectOnly(c *C) {
 	c.Assert(err, IsNil)
 }
 
-func (s *OssBucketSuite) TestSignURL(c *C) {
+func (s *OssBucketSuite) SignURLTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
 	objectName := objectNamePrefix + randStr(8)
 	objectValue := randStr(20)
 
@@ -227,12 +228,26 @@ func (s *OssBucketSuite) TestSignURL(c *C) {
 	notExistfilePath := randLowStr(10)
 	os.Remove(notExistfilePath)
 
+	oldType := s.bucket.Client.Config.AuthVersion
+	oldHeaders := s.bucket.Client.Config.AdditionalHeaders
+
+	s.bucket.Client.Config.AuthVersion = authVersion
+	s.bucket.Client.Config.AdditionalHeaders = extraHeaders
+
 	// Sign URL for put
 	str, err := s.bucket.SignURL(objectName, HTTPPut, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Error put object with URL
 	err = s.bucket.PutObjectWithURL(str, strings.NewReader(objectValue), ContentType("image/tiff"))
@@ -260,9 +275,16 @@ func (s *OssBucketSuite) TestSignURL(c *C) {
 	// Sign URL for function GetObjectWithURL
 	str, err = s.bucket.SignURL(objectName, HTTPGet, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Get object with URL
 	body, err := s.bucket.GetObjectWithURL(str)
@@ -280,9 +302,16 @@ func (s *OssBucketSuite) TestSignURL(c *C) {
 	}
 	str, err = s.bucket.SignURL(objectName, HTTPPut, 60, options...)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Put object with URL from file
 	// Without option, error
@@ -395,20 +424,42 @@ func (s *OssBucketSuite) TestSignURL(c *C) {
 
 	err = s.bucket.GetObjectToFileWithURL(str, newFile)
 	c.Assert(err, NotNil)
+
+	s.bucket.Client.Config.AuthVersion = oldType
+	s.bucket.Client.Config.AdditionalHeaders = oldHeaders
 }
 
-func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
+func (s *OssBucketSuite) TestSignURL(c *C) {
+	s.SignURLTestFunc(c, AuthV1, []string{})
+	s.SignURLTestFunc(c, AuthV2, []string{})
+	s.SignURLTestFunc(c, AuthV2, []string{"host", "range", "user-agent"})
+}
+
+func (s *OssBucketSuite) SignURLWithEscapedKeyTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
 	// Key with '/'
 	objectName := "zyimg/86/e8/653b5dc97bb0022051a84c632bc4"
 	objectValue := "弃我去者,昨日之日不可留;乱我心者,今日之日多烦忧。长风万里送秋雁,对此可以酣高楼。蓬莱文章建安骨,中间小谢又清发。" +
 		"俱怀逸兴壮思飞,欲上青天揽明月。抽刀断水水更流,举杯销愁愁更愁。人生在世不称意,明朝散发弄扁舟。"
 
+	oldType := s.bucket.Client.Config.AuthVersion
+	oldHeaders := s.bucket.Client.Config.AdditionalHeaders
+
+	s.bucket.Client.Config.AuthVersion = authVersion
+	s.bucket.Client.Config.AdditionalHeaders = extraHeaders
+
 	// Sign URL for function PutObjectWithURL
 	str, err := s.bucket.SignURL(objectName, HTTPPut, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Put object with URL
 	err = s.bucket.PutObjectWithURL(str, strings.NewReader(objectValue))
@@ -417,9 +468,16 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
 	// Sign URL for function GetObjectWithURL
 	str, err = s.bucket.SignURL(objectName, HTTPGet, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Get object with URL
 	body, err := s.bucket.GetObjectWithURL(str)
@@ -434,9 +492,16 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
 	// Sign URL for funciton PutObjectWithURL
 	str, err = s.bucket.SignURL(objectName, HTTPPut, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Put object with URL
 	err = s.bucket.PutObjectWithURL(str, strings.NewReader(objectValue))
@@ -445,9 +510,16 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
 	// Sign URL for function GetObjectWithURL
 	str, err = s.bucket.SignURL(objectName, HTTPGet, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Get object with URL
 	body, err = s.bucket.GetObjectWithURL(str)
@@ -462,9 +534,16 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
 	// Sign URL for function PutObjectWithURL
 	str, err = s.bucket.SignURL(objectName, HTTPPut, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Put object with URL
 	err = s.bucket.PutObjectWithURL(str, strings.NewReader(objectValue))
@@ -473,9 +552,16 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
 	// Sign URL for get function GetObjectWithURL
 	str, err = s.bucket.SignURL(objectName, HTTPGet, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Get object with URL
 	body, err = s.bucket.GetObjectWithURL(str)
@@ -520,23 +606,45 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
 	// Delete object
 	err = s.bucket.DeleteObject(objectName)
 	c.Assert(err, IsNil)
+
+	s.bucket.Client.Config.AuthVersion = oldType
+	s.bucket.Client.Config.AdditionalHeaders = oldHeaders
 }
 
-func (s *OssBucketSuite) TestSignURLWithEscapedKeyAndPorxy(c *C) {
+func (s *OssBucketSuite) TestSignURLWithEscapedKey(c *C) {
+	s.SignURLWithEscapedKeyTestFunc(c, AuthV1, []string{})
+	s.SignURLWithEscapedKeyTestFunc(c, AuthV2, []string{})
+	s.SignURLWithEscapedKeyTestFunc(c, AuthV2, []string{"host", "range", "user-agent"})
+}
+
+func (s *OssBucketSuite) SignURLWithEscapedKeyAndPorxyTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
 	// Key with '/'
 	objectName := "zyimg/86/e8/653b5dc97bb0022051a84c632bc4"
 	objectValue := "弃我去者,昨日之日不可留;乱我心者,今日之日多烦忧。长风万里送秋雁,对此可以酣高楼。蓬莱文章建安骨,中间小谢又清发。" +
 		"俱怀逸兴壮思飞,欲上青天揽明月。抽刀断水水更流,举杯销愁愁更愁。人生在世不称意,明朝散发弄扁舟。"
 
-	client, err := New(endpoint, accessID, accessKey, AuthProxy(proxyHost, proxyUser, proxyPasswd))
+	options := []ClientOption{
+		AuthProxy(proxyHost, proxyUser, proxyPasswd),
+		AuthVersion(authVersion),
+		AdditionalHeaders(extraHeaders),
+	}
+
+	client, err := New(endpoint, accessID, accessKey, options...)
 	bucket, err := client.Bucket(bucketName)
 
 	// Sign URL for put
 	str, err := bucket.SignURL(objectName, HTTPPut, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Put object with URL
 	err = bucket.PutObjectWithURL(str, strings.NewReader(objectValue))
@@ -545,9 +653,16 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKeyAndPorxy(c *C) {
 	// Sign URL for function GetObjectWithURL
 	str, err = bucket.SignURL(objectName, HTTPGet, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Get object with URL
 	body, err := bucket.GetObjectWithURL(str)
@@ -594,6 +709,42 @@ func (s *OssBucketSuite) TestSignURLWithEscapedKeyAndPorxy(c *C) {
 	c.Assert(err, IsNil)
 }
 
+func (s *OssBucketSuite) TestSignURLWithEscapedKeyAndPorxy(c *C) {
+	s.SignURLWithEscapedKeyAndPorxyTestFunc(c, AuthV1, []string{})
+	s.SignURLWithEscapedKeyAndPorxyTestFunc(c, AuthV2, []string{})
+	s.SignURLWithEscapedKeyAndPorxyTestFunc(c, AuthV2, []string{"host", "range", "user-agent"})
+}
+
+func (s *OssBucketSuite) TestQueryStringAuthV2(c *C) {
+	client, err := New(endpoint, accessID, accessKey)
+	c.Assert(err, IsNil)
+
+	// set oss v2 signatrue
+	client.Config.AuthVersion = AuthV2
+	bucketName := bucketNamePrefix + randLowStr(6)
+	err = client.CreateBucket(bucketName)
+	c.Assert(err, IsNil)
+
+	bucket, err := client.Bucket(bucketName)
+
+	// build QueryString
+	QueryKey1 := "abc"
+	QueryKey2 := "|abc"
+	c.Assert(strings.Compare(QueryKey1, QueryKey2) < 0, Equals, true)
+	c.Assert(strings.Compare(url.QueryEscape(QueryKey1), url.QueryEscape(QueryKey2)) > 0, Equals, true)
+
+	options := []Option{}
+	params := map[string]interface{}{}
+	params[QueryKey1] = "queryValue1"
+	params[QueryKey2] = "queryValue2"
+	objectKey := objectNamePrefix + randStr(8)
+	resp, _ := bucket.do("HEAD", objectKey, params, options, nil, nil)
+
+	// object not exist,no signature error
+	c.Assert(resp.StatusCode, Equals, 404)
+	forceDeleteBucket(client, bucketName, c)
+}
+
 // TestPutObjectType
 func (s *OssBucketSuite) TestPutObjectType(c *C) {
 	objectName := objectNamePrefix + randStr(8)
@@ -828,7 +979,7 @@ func (s *OssBucketSuite) TestPutObjectFromFileType(c *C) {
 }
 
 // TestGetObject
-func (s *OssBucketSuite) TestGetObject(c *C) {
+func (s *OssBucketSuite) TestGetObjectNormal(c *C) {
 	objectName := objectNamePrefix + randStr(8)
 	objectValue := "长忆观潮,满郭人争江上望。来疑沧海尽成空,万面鼓声中。弄潮儿向涛头立,手把红旗旗不湿。别来几向梦中看,梦觉尚心寒。"
 
@@ -1176,7 +1327,7 @@ func (s *OssBucketSuite) TestDeleteObject(c *C) {
 }
 
 // TestDeleteObjects
-func (s *OssBucketSuite) TestDeleteObjects(c *C) {
+func (s *OssBucketSuite) TestDeleteObjectsNormal(c *C) {
 	objectName := objectNamePrefix + randStr(8)
 
 	// Delete objects
@@ -4460,7 +4611,7 @@ func (s *OssBucketSuite) TestOptionsMethod(c *C) {
 	forceDeleteBucket(client, bucketName, c)
 }
 
-func (s *OssBucketSuite) TestBucketTrafficLimitObject(c *C) {
+func (s *OssBucketSuite) TestBucketTrafficLimitObject1(c *C) {
 	// create a bucket with default proprety
 	client, err := New(endpoint, accessID, accessKey)
 	c.Assert(err, IsNil)
@@ -4714,8 +4865,8 @@ func (s *OssBucketSuite) TestDeleteObjectsWithSpecialCharacter(c *C) {
 	bucket, err := client.Bucket(bucketName)
 
 	contentLength := 100
-    objectName1 := objectNamePrefix + randStr(8) + "<-->+&*\r%%"
-    objectName2 := objectNamePrefix + randStr(8) + "\r&*\r%%"
+	objectName1 := objectNamePrefix + randStr(8) + "<-->+&*\r%%"
+	objectName2 := objectNamePrefix + randStr(8) + "\r&*\r%%"
 	//objectName2 := objectNamePrefix + randStr(8) + "%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2F%C0%AE%C0%AE%2Fetc%2Fprofile"
 	//objectName2, err = url.QueryUnescape(objectName2)
 

+ 18 - 0
oss/client.go

@@ -68,6 +68,10 @@ func New(endpoint, accessKeyID, accessKeySecret string, options ...ClientOption)
 		option(client)
 	}
 
+	if config.AuthVersion != AuthV1 && config.AuthVersion != AuthV2 {
+		return nil, fmt.Errorf("Init client Error, invalid Auth version: %v", config.AuthVersion)
+	}
+
 	// Create HTTP connection
 	err = conn.init(config, url, client.HTTPClient)
 
@@ -1335,6 +1339,20 @@ func SetLocalAddr(localAddr net.Addr) ClientOption {
 	}
 }
 
+// AuthVersion  sets auth version: v1 or v2 signature which oss_server needed
+func AuthVersion(authVersion AuthVersionType) ClientOption {
+	return func(client *Client) {
+		client.Config.AuthVersion = authVersion
+	}
+}
+
+// AdditionalHeaders sets special http headers needed to be signed
+func AdditionalHeaders(headers []string) ClientOption {
+	return func(client *Client) {
+		client.Config.AdditionalHeaders = headers
+	}
+}
+
 // Private
 func (client Client) do(method, bucketName string, params map[string]interface{},
 	headers map[string]string, data io.Reader, options ...Option) (*Response, error) {

+ 60 - 11
oss/client_test.go

@@ -2103,13 +2103,18 @@ func (s *OssClientSuite) TestClientOption(c *C) {
 }
 
 // TestProxy
-func (s *OssClientSuite) TestProxy(c *C) {
+func (s *OssClientSuite) ProxyTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
 	bucketNameTest := bucketNamePrefix + randLowStr(6)
 	objectName := "体育/奥运/首金"
 	objectValue := "大江东去,浪淘尽,千古风流人物。 故垒西边,人道是、三国周郎赤壁。"
 
 	client, err := New(endpoint, accessID, accessKey, AuthProxy(proxyHost, proxyUser, proxyPasswd))
 
+	oldType := client.Config.AuthVersion
+	oldHeaders := client.Config.AdditionalHeaders
+	client.Config.AuthVersion = authVersion
+	client.Config.AdditionalHeaders = extraHeaders
+
 	// Create bucket
 	err = client.CreateBucket(bucketNameTest)
 	c.Assert(err, IsNil)
@@ -2123,9 +2128,16 @@ func (s *OssClientSuite) TestProxy(c *C) {
 	// Sign URL
 	str, err := bucket.SignURL(objectName, HTTPPut, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Put object with URL
 	err = bucket.PutObjectWithURL(str, strings.NewReader(objectValue))
@@ -2134,9 +2146,16 @@ func (s *OssClientSuite) TestProxy(c *C) {
 	// Sign URL for get object
 	str, err = bucket.SignURL(objectName, HTTPGet, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Get object with URL
 	body, err := bucket.GetObjectWithURL(str)
@@ -2164,6 +2183,15 @@ func (s *OssClientSuite) TestProxy(c *C) {
 	// Delete bucket
 	err = client.DeleteBucket(bucketNameTest)
 	c.Assert(err, IsNil)
+
+	client.Config.AuthVersion = oldType
+	client.Config.AdditionalHeaders = oldHeaders
+}
+
+func (s *OssClientSuite) TestProxy(c *C) {
+	s.ProxyTestFunc(c, AuthV1, []string{})
+	s.ProxyTestFunc(c, AuthV2, []string{})
+	s.ProxyTestFunc(c, AuthV2, []string{"host", "range", "user-agent"})
 }
 
 // TestProxy for https endpoint
@@ -2260,7 +2288,7 @@ func (s *OssClientSuite) TestHttpLogNotSignUrl(c *C) {
 	client.DeleteBucket(testBucketName)
 }
 
-func (s *OssClientSuite) TestHttpLogSignUrl(c *C) {
+func (s *OssClientSuite) HttpLogSignUrlTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
 	logName := "." + string(os.PathSeparator) + "test-go-sdk-httpdebug-signurl.log" + randStr(5)
 	f, err := os.OpenFile(logName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
 	c.Assert(err, IsNil)
@@ -2269,6 +2297,11 @@ func (s *OssClientSuite) TestHttpLogSignUrl(c *C) {
 	client.Config.LogLevel = Debug
 	client.Config.Logger = log.New(f, "", log.LstdFlags)
 
+	oldType := client.Config.AuthVersion
+	oldHeaders := client.Config.AdditionalHeaders
+	client.Config.AuthVersion = authVersion
+	client.Config.AdditionalHeaders = extraHeaders
+
 	var testBucketName = bucketNamePrefix + randLowStr(6)
 
 	// CreateBucket
@@ -2286,9 +2319,16 @@ func (s *OssClientSuite) TestHttpLogSignUrl(c *C) {
 	// Sign URL for put
 	str, err := bucket.SignURL(objectName, HTTPPut, 60)
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Error put object with URL
 	err = bucket.PutObjectWithURL(str, strings.NewReader(objectValue), ContentType("image/tiff"))
@@ -2307,6 +2347,15 @@ func (s *OssClientSuite) TestHttpLogSignUrl(c *C) {
 	// delete test bucket and log
 	os.Remove(logName)
 	client.DeleteBucket(testBucketName)
+
+	client.Config.AuthVersion = oldType
+	client.Config.AdditionalHeaders = oldHeaders
+}
+
+func (s *OssClientSuite) TestHttpLogSignUrl(c *C) {
+	s.HttpLogSignUrlTestFunc(c, AuthV1, []string{})
+	s.HttpLogSignUrlTestFunc(c, AuthV2, []string{})
+	s.HttpLogSignUrlTestFunc(c, AuthV2, []string{"host", "range", "user-agent"})
 }
 
 func (s *OssClientSuite) TestSetLimitUploadSpeed(c *C) {

+ 4 - 0
oss/conf.go

@@ -99,6 +99,8 @@ type Config struct {
 	UploadLimiter       *OssLimiter         // Bandwidth limit reader for upload
 	CredentialsProvider CredentialsProvider // User provides interface to get AccessKeyID, AccessKeySecret, SecurityToken
 	LocalAddr           net.Addr            // local client host info
+	AuthVersion         AuthVersionType     //  v1 or v2 signature,default is v1
+	AdditionalHeaders   []string            //  special http headers needed to be sign
 }
 
 // LimitUploadSpeed uploadSpeed:KB/s, 0 is unlimited,default is 0
@@ -174,5 +176,7 @@ func getDefaultOssConfig() *Config {
 	provider := &defaultCredentialsProvider{config: &config}
 	config.CredentialsProvider = provider
 
+	config.AuthVersion = AuthV1
+
 	return &config
 }

+ 53 - 26
oss/conn.go

@@ -82,7 +82,7 @@ func (conn Conn) Do(method, bucketName, objectName string, params map[string]int
 	urlParams := conn.getURLParams(params)
 	subResource := conn.getSubResource(params)
 	uri := conn.url.getURL(bucketName, objectName, urlParams)
-	resource := conn.url.getResource(bucketName, objectName, subResource)
+	resource := conn.getResource(bucketName, objectName, subResource)
 	return conn.doRequest(method, uri, resource, headers, data, initCRC, listener)
 }
 
@@ -121,7 +121,7 @@ func (conn Conn) DoURL(method HTTPMethod, signedURL string, headers map[string]s
 		req.Header.Set("Proxy-Authorization", basic)
 	}
 
-	req.Header.Set(HTTPHeaderHost, conn.config.Endpoint)
+	req.Header.Set(HTTPHeaderHost, req.Host)
 	req.Header.Set(HTTPHeaderUserAgent, conn.config.UserAgent)
 
 	if headers != nil {
@@ -175,7 +175,7 @@ func (conn Conn) getURLParams(params map[string]interface{}) string {
 		}
 		buf.WriteString(url.QueryEscape(k))
 		if params[k] != nil {
-			buf.WriteString("=" + url.QueryEscape(params[k].(string)))
+			buf.WriteString("=" + strings.Replace(url.QueryEscape(params[k].(string)), "+", "%20", -1))
 		}
 	}
 
@@ -185,9 +185,19 @@ func (conn Conn) getURLParams(params map[string]interface{}) string {
 func (conn Conn) getSubResource(params map[string]interface{}) string {
 	// Sort
 	keys := make([]string, 0, len(params))
+	signParams := make(map[string]string)
 	for k := range params {
-		if conn.isParamSign(k) {
+		if conn.config.AuthVersion == AuthV2 {
+			encodedKey := url.QueryEscape(k)
+			keys = append(keys, encodedKey)
+			if params[k] != nil && params[k] != "" {
+				signParams[encodedKey] = strings.Replace(url.QueryEscape(params[k].(string)), "+", "%20", -1)
+			}
+		} else if conn.isParamSign(k) {
 			keys = append(keys, k)
+			if params[k] != nil {
+				signParams[k] = params[k].(string)
+			}
 		}
 	}
 	sort.Strings(keys)
@@ -199,11 +209,10 @@ func (conn Conn) getSubResource(params map[string]interface{}) string {
 			buf.WriteByte('&')
 		}
 		buf.WriteString(k)
-		if params[k] != nil {
-			buf.WriteString("=" + params[k].(string))
+		if _, ok := signParams[k]; ok {
+			buf.WriteString("=" + signParams[k])
 		}
 	}
-
 	return buf.String()
 }
 
@@ -216,6 +225,23 @@ func (conn Conn) isParamSign(paramKey string) bool {
 	return false
 }
 
+// getResource gets canonicalized resource
+func (conn Conn) getResource(bucketName, objectName, subResource string) string {
+	if subResource != "" {
+		subResource = "?" + subResource
+	}
+	if bucketName == "" {
+		if conn.config.AuthVersion == AuthV2 {
+			return url.QueryEscape("/") + subResource
+		}
+		return fmt.Sprintf("/%s%s", bucketName, subResource)
+	}
+	if conn.config.AuthVersion == AuthV2 {
+		return url.QueryEscape("/"+bucketName+"/") + strings.Replace(url.QueryEscape(objectName), "+", "%20", -1) + subResource
+	}
+	return fmt.Sprintf("/%s/%s%s", bucketName, objectName, subResource)
+}
+
 func (conn Conn) doRequest(method string, uri *url.URL, canonicalizedResource string, headers map[string]string,
 	data io.Reader, initCRC uint64, listener ProgressListener) (*Response, error) {
 	method = strings.ToUpper(method)
@@ -246,7 +272,7 @@ func (conn Conn) doRequest(method string, uri *url.URL, canonicalizedResource st
 
 	date := time.Now().UTC().Format(http.TimeFormat)
 	req.Header.Set(HTTPHeaderDate, date)
-	req.Header.Set(HTTPHeaderHost, conn.config.Endpoint)
+	req.Header.Set(HTTPHeaderHost, req.Host)
 	req.Header.Set(HTTPHeaderUserAgent, conn.config.UserAgent)
 
 	akIf := conn.config.GetCredentials()
@@ -297,8 +323,6 @@ func (conn Conn) signURL(method HTTPMethod, bucketName, objectName string, expir
 	if akIf.GetSecurityToken() != "" {
 		params[HTTPParamSecurityToken] = akIf.GetSecurityToken()
 	}
-	subResource := conn.getSubResource(params)
-	canonicalizedResource := conn.url.getResource(bucketName, objectName, subResource)
 
 	m := strings.ToUpper(string(method))
 	req := &http.Request{
@@ -313,7 +337,6 @@ func (conn Conn) signURL(method HTTPMethod, bucketName, objectName string, expir
 	}
 
 	req.Header.Set(HTTPHeaderDate, strconv.FormatInt(expiration, 10))
-	req.Header.Set(HTTPHeaderHost, conn.config.Endpoint)
 	req.Header.Set(HTTPHeaderUserAgent, conn.config.UserAgent)
 
 	if headers != nil {
@@ -322,12 +345,27 @@ func (conn Conn) signURL(method HTTPMethod, bucketName, objectName string, expir
 		}
 	}
 
-	signedStr := conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret())
+	if conn.config.AuthVersion == AuthV2 {
+		params[HTTPParamSignatureVersion] = "OSS2"
+		params[HTTPParamExpiresV2] = strconv.FormatInt(expiration, 10)
+		params[HTTPParamAccessKeyIDV2] = conn.config.AccessKeyID
+		additionalList, _ := conn.getAdditionalHeaderKeys(req)
+		if len(additionalList) > 0 {
+			params[HTTPParamAdditionalHeadersV2] = strings.Join(additionalList, ";")
+		}
+	}
 
-	params[HTTPParamExpires] = strconv.FormatInt(expiration, 10)
-	params[HTTPParamAccessKeyID] = akIf.GetAccessKeyID()
-	params[HTTPParamSignature] = signedStr
+	subResource := conn.getSubResource(params)
+	canonicalizedResource := conn.getResource(bucketName, objectName, subResource)
+	signedStr := conn.getSignedStr(req, canonicalizedResource, akIf.GetAccessKeySecret())
 
+	if conn.config.AuthVersion == AuthV1 {
+		params[HTTPParamExpires] = strconv.FormatInt(expiration, 10)
+		params[HTTPParamAccessKeyID] = akIf.GetAccessKeyID()
+		params[HTTPParamSignature] = signedStr
+	} else if conn.config.AuthVersion == AuthV2 {
+		params[HTTPParamSignatureV2] = signedStr
+	}
 	urlParams := conn.getURLParams(params)
 	return conn.url.getSignURL(bucketName, objectName, urlParams)
 }
@@ -758,14 +796,3 @@ func (um urlMaker) buildURL(bucket, object string) (string, string) {
 
 	return host, path
 }
-
-// getResource gets canonicalized resource
-func (um urlMaker) getResource(bucketName, objectName, subResource string) string {
-	if subResource != "" {
-		subResource = "?" + subResource
-	}
-	if bucketName == "" {
-		return fmt.Sprintf("/%s%s", bucketName, subResource)
-	}
-	return fmt.Sprintf("/%s/%s%s", bucketName, objectName, subResource)
-}

+ 12 - 7
oss/conn_test.go

@@ -23,9 +23,13 @@ func (s *OssConnSuite) TestURLMarker(c *C) {
 	c.Assert(um.getURL("bucket", "object", "params").String(), Equals, "http://docs.github.com/object?params")
 	c.Assert(um.getURL("bucket", "object", "").String(), Equals, "http://docs.github.com/object")
 	c.Assert(um.getURL("", "object", "").String(), Equals, "http://docs.github.com/object")
-	c.Assert(um.getResource("bucket", "object", "subres"), Equals, "/bucket/object?subres")
-	c.Assert(um.getResource("bucket", "object", ""), Equals, "/bucket/object")
-	c.Assert(um.getResource("", "object", ""), Equals, "/")
+
+	var conn Conn
+	conn.config = getDefaultOssConfig()
+	conn.config.AuthVersion = AuthV1
+	c.Assert(conn.getResource("bucket", "object", "subres"), Equals, "/bucket/object?subres")
+	c.Assert(conn.getResource("bucket", "object", ""), Equals, "/bucket/object")
+	c.Assert(conn.getResource("", "object", ""), Equals, "/")
 
 	um.Init("https://docs.github.com", true, false)
 	c.Assert(um.Type, Equals, urlTypeCname)
@@ -45,9 +49,9 @@ func (s *OssConnSuite) TestURLMarker(c *C) {
 	c.Assert(um.getURL("bucket", "object", "params").String(), Equals, "http://bucket.docs.github.com:8080/object?params")
 	c.Assert(um.getURL("bucket", "object", "").String(), Equals, "http://bucket.docs.github.com:8080/object")
 	c.Assert(um.getURL("", "object", "").String(), Equals, "http://docs.github.com:8080/")
-	c.Assert(um.getResource("bucket", "object", "subres"), Equals, "/bucket/object?subres")
-	c.Assert(um.getResource("bucket", "object", ""), Equals, "/bucket/object")
-	c.Assert(um.getResource("", "object", ""), Equals, "/")
+	c.Assert(conn.getResource("bucket", "object", "subres"), Equals, "/bucket/object?subres")
+	c.Assert(conn.getResource("bucket", "object", ""), Equals, "/bucket/object")
+	c.Assert(conn.getResource("", "object", ""), Equals, "/")
 
 	um.Init("https://docs.github.com:8080", false, true)
 	c.Assert(um.Type, Equals, urlTypeAliyun)
@@ -85,6 +89,7 @@ func (s *OssConnSuite) TestURLMarker(c *C) {
 func (s *OssConnSuite) TestAuth(c *C) {
 	endpoint := "https://github.com/"
 	cfg := getDefaultOssConfig()
+	cfg.AuthVersion = AuthV1
 	um := urlMaker{}
 	um.Init(endpoint, false, false)
 	conn := Conn{cfg, &um, nil}
@@ -107,7 +112,7 @@ func (s *OssConnSuite) TestAuth(c *C) {
 	req.Header.Set("X-OSS-Magic", "abracadabra")
 	req.Header.Set("Content-Md5", "ODBGOERFMDMzQTczRUY3NUE3NzA5QzdFNUYzMDQxNEM=")
 
-	conn.signHeader(req, um.getResource("bucket", "object", ""))
+	conn.signHeader(req, conn.getResource("bucket", "object", ""))
 	testLogger.Println("AUTHORIZATION:", req.Header.Get(HTTPHeaderAuthorization))
 }
 

+ 17 - 1
oss/const.go

@@ -178,6 +178,12 @@ const (
 	HTTPParamSignature     = "Signature"
 	HTTPParamSecurityToken = "security-token"
 	HTTPParamPlaylistName  = "playlistName"
+
+	HTTPParamSignatureVersion    = "x-oss-signature-version"
+	HTTPParamExpiresV2           = "x-oss-expires"
+	HTTPParamAccessKeyIDV2       = "x-oss-access-key-id"
+	HTTPParamSignatureV2         = "x-oss-signature"
+	HTTPParamAdditionalHeadersV2 = "x-oss-additional-headers"
 )
 
 // Other constants
@@ -194,7 +200,7 @@ const (
 
 	NullVersion = "null"
 
-	Version = "v2.0.6" // Go SDK version
+	Version = "v2.0.7" // Go SDK version
 )
 
 // FrameType
@@ -205,3 +211,13 @@ const (
 	MetaEndFrameCSVType  = 8388614
 	MetaEndFrameJSONType = 8388615
 )
+
+// AuthVersion the version of auth
+type AuthVersionType string
+
+const (
+	// AuthV1 v1
+	AuthV1 AuthVersionType = "v1"
+	// AuthV2 v2
+	AuthV2 AuthVersionType = "v2"
+)

+ 35 - 7
oss/progress_test.go

@@ -176,19 +176,31 @@ func (s *OssProgressSuite) TestPutObject(c *C) {
 }
 
 // TestSignURL
-func (s *OssProgressSuite) TestSignURL(c *C) {
+func (s *OssProgressSuite) SignURLTestFunc(c *C, authVersion AuthVersionType, extraHeaders []string) {
 	objectName := objectNamePrefix + randStr(8)
 	filePath := randLowStr(10)
 	content := randStr(20)
 	createFile(filePath, content, c)
 
+	oldType := s.bucket.Client.Config.AuthVersion
+	oldHeaders := s.bucket.Client.Config.AdditionalHeaders
+	s.bucket.Client.Config.AuthVersion = authVersion
+	s.bucket.Client.Config.AdditionalHeaders = extraHeaders
+
 	// Sign URL for put
 	progressListener := OssProgressListener{}
 	str, err := s.bucket.SignURL(objectName, HTTPPut, 60, Progress(&progressListener))
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Put object with URL
 	fd, err := os.Open(filePath)
@@ -219,9 +231,16 @@ func (s *OssProgressSuite) TestSignURL(c *C) {
 	// Sign URL for get
 	str, err = s.bucket.SignURL(objectName, HTTPGet, 60, Progress(&progressListener))
 	c.Assert(err, IsNil)
-	c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
-	c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	if s.bucket.Client.Config.AuthVersion == AuthV1 {
+		c.Assert(strings.Contains(str, HTTPParamExpires+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyID+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignature+"="), Equals, true)
+	} else {
+		c.Assert(strings.Contains(str, HTTPParamSignatureVersion+"=OSS2"), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamExpiresV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamAccessKeyIDV2+"="), Equals, true)
+		c.Assert(strings.Contains(str, HTTPParamSignatureV2+"="), Equals, true)
+	}
 
 	// Get object with URL
 	progressListener.TotalRwBytes = 0
@@ -253,6 +272,15 @@ func (s *OssProgressSuite) TestSignURL(c *C) {
 	c.Assert(err, IsNil)
 
 	testLogger.Println("OssProgressSuite.TestSignURL")
+
+	s.bucket.Client.Config.AuthVersion = oldType
+	s.bucket.Client.Config.AdditionalHeaders = oldHeaders
+}
+
+func (s *OssProgressSuite) TestSignURL(c *C) {
+	s.SignURLTestFunc(c, AuthV1, []string{})
+	s.SignURLTestFunc(c, AuthV2, []string{})
+	s.SignURLTestFunc(c, AuthV2, []string{"host", "range", "user-agent"})
 }
 
 func (s *OssProgressSuite) TestPutObjectNegative(c *C) {