1""" 2HTTP Exceptions 3--------------- 4 5This module contains Pyramid HTTP exception classes. Each class relates to a 6single HTTP status code. Each class is a subclass of the 7:class:`~HTTPException`. Each exception class is also a :term:`response` 8object. 9 10Each exception class has a status code according to :rfc:`2068`: 11codes with 100-300 are not really errors; 400s are client errors, 12and 500s are server errors. 13 14Exception 15 HTTPException 16 HTTPSuccessful 17 * 200 - HTTPOk 18 * 201 - HTTPCreated 19 * 202 - HTTPAccepted 20 * 203 - HTTPNonAuthoritativeInformation 21 * 204 - HTTPNoContent 22 * 205 - HTTPResetContent 23 * 206 - HTTPPartialContent 24 HTTPRedirection 25 * 300 - HTTPMultipleChoices 26 * 301 - HTTPMovedPermanently 27 * 302 - HTTPFound 28 * 303 - HTTPSeeOther 29 * 304 - HTTPNotModified 30 * 305 - HTTPUseProxy 31 * 307 - HTTPTemporaryRedirect 32 HTTPError 33 HTTPClientError 34 * 400 - HTTPBadRequest 35 * 401 - HTTPUnauthorized 36 * 402 - HTTPPaymentRequired 37 * 403 - HTTPForbidden 38 * 404 - HTTPNotFound 39 * 405 - HTTPMethodNotAllowed 40 * 406 - HTTPNotAcceptable 41 * 407 - HTTPProxyAuthenticationRequired 42 * 408 - HTTPRequestTimeout 43 * 409 - HTTPConflict 44 * 410 - HTTPGone 45 * 411 - HTTPLengthRequired 46 * 412 - HTTPPreconditionFailed 47 * 413 - HTTPRequestEntityTooLarge 48 * 414 - HTTPRequestURITooLong 49 * 415 - HTTPUnsupportedMediaType 50 * 416 - HTTPRequestRangeNotSatisfiable 51 * 417 - HTTPExpectationFailed 52 * 422 - HTTPUnprocessableEntity 53 * 423 - HTTPLocked 54 * 424 - HTTPFailedDependency 55 * 428 - HTTPPreconditionRequired 56 * 429 - HTTPTooManyRequests 57 * 431 - HTTPRequestHeaderFieldsTooLarge 58 HTTPServerError 59 * 500 - HTTPInternalServerError 60 * 501 - HTTPNotImplemented 61 * 502 - HTTPBadGateway 62 * 503 - HTTPServiceUnavailable 63 * 504 - HTTPGatewayTimeout 64 * 505 - HTTPVersionNotSupported 65 * 507 - HTTPInsufficientStorage 66 67HTTP exceptions are also :term:`response` objects, thus they accept most of 68the same parameters that can be passed to a regular 69:class:`~pyramid.response.Response`. Each HTTP exception also has the 70following attributes: 71 72 ``code`` 73 the HTTP status code for the exception 74 75 ``title`` 76 remainder of the status line (stuff after the code) 77 78 ``explanation`` 79 a plain-text explanation of the error message that is 80 not subject to environment or header substitutions; 81 it is accessible in the template via ${explanation} 82 83 ``detail`` 84 a plain-text message customization that is not subject 85 to environment or header substitutions; accessible in 86 the template via ${detail} 87 88 ``body_template`` 89 a ``String.template``-format content fragment used for environment 90 and header substitution; the default template includes both 91 the explanation and further detail provided in the 92 message. 93 94Each HTTP exception accepts the following parameters, any others will 95be forwarded to its :class:`~pyramid.response.Response` superclass: 96 97 ``detail`` 98 a plain-text override of the default ``detail`` 99 100 ``headers`` 101 a list of (k,v) header pairs 102 103 ``comment`` 104 a plain-text additional information which is 105 usually stripped/hidden for end-users 106 107 ``body_template`` 108 a ``string.Template`` object containing a content fragment in HTML 109 that frames the explanation and further detail 110 111 ``body`` 112 a string that will override the ``body_template`` and be used as the 113 body of the response. 114 115Substitution of response headers into template values is always performed. 116Substitution of WSGI environment values is performed if a ``request`` is 117passed to the exception's constructor. 118 119The subclasses of :class:`~_HTTPMove` 120(:class:`~HTTPMultipleChoices`, :class:`~HTTPMovedPermanently`, 121:class:`~HTTPFound`, :class:`~HTTPSeeOther`, :class:`~HTTPUseProxy` and 122:class:`~HTTPTemporaryRedirect`) are redirections that require a ``Location`` 123field. Reflecting this, these subclasses have one additional keyword argument: 124``location``, which indicates the location to which to redirect. 125""" 126import json 127 128from string import Template 129 130from zope.interface import implementer 131 132from webob import html_escape as _html_escape 133from webob.acceptparse import MIMEAccept 134 135from pyramid.compat import ( 136 class_types, 137 text_type, 138 binary_type, 139 text_, 140 ) 141 142from pyramid.interfaces import IExceptionResponse 143from pyramid.response import Response 144 145def _no_escape(value): 146 if value is None: 147 return '' 148 if not isinstance(value, text_type): 149 if hasattr(value, '__unicode__'): 150 value = value.__unicode__() 151 if isinstance(value, binary_type): 152 value = text_(value, 'utf-8') 153 else: 154 value = text_type(value) 155 return value 156 157@implementer(IExceptionResponse) 158class HTTPException(Response, Exception): 159 160 ## You should set in subclasses: 161 # code = 200 162 # title = 'OK' 163 # explanation = 'why this happens' 164 # body_template_obj = Template('response template') 165 # 166 # This class itself uses the error code "520" with the error message/title 167 # of "Unknown Error". This is not an RFC standard, however it is 168 # implemented in practice. Sub-classes should be overriding the default 169 # values and 520 should not be seen in the wild from Pyramid applications. 170 # Due to changes in WebOb, a code of "None" is not valid, and WebOb due to 171 # more strict error checking rejects it now. 172 173 # differences from webob.exc.WSGIHTTPException: 174 # 175 # - doesn't use "strip_tags" (${br} placeholder for <br/>, no other html 176 # in default body template) 177 # 178 # - __call__ never generates a new Response, it always mutates self 179 # 180 # - explicitly sets self.message = detail to prevent whining by Python 181 # 2.6.5+ access of Exception.message 182 # 183 # - its base class of HTTPException is no longer a Python 2.4 compatibility 184 # shim; it's purely a base class that inherits from Exception. This 185 # implies that this class' ``exception`` property always returns 186 # ``self`` (it exists only for bw compat at this point). 187 # 188 # - documentation improvements (Pyramid-specific docstrings where necessary) 189 # 190 code = 520 191 title = 'Unknown Error' 192 explanation = '' 193 body_template_obj = Template('''\ 194${explanation}${br}${br} 195${detail} 196${html_comment} 197''') 198 199 plain_template_obj = Template('''\ 200${status} 201 202${body}''') 203 204 html_template_obj = Template('''\ 205<html> 206 <head> 207 <title>${status}</title> 208 </head> 209 <body> 210 <h1>${status}</h1> 211 ${body} 212 </body> 213</html>''') 214 215 ## Set this to True for responses that should have no request body 216 empty_body = False 217 218 def __init__(self, detail=None, headers=None, comment=None, 219 body_template=None, json_formatter=None, **kw): 220 status = '%s %s' % (self.code, self.title) 221 Response.__init__(self, status=status, **kw) 222 Exception.__init__(self, detail) 223 self.detail = self.message = detail 224 if headers: 225 self.headers.extend(headers) 226 self.comment = comment 227 if body_template is not None: 228 self.body_template = body_template 229 self.body_template_obj = Template(body_template) 230 if json_formatter is not None: 231 self._json_formatter = json_formatter 232 233 if self.empty_body: 234 del self.content_type 235 del self.content_length 236 237 def __str__(self): 238 return self.detail or self.explanation 239 240 def _json_formatter(self, status, body, title, environ): 241 return {'message': body, 242 'code': status, 243 'title': self.title} 244 245 def prepare(self, environ): 246 if not self.body and not self.empty_body: 247 html_comment = '' 248 comment = self.comment or '' 249 accept_value = environ.get('HTTP_ACCEPT', '') 250 accept = MIMEAccept(accept_value) 251 # Attempt to match text/html or application/json, if those don't 252 # match, we will fall through to defaulting to text/plain 253 match = accept.best_match(['text/html', 'application/json']) 254 255 if match == 'text/html': 256 self.content_type = 'text/html' 257 escape = _html_escape 258 page_template = self.html_template_obj 259 br = '<br/>' 260 if comment: 261 html_comment = '<!-- %s -->' % escape(comment) 262 elif match == 'application/json': 263 self.content_type = 'application/json' 264 self.charset = None 265 escape = _no_escape 266 br = '\n' 267 if comment: 268 html_comment = escape(comment) 269 270 class JsonPageTemplate(object): 271 def __init__(self, excobj): 272 self.excobj = excobj 273 274 def substitute(self, status, body): 275 jsonbody = self.excobj._json_formatter( 276 status=status, 277 body=body, title=self.excobj.title, 278 environ=environ) 279 return json.dumps(jsonbody) 280 281 page_template = JsonPageTemplate(self) 282 else: 283 self.content_type = 'text/plain' 284 escape = _no_escape 285 page_template = self.plain_template_obj 286 br = '\n' 287 if comment: 288 html_comment = escape(comment) 289 args = { 290 'br': br, 291 'explanation': escape(self.explanation), 292 'detail': escape(self.detail or ''), 293 'comment': escape(comment), 294 'html_comment': html_comment, 295 } 296 body_tmpl = self.body_template_obj 297 if HTTPException.body_template_obj is not body_tmpl: 298 # Custom template; add headers to args 299 for k, v in environ.items(): 300 if (not k.startswith('wsgi.')) and ('.' in k): 301 # omit custom environ variables, stringifying them may 302 # trigger code that should not be executed here; see 303 # https://github.com/Pylons/pyramid/issues/239 304 continue 305 args[k] = escape(v) 306 for k, v in self.headers.items(): 307 args[k.lower()] = escape(v) 308 body = body_tmpl.substitute(args) 309 page = page_template.substitute(status=self.status, body=body) 310 if isinstance(page, text_type): 311 page = page.encode(self.charset if self.charset else 'UTF-8') 312 self.app_iter = [page] 313 self.body = page 314 315 @property 316 def wsgi_response(self): 317 # bw compat only 318 return self 319 320 exception = wsgi_response # bw compat only 321 322 def __call__(self, environ, start_response): 323 # differences from webob.exc.WSGIHTTPException 324 # 325 # - does not try to deal with HEAD requests 326 # 327 # - does not manufacture a new response object when generating 328 # the default response 329 # 330 self.prepare(environ) 331 return Response.__call__(self, environ, start_response) 332 333WSGIHTTPException = HTTPException # b/c post 1.5 334 335class HTTPError(HTTPException): 336 """ 337 base class for exceptions with status codes in the 400s and 500s 338 339 This is an exception which indicates that an error has occurred, 340 and that any work in progress should not be committed. 341 """ 342 343class HTTPRedirection(HTTPException): 344 """ 345 base class for exceptions with status codes in the 300s (redirections) 346 347 This is an abstract base class for 3xx redirection. It indicates 348 that further action needs to be taken by the user agent in order 349 to fulfill the request. It does not necessarly signal an error 350 condition. 351 """ 352 353class HTTPSuccessful(HTTPException): 354 """ 355 Base class for exceptions with status codes in the 200s (successful 356 responses) 357 """ 358 359############################################################ 360## 2xx success 361############################################################ 362 363class HTTPOk(HTTPSuccessful): 364 """ 365 subclass of :class:`~HTTPSuccessful` 366 367 Indicates that the request has succeeded. 368 369 code: 200, title: OK 370 """ 371 code = 200 372 title = 'OK' 373 374class HTTPCreated(HTTPSuccessful): 375 """ 376 subclass of :class:`~HTTPSuccessful` 377 378 This indicates that request has been fulfilled and resulted in a new 379 resource being created. 380 381 code: 201, title: Created 382 """ 383 code = 201 384 title = 'Created' 385 386class HTTPAccepted(HTTPSuccessful): 387 """ 388 subclass of :class:`~HTTPSuccessful` 389 390 This indicates that the request has been accepted for processing, but the 391 processing has not been completed. 392 393 code: 202, title: Accepted 394 """ 395 code = 202 396 title = 'Accepted' 397 explanation = 'The request is accepted for processing.' 398 399class HTTPNonAuthoritativeInformation(HTTPSuccessful): 400 """ 401 subclass of :class:`~HTTPSuccessful` 402 403 This indicates that the returned metainformation in the entity-header is 404 not the definitive set as available from the origin server, but is 405 gathered from a local or a third-party copy. 406 407 code: 203, title: Non-Authoritative Information 408 """ 409 code = 203 410 title = 'Non-Authoritative Information' 411 412class HTTPNoContent(HTTPSuccessful): 413 """ 414 subclass of :class:`~HTTPSuccessful` 415 416 This indicates that the server has fulfilled the request but does 417 not need to return an entity-body, and might want to return updated 418 metainformation. 419 420 code: 204, title: No Content 421 """ 422 code = 204 423 title = 'No Content' 424 empty_body = True 425 426class HTTPResetContent(HTTPSuccessful): 427 """ 428 subclass of :class:`~HTTPSuccessful` 429 430 This indicates that the server has fulfilled the request and 431 the user agent SHOULD reset the document view which caused the 432 request to be sent. 433 434 code: 205, title: Reset Content 435 """ 436 code = 205 437 title = 'Reset Content' 438 empty_body = True 439 440class HTTPPartialContent(HTTPSuccessful): 441 """ 442 subclass of :class:`~HTTPSuccessful` 443 444 This indicates that the server has fulfilled the partial GET 445 request for the resource. 446 447 code: 206, title: Partial Content 448 """ 449 code = 206 450 title = 'Partial Content' 451 452## FIXME: add 207 Multi-Status (but it's complicated) 453 454############################################################ 455## 3xx redirection 456############################################################ 457 458class _HTTPMove(HTTPRedirection): 459 """ 460 redirections which require a Location field 461 462 Since a 'Location' header is a required attribute of 301, 302, 303, 463 305 and 307 (but not 304), this base class provides the mechanics to 464 make this easy. 465 466 You must provide a ``location`` keyword argument. 467 """ 468 # differences from webob.exc._HTTPMove: 469 # 470 # - ${location} isn't wrapped in an <a> tag in body 471 # 472 # - location keyword arg defaults to '' 473 # 474 # - location isn't prepended with req.path_url when adding it as 475 # a header 476 # 477 # - ``location`` is first keyword (and positional) argument 478 # 479 # - ``add_slash`` argument is no longer accepted: code that passes 480 # add_slash argument to the constructor will receive an exception. 481 explanation = 'The resource has been moved to' 482 body_template_obj = Template('''\ 483${explanation} ${location}; you should be redirected automatically. 484${detail} 485${html_comment}''') 486 487 def __init__(self, location='', detail=None, headers=None, comment=None, 488 body_template=None, **kw): 489 if location is None: 490 raise ValueError("HTTP redirects need a location to redirect to.") 491 super(_HTTPMove, self).__init__( 492 detail=detail, headers=headers, comment=comment, 493 body_template=body_template, location=location, **kw) 494 495class HTTPMultipleChoices(_HTTPMove): 496 """ 497 subclass of :class:`~_HTTPMove` 498 499 This indicates that the requested resource corresponds to any one 500 of a set of representations, each with its own specific location, 501 and agent-driven negotiation information is being provided so that 502 the user can select a preferred representation and redirect its 503 request to that location. 504 505 code: 300, title: Multiple Choices 506 """ 507 code = 300 508 title = 'Multiple Choices' 509 510class HTTPMovedPermanently(_HTTPMove): 511 """ 512 subclass of :class:`~_HTTPMove` 513 514 This indicates that the requested resource has been assigned a new 515 permanent URI and any future references to this resource SHOULD use 516 one of the returned URIs. 517 518 code: 301, title: Moved Permanently 519 """ 520 code = 301 521 title = 'Moved Permanently' 522 523class HTTPFound(_HTTPMove): 524 """ 525 subclass of :class:`~_HTTPMove` 526 527 This indicates that the requested resource resides temporarily under 528 a different URI. 529 530 code: 302, title: Found 531 """ 532 code = 302 533 title = 'Found' 534 explanation = 'The resource was found at' 535 536# This one is safe after a POST (the redirected location will be 537# retrieved with GET): 538class HTTPSeeOther(_HTTPMove): 539 """ 540 subclass of :class:`~_HTTPMove` 541 542 This indicates that the response to the request can be found under 543 a different URI and SHOULD be retrieved using a GET method on that 544 resource. 545 546 code: 303, title: See Other 547 """ 548 code = 303 549 title = 'See Other' 550 551class HTTPNotModified(HTTPRedirection): 552 """ 553 subclass of :class:`~HTTPRedirection` 554 555 This indicates that if the client has performed a conditional GET 556 request and access is allowed, but the document has not been 557 modified, the server SHOULD respond with this status code. 558 559 code: 304, title: Not Modified 560 """ 561 # FIXME: this should include a date or etag header 562 code = 304 563 title = 'Not Modified' 564 empty_body = True 565 566class HTTPUseProxy(_HTTPMove): 567 """ 568 subclass of :class:`~_HTTPMove` 569 570 This indicates that the requested resource MUST be accessed through 571 the proxy given by the Location field. 572 573 code: 305, title: Use Proxy 574 """ 575 # Not a move, but looks a little like one 576 code = 305 577 title = 'Use Proxy' 578 explanation = ( 579 'The resource must be accessed through a proxy located at') 580 581class HTTPTemporaryRedirect(_HTTPMove): 582 """ 583 subclass of :class:`~_HTTPMove` 584 585 This indicates that the requested resource resides temporarily 586 under a different URI. 587 588 code: 307, title: Temporary Redirect 589 """ 590 code = 307 591 title = 'Temporary Redirect' 592 593############################################################ 594## 4xx client error 595############################################################ 596 597class HTTPClientError(HTTPError): 598 """ 599 base class for the 400s, where the client is in error 600 601 This is an error condition in which the client is presumed to be 602 in-error. This is an expected problem, and thus is not considered 603 a bug. A server-side traceback is not warranted. Unless specialized, 604 this is a '400 Bad Request' 605 """ 606 pass 607 608class HTTPBadRequest(HTTPClientError): 609 """ 610 subclass of :class:`~HTTPClientError` 611 612 This indicates that the body or headers failed validity checks, 613 preventing the server from being able to continue processing. 614 615 code: 400, title: Bad Request 616 """ 617 code = 400 618 title = 'Bad Request' 619 explanation = ('The server could not comply with the request since ' 620 'it is either malformed or otherwise incorrect.') 621 622class HTTPUnauthorized(HTTPClientError): 623 """ 624 subclass of :class:`~HTTPClientError` 625 626 This indicates that the request requires user authentication. 627 628 code: 401, title: Unauthorized 629 """ 630 code = 401 631 title = 'Unauthorized' 632 explanation = ( 633 'This server could not verify that you are authorized to ' 634 'access the document you requested. Either you supplied the ' 635 'wrong credentials (e.g., bad password), or your browser ' 636 'does not understand how to supply the credentials required.') 637 638class HTTPPaymentRequired(HTTPClientError): 639 """ 640 subclass of :class:`~HTTPClientError` 641 642 code: 402, title: Payment Required 643 """ 644 code = 402 645 title = 'Payment Required' 646 explanation = ('Access was denied for financial reasons.') 647 648class HTTPForbidden(HTTPClientError): 649 """ 650 subclass of :class:`~HTTPClientError` 651 652 This indicates that the server understood the request, but is 653 refusing to fulfill it. 654 655 code: 403, title: Forbidden 656 657 Raise this exception within :term:`view` code to immediately return the 658 :term:`forbidden view` to the invoking user. Usually this is a basic 659 ``403`` page, but the forbidden view can be customized as necessary. See 660 :ref:`changing_the_forbidden_view`. A ``Forbidden`` exception will be 661 the ``context`` of a :term:`Forbidden View`. 662 663 This exception's constructor treats two arguments specially. The first 664 argument, ``detail``, should be a string. The value of this string will 665 be used as the ``message`` attribute of the exception object. The second 666 special keyword argument, ``result`` is usually an instance of 667 :class:`pyramid.security.Denied` or :class:`pyramid.security.ACLDenied` 668 each of which indicates a reason for the forbidden error. However, 669 ``result`` is also permitted to be just a plain boolean ``False`` object 670 or ``None``. The ``result`` value will be used as the ``result`` 671 attribute of the exception object. It defaults to ``None``. 672 673 The :term:`Forbidden View` can use the attributes of a Forbidden 674 exception as necessary to provide extended information in an error 675 report shown to a user. 676 """ 677 # differences from webob.exc.HTTPForbidden: 678 # 679 # - accepts a ``result`` keyword argument 680 # 681 # - overrides constructor to set ``self.result`` 682 # 683 # differences from older ``pyramid.exceptions.Forbidden``: 684 # 685 # - ``result`` must be passed as a keyword argument. 686 # 687 code = 403 688 title = 'Forbidden' 689 explanation = ('Access was denied to this resource.') 690 def __init__(self, detail=None, headers=None, comment=None, 691 body_template=None, result=None, **kw): 692 HTTPClientError.__init__(self, detail=detail, headers=headers, 693 comment=comment, body_template=body_template, 694 **kw) 695 self.result = result 696 697class HTTPNotFound(HTTPClientError): 698 """ 699 subclass of :class:`~HTTPClientError` 700 701 This indicates that the server did not find anything matching the 702 Request-URI. 703 704 code: 404, title: Not Found 705 706 Raise this exception within :term:`view` code to immediately 707 return the :term:`Not Found View` to the invoking user. Usually 708 this is a basic ``404`` page, but the Not Found View can be 709 customized as necessary. See :ref:`changing_the_notfound_view`. 710 711 This exception's constructor accepts a ``detail`` argument 712 (the first argument), which should be a string. The value of this 713 string will be available as the ``message`` attribute of this exception, 714 for availability to the :term:`Not Found View`. 715 """ 716 code = 404 717 title = 'Not Found' 718 explanation = ('The resource could not be found.') 719 720class HTTPMethodNotAllowed(HTTPClientError): 721 """ 722 subclass of :class:`~HTTPClientError` 723 724 This indicates that the method specified in the Request-Line is 725 not allowed for the resource identified by the Request-URI. 726 727 code: 405, title: Method Not Allowed 728 """ 729 # differences from webob.exc.HTTPMethodNotAllowed: 730 # 731 # - body_template_obj uses ${br} instead of <br /> 732 code = 405 733 title = 'Method Not Allowed' 734 body_template_obj = Template('''\ 735The method ${REQUEST_METHOD} is not allowed for this resource. ${br}${br} 736${detail}''') 737 738class HTTPNotAcceptable(HTTPClientError): 739 """ 740 subclass of :class:`~HTTPClientError` 741 742 This indicates the resource identified by the request is only 743 capable of generating response entities which have content 744 characteristics not acceptable according to the accept headers 745 sent in the request. 746 747 code: 406, title: Not Acceptable 748 """ 749 # differences from webob.exc.HTTPNotAcceptable: 750 # 751 # - "template" attribute left off (useless, bug in webob?) 752 code = 406 753 title = 'Not Acceptable' 754 755class HTTPProxyAuthenticationRequired(HTTPClientError): 756 """ 757 subclass of :class:`~HTTPClientError` 758 759 This is similar to 401, but indicates that the client must first 760 authenticate itself with the proxy. 761 762 code: 407, title: Proxy Authentication Required 763 """ 764 code = 407 765 title = 'Proxy Authentication Required' 766 explanation = ('Authentication with a local proxy is needed.') 767 768class HTTPRequestTimeout(HTTPClientError): 769 """ 770 subclass of :class:`~HTTPClientError` 771 772 This indicates that the client did not produce a request within 773 the time that the server was prepared to wait. 774 775 code: 408, title: Request Timeout 776 """ 777 code = 408 778 title = 'Request Timeout' 779 explanation = ('The server has waited too long for the request to ' 780 'be sent by the client.') 781 782class HTTPConflict(HTTPClientError): 783 """ 784 subclass of :class:`~HTTPClientError` 785 786 This indicates that the request could not be completed due to a 787 conflict with the current state of the resource. 788 789 code: 409, title: Conflict 790 """ 791 code = 409 792 title = 'Conflict' 793 explanation = ('There was a conflict when trying to complete ' 794 'your request.') 795 796class HTTPGone(HTTPClientError): 797 """ 798 subclass of :class:`~HTTPClientError` 799 800 This indicates that the requested resource is no longer available 801 at the server and no forwarding address is known. 802 803 code: 410, title: Gone 804 """ 805 code = 410 806 title = 'Gone' 807 explanation = ('This resource is no longer available. No forwarding ' 808 'address is given.') 809 810class HTTPLengthRequired(HTTPClientError): 811 """ 812 subclass of :class:`~HTTPClientError` 813 814 This indicates that the server refuses to accept the request 815 without a defined Content-Length. 816 817 code: 411, title: Length Required 818 """ 819 code = 411 820 title = 'Length Required' 821 explanation = ('Content-Length header required.') 822 823class HTTPPreconditionFailed(HTTPClientError): 824 """ 825 subclass of :class:`~HTTPClientError` 826 827 This indicates that the precondition given in one or more of the 828 request-header fields evaluated to false when it was tested on the 829 server. 830 831 code: 412, title: Precondition Failed 832 """ 833 code = 412 834 title = 'Precondition Failed' 835 explanation = ('Request precondition failed.') 836 837class HTTPRequestEntityTooLarge(HTTPClientError): 838 """ 839 subclass of :class:`~HTTPClientError` 840 841 This indicates that the server is refusing to process a request 842 because the request entity is larger than the server is willing or 843 able to process. 844 845 code: 413, title: Request Entity Too Large 846 """ 847 code = 413 848 title = 'Request Entity Too Large' 849 explanation = ('The body of your request was too large for this server.') 850 851class HTTPRequestURITooLong(HTTPClientError): 852 """ 853 subclass of :class:`~HTTPClientError` 854 855 This indicates that the server is refusing to service the request 856 because the Request-URI is longer than the server is willing to 857 interpret. 858 859 code: 414, title: Request-URI Too Long 860 """ 861 code = 414 862 title = 'Request-URI Too Long' 863 explanation = ('The request URI was too long for this server.') 864 865class HTTPUnsupportedMediaType(HTTPClientError): 866 """ 867 subclass of :class:`~HTTPClientError` 868 869 This indicates that the server is refusing to service the request 870 because the entity of the request is in a format not supported by 871 the requested resource for the requested method. 872 873 code: 415, title: Unsupported Media Type 874 """ 875 # differences from webob.exc.HTTPUnsupportedMediaType: 876 # 877 # - "template_obj" attribute left off (useless, bug in webob?) 878 code = 415 879 title = 'Unsupported Media Type' 880 881class HTTPRequestRangeNotSatisfiable(HTTPClientError): 882 """ 883 subclass of :class:`~HTTPClientError` 884 885 The server SHOULD return a response with this status code if a 886 request included a Range request-header field, and none of the 887 range-specifier values in this field overlap the current extent 888 of the selected resource, and the request did not include an 889 If-Range request-header field. 890 891 code: 416, title: Request Range Not Satisfiable 892 """ 893 code = 416 894 title = 'Request Range Not Satisfiable' 895 explanation = ('The Range requested is not available.') 896 897class HTTPExpectationFailed(HTTPClientError): 898 """ 899 subclass of :class:`~HTTPClientError` 900 901 This indidcates that the expectation given in an Expect 902 request-header field could not be met by this server. 903 904 code: 417, title: Expectation Failed 905 """ 906 code = 417 907 title = 'Expectation Failed' 908 explanation = ('Expectation failed.') 909 910class HTTPUnprocessableEntity(HTTPClientError): 911 """ 912 subclass of :class:`~HTTPClientError` 913 914 This indicates that the server is unable to process the contained 915 instructions. 916 917 May be used to notify the client that their JSON/XML is well formed, but 918 not correct for the current request. 919 920 See RFC4918 section 11 for more information. 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 963 request to be conditional. 964 965 Its typical use is to avoid the "lost update" problem, where a client 966 GETs a resource's state, modifies it, and PUTs it back to the server, 967 when meanwhile a third party has modified the state on the server, 968 leading to a conflict. By requiring requests to be conditional, the 969 server can assure that clients are working with the correct copies. 970 971 RFC 6585.3 972 973 code: 428, title: Precondition Required 974 """ 975 code = 428 976 title = 'Precondition Required' 977 explanation = ( 978 'The origin server requires the request to be conditional.') 979 980class HTTPTooManyRequests(HTTPClientError): 981 """ 982 subclass of :class:`~HTTPClientError` 983 984 This indicates that the user has sent too many 985 requests in a given amount of time ("rate limiting"). 986 987 RFC 6585.4 988 989 code: 429, title: Too Many Requests 990 """ 991 code = 429 992 title = 'Too Many Requests' 993 explanation = ( 994 'The action could not be performed because there were too ' 995 'many requests by the client.') 996 997class HTTPRequestHeaderFieldsTooLarge(HTTPClientError): 998 """ 999 subclass of :class:`~HTTPClientError` 1000 1001 This indicates that the server is unwilling to process 1002 the request because its header fields are too large. The request MAY 1003 be resubmitted after reducing the size of the request header fields. 1004 1005 RFC 6585.5 1006 1007 code: 431, title: Request Header Fields Too Large 1008 """ 1009 code = 431 1010 title = 'Request Header Fields Too Large' 1011 explanation = ( 1012 'The requests header fields were too large.') 1013 1014############################################################ 1015## 5xx Server Error 1016############################################################ 1017# Response status codes beginning with the digit "5" indicate cases in 1018# which the server is aware that it has erred or is incapable of 1019# performing the request. Except when responding to a HEAD request, the 1020# server SHOULD include an entity containing an explanation of the error 1021# situation, and whether it is a temporary or permanent condition. User 1022# agents SHOULD display any included entity to the user. These response 1023# codes are applicable to any request method. 1024 1025class HTTPServerError(HTTPError): 1026 """ 1027 base class for the 500s, where the server is in-error 1028 1029 This is an error condition in which the server is presumed to be 1030 in-error. Unless specialized, this is a '500 Internal Server Error'. 1031 """ 1032 pass 1033 1034class HTTPInternalServerError(HTTPServerError): 1035 code = 500 1036 title = 'Internal Server Error' 1037 explanation = ( 1038 'The server has either erred or is incapable of performing ' 1039 'the requested operation.') 1040 1041class HTTPNotImplemented(HTTPServerError): 1042 """ 1043 subclass of :class:`~HTTPServerError` 1044 1045 This indicates that the server does not support the functionality 1046 required to fulfill the request. 1047 1048 code: 501, title: Not Implemented 1049 """ 1050 # differences from webob.exc.HTTPNotAcceptable: 1051 # 1052 # - "template" attr left off (useless, bug in webob?) 1053 code = 501 1054 title = 'Not Implemented' 1055 1056class HTTPBadGateway(HTTPServerError): 1057 """ 1058 subclass of :class:`~HTTPServerError` 1059 1060 This indicates that the server, while acting as a gateway or proxy, 1061 received an invalid response from the upstream server it accessed 1062 in attempting to fulfill the request. 1063 1064 code: 502, title: Bad Gateway 1065 """ 1066 code = 502 1067 title = 'Bad Gateway' 1068 explanation = ('Bad gateway.') 1069 1070class HTTPServiceUnavailable(HTTPServerError): 1071 """ 1072 subclass of :class:`~HTTPServerError` 1073 1074 This indicates that the server is currently unable to handle the 1075 request due to a temporary overloading or maintenance of the server. 1076 1077 code: 503, title: Service Unavailable 1078 """ 1079 code = 503 1080 title = 'Service Unavailable' 1081 explanation = ('The server is currently unavailable. ' 1082 'Please try again at a later time.') 1083 1084class HTTPGatewayTimeout(HTTPServerError): 1085 """ 1086 subclass of :class:`~HTTPServerError` 1087 1088 This indicates that the server, while acting as a gateway or proxy, 1089 did not receive a timely response from the upstream server specified 1090 by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server 1091 (e.g. DNS) it needed to access in attempting to complete the request. 1092 1093 code: 504, title: Gateway Timeout 1094 """ 1095 code = 504 1096 title = 'Gateway Timeout' 1097 explanation = ('The gateway has timed out.') 1098 1099class HTTPVersionNotSupported(HTTPServerError): 1100 """ 1101 subclass of :class:`~HTTPServerError` 1102 1103 This indicates that the server does not support, or refuses to 1104 support, the HTTP protocol version that was used in the request 1105 message. 1106 1107 code: 505, title: HTTP Version Not Supported 1108 """ 1109 code = 505 1110 title = 'HTTP Version Not Supported' 1111 explanation = ('The HTTP version is not supported.') 1112 1113class HTTPInsufficientStorage(HTTPServerError): 1114 """ 1115 subclass of :class:`~HTTPServerError` 1116 1117 This indicates that the server does not have enough space to save 1118 the resource. 1119 1120 code: 507, title: Insufficient Storage 1121 """ 1122 code = 507 1123 title = 'Insufficient Storage' 1124 explanation = ('There was not enough space to save the resource') 1125 1126def exception_response(status_code, **kw): 1127 """Creates an HTTP exception based on a status code. Example:: 1128 1129 raise exception_response(404) # raises an HTTPNotFound exception. 1130 1131 The values passed as ``kw`` are provided to the exception's constructor. 1132 """ 1133 exc = status_map[status_code](**kw) 1134 return exc 1135 1136def default_exceptionresponse_view(context, request): 1137 if not isinstance(context, Exception): 1138 # backwards compat for an exception response view registered via 1139 # config.set_notfound_view or config.set_forbidden_view 1140 # instead of as a proper exception view 1141 context = request.exception or context 1142 return context # assumed to be an IResponse 1143 1144status_map = {} 1145code = None 1146for name, value in list(globals().items()): 1147 if ( 1148 isinstance(value, class_types) and 1149 issubclass(value, HTTPException) and 1150 not name.startswith('_') 1151 ): 1152 code = getattr(value, 'code', None) 1153 if code: 1154 status_map[code] = value 1155del name, value, code 1156