api-error-response.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * Minio Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 Minio, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "encoding/xml"
  20. "fmt"
  21. "net/http"
  22. )
  23. /* **** SAMPLE ERROR RESPONSE ****
  24. <?xml version="1.0" encoding="UTF-8"?>
  25. <Error>
  26. <Code>AccessDenied</Code>
  27. <Message>Access Denied</Message>
  28. <BucketName>bucketName</BucketName>
  29. <Key>objectName</Key>
  30. <RequestId>F19772218238A85A</RequestId>
  31. <HostId>GuWkjyviSiGHizehqpmsD1ndz5NClSP19DOT+s2mv7gXGQ8/X1lhbDGiIJEXpGFD</HostId>
  32. </Error>
  33. */
  34. // ErrorResponse - Is the typed error returned by all API operations.
  35. type ErrorResponse struct {
  36. XMLName xml.Name `xml:"Error" json:"-"`
  37. Code string
  38. Message string
  39. BucketName string
  40. Key string
  41. RequestID string `xml:"RequestId"`
  42. HostID string `xml:"HostId"`
  43. // Region where the bucket is located. This header is returned
  44. // only in HEAD bucket and ListObjects response.
  45. Region string
  46. // Underlying HTTP status code for the returned error
  47. StatusCode int `xml:"-" json:"-"`
  48. // Headers of the returned S3 XML error
  49. Headers http.Header `xml:"-" json:"-"`
  50. }
  51. // ToErrorResponse - Returns parsed ErrorResponse struct from body and
  52. // http headers.
  53. //
  54. // For example:
  55. //
  56. // import s3 "github.com/minio/minio-go"
  57. // ...
  58. // ...
  59. // reader, stat, err := s3.GetObject(...)
  60. // if err != nil {
  61. // resp := s3.ToErrorResponse(err)
  62. // }
  63. // ...
  64. func ToErrorResponse(err error) ErrorResponse {
  65. switch err := err.(type) {
  66. case ErrorResponse:
  67. return err
  68. default:
  69. return ErrorResponse{}
  70. }
  71. }
  72. // Error - Returns S3 error string.
  73. func (e ErrorResponse) Error() string {
  74. if e.Message == "" {
  75. msg, ok := s3ErrorResponseMap[e.Code]
  76. if !ok {
  77. msg = fmt.Sprintf("Error response code %s.", e.Code)
  78. }
  79. return msg
  80. }
  81. return e.Message
  82. }
  83. // Common string for errors to report issue location in unexpected
  84. // cases.
  85. const (
  86. reportIssue = "Please report this issue at https://github.com/minio/minio-go/issues."
  87. )
  88. // httpRespToErrorResponse returns a new encoded ErrorResponse
  89. // structure as error.
  90. func httpRespToErrorResponse(resp *http.Response, bucketName, objectName string) error {
  91. if resp == nil {
  92. msg := "Response is empty. " + reportIssue
  93. return ErrInvalidArgument(msg)
  94. }
  95. errResp := ErrorResponse{
  96. StatusCode: resp.StatusCode,
  97. }
  98. err := xmlDecoder(resp.Body, &errResp)
  99. // Xml decoding failed with no body, fall back to HTTP headers.
  100. if err != nil {
  101. switch resp.StatusCode {
  102. case http.StatusNotFound:
  103. if objectName == "" {
  104. errResp = ErrorResponse{
  105. StatusCode: resp.StatusCode,
  106. Code: "NoSuchBucket",
  107. Message: "The specified bucket does not exist.",
  108. BucketName: bucketName,
  109. }
  110. } else {
  111. errResp = ErrorResponse{
  112. StatusCode: resp.StatusCode,
  113. Code: "NoSuchKey",
  114. Message: "The specified key does not exist.",
  115. BucketName: bucketName,
  116. Key: objectName,
  117. }
  118. }
  119. case http.StatusForbidden:
  120. errResp = ErrorResponse{
  121. StatusCode: resp.StatusCode,
  122. Code: "AccessDenied",
  123. Message: "Access Denied.",
  124. BucketName: bucketName,
  125. Key: objectName,
  126. }
  127. case http.StatusConflict:
  128. errResp = ErrorResponse{
  129. StatusCode: resp.StatusCode,
  130. Code: "Conflict",
  131. Message: "Bucket not empty.",
  132. BucketName: bucketName,
  133. }
  134. case http.StatusPreconditionFailed:
  135. errResp = ErrorResponse{
  136. StatusCode: resp.StatusCode,
  137. Code: "PreconditionFailed",
  138. Message: s3ErrorResponseMap["PreconditionFailed"],
  139. BucketName: bucketName,
  140. Key: objectName,
  141. }
  142. default:
  143. errResp = ErrorResponse{
  144. StatusCode: resp.StatusCode,
  145. Code: resp.Status,
  146. Message: resp.Status,
  147. BucketName: bucketName,
  148. }
  149. }
  150. }
  151. // Save hostID, requestID and region information
  152. // from headers if not available through error XML.
  153. if errResp.RequestID == "" {
  154. errResp.RequestID = resp.Header.Get("x-amz-request-id")
  155. }
  156. if errResp.HostID == "" {
  157. errResp.HostID = resp.Header.Get("x-amz-id-2")
  158. }
  159. if errResp.Region == "" {
  160. errResp.Region = resp.Header.Get("x-amz-bucket-region")
  161. }
  162. if errResp.Code == "InvalidRegion" && errResp.Region != "" {
  163. errResp.Message = fmt.Sprintf("Region does not match, expecting region ‘%s’.", errResp.Region)
  164. }
  165. // Save headers returned in the API XML error
  166. errResp.Headers = resp.Header
  167. return errResp
  168. }
  169. // ErrTransferAccelerationBucket - bucket name is invalid to be used with transfer acceleration.
  170. func ErrTransferAccelerationBucket(bucketName string) error {
  171. return ErrorResponse{
  172. StatusCode: http.StatusBadRequest,
  173. Code: "InvalidArgument",
  174. Message: "The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods ‘.’.",
  175. BucketName: bucketName,
  176. }
  177. }
  178. // ErrEntityTooLarge - Input size is larger than supported maximum.
  179. func ErrEntityTooLarge(totalSize, maxObjectSize int64, bucketName, objectName string) error {
  180. msg := fmt.Sprintf("Your proposed upload size ‘%d’ exceeds the maximum allowed object size ‘%d’ for single PUT operation.", totalSize, maxObjectSize)
  181. return ErrorResponse{
  182. StatusCode: http.StatusBadRequest,
  183. Code: "EntityTooLarge",
  184. Message: msg,
  185. BucketName: bucketName,
  186. Key: objectName,
  187. }
  188. }
  189. // ErrEntityTooSmall - Input size is smaller than supported minimum.
  190. func ErrEntityTooSmall(totalSize int64, bucketName, objectName string) error {
  191. msg := fmt.Sprintf("Your proposed upload size ‘%d’ is below the minimum allowed object size ‘0B’ for single PUT operation.", totalSize)
  192. return ErrorResponse{
  193. StatusCode: http.StatusBadRequest,
  194. Code: "EntityTooSmall",
  195. Message: msg,
  196. BucketName: bucketName,
  197. Key: objectName,
  198. }
  199. }
  200. // ErrUnexpectedEOF - Unexpected end of file reached.
  201. func ErrUnexpectedEOF(totalRead, totalSize int64, bucketName, objectName string) error {
  202. msg := fmt.Sprintf("Data read ‘%d’ is not equal to the size ‘%d’ of the input Reader.", totalRead, totalSize)
  203. return ErrorResponse{
  204. StatusCode: http.StatusBadRequest,
  205. Code: "UnexpectedEOF",
  206. Message: msg,
  207. BucketName: bucketName,
  208. Key: objectName,
  209. }
  210. }
  211. // ErrInvalidBucketName - Invalid bucket name response.
  212. func ErrInvalidBucketName(message string) error {
  213. return ErrorResponse{
  214. StatusCode: http.StatusBadRequest,
  215. Code: "InvalidBucketName",
  216. Message: message,
  217. RequestID: "minio",
  218. }
  219. }
  220. // ErrInvalidObjectName - Invalid object name response.
  221. func ErrInvalidObjectName(message string) error {
  222. return ErrorResponse{
  223. StatusCode: http.StatusNotFound,
  224. Code: "NoSuchKey",
  225. Message: message,
  226. RequestID: "minio",
  227. }
  228. }
  229. // ErrInvalidObjectPrefix - Invalid object prefix response is
  230. // similar to object name response.
  231. var ErrInvalidObjectPrefix = ErrInvalidObjectName
  232. // ErrInvalidArgument - Invalid argument response.
  233. func ErrInvalidArgument(message string) error {
  234. return ErrorResponse{
  235. StatusCode: http.StatusBadRequest,
  236. Code: "InvalidArgument",
  237. Message: message,
  238. RequestID: "minio",
  239. }
  240. }
  241. // ErrNoSuchBucketPolicy - No Such Bucket Policy response
  242. // The specified bucket does not have a bucket policy.
  243. func ErrNoSuchBucketPolicy(message string) error {
  244. return ErrorResponse{
  245. StatusCode: http.StatusNotFound,
  246. Code: "NoSuchBucketPolicy",
  247. Message: message,
  248. RequestID: "minio",
  249. }
  250. }
  251. // ErrAPINotSupported - API not supported response
  252. // The specified API call is not supported
  253. func ErrAPINotSupported(message string) error {
  254. return ErrorResponse{
  255. StatusCode: http.StatusNotImplemented,
  256. Code: "APINotSupported",
  257. Message: message,
  258. RequestID: "minio",
  259. }
  260. }