1""" 2This module processes Python exceptions that relate to HTTP exceptions 3by defining a set of exceptions, all subclasses of HTTPException. 4Each exception, in addition to being a Python exception that can be 5raised and caught, is also a WSGI application and ``webob.Response`` 6object. 7 8This module defines exceptions according to RFC 2068 [1]_ : codes with 9100-300 are not really errors; 400's are client errors, and 500's are 10server errors. According to the WSGI specification [2]_ , the application 11can call ``start_response`` more then once only under two conditions: 12(a) the response has not yet been sent, or (b) if the second and 13subsequent invocations of ``start_response`` have a valid ``exc_info`` 14argument obtained from ``sys.exc_info()``. The WSGI specification then 15requires the server or gateway to handle the case where content has been 16sent and then an exception was encountered. 17 18Exception 19 HTTPException 20 HTTPOk 21 * 200 - :class:`HTTPOk` 22 * 201 - :class:`HTTPCreated` 23 * 202 - :class:`HTTPAccepted` 24 * 203 - :class:`HTTPNonAuthoritativeInformation` 25 * 204 - :class:`HTTPNoContent` 26 * 205 - :class:`HTTPResetContent` 27 * 206 - :class:`HTTPPartialContent` 28 HTTPRedirection 29 * 300 - :class:`HTTPMultipleChoices` 30 * 301 - :class:`HTTPMovedPermanently` 31 * 302 - :class:`HTTPFound` 32 * 303 - :class:`HTTPSeeOther` 33 * 304 - :class:`HTTPNotModified` 34 * 305 - :class:`HTTPUseProxy` 35 * 307 - :class:`HTTPTemporaryRedirect` 36 * 308 - :class:`HTTPPermanentRedirect` 37 HTTPError 38 HTTPClientError 39 * 400 - :class:`HTTPBadRequest` 40 * 401 - :class:`HTTPUnauthorized` 41 * 402 - :class:`HTTPPaymentRequired` 42 * 403 - :class:`HTTPForbidden` 43 * 404 - :class:`HTTPNotFound` 44 * 405 - :class:`HTTPMethodNotAllowed` 45 * 406 - :class:`HTTPNotAcceptable` 46 * 407 - :class:`HTTPProxyAuthenticationRequired` 47 * 408 - :class:`HTTPRequestTimeout` 48 * 409 - :class:`HTTPConflict` 49 * 410 - :class:`HTTPGone` 50 * 411 - :class:`HTTPLengthRequired` 51 * 412 - :class:`HTTPPreconditionFailed` 52 * 413 - :class:`HTTPRequestEntityTooLarge` 53 * 414 - :class:`HTTPRequestURITooLong` 54 * 415 - :class:`HTTPUnsupportedMediaType` 55 * 416 - :class:`HTTPRequestRangeNotSatisfiable` 56 * 417 - :class:`HTTPExpectationFailed` 57 * 422 - :class:`HTTPUnprocessableEntity` 58 * 423 - :class:`HTTPLocked` 59 * 424 - :class:`HTTPFailedDependency` 60 * 428 - :class:`HTTPPreconditionRequired` 61 * 429 - :class:`HTTPTooManyRequests` 62 * 431 - :class:`HTTPRequestHeaderFieldsTooLarge` 63 * 451 - :class:`HTTPUnavailableForLegalReasons` 64 HTTPServerError 65 * 500 - :class:`HTTPInternalServerError` 66 * 501 - :class:`HTTPNotImplemented` 67 * 502 - :class:`HTTPBadGateway` 68 * 503 - :class:`HTTPServiceUnavailable` 69 * 504 - :class:`HTTPGatewayTimeout` 70 * 505 - :class:`HTTPVersionNotSupported` 71 * 511 - :class:`HTTPNetworkAuthenticationRequired` 72 73Usage notes 74----------- 75 76The HTTPException class is complicated by 4 factors: 77 78 1. The content given to the exception may either be plain-text or 79 as html-text. 80 81 2. The template may want to have string-substitutions taken from 82 the current ``environ`` or values from incoming headers. This 83 is especially troublesome due to case sensitivity. 84 85 3. The final output may either be text/plain or text/html 86 mime-type as requested by the client application. 87 88 4. Each exception has a default explanation, but those who 89 raise exceptions may want to provide additional detail. 90 91Subclass attributes and call parameters are designed to provide an easier path 92through the complications. 93 94Attributes: 95 96 ``code`` 97 the HTTP status code for the exception 98 99 ``title`` 100 remainder of the status line (stuff after the code) 101 102 ``explanation`` 103 a plain-text explanation of the error message that is 104 not subject to environment or header substitutions; 105 it is accessible in the template via %(explanation)s 106 107 ``detail`` 108 a plain-text message customization that is not subject 109 to environment or header substitutions; accessible in 110 the template via %(detail)s 111 112 ``body_template`` 113 a content fragment (in HTML) used for environment and 114 header substitution; the default template includes both 115 the explanation and further detail provided in the 116 message 117 118Parameters: 119 120 ``detail`` 121 a plain-text override of the default ``detail`` 122 123 ``headers`` 124 a list of (k,v) header pairs 125 126 ``comment`` 127 a plain-text additional information which is 128 usually stripped/hidden for end-users 129 130 ``body_template`` 131 a string.Template object containing a content fragment in HTML 132 that frames the explanation and further detail 133 134To override the template (which is HTML content) or the plain-text 135explanation, one must subclass the given exception; or customize it 136after it has been created. This particular breakdown of a message 137into explanation, detail and template allows both the creation of 138plain-text and html messages for various clients as well as 139error-free substitution of environment variables and headers. 140 141 142The subclasses of :class:`~_HTTPMove` 143(:class:`~HTTPMultipleChoices`, :class:`~HTTPMovedPermanently`, 144:class:`~HTTPFound`, :class:`~HTTPSeeOther`, :class:`~HTTPUseProxy` and 145:class:`~HTTPTemporaryRedirect`) are redirections that require a ``Location`` 146field. Reflecting this, these subclasses have two additional keyword arguments: 147``location`` and ``add_slash``. 148 149Parameters: 150 151 ``location`` 152 to set the location immediately 153 154 ``add_slash`` 155 set to True to redirect to the same URL as the request, except with a 156 ``/`` appended 157 158Relative URLs in the location will be resolved to absolute. 159 160References: 161 162.. [1] https://www.python.org/dev/peps/pep-0333/#error-handling 163.. [2] https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5 164 165 166""" 167 168import json 169from string import Template 170import re 171import sys 172 173from webob.acceptparse import create_accept_header 174from webob.compat import ( 175 class_types, 176 text_, 177 text_type, 178 urlparse, 179 ) 180from webob.request import Request 181from webob.response import Response 182from webob.util import html_escape 183 184tag_re = re.compile(r'<.*?>', re.S) 185br_re = re.compile(r'<br.*?>', re.I | re.S) 186comment_re = re.compile(r'<!--|-->') 187 188class _lazified(object): 189 def __init__(self, func, value): 190 self.func = func 191 self.value = value 192 193 def __str__(self): 194 return self.func(self.value) 195 196def lazify(func): 197 def wrapper(value): 198 return _lazified(func, value) 199 return wrapper 200 201def no_escape(value): 202 if value is None: 203 return '' 204 if not isinstance(value, text_type): 205 if hasattr(value, '__unicode__'): 206 value = value.__unicode__() 207 if isinstance(value, bytes): 208 value = text_(value, 'utf-8') 209 else: 210 value = text_type(value) 211 return value 212 213def strip_tags(value): 214 value = value.replace('\n', ' ') 215 value = value.replace('\r', '') 216 value = br_re.sub('\n', value) 217 value = comment_re.sub('', value) 218 value = tag_re.sub('', value) 219 return value 220 221class HTTPException(Exception): 222 def __init__(self, message, wsgi_response): 223 Exception.__init__(self, message) 224 self.wsgi_response = wsgi_response 225 226 def __call__(self, environ, start_response): 227 return self.wsgi_response(environ, start_response) 228 229class WSGIHTTPException(Response, HTTPException): 230 231 ## You should set in subclasses: 232 # code = 200 233 # title = 'OK' 234 # explanation = 'why this happens' 235 # body_template_obj = Template('response template') 236 code = 500 237 title = 'Internal Server Error' 238 explanation = '' 239 body_template_obj = Template('''\ 240${explanation}<br /><br /> 241${detail} 242${html_comment} 243''') 244 245 plain_template_obj = Template('''\ 246${status} 247 248${body}''') 249 250 html_template_obj = Template('''\ 251<html> 252 <head> 253 <title>${status}</title> 254 </head> 255 <body> 256 <h1>${status}</h1> 257 ${body} 258 </body> 259</html>''') 260 261 ## Set this to True for responses that should have no request body 262 empty_body = False 263 264 def __init__(self, detail=None, headers=None, comment=None, 265 body_template=None, json_formatter=None, **kw): 266 Response.__init__(self, 267 status='%s %s' % (self.code, self.title), 268 **kw) 269 Exception.__init__(self, detail) 270 if headers: 271 self.headers.extend(headers) 272 self.detail = detail 273 self.comment = comment 274 if body_template is not None: 275 self.body_template = body_template 276 self.body_template_obj = Template(body_template) 277 if self.empty_body: 278 del self.content_type 279 del self.content_length 280 if json_formatter is not None: 281 self.json_formatter = json_formatter 282 283 def __str__(self): 284 return self.detail or self.explanation 285 286 def _make_body(self, environ, escape): 287 escape = lazify(escape) 288 args = { 289 'explanation': escape(self.explanation), 290 'detail': escape(self.detail or ''), 291 'comment': escape(self.comment or ''), 292 } 293 if self.comment: 294 args['html_comment'] = '<!-- %s -->' % escape(self.comment) 295 else: 296 args['html_comment'] = '' 297 if WSGIHTTPException.body_template_obj is not self.body_template_obj: 298 # Custom template; add headers to args 299 for k, v in environ.items(): 300 args[k] = escape(v) 301 for k, v in self.headers.items(): 302 args[k.lower()] = escape(v) 303 t_obj = self.body_template_obj 304 return t_obj.safe_substitute(args) 305 306 def plain_body(self, environ): 307 body = self._make_body(environ, no_escape) 308 body = strip_tags(body) 309 return self.plain_template_obj.substitute(status=self.status, 310 title=self.title, 311 body=body) 312 313 def html_body(self, environ): 314 body = self._make_body(environ, html_escape) 315 return self.html_template_obj.substitute(status=self.status, 316 body=body) 317 318 def json_formatter(self, body, status, title, environ): 319 return {'message': body, 320 'code': status, 321 'title': title} 322 323 def json_body(self, environ): 324 body = self._make_body(environ, no_escape) 325 jsonbody = self.json_formatter(body=body, status=self.status, 326 title=self.title, environ=environ) 327 return json.dumps(jsonbody) 328 329 def generate_response(self, environ, start_response): 330 if self.content_length is not None: 331 del self.content_length 332 headerlist = list(self.headerlist) 333 accept_value = environ.get('HTTP_ACCEPT', '') 334 accept_header = create_accept_header(header_value=accept_value) 335 acceptable_offers = accept_header.acceptable_offers( 336 offers=['text/html', 'application/json'], 337 ) 338 match = acceptable_offers[0][0] if acceptable_offers else None 339 340 if match == 'text/html': 341 content_type = 'text/html' 342 body = self.html_body(environ) 343 elif match == 'application/json': 344 content_type = 'application/json' 345 body = self.json_body(environ) 346 else: 347 content_type = 'text/plain' 348 body = self.plain_body(environ) 349 resp = Response(body, 350 status=self.status, 351 headerlist=headerlist, 352 content_type=content_type, 353 ) 354 resp.content_type = content_type 355 return resp(environ, start_response) 356 357 def __call__(self, environ, start_response): 358 is_head = environ['REQUEST_METHOD'] == 'HEAD' 359 if self.has_body or self.empty_body or is_head: 360 app_iter = Response.__call__(self, environ, start_response) 361 else: 362 app_iter = self.generate_response(environ, start_response) 363 if is_head: 364 app_iter = [] 365 return app_iter 366 367 @property 368 def wsgi_response(self): 369 return self 370 371 372 373class HTTPError(WSGIHTTPException): 374 """ 375 base class for status codes in the 400's and 500's 376 377 This is an exception which indicates that an error has occurred, 378 and that any work in progress should not be committed. These are 379 typically results in the 400's and 500's. 380 """ 381 382class HTTPRedirection(WSGIHTTPException): 383 """ 384 base class for 300's status code (redirections) 385 386 This is an abstract base class for 3xx redirection. It indicates 387 that further action needs to be taken by the user agent in order 388 to fulfill the request. It does not necessarly signal an error 389 condition. 390 """ 391 392class HTTPOk(WSGIHTTPException): 393 """ 394 Base class for the 200's status code (successful responses) 395 396 code: 200, title: OK 397 """ 398 code = 200 399 title = 'OK' 400 401############################################################ 402## 2xx success 403############################################################ 404 405class HTTPCreated(HTTPOk): 406 """ 407 subclass of :class:`~HTTPOk` 408 409 This indicates that request has been fulfilled and resulted in a new 410 resource being created. 411 412 code: 201, title: Created 413 """ 414 code = 201 415 title = 'Created' 416 417class HTTPAccepted(HTTPOk): 418 """ 419 subclass of :class:`~HTTPOk` 420 421 This indicates that the request has been accepted for processing, but the 422 processing has not been completed. 423 424 code: 202, title: Accepted 425 """ 426 code = 202 427 title = 'Accepted' 428 explanation = 'The request is accepted for processing.' 429 430class HTTPNonAuthoritativeInformation(HTTPOk): 431 """ 432 subclass of :class:`~HTTPOk` 433 434 This indicates that the returned metainformation in the entity-header is 435 not the definitive set as available from the origin server, but is 436 gathered from a local or a third-party copy. 437 438 code: 203, title: Non-Authoritative Information 439 """ 440 code = 203 441 title = 'Non-Authoritative Information' 442 443class HTTPNoContent(HTTPOk): 444 """ 445 subclass of :class:`~HTTPOk` 446 447 This indicates that the server has fulfilled the request but does 448 not need to return an entity-body, and might want to return updated 449 metainformation. 450 451 code: 204, title: No Content 452 """ 453 code = 204 454 title = 'No Content' 455 empty_body = True 456 457class HTTPResetContent(HTTPOk): 458 """ 459 subclass of :class:`~HTTPOk` 460 461 This indicates that the the server has fulfilled the request and 462 the user agent SHOULD reset the document view which caused the 463 request to be sent. 464 465 code: 205, title: Reset Content 466 """ 467 code = 205 468 title = 'Reset Content' 469 empty_body = True 470 471class HTTPPartialContent(HTTPOk): 472 """ 473 subclass of :class:`~HTTPOk` 474 475 This indicates that the server has fulfilled the partial GET 476 request for the resource. 477 478 code: 206, title: Partial Content 479 """ 480 code = 206 481 title = 'Partial Content' 482 483############################################################ 484## 3xx redirection 485############################################################ 486 487class _HTTPMove(HTTPRedirection): 488 """ 489 redirections which require a Location field 490 491 Since a 'Location' header is a required attribute of 301, 302, 303, 492 305, 307 and 308 (but not 304), this base class provides the mechanics to 493 make this easy. 494 495 You can provide a location keyword argument to set the location 496 immediately. You may also give ``add_slash=True`` if you want to 497 redirect to the same URL as the request, except with a ``/`` added 498 to the end. 499 500 Relative URLs in the location will be resolved to absolute. 501 """ 502 explanation = 'The resource has been moved to' 503 body_template_obj = Template('''\ 504${explanation} <a href="${location}">${location}</a>; 505you should be redirected automatically. 506${detail} 507${html_comment}''') 508 509 def __init__(self, detail=None, headers=None, comment=None, 510 body_template=None, location=None, add_slash=False): 511 super(_HTTPMove, self).__init__( 512 detail=detail, headers=headers, comment=comment, 513 body_template=body_template) 514 if location is not None: 515 if '\n' in location or '\r' in location: 516 raise ValueError('Control characters are not allowed in location') 517 518 self.location = location 519 if add_slash: 520 raise TypeError( 521 "You can only provide one of the arguments location " 522 "and add_slash") 523 self.add_slash = add_slash 524 525 def __call__(self, environ, start_response): 526 req = Request(environ) 527 if self.add_slash: 528 url = req.path_url 529 url += '/' 530 if req.environ.get('QUERY_STRING'): 531 url += '?' + req.environ['QUERY_STRING'] 532 self.location = url 533 self.location = urlparse.urljoin(req.path_url, self.location) 534 return super(_HTTPMove, self).__call__( 535 environ, start_response) 536 537class HTTPMultipleChoices(_HTTPMove): 538 """ 539 subclass of :class:`~_HTTPMove` 540 541 This indicates that the requested resource corresponds to any one 542 of a set of representations, each with its own specific location, 543 and agent-driven negotiation information is being provided so that 544 the user can select a preferred representation and redirect its 545 request to that location. 546 547 code: 300, title: Multiple Choices 548 """ 549 code = 300 550 title = 'Multiple Choices' 551 552class HTTPMovedPermanently(_HTTPMove): 553 """ 554 subclass of :class:`~_HTTPMove` 555 556 This indicates that the requested resource has been assigned a new 557 permanent URI and any future references to this resource SHOULD use 558 one of the returned URIs. 559 560 code: 301, title: Moved Permanently 561 """ 562 code = 301 563 title = 'Moved Permanently' 564 565class HTTPFound(_HTTPMove): 566 """ 567 subclass of :class:`~_HTTPMove` 568 569 This indicates that the requested resource resides temporarily under 570 a different URI. 571 572 code: 302, title: Found 573 """ 574 code = 302 575 title = 'Found' 576 explanation = 'The resource was found at' 577 578# This one is safe after a POST (the redirected location will be 579# retrieved with GET): 580class HTTPSeeOther(_HTTPMove): 581 """ 582 subclass of :class:`~_HTTPMove` 583 584 This indicates that the response to the request can be found under 585 a different URI and SHOULD be retrieved using a GET method on that 586 resource. 587 588 code: 303, title: See Other 589 """ 590 code = 303 591 title = 'See Other' 592 593class HTTPNotModified(HTTPRedirection): 594 """ 595 subclass of :class:`~HTTPRedirection` 596 597 This indicates that if the client has performed a conditional GET 598 request and access is allowed, but the document has not been 599 modified, the server SHOULD respond with this status code. 600 601 code: 304, title: Not Modified 602 """ 603 # TODO: this should include a date or etag header 604 code = 304 605 title = 'Not Modified' 606 empty_body = True 607 608class HTTPUseProxy(_HTTPMove): 609 """ 610 subclass of :class:`~_HTTPMove` 611 612 This indicates that the requested resource MUST be accessed through 613 the proxy given by the Location field. 614 615 code: 305, title: Use Proxy 616 """ 617 # Not a move, but looks a little like one 618 code = 305 619 title = 'Use Proxy' 620 explanation = ( 621 'The resource must be accessed through a proxy located at') 622 623class HTTPTemporaryRedirect(_HTTPMove): 624 """ 625 subclass of :class:`~_HTTPMove` 626 627 This indicates that the requested resource resides temporarily 628 under a different URI. 629 630 code: 307, title: Temporary Redirect 631 """ 632 code = 307 633 title = 'Temporary Redirect' 634 635class HTTPPermanentRedirect(_HTTPMove): 636 """ 637 subclass of :class:`~_HTTPMove` 638 639 This indicates that the requested resource resides permanently 640 under a different URI. 641 642 code: 308, title: Permanent Redirect 643 """ 644 code = 308 645 title = 'Permanent Redirect' 646 647 648############################################################ 649## 4xx client error 650############################################################ 651 652class HTTPClientError(HTTPError): 653 """ 654 base class for the 400's, where the client is in error 655 656 This is an error condition in which the client is presumed to be 657 in-error. This is an expected problem, and thus is not considered 658 a bug. A server-side traceback is not warranted. Unless specialized, 659 this is a '400 Bad Request' 660 661 code: 400, title: Bad Request 662 """ 663 code = 400 664 title = 'Bad Request' 665 explanation = ('The server could not comply with the request since\r\n' 666 'it is either malformed or otherwise incorrect.\r\n') 667 668class HTTPBadRequest(HTTPClientError): 669 pass 670 671class HTTPUnauthorized(HTTPClientError): 672 """ 673 subclass of :class:`~HTTPClientError` 674 675 This indicates that the request requires user authentication. 676 677 code: 401, title: Unauthorized 678 """ 679 code = 401 680 title = 'Unauthorized' 681 explanation = ( 682 'This server could not verify that you are authorized to\r\n' 683 'access the document you requested. Either you supplied the\r\n' 684 'wrong credentials (e.g., bad password), or your browser\r\n' 685 'does not understand how to supply the credentials required.\r\n') 686 687class HTTPPaymentRequired(HTTPClientError): 688 """ 689 subclass of :class:`~HTTPClientError` 690 691 code: 402, title: Payment Required 692 """ 693 code = 402 694 title = 'Payment Required' 695 explanation = ('Access was denied for financial reasons.') 696 697class HTTPForbidden(HTTPClientError): 698 """ 699 subclass of :class:`~HTTPClientError` 700 701 This indicates that the server understood the request, but is 702 refusing to fulfill it. 703 704 code: 403, title: Forbidden 705 """ 706 code = 403 707 title = 'Forbidden' 708 explanation = ('Access was denied to this resource.') 709 710class HTTPNotFound(HTTPClientError): 711 """ 712 subclass of :class:`~HTTPClientError` 713 714 This indicates that the server did not find anything matching the 715 Request-URI. 716 717 code: 404, title: Not Found 718 """ 719 code = 404 720 title = 'Not Found' 721 explanation = ('The resource could not be found.') 722 723class HTTPMethodNotAllowed(HTTPClientError): 724 """ 725 subclass of :class:`~HTTPClientError` 726 727 This indicates that the method specified in the Request-Line is 728 not allowed for the resource identified by the Request-URI. 729 730 code: 405, title: Method Not Allowed 731 """ 732 code = 405 733 title = 'Method Not Allowed' 734 # override template since we need an environment variable 735 body_template_obj = Template('''\ 736The method ${REQUEST_METHOD} is not allowed for this resource. <br /><br /> 737${detail}''') 738 739class HTTPNotAcceptable(HTTPClientError): 740 """ 741 subclass of :class:`~HTTPClientError` 742 743 This indicates the resource identified by the request is only 744 capable of generating response entities which have content 745 characteristics not acceptable according to the accept headers 746 sent in the request. 747 748 code: 406, title: Not Acceptable 749 """ 750 code = 406 751 title = 'Not Acceptable' 752 # override template since we need an environment variable 753 body_template_obj = Template('''\ 754The resource could not be generated that was acceptable to your browser 755(content of type ${HTTP_ACCEPT}. <br /><br /> 756${detail}''') 757 758class HTTPProxyAuthenticationRequired(HTTPClientError): 759 """ 760 subclass of :class:`~HTTPClientError` 761 762 This is similar to 401, but indicates that the client must first 763 authenticate itself with the proxy. 764 765 code: 407, title: Proxy Authentication Required 766 """ 767 code = 407 768 title = 'Proxy Authentication Required' 769 explanation = ('Authentication with a local proxy is needed.') 770 771class HTTPRequestTimeout(HTTPClientError): 772 """ 773 subclass of :class:`~HTTPClientError` 774 775 This indicates that the client did not produce a request within 776 the time that the server was prepared to wait. 777 778 code: 408, title: Request Timeout 779 """ 780 code = 408 781 title = 'Request Timeout' 782 explanation = ('The server has waited too long for the request to ' 783 'be sent by the client.') 784 785class HTTPConflict(HTTPClientError): 786 """ 787 subclass of :class:`~HTTPClientError` 788 789 This indicates that the request could not be completed due to a 790 conflict with the current state of the resource. 791 792 code: 409, title: Conflict 793 """ 794 code = 409 795 title = 'Conflict' 796 explanation = ('There was a conflict when trying to complete ' 797 'your request.') 798 799class HTTPGone(HTTPClientError): 800 """ 801 subclass of :class:`~HTTPClientError` 802 803 This indicates that the requested resource is no longer available 804 at the server and no forwarding address is known. 805 806 code: 410, title: Gone 807 """ 808 code = 410 809 title = 'Gone' 810 explanation = ('This resource is no longer available. No forwarding ' 811 'address is given.') 812 813class HTTPLengthRequired(HTTPClientError): 814 """ 815 subclass of :class:`~HTTPClientError` 816 817 This indicates that the the server refuses to accept the request 818 without a defined Content-Length. 819 820 code: 411, title: Length Required 821 """ 822 code = 411 823 title = 'Length Required' 824 explanation = ('Content-Length header required.') 825 826class HTTPPreconditionFailed(HTTPClientError): 827 """ 828 subclass of :class:`~HTTPClientError` 829 830 This indicates that the precondition given in one or more of the 831 request-header fields evaluated to false when it was tested on the 832 server. 833 834 code: 412, title: Precondition Failed 835 """ 836 code = 412 837 title = 'Precondition Failed' 838 explanation = ('Request precondition failed.') 839 840class HTTPRequestEntityTooLarge(HTTPClientError): 841 """ 842 subclass of :class:`~HTTPClientError` 843 844 This indicates that the server is refusing to process a request 845 because the request entity is larger than the server is willing or 846 able to process. 847 848 code: 413, title: Request Entity Too Large 849 """ 850 code = 413 851 title = 'Request Entity Too Large' 852 explanation = ('The body of your request was too large for this server.') 853 854class HTTPRequestURITooLong(HTTPClientError): 855 """ 856 subclass of :class:`~HTTPClientError` 857 858 This indicates that the server is refusing to service the request 859 because the Request-URI is longer than the server is willing to 860 interpret. 861 862 code: 414, title: Request-URI Too Long 863 """ 864 code = 414 865 title = 'Request-URI Too Long' 866 explanation = ('The request URI was too long for this server.') 867 868class HTTPUnsupportedMediaType(HTTPClientError): 869 """ 870 subclass of :class:`~HTTPClientError` 871 872 This indicates that the server is refusing to service the request 873 because the entity of the request is in a format not supported by 874 the requested resource for the requested method. 875 876 code: 415, title: Unsupported Media Type 877 """ 878 code = 415 879 title = 'Unsupported Media Type' 880 # override template since we need an environment variable 881 body_template_obj = Template('''\ 882The request media type ${CONTENT_TYPE} is not supported by this server. 883<br /><br /> 884${detail}''') 885 886class HTTPRequestRangeNotSatisfiable(HTTPClientError): 887 """ 888 subclass of :class:`~HTTPClientError` 889 890 The server SHOULD return a response with this status code if a 891 request included a Range request-header field, and none of the 892 range-specifier values in this field overlap the current extent 893 of the selected resource, and the request did not include an 894 If-Range request-header field. 895 896 code: 416, title: Request Range Not Satisfiable 897 """ 898 code = 416 899 title = 'Request Range Not Satisfiable' 900 explanation = ('The Range requested is not available.') 901 902class HTTPExpectationFailed(HTTPClientError): 903 """ 904 subclass of :class:`~HTTPClientError` 905 906 This indidcates that the expectation given in an Expect 907 request-header field could not be met by this server. 908 909 code: 417, title: Expectation Failed 910 """ 911 code = 417 912 title = 'Expectation Failed' 913 explanation = ('Expectation failed.') 914 915class HTTPUnprocessableEntity(HTTPClientError): 916 """ 917 subclass of :class:`~HTTPClientError` 918 919 This indicates that the server is unable to process the contained 920 instructions. 921 922 code: 422, title: Unprocessable Entity 923 """ 924 ## Note: from WebDAV 925 code = 422 926 title = 'Unprocessable Entity' 927 explanation = 'Unable to process the contained instructions' 928 929class HTTPLocked(HTTPClientError): 930 """ 931 subclass of :class:`~HTTPClientError` 932 933 This indicates that the resource is locked. 934 935 code: 423, title: Locked 936 """ 937 ## Note: from WebDAV 938 code = 423 939 title = 'Locked' 940 explanation = ('The resource is locked') 941 942class HTTPFailedDependency(HTTPClientError): 943 """ 944 subclass of :class:`~HTTPClientError` 945 946 This indicates that the method could not be performed because the 947 requested action depended on another action and that action failed. 948 949 code: 424, title: Failed Dependency 950 """ 951 ## Note: from WebDAV 952 code = 424 953 title = 'Failed Dependency' 954 explanation = ( 955 'The method could not be performed because the requested ' 956 'action dependended on another action and that action failed') 957 958class HTTPPreconditionRequired(HTTPClientError): 959 """ 960 subclass of :class:`~HTTPClientError` 961 962 This indicates that the origin server requires the request to be 963 conditional. From RFC 6585, "Additional HTTP Status Codes". 964 965 code: 428, title: Precondition Required 966 """ 967 code = 428 968 title = 'Precondition Required' 969 explanation = ('This request is required to be conditional') 970 971class HTTPTooManyRequests(HTTPClientError): 972 """ 973 subclass of :class:`~HTTPClientError` 974 975 This indicates that the client has sent too many requests in a 976 given amount of time. Useful for rate limiting. 977 978 From RFC 6585, "Additional HTTP Status Codes". 979 980 code: 429, title: Too Many Requests 981 """ 982 code = 429 983 title = 'Too Many Requests' 984 explanation = ( 985 'The client has sent too many requests in a given amount of time') 986 987class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): 988 """ 989 subclass of :class:`~HTTPClientError` 990 991 This indicates that the server is unwilling to process the request 992 because its header fields are too large. The request may be resubmitted 993 after reducing the size of the request header fields. 994 995 From RFC 6585, "Additional HTTP Status Codes". 996 997 code: 431, title: Request Header Fields Too Large 998 """ 999 code = 431 1000 title = 'Request Header Fields Too Large' 1001 explanation = ( 1002 'The request header fields were too large') 1003 1004class HTTPUnavailableForLegalReasons(HTTPClientError): 1005 """ 1006 subclass of :class:`~HTTPClientError` 1007 1008 This indicates that the server is unable to process the request 1009 because of legal reasons, e.g. censorship or government-mandated 1010 blocked access. 1011 1012 From the draft "A New HTTP Status Code for Legally-restricted Resources" 1013 by Tim Bray: 1014 1015 https://tools.ietf.org/html/draft-tbray-http-legally-restricted-status-00 1016 1017 code: 451, title: Unavailable For Legal Reasons 1018 """ 1019 code = 451 1020 title = 'Unavailable For Legal Reasons' 1021 explanation = ('The resource is not available due to legal reasons.') 1022 1023############################################################ 1024## 5xx Server Error 1025############################################################ 1026# Response status codes beginning with the digit "5" indicate cases in 1027# which the server is aware that it has erred or is incapable of 1028# performing the request. Except when responding to a HEAD request, the 1029# server SHOULD include an entity containing an explanation of the error 1030# situation, and whether it is a temporary or permanent condition. User 1031# agents SHOULD display any included entity to the user. These response 1032# codes are applicable to any request method. 1033 1034class HTTPServerError(HTTPError): 1035 """ 1036 base class for the 500's, where the server is in-error 1037 1038 This is an error condition in which the server is presumed to be 1039 in-error. This is usually unexpected, and thus requires a traceback; 1040 ideally, opening a support ticket for the customer. Unless specialized, 1041 this is a '500 Internal Server Error' 1042 """ 1043 code = 500 1044 title = 'Internal Server Error' 1045 explanation = ( 1046 'The server has either erred or is incapable of performing\r\n' 1047 'the requested operation.\r\n') 1048 1049class HTTPInternalServerError(HTTPServerError): 1050 pass 1051 1052class HTTPNotImplemented(HTTPServerError): 1053 """ 1054 subclass of :class:`~HTTPServerError` 1055 1056 This indicates that the server does not support the functionality 1057 required to fulfill the request. 1058 1059 code: 501, title: Not Implemented 1060 """ 1061 code = 501 1062 title = 'Not Implemented' 1063 body_template_obj = Template(''' 1064The request method ${REQUEST_METHOD} is not implemented for this server. <br /><br /> 1065${detail}''') 1066 1067class HTTPBadGateway(HTTPServerError): 1068 """ 1069 subclass of :class:`~HTTPServerError` 1070 1071 This indicates that the server, while acting as a gateway or proxy, 1072 received an invalid response from the upstream server it accessed 1073 in attempting to fulfill the request. 1074 1075 code: 502, title: Bad Gateway 1076 """ 1077 code = 502 1078 title = 'Bad Gateway' 1079 explanation = ('Bad gateway.') 1080 1081class HTTPServiceUnavailable(HTTPServerError): 1082 """ 1083 subclass of :class:`~HTTPServerError` 1084 1085 This indicates that the server is currently unable to handle the 1086 request due to a temporary overloading or maintenance of the server. 1087 1088 code: 503, title: Service Unavailable 1089 """ 1090 code = 503 1091 title = 'Service Unavailable' 1092 explanation = ('The server is currently unavailable. ' 1093 'Please try again at a later time.') 1094 1095class HTTPGatewayTimeout(HTTPServerError): 1096 """ 1097 subclass of :class:`~HTTPServerError` 1098 1099 This indicates that the server, while acting as a gateway or proxy, 1100 did not receive a timely response from the upstream server specified 1101 by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server 1102 (e.g. DNS) it needed to access in attempting to complete the request. 1103 1104 code: 504, title: Gateway Timeout 1105 """ 1106 code = 504 1107 title = 'Gateway Timeout' 1108 explanation = ('The gateway has timed out.') 1109 1110class HTTPVersionNotSupported(HTTPServerError): 1111 """ 1112 subclass of :class:`~HTTPServerError` 1113 1114 This indicates that the server does not support, or refuses to 1115 support, the HTTP protocol version that was used in the request 1116 message. 1117 1118 code: 505, title: HTTP Version Not Supported 1119 """ 1120 code = 505 1121 title = 'HTTP Version Not Supported' 1122 explanation = ('The HTTP version is not supported.') 1123 1124class HTTPInsufficientStorage(HTTPServerError): 1125 """ 1126 subclass of :class:`~HTTPServerError` 1127 1128 This indicates that the server does not have enough space to save 1129 the resource. 1130 1131 code: 507, title: Insufficient Storage 1132 """ 1133 code = 507 1134 title = 'Insufficient Storage' 1135 explanation = ('There was not enough space to save the resource') 1136 1137class HTTPNetworkAuthenticationRequired(HTTPServerError): 1138 """ 1139 subclass of :class:`~HTTPServerError` 1140 1141 This indicates that the client needs to authenticate to gain 1142 network access. From RFC 6585, "Additional HTTP Status Codes". 1143 1144 code: 511, title: Network Authentication Required 1145 """ 1146 code = 511 1147 title = 'Network Authentication Required' 1148 explanation = ('Network authentication is required') 1149 1150class HTTPExceptionMiddleware(object): 1151 """ 1152 Middleware that catches exceptions in the sub-application. This 1153 does not catch exceptions in the app_iter; only during the initial 1154 calling of the application. 1155 1156 This should be put *very close* to applications that might raise 1157 these exceptions. This should not be applied globally; letting 1158 *expected* exceptions raise through the WSGI stack is dangerous. 1159 """ 1160 1161 def __init__(self, application): 1162 self.application = application 1163 def __call__(self, environ, start_response): 1164 try: 1165 return self.application(environ, start_response) 1166 except HTTPException: 1167 parent_exc_info = sys.exc_info() 1168 def repl_start_response(status, headers, exc_info=None): 1169 if exc_info is None: 1170 exc_info = parent_exc_info 1171 return start_response(status, headers, exc_info) 1172 return parent_exc_info[1](environ, repl_start_response) 1173 1174try: 1175 from paste import httpexceptions 1176except ImportError: # pragma: no cover 1177 # Without Paste we don't need to do this fixup 1178 pass 1179else: # pragma: no cover 1180 for name in dir(httpexceptions): 1181 obj = globals().get(name) 1182 if (obj and isinstance(obj, type) and issubclass(obj, HTTPException) 1183 and obj is not HTTPException 1184 and obj is not WSGIHTTPException): 1185 obj.__bases__ = obj.__bases__ + (getattr(httpexceptions, name),) 1186 del name, obj, httpexceptions 1187 1188__all__ = ['HTTPExceptionMiddleware', 'status_map'] 1189status_map={} 1190for name, value in list(globals().items()): 1191 if (isinstance(value, (type, class_types)) and 1192 issubclass(value, HTTPException) 1193 and not name.startswith('_')): 1194 __all__.append(name) 1195 if all(( 1196 getattr(value, 'code', None), 1197 value not in (HTTPRedirection, HTTPClientError, HTTPServerError), 1198 issubclass( 1199 value, 1200 (HTTPOk, HTTPRedirection, HTTPClientError, HTTPServerError) 1201 ) 1202 )): 1203 status_map[value.code]=value 1204 if hasattr(value, 'explanation'): 1205 value.explanation = ' '.join(value.explanation.strip().split()) 1206del name, value 1207