Bläddra i källkod

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 år sedan
förälder
incheckning
9ef11c550d

+ 3 - 0
sdk/auth/roa_signature_composer.go

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

+ 6 - 0
sdk/client.go

@@ -222,6 +222,12 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r
 		break
 	}
 	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
 }
 

+ 80 - 9
sdk/errors/server_error.go

@@ -14,20 +14,79 @@
 
 package errors
 
-import "fmt"
+import (
+	"fmt"
+	"encoding/json"
+	"github.com/jmespath/go-jmespath"
+)
+
+var wrapperList = []ServerErrorWrapper{
+	&SignatureDostNotMatchWrapper{},
+}
 
 type ServerError struct {
 	httpStatus int
+	requestId  string
+	hostId     string
 	errorCode  string
+	recommend  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,
-		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 {
@@ -42,10 +101,22 @@ func (err *ServerError) Message() string {
 	return err.message
 }
 
-func (err *ServerError) Error() string {
-	return fmt.Sprintf("SDK.ServerError %s %s", err.errorCode, err.message)
-}
-
 func (err *ServerError) OriginError() error {
 	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
 	GetLocationEndpointType() string
 
+	SetStringToSign(stringToSign string)
+	GetStringToSign() string
+
 	SetDomain(domain string)
 	SetContent(content []byte)
 	BuildUrl() string
@@ -107,6 +110,8 @@ type baseRequest struct {
 	locationEndpointType string
 
 	queries string
+
+	stringToSign string
 }
 
 func (request *baseRequest) GetQueryParams() map[string]string {
@@ -198,6 +203,14 @@ func (request *baseRequest) GetContentType() (contentType string, contains bool)
 	return
 }
 
+func (request *baseRequest) SetStringToSign(stringToSign string) {
+	request.stringToSign = stringToSign
+}
+
+func (request *baseRequest) GetStringToSign() string {
+	return request.stringToSign
+}
+
 func defaultBaseRequest() (request *baseRequest) {
 	request = &baseRequest{
 		Scheme:       HTTP,

+ 23 - 3
sdk/requests/roa_request.go

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

+ 1 - 1
sdk/responses/response.go

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