Browse Source

1. Fixed bugs that could cause signature errors while a ROA request contains lots of special symbols
2. Optimization of the serverError.Error() interface
3. When there is an error of "SignatureDoseNotMatch", it is now possible to tell user if the AccessKeySecret is wrong or caused by a sdk bug

Signed-off-by: gaort <rutong.grt@alibaba-inc.com>

gaort 7 years ago
parent
commit
9ef11c550d

+ 3 - 0
sdk/auth/roa_signature_composer.go

@@ -25,6 +25,7 @@ import (
 func signRoaRequest(request requests.AcsRequest, signer Signer, regionId string) {
 func signRoaRequest(request requests.AcsRequest, signer Signer, regionId string) {
 	completeROASignParams(request, signer, regionId)
 	completeROASignParams(request, signer, regionId)
 	stringToSign := buildRoaStringToSign(request)
 	stringToSign := buildRoaStringToSign(request)
+	request.SetStringToSign(stringToSign)
 	signature := signer.Sign(stringToSign, "")
 	signature := signer.Sign(stringToSign, "")
 	request.GetHeaders()["Authorization"] = "acs " + signer.GetAccessKeyId() + ":" + signature
 	request.GetHeaders()["Authorization"] = "acs " + signer.GetAccessKeyId() + ":" + signature
 }
 }
@@ -99,6 +100,8 @@ func buildRoaStringToSign(request requests.AcsRequest) (stringToSign string) {
 	return
 	return
 }
 }
 
 
+
+
 func appendIfContain(sourceMap map[string]string, target *bytes.Buffer, key, separator string) {
 func appendIfContain(sourceMap map[string]string, target *bytes.Buffer, key, separator string) {
 	if value, contain := sourceMap[key]; contain && len(value) > 0 {
 	if value, contain := sourceMap[key]; contain && len(value) > 0 {
 		target.WriteString(sourceMap[key])
 		target.WriteString(sourceMap[key])

+ 1 - 0
sdk/auth/rpc_signature_composer.go

@@ -29,6 +29,7 @@ func signRpcRequest(request requests.AcsRequest, signer Signer, regionId string)
 		delete(request.GetQueryParams(), "Signature")
 		delete(request.GetQueryParams(), "Signature")
 	}
 	}
 	stringToSign := buildRpcStringToSign(request)
 	stringToSign := buildRpcStringToSign(request)
+	request.SetStringToSign(stringToSign)
 	signature := signer.Sign(stringToSign, "&")
 	signature := signer.Sign(stringToSign, "&")
 	request.GetQueryParams()["Signature"] = signature
 	request.GetQueryParams()["Signature"] = signature
 }
 }

+ 2 - 2
sdk/auth/signers/signer_key_pair.go

@@ -118,8 +118,8 @@ func (signerKeyPair *SignerKeyPair) refreshApi(request *requests.CommonRequest)
 
 
 func (signer *SignerKeyPair) refreshCredential(response *responses.CommonResponse) (err error) {
 func (signer *SignerKeyPair) refreshCredential(response *responses.CommonResponse) (err error) {
 	if response.GetHttpStatus() != http.StatusOK {
 	if response.GetHttpStatus() != http.StatusOK {
-		message := "refresh session AccessKey failed, message = " + response.GetHttpContentString()
-		err = errors.NewServerError(response.GetHttpStatus(), response.GetOriginHttpResponse().Status, message)
+		message := "refresh session AccessKey failed"
+		err = errors.NewServerError(response.GetHttpStatus(), response.GetHttpContentString(), message)
 		if signer.sessionCredential == nil {
 		if signer.sessionCredential == nil {
 			panic(err)
 			panic(err)
 		}
 		}

+ 2 - 2
sdk/auth/signers/signer_ram_role_arn.go

@@ -135,8 +135,8 @@ func (signerStsAssumeRole *RamRoleArnSigner) refreshApi(request *requests.Common
 
 
 func (signer *RamRoleArnSigner) refreshCredential(response *responses.CommonResponse) (err error) {
 func (signer *RamRoleArnSigner) refreshCredential(response *responses.CommonResponse) (err error) {
 	if response.GetHttpStatus() != http.StatusOK {
 	if response.GetHttpStatus() != http.StatusOK {
-		message := "refresh session token failed, message = " + response.GetHttpContentString()
-		err = errors.NewServerError(response.GetHttpStatus(), response.GetOriginHttpResponse().Status, message)
+		message := "refresh session token failed"
+		err = errors.NewServerError(response.GetHttpStatus(), response.GetHttpContentString(), message)
 		if signer.sessionCredential == nil {
 		if signer.sessionCredential == nil {
 			panic(err)
 			panic(err)
 		}
 		}

+ 6 - 0
sdk/client.go

@@ -222,6 +222,12 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r
 		break
 		break
 	}
 	}
 	err = responses.Unmarshal(response, httpResponse, request.GetAcceptFormat())
 	err = responses.Unmarshal(response, httpResponse, request.GetAcceptFormat())
+	// wrap server errors
+	if serverErr, ok := err.(*errors.ServerError); ok {
+		var wrapInfo = map[string]string{}
+		wrapInfo["StringToSign"] = request.GetStringToSign()
+		err = errors.WrapServerError(serverErr, wrapInfo)
+	}
 	return
 	return
 }
 }
 
 

+ 80 - 9
sdk/errors/server_error.go

@@ -14,20 +14,79 @@
 
 
 package errors
 package errors
 
 
-import "fmt"
+import (
+	"fmt"
+	"encoding/json"
+	"github.com/jmespath/go-jmespath"
+)
+
+var wrapperList = []ServerErrorWrapper{
+	&SignatureDostNotMatchWrapper{},
+}
 
 
 type ServerError struct {
 type ServerError struct {
 	httpStatus int
 	httpStatus int
+	requestId  string
+	hostId     string
 	errorCode  string
 	errorCode  string
+	recommend  string
 	message    string
 	message    string
+	comment    string
+}
+
+type ServerErrorWrapper interface {
+	tryWrap(error *ServerError, wrapInfo map[string]string) (bool, *ServerError)
+}
+
+func (err *ServerError) Error() string {
+	return fmt.Sprintf("SDK.ServerError\nErrorCode: %s\nRecommend: %s\nRequestId: %s\nMessage: %s",
+		err.errorCode, err.comment+err.recommend, err.requestId, err.message)
 }
 }
 
 
-func NewServerError(httpStatus int, errorCode, message string) Error {
-	return &ServerError{
+func NewServerError(httpStatus int, responseContent, comment string) Error {
+	result := &ServerError{
 		httpStatus: httpStatus,
 		httpStatus: httpStatus,
-		errorCode:  errorCode,
-		message:    message,
+		message:    responseContent,
+		comment:    comment,
 	}
 	}
+
+	var data interface{}
+	err := json.Unmarshal([]byte(responseContent), &data)
+	if err == nil {
+		requestId, _ := jmespath.Search("RequestId", data)
+		hostId, _ := jmespath.Search("HostId", data)
+		errorCode, _ := jmespath.Search("Code", data)
+		recommend, _ := jmespath.Search("Recommend", data)
+		message, _ := jmespath.Search("Message", data)
+
+		if requestId != nil {
+			result.requestId = requestId.(string)
+		}
+		if hostId != nil {
+			result.hostId = hostId.(string)
+		}
+		if errorCode != nil {
+			result.errorCode = errorCode.(string)
+		}
+		if recommend != nil {
+			result.recommend = recommend.(string)
+		}
+		if message != nil {
+			result.message = message.(string)
+		}
+	}
+
+	return result
+}
+
+func WrapServerError(originError *ServerError, wrapInfo map[string]string) *ServerError {
+	for _, wrapper := range wrapperList {
+		ok, newError := wrapper.tryWrap(originError, wrapInfo)
+		if ok {
+			return newError
+		}
+	}
+	return originError
 }
 }
 
 
 func (err *ServerError) HttpStatus() int {
 func (err *ServerError) HttpStatus() int {
@@ -42,10 +101,22 @@ func (err *ServerError) Message() string {
 	return err.message
 	return err.message
 }
 }
 
 
-func (err *ServerError) Error() string {
-	return fmt.Sprintf("SDK.ServerError %s %s", err.errorCode, err.message)
-}
-
 func (err *ServerError) OriginError() error {
 func (err *ServerError) OriginError() error {
 	return nil
 	return nil
 }
 }
+
+func (err *ServerError) HostId() string {
+	return err.hostId
+}
+
+func (err *ServerError) RequestId() string {
+	return err.requestId
+}
+
+func (err *ServerError) Recommend() string {
+	return err.recommend
+}
+
+func (err *ServerError) Comment() string {
+	return err.comment
+}

+ 29 - 0
sdk/errors/signature_does_not_match_wrapper.go

@@ -0,0 +1,29 @@
+package errors
+
+import "strings"
+
+const SignatureDostNotMatchErrorCode = "SignatureDoesNotMatch"
+const MessagePrefix = "Specified signature is not matched with our calculation. server string to sign is:"
+
+type SignatureDostNotMatchWrapper struct {
+}
+
+func (*SignatureDostNotMatchWrapper) tryWrap(error *ServerError, wrapInfo map[string]string) (bool, *ServerError) {
+	clientStringToSign := wrapInfo["StringToSign"]
+	if error.errorCode == SignatureDostNotMatchErrorCode && clientStringToSign != "" {
+		message := error.message
+		if strings.HasPrefix(message, MessagePrefix){
+			serverStringToSign := message[len(MessagePrefix):len(message)]
+			if clientStringToSign == serverStringToSign{
+				// user secret is error
+				error.recommend = "Please check you AccessKeySecret"
+			}else{
+				error.recommend = "This may be a bug with the SDK and we hope you can submit this question in the " +
+						"github issue(https://github.com/aliyun/alibaba-cloud-sdk-go/issues), thanks very much"
+			}
+		}
+		return true, error
+	} else {
+		return false, nil
+	}
+}

+ 13 - 0
sdk/requests/acs_reqeust.go

@@ -73,6 +73,9 @@ type AcsRequest interface {
 	GetLocationServiceCode() string
 	GetLocationServiceCode() string
 	GetLocationEndpointType() string
 	GetLocationEndpointType() string
 
 
+	SetStringToSign(stringToSign string)
+	GetStringToSign() string
+
 	SetDomain(domain string)
 	SetDomain(domain string)
 	SetContent(content []byte)
 	SetContent(content []byte)
 	BuildUrl() string
 	BuildUrl() string
@@ -107,6 +110,8 @@ type baseRequest struct {
 	locationEndpointType string
 	locationEndpointType string
 
 
 	queries string
 	queries string
+
+	stringToSign string
 }
 }
 
 
 func (request *baseRequest) GetQueryParams() map[string]string {
 func (request *baseRequest) GetQueryParams() map[string]string {
@@ -198,6 +203,14 @@ func (request *baseRequest) GetContentType() (contentType string, contains bool)
 	return
 	return
 }
 }
 
 
+func (request *baseRequest) SetStringToSign(stringToSign string) {
+	request.stringToSign = stringToSign
+}
+
+func (request *baseRequest) GetStringToSign() string {
+	return request.stringToSign
+}
+
 func defaultBaseRequest() (request *baseRequest) {
 func defaultBaseRequest() (request *baseRequest) {
 	request = &baseRequest{
 	request = &baseRequest{
 		Scheme:       HTTP,
 		Scheme:       HTTP,

+ 23 - 3
sdk/requests/roa_request.go

@@ -20,6 +20,7 @@ import (
 	"io"
 	"io"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
+	"net/url"
 )
 )
 
 
 type RoaRequest struct {
 type RoaRequest struct {
@@ -47,7 +48,12 @@ func (request *RoaRequest) GetQueries() string {
 	return request.queries
 	return request.queries
 }
 }
 
 
+// for sign method, need not url encoded
 func (request *RoaRequest) BuildQueries() string {
 func (request *RoaRequest) BuildQueries() string {
+	return request.buildQueries(false)
+}
+
+func (request *RoaRequest) buildQueries(needParamEncode bool) string {
 	// replace path params with value
 	// replace path params with value
 	path := request.pathPattern
 	path := request.pathPattern
 	for key, value := range request.PathParams {
 	for key, value := range request.PathParams {
@@ -77,22 +83,36 @@ func (request *RoaRequest) BuildQueries() string {
 		urlBuilder.WriteString(queryKey)
 		urlBuilder.WriteString(queryKey)
 		if value := queryParams[queryKey]; len(value) > 0 {
 		if value := queryParams[queryKey]; len(value) > 0 {
 			urlBuilder.WriteString("=")
 			urlBuilder.WriteString("=")
-			urlBuilder.WriteString(value)
+			if needParamEncode{
+				urlBuilder.WriteString(url.QueryEscape(value))
+			}else{
+				urlBuilder.WriteString(value)
+			}
 		}
 		}
 		if i < len(queryKeys)-1 {
 		if i < len(queryKeys)-1 {
 			urlBuilder.WriteString("&")
 			urlBuilder.WriteString("&")
 		}
 		}
 	}
 	}
-	request.queries = urlBuilder.String()
+	result := urlBuilder.String()
+	result = popStandardUrlencode(result)
+	request.queries = result
 	return request.queries
 	return request.queries
 }
 }
 
 
+func popStandardUrlencode(stringToSign string)(result string){
+	result = strings.Replace(stringToSign, "+", "%20", -1)
+	result = strings.Replace(result, "*", "%2A", -1)
+	result = strings.Replace(result, "%7E", "~", -1)
+	return
+}
+
 func (request *RoaRequest) GetUrl() string {
 func (request *RoaRequest) GetUrl() string {
 	return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.GetQueries()
 	return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.GetQueries()
 }
 }
 
 
 func (request *RoaRequest) BuildUrl() string {
 func (request *RoaRequest) BuildUrl() string {
-	return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.BuildQueries()
+	// for network trans, need url encoded
+	return strings.ToLower(request.Scheme) + "://" + request.Domain + ":" + request.Port + request.buildQueries(true)
 }
 }
 
 
 func (request *RoaRequest) addPathParam(key, value string) {
 func (request *RoaRequest) addPathParam(key, value string) {

+ 1 - 1
sdk/responses/response.go

@@ -40,7 +40,7 @@ func Unmarshal(response AcsResponse, httpResponse *http.Response, format string)
 		return
 		return
 	}
 	}
 	if !response.IsSuccess() {
 	if !response.IsSuccess() {
-		err = errors.NewServerError(response.GetHttpStatus(), response.GetOriginHttpResponse().Status, response.GetHttpContentString())
+		err = errors.NewServerError(response.GetHttpStatus(), response.GetHttpContentString(), "")
 		return
 		return
 	}
 	}
 	if _, isCommonResponse := response.(CommonResponse); isCommonResponse {
 	if _, isCommonResponse := response.(CommonResponse); isCommonResponse {