You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1 년 전
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. # -*- coding: utf-8 -*-
  2. """
  3. oss2.exceptions
  4. ~~~~~~~~~~~~~~
  5. 异常类。
  6. """
  7. import re
  8. import base64
  9. import xml.etree.ElementTree as ElementTree
  10. from xml.parsers import expat
  11. from .compat import to_string
  12. from .headers import *
  13. _OSS_ERROR_TO_EXCEPTION = {} # populated at end of module
  14. OSS_CLIENT_ERROR_STATUS = -1
  15. OSS_REQUEST_ERROR_STATUS = -2
  16. OSS_INCONSISTENT_ERROR_STATUS = -3
  17. OSS_FORMAT_ERROR_STATUS = -4
  18. OSS_SELECT_CLIENT_ERROR_STATUS = -5
  19. class OssError(Exception):
  20. def __init__(self, status, headers, body, details):
  21. #: HTTP 状态码
  22. self.status = status
  23. #: 请求ID,用于跟踪一个OSS请求。提交工单时,最好能够提供请求ID
  24. self.request_id = headers.get(OSS_REQUEST_ID, '')
  25. #: HTTP响应体(部分)
  26. self.body = body
  27. #: 详细错误信息,是一个string到string的dict
  28. self.details = details
  29. #: OSS错误码
  30. self.code = self.details.get('Code', '')
  31. #: OSS错误信息
  32. self.message = self.details.get('Message', '')
  33. #: OSS新的错误码
  34. self.ec = self.details.get('EC', '')
  35. #: header信息
  36. self.headers = headers
  37. def __str__(self):
  38. error = {'status': self.status,
  39. OSS_REQUEST_ID : self.request_id,
  40. 'details': self.details}
  41. return str(error)
  42. def _str_with_body(self):
  43. error = {'status': self.status,
  44. OSS_REQUEST_ID : self.request_id,
  45. 'details': self.body}
  46. return str(error)
  47. class ClientError(OssError):
  48. def __init__(self, message):
  49. OssError.__init__(self, OSS_CLIENT_ERROR_STATUS, {}, 'ClientError: ' + message, {})
  50. def __str__(self):
  51. return self._str_with_body()
  52. class RequestError(OssError):
  53. def __init__(self, e):
  54. OssError.__init__(self, OSS_REQUEST_ERROR_STATUS, {}, 'RequestError: ' + str(e), {})
  55. self.exception = e
  56. def __str__(self):
  57. return self._str_with_body()
  58. class InconsistentError(OssError):
  59. def __init__(self, message, request_id=''):
  60. OssError.__init__(self, OSS_INCONSISTENT_ERROR_STATUS, {OSS_REQUEST_ID : request_id}, 'InconsistentError: ' + message, {})
  61. def __str__(self):
  62. return self._str_with_body()
  63. class OpenApiFormatError(OssError):
  64. def __init__(self, message):
  65. OssError.__init__(self, OSS_FORMAT_ERROR_STATUS, {}, message, {})
  66. def __str__(self):
  67. return self._str_with_body()
  68. class OpenApiServerError(OssError):
  69. def __init__(self, status, request_id, message, error_code):
  70. OssError.__init__(self, status, {OSS_REQUEST_ID : request_id}, '', {'Code': error_code, 'Message': message})
  71. class ServerError(OssError):
  72. pass
  73. class NotFound(ServerError):
  74. status = 404
  75. code = ''
  76. class MalformedXml(ServerError):
  77. status = 400
  78. code = 'MalformedXML'
  79. class InvalidRequest(ServerError):
  80. status = 400
  81. code = 'InvalidRequest'
  82. class OperationNotSupported(ServerError):
  83. status = 400
  84. code = 'OperationNotSupported'
  85. class RestoreAlreadyInProgress(ServerError):
  86. status = 409
  87. code = 'RestoreAlreadyInProgress'
  88. class InvalidArgument(ServerError):
  89. status = 400
  90. code = 'InvalidArgument'
  91. def __init__(self, status, headers, body, details):
  92. super(InvalidArgument, self).__init__(status, headers, body, details)
  93. self.name = details.get('ArgumentName')
  94. self.value = details.get('ArgumentValue')
  95. class InvalidDigest(ServerError):
  96. status = 400
  97. code = 'InvalidDigest'
  98. class InvalidObjectName(ServerError):
  99. status = 400
  100. code = 'InvalidObjectName'
  101. class NotImplemented(ServerError):
  102. status = 400
  103. code = 'NotImplemented'
  104. class InvalidEncryptionRequest(ServerError):
  105. status = 400
  106. code = 'InvalidEncryptionRequest'
  107. class BucketReplicationAlreadyExist(ServerError):
  108. status = 400
  109. code = 'BucketReplicationAlreadyExist'
  110. class NoSuchBucket(NotFound):
  111. status = 404
  112. code = 'NoSuchBucket'
  113. class NoSuchKey(NotFound):
  114. status = 404
  115. code = 'NoSuchKey'
  116. class NoSuchUpload(NotFound):
  117. status = 404
  118. code = 'NoSuchUpload'
  119. class NoSuchWebsite(NotFound):
  120. status = 404
  121. code = 'NoSuchWebsiteConfiguration'
  122. class NoSuchLifecycle(NotFound):
  123. status = 404
  124. code = 'NoSuchLifecycle'
  125. class NoSuchCors(NotFound):
  126. status = 404
  127. code = 'NoSuchCORSConfiguration'
  128. class NoSuchLiveChannel(NotFound):
  129. status = 404
  130. code = 'NoSuchLiveChannel'
  131. class NoSuchBucketPolicy(NotFound):
  132. status = 404
  133. code = 'NoSuchBucketPolicy'
  134. class NoSuchInventory(NotFound):
  135. status = 404
  136. code = 'NoSuchInventory'
  137. class NoSuchReplicationRule(NotFound):
  138. status = 404
  139. code = 'NoSuchReplicationRule'
  140. class Conflict(ServerError):
  141. status = 409
  142. code = ''
  143. class BucketNotEmpty(Conflict):
  144. status = 409
  145. code = 'BucketNotEmpty'
  146. class PositionNotEqualToLength(Conflict):
  147. status = 409
  148. code = 'PositionNotEqualToLength'
  149. def __init__(self, status, headers, body, details):
  150. super(PositionNotEqualToLength, self).__init__(status, headers, body, details)
  151. self.next_position = int(headers[OSS_NEXT_APPEND_POSITION])
  152. class ObjectNotAppendable(Conflict):
  153. status = 409
  154. code = 'ObjectNotAppendable'
  155. class ChannelStillLive(Conflict):
  156. status = 409
  157. code = 'ChannelStillLive'
  158. class LiveChannelDisabled(Conflict):
  159. status = 409
  160. code = 'LiveChannelDisabled'
  161. class PreconditionFailed(ServerError):
  162. status = 412
  163. code = 'PreconditionFailed'
  164. class NotModified(ServerError):
  165. status = 304
  166. code = ''
  167. class AccessDenied(ServerError):
  168. status = 403
  169. code = 'AccessDenied'
  170. class NoSuchServerSideEncryptionRule(NotFound):
  171. status = 404
  172. code = 'NoSuchServerSideEncryptionRule'
  173. class InvalidEncryptionAlgorithmError(ServerError):
  174. status = 400
  175. code = 'InvalidEncryptionAlgorithmError'
  176. class SelectOperationFailed(ServerError):
  177. code = 'SelectOperationFailed'
  178. def __init__(self, status, code, message):
  179. self.status = status
  180. self.code = code
  181. self.message = message
  182. def __str__(self):
  183. error = {'status': self.status,
  184. 'code': self.code,
  185. 'details': self.message}
  186. return str(error)
  187. class SelectOperationClientError(OssError):
  188. def __init__(self, message, request_id):
  189. OssError.__init__(self, OSS_SELECT_CLIENT_ERROR_STATUS, {'x-oss-request-id': request_id}, 'SelectOperationClientError: ' + message, {})
  190. def __str__(self):
  191. error = {'x-oss-request-id':self.request_id,
  192. 'message': self.message}
  193. return str(error)
  194. class SignatureDoesNotMatch(ServerError):
  195. status = 403
  196. code = 'SignatureDoesNotMatch'
  197. class ObjectAlreadyExists(ServerError):
  198. status = 400
  199. code = 'ObjectAlreadyExists'
  200. class PartNotSequential(ServerError):
  201. status = 400
  202. code = 'PartNotSequential'
  203. class NoSuchWORMConfiguration(ServerError):
  204. status = 404
  205. code = 'NoSuchWORMConfiguration'
  206. class WORMConfigurationLocked(ServerError):
  207. status = 403
  208. code = 'WORMConfigurationLocked'
  209. class InvalidWORMConfiguration(ServerError):
  210. status = 400
  211. code = 'InvalidWORMConfiguration'
  212. class NoSuchTransferAccelerationConfiguration(ServerError):
  213. status = 404
  214. code = 'NoSuchTransferAccelerationConfiguration'
  215. def make_exception(resp):
  216. status = resp.status
  217. headers = resp.headers
  218. body = resp.read(4096)
  219. if not body and headers.get('x-oss-err') is not None:
  220. try:
  221. value = base64.b64decode(to_string(headers.get('x-oss-err')))
  222. except:
  223. value = body
  224. details = _parse_error_body(value)
  225. else:
  226. details = _parse_error_body(body)
  227. code = details.get('Code', '')
  228. try:
  229. klass = _OSS_ERROR_TO_EXCEPTION[(status, code)]
  230. return klass(status, headers, body, details)
  231. except KeyError:
  232. return ServerError(status, headers, body, details)
  233. def _walk_subclasses(klass):
  234. for sub in klass.__subclasses__():
  235. yield sub
  236. for subsub in _walk_subclasses(sub):
  237. yield subsub
  238. for klass in _walk_subclasses(ServerError):
  239. status = getattr(klass, 'status', None)
  240. code = getattr(klass, 'code', None)
  241. if status is not None and code is not None:
  242. _OSS_ERROR_TO_EXCEPTION[(status, code)] = klass
  243. # XML parsing exceptions have changed in Python2.7 and ElementTree 1.3
  244. if hasattr(ElementTree, 'ParseError'):
  245. ElementTreeParseError = (ElementTree.ParseError, expat.ExpatError)
  246. else:
  247. ElementTreeParseError = (expat.ExpatError)
  248. def _parse_error_body(body):
  249. try:
  250. root = ElementTree.fromstring(body)
  251. if root.tag != 'Error':
  252. return {}
  253. details = {}
  254. for child in root:
  255. details[child.tag] = child.text
  256. return details
  257. except ElementTreeParseError:
  258. return _guess_error_details(body)
  259. def _guess_error_details(body):
  260. details = {}
  261. body = to_string(body)
  262. if '<Error>' not in body or '</Error>' not in body:
  263. return details
  264. m = re.search('<Code>(.*)</Code>', body)
  265. if m:
  266. details['Code'] = m.group(1)
  267. m = re.search('<Message>(.*)</Message>', body)
  268. if m:
  269. details['Message'] = m.group(1)
  270. return details