import warnings from typing import Any, Dict, Iterable, List, Optional, Set # noqa from yarl import URL from .typedefs import LooseHeaders, StrOrURL from .web_response import Response __all__ = ( 'HTTPException', 'HTTPError', 'HTTPRedirection', 'HTTPSuccessful', 'HTTPOk', 'HTTPCreated', 'HTTPAccepted', 'HTTPNonAuthoritativeInformation', 'HTTPNoContent', 'HTTPResetContent', 'HTTPPartialContent', 'HTTPMultipleChoices', 'HTTPMovedPermanently', 'HTTPFound', 'HTTPSeeOther', 'HTTPNotModified', 'HTTPUseProxy', 'HTTPTemporaryRedirect', 'HTTPPermanentRedirect', 'HTTPClientError', 'HTTPBadRequest', 'HTTPUnauthorized', 'HTTPPaymentRequired', 'HTTPForbidden', 'HTTPNotFound', 'HTTPMethodNotAllowed', 'HTTPNotAcceptable', 'HTTPProxyAuthenticationRequired', 'HTTPRequestTimeout', 'HTTPConflict', 'HTTPGone', 'HTTPLengthRequired', 'HTTPPreconditionFailed', 'HTTPRequestEntityTooLarge', 'HTTPRequestURITooLong', 'HTTPUnsupportedMediaType', 'HTTPRequestRangeNotSatisfiable', 'HTTPExpectationFailed', 'HTTPMisdirectedRequest', 'HTTPUnprocessableEntity', 'HTTPFailedDependency', 'HTTPUpgradeRequired', 'HTTPPreconditionRequired', 'HTTPTooManyRequests', 'HTTPRequestHeaderFieldsTooLarge', 'HTTPUnavailableForLegalReasons', 'HTTPServerError', 'HTTPInternalServerError', 'HTTPNotImplemented', 'HTTPBadGateway', 'HTTPServiceUnavailable', 'HTTPGatewayTimeout', 'HTTPVersionNotSupported', 'HTTPVariantAlsoNegotiates', 'HTTPInsufficientStorage', 'HTTPNotExtended', 'HTTPNetworkAuthenticationRequired', ) ############################################################ # HTTP Exceptions ############################################################ class HTTPException(Response, Exception): # You should set in subclasses: # status = 200 status_code = -1 empty_body = False __http_exception__ = True def __init__(self, *, headers: Optional[LooseHeaders]=None, reason: Optional[str]=None, body: Any=None, text: Optional[str]=None, content_type: Optional[str]=None) -> None: if body is not None: warnings.warn( "body argument is deprecated for http web exceptions", DeprecationWarning) Response.__init__(self, status=self.status_code, headers=headers, reason=reason, body=body, text=text, content_type=content_type) Exception.__init__(self, self.reason) if self.body is None and not self.empty_body: self.text = "{}: {}".format(self.status, self.reason) def __bool__(self) -> bool: return True class HTTPError(HTTPException): """Base class for exceptions with status codes in the 400s and 500s.""" class HTTPRedirection(HTTPException): """Base class for exceptions with status codes in the 300s.""" class HTTPSuccessful(HTTPException): """Base class for exceptions with status codes in the 200s.""" class HTTPOk(HTTPSuccessful): status_code = 200 class HTTPCreated(HTTPSuccessful): status_code = 201 class HTTPAccepted(HTTPSuccessful): status_code = 202 class HTTPNonAuthoritativeInformation(HTTPSuccessful): status_code = 203 class HTTPNoContent(HTTPSuccessful): status_code = 204 empty_body = True class HTTPResetContent(HTTPSuccessful): status_code = 205 empty_body = True class HTTPPartialContent(HTTPSuccessful): status_code = 206 ############################################################ # 3xx redirection ############################################################ class _HTTPMove(HTTPRedirection): def __init__(self, location: StrOrURL, *, headers: Optional[LooseHeaders]=None, reason: Optional[str]=None, body: Any=None, text: Optional[str]=None, content_type: Optional[str]=None) -> None: if not location: raise ValueError("HTTP redirects need a location to redirect to.") super().__init__(headers=headers, reason=reason, body=body, text=text, content_type=content_type) self.headers['Location'] = str(URL(location)) self.location = location class HTTPMultipleChoices(_HTTPMove): status_code = 300 class HTTPMovedPermanently(_HTTPMove): status_code = 301 class HTTPFound(_HTTPMove): status_code = 302 # This one is safe after a POST (the redirected location will be # retrieved with GET): class HTTPSeeOther(_HTTPMove): status_code = 303 class HTTPNotModified(HTTPRedirection): # FIXME: this should include a date or etag header status_code = 304 empty_body = True class HTTPUseProxy(_HTTPMove): # Not a move, but looks a little like one status_code = 305 class HTTPTemporaryRedirect(_HTTPMove): status_code = 307 class HTTPPermanentRedirect(_HTTPMove): status_code = 308 ############################################################ # 4xx client error ############################################################ class HTTPClientError(HTTPError): pass class HTTPBadRequest(HTTPClientError): status_code = 400 class HTTPUnauthorized(HTTPClientError): status_code = 401 class HTTPPaymentRequired(HTTPClientError): status_code = 402 class HTTPForbidden(HTTPClientError): status_code = 403 class HTTPNotFound(HTTPClientError): status_code = 404 class HTTPMethodNotAllowed(HTTPClientError): status_code = 405 def __init__(self, method: str, allowed_methods: Iterable[str], *, headers: Optional[LooseHeaders]=None, reason: Optional[str]=None, body: Any=None, text: Optional[str]=None, content_type: Optional[str]=None) -> None: allow = ','.join(sorted(allowed_methods)) super().__init__(headers=headers, reason=reason, body=body, text=text, content_type=content_type) self.headers['Allow'] = allow self.allowed_methods = set(allowed_methods) # type: Set[str] self.method = method.upper() class HTTPNotAcceptable(HTTPClientError): status_code = 406 class HTTPProxyAuthenticationRequired(HTTPClientError): status_code = 407 class HTTPRequestTimeout(HTTPClientError): status_code = 408 class HTTPConflict(HTTPClientError): status_code = 409 class HTTPGone(HTTPClientError): status_code = 410 class HTTPLengthRequired(HTTPClientError): status_code = 411 class HTTPPreconditionFailed(HTTPClientError): status_code = 412 class HTTPRequestEntityTooLarge(HTTPClientError): status_code = 413 def __init__(self, max_size: float, actual_size: float, **kwargs: Any) -> None: kwargs.setdefault( 'text', 'Maximum request body size {} exceeded, ' 'actual body size {}'.format(max_size, actual_size) ) super().__init__(**kwargs) class HTTPRequestURITooLong(HTTPClientError): status_code = 414 class HTTPUnsupportedMediaType(HTTPClientError): status_code = 415 class HTTPRequestRangeNotSatisfiable(HTTPClientError): status_code = 416 class HTTPExpectationFailed(HTTPClientError): status_code = 417 class HTTPMisdirectedRequest(HTTPClientError): status_code = 421 class HTTPUnprocessableEntity(HTTPClientError): status_code = 422 class HTTPFailedDependency(HTTPClientError): status_code = 424 class HTTPUpgradeRequired(HTTPClientError): status_code = 426 class HTTPPreconditionRequired(HTTPClientError): status_code = 428 class HTTPTooManyRequests(HTTPClientError): status_code = 429 class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): status_code = 431 class HTTPUnavailableForLegalReasons(HTTPClientError): status_code = 451 def __init__(self, link: str, *, headers: Optional[LooseHeaders]=None, reason: Optional[str]=None, body: Any=None, text: Optional[str]=None, content_type: Optional[str]=None) -> None: super().__init__(headers=headers, reason=reason, body=body, text=text, content_type=content_type) self.headers['Link'] = '<%s>; rel="blocked-by"' % link self.link = link ############################################################ # 5xx Server Error ############################################################ # Response status codes beginning with the digit "5" indicate cases in # which the server is aware that it has erred or is incapable of # performing the request. Except when responding to a HEAD request, the # server SHOULD include an entity containing an explanation of the error # situation, and whether it is a temporary or permanent condition. User # agents SHOULD display any included entity to the user. These response # codes are applicable to any request method. class HTTPServerError(HTTPError): pass class HTTPInternalServerError(HTTPServerError): status_code = 500 class HTTPNotImplemented(HTTPServerError): status_code = 501 class HTTPBadGateway(HTTPServerError): status_code = 502 class HTTPServiceUnavailable(HTTPServerError): status_code = 503 class HTTPGatewayTimeout(HTTPServerError): status_code = 504 class HTTPVersionNotSupported(HTTPServerError): status_code = 505 class HTTPVariantAlsoNegotiates(HTTPServerError): status_code = 506 class HTTPInsufficientStorage(HTTPServerError): status_code = 507 class HTTPNotExtended(HTTPServerError): status_code = 510 class HTTPNetworkAuthenticationRequired(HTTPServerError): status_code = 511