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