| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- package tos
- import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- )
- var (
- InputIsNilClientError = newTosClientError("input is nil. ", nil)
- InputInvalidClientError = newTosClientError("input data is invalid. ", nil)
- InvalidPartNumber = newTosClientError("input part number is invalid. ", nil)
- InvalidUploadID = newTosClientError("input upload id is invalid. ", nil)
- InvalidBucketNameLength = newTosClientError("invalid bucket name, the length must be [3, 63]", nil)
- InvalidBucketNameCharacter = newTosClientError("invalid bucket name, the character set is illegal", nil)
- InvalidBucketNameStartingOrEnding = newTosClientError("invalid bucket name, the bucket name can be neither starting with '-' nor ending with '-'", nil)
- InvalidObjectNameLength = newTosClientError("invalid object name, the length must be [1, 696]", nil)
- InvalidObjectNameStartingOrEnding = newTosClientError("invalid object name, the object name can not start with '\\'", nil)
- InvalidObjectNameCharacterSet = newTosClientError("invalid object name, the character set is illegal", nil)
- InvalidACL = newTosClientError("invalid acl type", nil)
- InvalidStorageClass = newTosClientError("invalid storage class", nil)
- InvalidGrantee = newTosClientError("invalid grantee type", nil)
- InvalidCanned = newTosClientError("invalid canned type", nil)
- InvalidAzRedundancy = newTosClientError("invalid az redundancy type", nil)
- InvalidMetadataDirective = newTosClientError("invalid metadata directive type", nil)
- InvalidPermission = newTosClientError("invalid permission type", nil)
- InvalidSSECAlgorithm = newTosClientError("invalid encryption-decryption algorithm", nil)
- InvalidPartSize = newTosClientError("invalid part size, the size must be [5242880, 5368709120]", nil)
- InvalidSrcFilePath = newTosClientError("invalid file path, the file does not exist", nil)
- InvalidFilePartNum = newTosClientError("unsupported part number, the maximum is 10000", nil)
- InvalidMarshal = newTosClientError("unable to do serialization/deserialization", nil)
- InvalidPreSignedURLExpires = newTosClientError("invalid pre signed url expires, the time must be less 604800 seconds.", nil)
- InvalidPreSignedConditions = newTosClientError("invalid pre signed url conditions.", nil)
- InvalidFilePath = newTosClientError("invalid file path", nil)
- InvalidCheckpointFilePath = newTosClientError("invalid checkpoint file path", nil)
- CrcCheckFail = newTosClientError("crc check not equal", nil)
- InvalidS3Endpoint = newTosClientError("do not support s3 endpoint, please use tos endpoint", nil)
- NotSupportSeek = newTosClientError("reader not support seek", nil)
- ProxyNotSupportHttps = newTosClientError("proxy not support https", nil)
- ProxyUrlInvalid = newTosClientError("proxy url invalid", nil)
- NotificationConfigurationsInvalid = newTosClientError("Notification Configurations invalid", nil)
- InvalidCompleteAllPartsLength = newTosClientError("Should not specify both complete all and Parts", nil)
- InvalidPartsLength = newTosClientError("You must specify at least one part", nil)
- InvlidDeleteMultiObjectsLength = newTosClientError("You must specify at least one object", nil)
- )
- type TosError struct {
- Message string
- }
- func (e *TosError) Error() string {
- return e.Message
- }
- // for simplify code
- func newTosClientError(message string, cause error) *TosClientError {
- return &TosClientError{
- TosError: TosError{
- Message: message,
- },
- Cause: cause,
- }
- }
- type TosClientError struct {
- TosError
- Cause error
- }
- func (t *TosClientError) withCause(err error) error {
- t.Cause = err
- return t
- }
- // try to unmarshal server error from response
- func newTosServerError(res *Response) error {
- data, err := ioutil.ReadAll(io.LimitReader(res.Body, 64<<10)) // avoid too large
- if err != nil && len(data) <= 0 {
- return &TosServerError{
- TosError: TosError{"tos: server returned an empty body"},
- RequestInfo: res.RequestInfo(),
- }
- }
- se := Error{StatusCode: res.StatusCode}
- if err = json.Unmarshal(data, &se); err != nil {
- return &TosServerError{
- TosError: TosError{"tos: server returned an invalid body"},
- RequestInfo: res.RequestInfo(),
- }
- }
- return &TosServerError{
- TosError: TosError{se.Message},
- RequestInfo: res.RequestInfo(),
- Code: se.Code,
- HostID: se.HostID,
- Resource: se.Resource,
- }
- }
- // 服务端错误定义参考:https://www.volcengine.com/docs/6349/74874
- type TosServerError struct {
- TosError `json:"TosError"`
- RequestInfo `json:"RequestInfo"`
- Code string `json:"Code,omitempty"`
- HostID string `json:"HostID,omitempty"`
- Resource string `json:"Resource,omitempty"`
- }
- type Error struct {
- StatusCode int `json:"-"`
- Code string `json:"Code,omitempty"`
- Message string `json:"Message,omitempty"`
- RequestID string `json:"RequestId,omitempty"`
- HostID string `json:"HostId,omitempty"`
- Resource string `json:"Resource,omitempty"`
- }
- func (e *Error) Error() string {
- return fmt.Sprintf("tos: request error: StatusCode=%d, Code=%s, Message=%q, RequestID=%s, HostID=%s",
- e.StatusCode, e.Code, e.Message, e.RequestID, e.HostID)
- }
- // Code return error code saved in TosServerError
- func Code(err error) string {
- if er, ok := err.(*TosServerError); ok {
- return er.Code
- }
- return ""
- }
- // StatueCode return status code saved in TosServerError or UnexpectedStatusCodeError
- //
- // Deprecated: use StatusCode instead
- func StatueCode(err error) int {
- return StatusCode(err)
- }
- // StatusCode return status code saved in TosServerError or UnexpectedStatusCodeError
- func StatusCode(err error) int {
- if er, ok := err.(*TosServerError); ok {
- return er.StatusCode
- }
- if er, ok := err.(*UnexpectedStatusCodeError); ok {
- return er.StatusCode
- }
- return 0
- }
- func RequestID(err error) string {
- switch ev := err.(type) {
- case *TosServerError:
- return ev.RequestID
- case *UnexpectedStatusCodeError:
- return ev.RequestID
- case *ChecksumError:
- return ev.RequestID
- case *SerializeError:
- return ev.RequestID
- }
- return ""
- }
- type UnexpectedStatusCodeError struct {
- StatusCode int `json:"StatusCode,omitempty"`
- ExpectedCodes []int `json:"ExpectedCodes,omitempty"`
- RequestID string `json:"RequestId,omitempty"`
- expectedCodes [2]int
- responseMsg string
- err Error
- }
- func NewUnexpectedStatusCodeError(statusCode int, expectedCode int, expectedCodes ...int) *UnexpectedStatusCodeError {
- err := UnexpectedStatusCodeError{
- StatusCode: statusCode,
- }
- err.ExpectedCodes = err.expectedCodes[:0]
- err.ExpectedCodes = append(err.ExpectedCodes, expectedCode)
- err.ExpectedCodes = append(err.ExpectedCodes, expectedCodes...)
- return &err
- }
- func (us *UnexpectedStatusCodeError) WithRequestBody(res *Response) *UnexpectedStatusCodeError {
- data, err := ioutil.ReadAll(io.LimitReader(res.Body, 64<<10))
- if err != nil || len(data) <= 0 {
- return us
- }
- us.responseMsg = string(data)
- se := Error{StatusCode: res.StatusCode}
- err = json.Unmarshal(data, &se)
- if err != nil {
- return us
- }
- us.err = se
- return us
- }
- func (us *UnexpectedStatusCodeError) WithRequestID(requestID string) *UnexpectedStatusCodeError {
- us.RequestID = requestID
- return us
- }
- func (us *UnexpectedStatusCodeError) GoString() string {
- if us.responseMsg != "" {
- return fmt.Sprintf("tos.UnexpectedStatusCodeError{StatusCode:%d, ExpectedCodes:%v, RequestID:%s, ResponseErr:%s}",
- us.StatusCode, us.ExpectedCodes, us.RequestID, us.responseMsg)
- }
- return fmt.Sprintf("tos.UnexpectedStatusCodeError{StatusCode:%d, ExpectedCodes:%v, RequestID:%s}",
- us.StatusCode, us.ExpectedCodes, us.RequestID)
- }
- func (us *UnexpectedStatusCodeError) Error() string {
- if us.responseMsg != "" {
- return fmt.Sprintf("tos: unexpected status code error: StatusCode=%d, ExpectedCodes=%v, RequestID=%s, ResponseErr:%s",
- us.StatusCode, us.ExpectedCodes, us.RequestID, us.responseMsg)
- }
- return fmt.Sprintf("tos: unexpected status code error: StatusCode=%d, ExpectedCodes=%v, RequestID=%s",
- us.StatusCode, us.ExpectedCodes, us.RequestID)
- }
- type ChecksumError struct {
- RequestID string `json:"RequestId,omitempty"`
- ExpectedChecksum string `json:"ExpectedChecksum,omitempty"`
- ActualChecksum string `json:"ActualChecksum,omitempty"`
- }
- func (ce *ChecksumError) Error() string {
- return fmt.Sprintf("tos: checksum error: RequestID=%s, ExpectedChecksum=%s, ActualChecksum=%s",
- ce.RequestID, ce.ExpectedChecksum, ce.ActualChecksum)
- }
- type SerializeError struct {
- RequestID string `json:"RequestId,omitempty"`
- Message string `json:"Message,omitempty"`
- }
- func (se *SerializeError) Error() string {
- return fmt.Sprintf("tos: serialize error: RequestID=%s, Message=%q", se.RequestID, se.Message)
- }
- func checkError(res *Response, readBody bool, okCode int, okCodes ...int) error {
- if res.StatusCode == okCode {
- return nil
- }
- for _, code := range okCodes {
- if res.StatusCode == code {
- return nil
- }
- }
- defer res.Close()
- if readBody && res.StatusCode >= http.StatusBadRequest && res.Body != nil {
- return newTosServerError(res)
- // fall through
- }
- unexpected := NewUnexpectedStatusCodeError(res.StatusCode, okCode, okCodes...).
- WithRequestID(res.RequestInfo().RequestID)
- if readBody && res.Body != nil {
- unexpected = unexpected.WithRequestBody(res)
- }
- return &TosServerError{
- TosError: TosError{unexpected.Error()},
- RequestInfo: res.RequestInfo(),
- Code: unexpected.err.Code,
- HostID: unexpected.err.HostID,
- Resource: unexpected.err.Resource,
- }
- }
- // StatusCodeClassifier classifies Errors.
- // If the error is nil, it returns NoRetry;
- // if the error is TimeoutException or can be interpreted as TosServerError, and the StatusCode is 5xx or 429, it returns Retry;
- // otherwise, it returns NoRetry.
- type StatusCodeClassifier struct{}
- // Classify implements the classifier interface.
- func (classifier StatusCodeClassifier) Classify(err error) retryAction {
- if err == nil {
- return NoRetry
- }
- e, ok := err.(*TosServerError)
- if ok {
- if e.StatusCode >= 500 || e.StatusCode == 429 {
- return Retry
- }
- }
- cErr, ok := err.(*TosClientError)
- if ok {
- _, ok = cErr.Cause.(interface{ Timeout() bool })
- if ok {
- return Retry
- }
- }
- return NoRetry
- }
- // ServerErrorClassifier classify errors returned by POST method.
- // If the error is nil, it returns NoRetry;
- // if the error can be interpreted as TosServerError and its StatusCode is 5xx or 429, it returns Retry;
- // otherwise, it returns NoRetry.
- type ServerErrorClassifier struct{}
- // Classify implements the classifier interface.
- func (classifier ServerErrorClassifier) Classify(err error) retryAction {
- if err == nil {
- return NoRetry
- }
- e, ok := err.(*TosServerError)
- if ok {
- if e.StatusCode >= 500 || e.StatusCode == 429 {
- return Retry
- }
- }
- return NoRetry
- }
- type NoRetryClassifier struct{}
- // Classify implements the classifier interface.
- func (classifier NoRetryClassifier) Classify(_ error) retryAction {
- return NoRetry
- }
|