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.

378 lines
8.7 KiB

4 years ago
  1. from .web_response import Response
  2. __all__ = (
  3. 'HTTPException',
  4. 'HTTPError',
  5. 'HTTPRedirection',
  6. 'HTTPSuccessful',
  7. 'HTTPOk',
  8. 'HTTPCreated',
  9. 'HTTPAccepted',
  10. 'HTTPNonAuthoritativeInformation',
  11. 'HTTPNoContent',
  12. 'HTTPResetContent',
  13. 'HTTPPartialContent',
  14. 'HTTPMultipleChoices',
  15. 'HTTPMovedPermanently',
  16. 'HTTPFound',
  17. 'HTTPSeeOther',
  18. 'HTTPNotModified',
  19. 'HTTPUseProxy',
  20. 'HTTPTemporaryRedirect',
  21. 'HTTPPermanentRedirect',
  22. 'HTTPClientError',
  23. 'HTTPBadRequest',
  24. 'HTTPUnauthorized',
  25. 'HTTPPaymentRequired',
  26. 'HTTPForbidden',
  27. 'HTTPNotFound',
  28. 'HTTPMethodNotAllowed',
  29. 'HTTPNotAcceptable',
  30. 'HTTPProxyAuthenticationRequired',
  31. 'HTTPRequestTimeout',
  32. 'HTTPConflict',
  33. 'HTTPGone',
  34. 'HTTPLengthRequired',
  35. 'HTTPPreconditionFailed',
  36. 'HTTPRequestEntityTooLarge',
  37. 'HTTPRequestURITooLong',
  38. 'HTTPUnsupportedMediaType',
  39. 'HTTPRequestRangeNotSatisfiable',
  40. 'HTTPExpectationFailed',
  41. 'HTTPMisdirectedRequest',
  42. 'HTTPUnprocessableEntity',
  43. 'HTTPFailedDependency',
  44. 'HTTPUpgradeRequired',
  45. 'HTTPPreconditionRequired',
  46. 'HTTPTooManyRequests',
  47. 'HTTPRequestHeaderFieldsTooLarge',
  48. 'HTTPUnavailableForLegalReasons',
  49. 'HTTPServerError',
  50. 'HTTPInternalServerError',
  51. 'HTTPNotImplemented',
  52. 'HTTPBadGateway',
  53. 'HTTPServiceUnavailable',
  54. 'HTTPGatewayTimeout',
  55. 'HTTPVersionNotSupported',
  56. 'HTTPVariantAlsoNegotiates',
  57. 'HTTPInsufficientStorage',
  58. 'HTTPNotExtended',
  59. 'HTTPNetworkAuthenticationRequired',
  60. )
  61. ############################################################
  62. # HTTP Exceptions
  63. ############################################################
  64. class HTTPException(Response, Exception):
  65. # You should set in subclasses:
  66. # status = 200
  67. status_code = -1
  68. empty_body = False
  69. __http_exception__ = True
  70. def __init__(self, *, headers=None, reason=None,
  71. body=None, text=None, content_type=None):
  72. Response.__init__(self, status=self.status_code,
  73. headers=headers, reason=reason,
  74. body=body, text=text, content_type=content_type)
  75. Exception.__init__(self, self.reason)
  76. if self.body is None and not self.empty_body:
  77. self.text = "{}: {}".format(self.status, self.reason)
  78. def __bool__(self):
  79. return True
  80. class HTTPError(HTTPException):
  81. """Base class for exceptions with status codes in the 400s and 500s."""
  82. class HTTPRedirection(HTTPException):
  83. """Base class for exceptions with status codes in the 300s."""
  84. class HTTPSuccessful(HTTPException):
  85. """Base class for exceptions with status codes in the 200s."""
  86. class HTTPOk(HTTPSuccessful):
  87. status_code = 200
  88. class HTTPCreated(HTTPSuccessful):
  89. status_code = 201
  90. class HTTPAccepted(HTTPSuccessful):
  91. status_code = 202
  92. class HTTPNonAuthoritativeInformation(HTTPSuccessful):
  93. status_code = 203
  94. class HTTPNoContent(HTTPSuccessful):
  95. status_code = 204
  96. empty_body = True
  97. class HTTPResetContent(HTTPSuccessful):
  98. status_code = 205
  99. empty_body = True
  100. class HTTPPartialContent(HTTPSuccessful):
  101. status_code = 206
  102. ############################################################
  103. # 3xx redirection
  104. ############################################################
  105. class _HTTPMove(HTTPRedirection):
  106. def __init__(self, location, *, headers=None, reason=None,
  107. body=None, text=None, content_type=None):
  108. if not location:
  109. raise ValueError("HTTP redirects need a location to redirect to.")
  110. super().__init__(headers=headers, reason=reason,
  111. body=body, text=text, content_type=content_type)
  112. self.headers['Location'] = str(location)
  113. self.location = location
  114. class HTTPMultipleChoices(_HTTPMove):
  115. status_code = 300
  116. class HTTPMovedPermanently(_HTTPMove):
  117. status_code = 301
  118. class HTTPFound(_HTTPMove):
  119. status_code = 302
  120. # This one is safe after a POST (the redirected location will be
  121. # retrieved with GET):
  122. class HTTPSeeOther(_HTTPMove):
  123. status_code = 303
  124. class HTTPNotModified(HTTPRedirection):
  125. # FIXME: this should include a date or etag header
  126. status_code = 304
  127. empty_body = True
  128. class HTTPUseProxy(_HTTPMove):
  129. # Not a move, but looks a little like one
  130. status_code = 305
  131. class HTTPTemporaryRedirect(_HTTPMove):
  132. status_code = 307
  133. class HTTPPermanentRedirect(_HTTPMove):
  134. status_code = 308
  135. ############################################################
  136. # 4xx client error
  137. ############################################################
  138. class HTTPClientError(HTTPError):
  139. pass
  140. class HTTPBadRequest(HTTPClientError):
  141. status_code = 400
  142. class HTTPUnauthorized(HTTPClientError):
  143. status_code = 401
  144. class HTTPPaymentRequired(HTTPClientError):
  145. status_code = 402
  146. class HTTPForbidden(HTTPClientError):
  147. status_code = 403
  148. class HTTPNotFound(HTTPClientError):
  149. status_code = 404
  150. class HTTPMethodNotAllowed(HTTPClientError):
  151. status_code = 405
  152. def __init__(self, method, allowed_methods, *, headers=None, reason=None,
  153. body=None, text=None, content_type=None):
  154. allow = ','.join(sorted(allowed_methods))
  155. super().__init__(headers=headers, reason=reason,
  156. body=body, text=text, content_type=content_type)
  157. self.headers['Allow'] = allow
  158. self.allowed_methods = allowed_methods
  159. self.method = method.upper()
  160. class HTTPNotAcceptable(HTTPClientError):
  161. status_code = 406
  162. class HTTPProxyAuthenticationRequired(HTTPClientError):
  163. status_code = 407
  164. class HTTPRequestTimeout(HTTPClientError):
  165. status_code = 408
  166. class HTTPConflict(HTTPClientError):
  167. status_code = 409
  168. class HTTPGone(HTTPClientError):
  169. status_code = 410
  170. class HTTPLengthRequired(HTTPClientError):
  171. status_code = 411
  172. class HTTPPreconditionFailed(HTTPClientError):
  173. status_code = 412
  174. class HTTPRequestEntityTooLarge(HTTPClientError):
  175. status_code = 413
  176. def __init__(self, max_size, actual_size, **kwargs):
  177. kwargs.setdefault(
  178. 'text',
  179. 'Maximum request body size {} exceeded, '
  180. 'actual body size {}'.format(max_size, actual_size)
  181. )
  182. super().__init__(**kwargs)
  183. class HTTPRequestURITooLong(HTTPClientError):
  184. status_code = 414
  185. class HTTPUnsupportedMediaType(HTTPClientError):
  186. status_code = 415
  187. class HTTPRequestRangeNotSatisfiable(HTTPClientError):
  188. status_code = 416
  189. class HTTPExpectationFailed(HTTPClientError):
  190. status_code = 417
  191. class HTTPMisdirectedRequest(HTTPClientError):
  192. status_code = 421
  193. class HTTPUnprocessableEntity(HTTPClientError):
  194. status_code = 422
  195. class HTTPFailedDependency(HTTPClientError):
  196. status_code = 424
  197. class HTTPUpgradeRequired(HTTPClientError):
  198. status_code = 426
  199. class HTTPPreconditionRequired(HTTPClientError):
  200. status_code = 428
  201. class HTTPTooManyRequests(HTTPClientError):
  202. status_code = 429
  203. class HTTPRequestHeaderFieldsTooLarge(HTTPClientError):
  204. status_code = 431
  205. class HTTPUnavailableForLegalReasons(HTTPClientError):
  206. status_code = 451
  207. def __init__(self, link, *, headers=None, reason=None,
  208. body=None, text=None, content_type=None):
  209. super().__init__(headers=headers, reason=reason,
  210. body=body, text=text, content_type=content_type)
  211. self.headers['Link'] = '<%s>; rel="blocked-by"' % link
  212. self.link = link
  213. ############################################################
  214. # 5xx Server Error
  215. ############################################################
  216. # Response status codes beginning with the digit "5" indicate cases in
  217. # which the server is aware that it has erred or is incapable of
  218. # performing the request. Except when responding to a HEAD request, the
  219. # server SHOULD include an entity containing an explanation of the error
  220. # situation, and whether it is a temporary or permanent condition. User
  221. # agents SHOULD display any included entity to the user. These response
  222. # codes are applicable to any request method.
  223. class HTTPServerError(HTTPError):
  224. pass
  225. class HTTPInternalServerError(HTTPServerError):
  226. status_code = 500
  227. class HTTPNotImplemented(HTTPServerError):
  228. status_code = 501
  229. class HTTPBadGateway(HTTPServerError):
  230. status_code = 502
  231. class HTTPServiceUnavailable(HTTPServerError):
  232. status_code = 503
  233. class HTTPGatewayTimeout(HTTPServerError):
  234. status_code = 504
  235. class HTTPVersionNotSupported(HTTPServerError):
  236. status_code = 505
  237. class HTTPVariantAlsoNegotiates(HTTPServerError):
  238. status_code = 506
  239. class HTTPInsufficientStorage(HTTPServerError):
  240. status_code = 507
  241. class HTTPNotExtended(HTTPServerError):
  242. status_code = 510
  243. class HTTPNetworkAuthenticationRequired(HTTPServerError):
  244. status_code = 511