1package oss
2
3import (
4	"encoding/xml"
5	"fmt"
6	"net/http"
7	"strings"
8)
9
10// ServiceError contains fields of the error response from Oss Service REST API.
11type ServiceError struct {
12	XMLName    xml.Name `xml:"Error"`
13	Code       string   `xml:"Code"`      // The error code returned from OSS to the caller
14	Message    string   `xml:"Message"`   // The detail error message from OSS
15	RequestID  string   `xml:"RequestId"` // The UUID used to uniquely identify the request
16	HostID     string   `xml:"HostId"`    // The OSS server cluster's Id
17	Endpoint   string   `xml:"Endpoint"`
18	RawMessage string   // The raw messages from OSS
19	StatusCode int      // HTTP status code
20}
21
22// Error implements interface error
23func (e ServiceError) Error() string {
24	if e.Endpoint == "" {
25		return fmt.Sprintf("oss: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=\"%s\", RequestId=%s",
26			e.StatusCode, e.Code, e.Message, e.RequestID)
27	}
28	return fmt.Sprintf("oss: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=\"%s\", RequestId=%s, Endpoint=%s",
29		e.StatusCode, e.Code, e.Message, e.RequestID, e.Endpoint)
30}
31
32// UnexpectedStatusCodeError is returned when a storage service responds with neither an error
33// nor with an HTTP status code indicating success.
34type UnexpectedStatusCodeError struct {
35	allowed []int // The expected HTTP stats code returned from OSS
36	got     int   // The actual HTTP status code from OSS
37}
38
39// Error implements interface error
40func (e UnexpectedStatusCodeError) Error() string {
41	s := func(i int) string { return fmt.Sprintf("%d %s", i, http.StatusText(i)) }
42
43	got := s(e.got)
44	expected := []string{}
45	for _, v := range e.allowed {
46		expected = append(expected, s(v))
47	}
48	return fmt.Sprintf("oss: status code from service response is %s; was expecting %s",
49		got, strings.Join(expected, " or "))
50}
51
52// Got is the actual status code returned by oss.
53func (e UnexpectedStatusCodeError) Got() int {
54	return e.got
55}
56
57// checkRespCode returns UnexpectedStatusError if the given response code is not
58// one of the allowed status codes; otherwise nil.
59func checkRespCode(respCode int, allowed []int) error {
60	for _, v := range allowed {
61		if respCode == v {
62			return nil
63		}
64	}
65	return UnexpectedStatusCodeError{allowed, respCode}
66}
67
68// CRCCheckError is returned when crc check is inconsistent between client and server
69type CRCCheckError struct {
70	clientCRC uint64 // Calculated CRC64 in client
71	serverCRC uint64 // Calculated CRC64 in server
72	operation string // Upload operations such as PutObject/AppendObject/UploadPart, etc
73	requestID string // The request id of this operation
74}
75
76// Error implements interface error
77func (e CRCCheckError) Error() string {
78	return fmt.Sprintf("oss: the crc of %s is inconsistent, client %d but server %d; request id is %s",
79		e.operation, e.clientCRC, e.serverCRC, e.requestID)
80}
81
82func checkDownloadCRC(clientCRC, serverCRC uint64) error {
83	if clientCRC == serverCRC {
84		return nil
85	}
86	return CRCCheckError{clientCRC, serverCRC, "DownloadFile", ""}
87}
88
89func checkCRC(resp *Response, operation string) error {
90	if resp.Headers.Get(HTTPHeaderOssCRC64) == "" || resp.ClientCRC == resp.ServerCRC {
91		return nil
92	}
93	return CRCCheckError{resp.ClientCRC, resp.ServerCRC, operation, resp.Headers.Get(HTTPHeaderOssRequestID)}
94}
95