Home | History | Annotate | Download | only in webob
      1 """
      2 This module processes Python exceptions that relate to HTTP exceptions
      3 by defining a set of exceptions, all subclasses of HTTPException.
      4 Each exception, in addition to being a Python exception that can be
      5 raised and caught, is also a WSGI application and ``webob.Response``
      6 object.
      7 
      8 This module defines exceptions according to RFC 2068 [1]_ : codes with
      9 100-300 are not really errors; 400's are client errors, and 500's are
     10 server errors.  According to the WSGI specification [2]_ , the application
     11 can 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
     13 subsequent invocations of ``start_response`` have a valid ``exc_info``
     14 argument obtained from ``sys.exc_info()``.  The WSGI specification then
     15 requires the server or gateway to handle the case where content has been
     16 sent and then an exception was encountered.
     17 
     18 Exception
     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 
     72 Usage notes
     73 -----------
     74 
     75 The 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 
     90 Subclass attributes and call parameters are designed to provide an easier path
     91 through the complications.
     92 
     93 Attributes:
     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 
    117 Parameters:
    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 
    133 To override the template (which is HTML content) or the plain-text
    134 explanation, one must subclass the given exception; or customize it
    135 after it has been created.  This particular breakdown of a message
    136 into explanation, detail and template allows both the creation of
    137 plain-text and html messages for various clients as well as
    138 error-free substitution of environment variables and headers.
    139 
    140 
    141 The 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``
    145 field. Reflecting this, these subclasses have two additional keyword arguments:
    146 ``location`` and ``add_slash``.
    147 
    148 Parameters:
    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 
    157 Relative URLs in the location will be resolved to absolute.
    158 
    159 References:
    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 
    167 from string import Template
    168 import re
    169 import sys
    170 
    171 from webob.compat import (
    172     class_types,
    173     text_,
    174     text_type,
    175     urlparse,
    176     )
    177 from webob.request import Request
    178 from webob.response import Response
    179 from webob.util import (
    180     html_escape,
    181     warn_deprecation,
    182     )
    183 
    184 tag_re = re.compile(r'<.*?>', re.S)
    185 br_re = re.compile(r'<br.*?>', re.I|re.S)
    186 comment_re = re.compile(r'<!--|-->')
    187 
    188 def 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 
    200 def 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 
    208 class 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 
    216 class 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 
    341 class 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 
    350 class 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 
    360 class 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 
    373 class 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 
    385 class 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 
    398 class 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 
    411 class 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 
    425 class 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 
    439 class 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 
    455 class _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>;
    473 you 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 
    502 class 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 
    517 class 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 
    530 class 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):
    545 class 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 
    558 class 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 
    573 class 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 
    588 class 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 
    604 class 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 
    620 class HTTPBadRequest(HTTPClientError):
    621     pass
    622 
    623 class 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 
    639 class 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 
    649 class 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 
    662 class 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 
    675 class 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('''\
    688 The method ${REQUEST_METHOD} is not allowed for this resource. <br /><br />
    689 ${detail}''')
    690 
    691 class 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('''\
    706 The resource could not be generated that was acceptable to your browser
    707 (content of type ${HTTP_ACCEPT}. <br /><br />
    708 ${detail}''')
    709 
    710 class 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 
    723 class 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 
    737 class 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 
    751 class 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 
    765 class 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 
    778 class 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 
    792 class 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 
    806 class 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 
    820 class 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('''\
    834 The request media type ${CONTENT_TYPE} is not supported by this server.
    835 <br /><br />
    836 ${detail}''')
    837 
    838 class 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 
    854 class 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 
    867 class 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 
    881 class 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 
    894 class 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 
    910 class 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 
    923 class 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 
    939 class 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 
    956 class 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 
    986 class 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 
   1001 class HTTPInternalServerError(HTTPServerError):
   1002     pass
   1003 
   1004 class 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('''
   1016 The request method ${REQUEST_METHOD} is not implemented for this server. <br /><br />
   1017 ${detail}''')
   1018 
   1019 class 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 
   1033 class 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 
   1047 class 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 
   1062 class 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 
   1076 class 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 
   1089 class 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 
   1102 class 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 
   1126 try:
   1127     from paste import httpexceptions
   1128 except ImportError:   # pragma: no cover
   1129     # Without Paste we don't need to do this fixup
   1130     pass
   1131 else: # 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']
   1141 status_map={}
   1142 for 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())
   1151 del name, value
   1152