Package wtf :: Package app :: Module http_response
[hide private]
[frames] | no frames]

Source Code for Module wtf.app.http_response

  1  # -*- coding: ascii -*- 
  2  # 
  3  # Copyright 2006-2012 
  4  # Andr\xe9 Malo or his licensors, as applicable 
  5  # 
  6  # Licensed under the Apache License, Version 2.0 (the "License"); 
  7  # you may not use this file except in compliance with the License. 
  8  # You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 17  """ 
 18  Response codes 
 19  ============== 
 20   
 21  This module stores HTTP response codes. 
 22   
 23  :Variables: 
 24   - `classes`: Mapping status code -> HTTPResponse 
 25     (``{status: HTTPResponse, ...}``) 
 26   - `reasons`: Mapping status code -> reason phrase 
 27     (``{status: 'reason', ...}``) 
 28   
 29  :Types: 
 30   - `classes`: ``dict`` 
 31   - `reasons`: ``dict`` 
 32  """ 
 33  __author__ = u"Andr\xe9 Malo" 
 34  __docformat__ = "restructuredtext en" 
 35   
 36  from wtf import webutil as _webutil 
 37   
 38   
39 -class HTTPResponse(SystemExit):
40 """ 41 Base HTTP error response exception class 42 43 The exception is derived from `SystemExit` on purpose - that way it 44 should wind up the whole try-except stack (if well-written: nobody should 45 swallow `SystemExit`) until explicitly caught. 46 47 :CVariables: 48 - `_FRAME`: Frame around the actual message 49 - `status`: HTTP response status 50 - `reason`: HTTP response reason phrase 51 52 :IVariables: 53 - `message`: Message template 54 - `param`: Additional parameters for response template fill-in 55 - `_escaped`: Already escaped parameters 56 - `_content_type`: Content-Type 57 - `_replace`: Replace parameters in message? 58 59 :Types: 60 - `_FRAME`: ``str`` 61 - `status`: ``int`` 62 - `reason`: ``str`` 63 - `message`: ``str`` 64 - `param`: ``dict`` 65 - `_escaped`: ``dict`` 66 - `_content_type`: ``str`` 67 - `_replace`: ``bool`` 68 """ 69 status, reason, message = None, None, None 70 _FRAME = """ 71 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> 72 <html><head> 73 <title>%%(status)s %%(reason)s</title> 74 </head><body> 75 <h1>%%(reason)s</h1> 76 %s 77 </body></html> 78 """.strip() 79
80 - def __init__(self, request, message=None, 81 content_type='text/html; charset=us-ascii', replace=True, 82 **param):
83 """ 84 Initialization 85 86 :Parameters: 87 - `request`: Request object 88 - `message`: message template 89 - `content_type`: Response content type 90 - `replace`: Replace parameters in message? 91 - `param`: Additional parameters for response template fill-in 92 93 :Types: 94 - `request`: `wtf.app.request.Request` 95 - `message`: ``str`` 96 - `content_type`: ``str`` 97 - `replace`: ``bool`` 98 - `param`: ``dict`` 99 """ 100 SystemExit.__init__(self, 0) 101 if message is not None: 102 self.message = message 103 elif self.message is not None: 104 self.message = self._FRAME % self.message 105 self._content_type = content_type 106 self._replace = bool(replace) 107 self._request = request 108 self.param, self._escaped = self.init(**param) 109 url = request.url.copy() 110 url.scheme, url.netloc = u'', u'' 111 self.param.setdefault('url', str(url)) 112 self.param.setdefault('method', str(request.method))
113
114 - def init(self):
115 """ 116 Custom initializer and easy check for required parameters 117 118 :return: tuple of unescaped and escaped parameters (``(dict, dict)``) 119 :rtype: ``tuple`` 120 """ 121 return {}, {}
122
123 - def headers(self, collection):
124 """ Modify response headers """ 125 collection.set('content-type', self._content_type)
126
127 - def body(self):
128 """ 129 Compute the response body 130 131 :return: The response body 132 :rtype: ``str`` 133 """ 134 if self.message is None: 135 return "" 136 elif not self._replace: 137 return self.message 138 param = dict(self.param) 139 param.update({'status': str(self.status), 'reason': str(self.reason)}) 140 param = dict((key, _webutil.escape_html(str(val))) 141 for key, val in param.iteritems()) 142 param.update(self._escaped) 143 return self.message % param
144 145
146 -class Continue(HTTPResponse):
147 """ 100 Continue (RFC 2616) """ 148 status, reason, message = 100, "Continue", None
149 150
151 -class SwitchingProtocols(HTTPResponse):
152 """ 101 Switching Protocols (RFC 2616) """ 153 status, reason, message = 101, "Switching Protocols", None
154 155
156 -class Processing(HTTPResponse):
157 """ 102 Processing (RFC 2518) """ 158 status, reason, message = 102, "Processing", None
159 160
161 -class OK(HTTPResponse):
162 """ 200 OK (RFC 2616) """ 163 status, reason, message = 200, "OK", None
164 165 166 # pylint: disable = W0221 167
168 -class Created(HTTPResponse):
169 """ 201 Created (RFC 2616) """ 170 status, reason = 201, "Created" 171 message = """ 172 <p>A new resource has been created and is available under the following 173 URI(s):</p> 174 <ul> 175 <li><a href="%(location)s">%(location)s</a></li> 176 %(uris)s 177 </ul> 178 """.strip() 179
180 - def init(self, location, additional=()):
181 """ 182 Add main location and (optional) less significant URIs 183 184 :Parameters: 185 - `location`: Main location of the created resource 186 - `additional`: List of additional (less significant) locations 187 (``('uri', ...)``) 188 189 :Types: 190 - `location`: ``str`` 191 - `additional`: ``iterable`` 192 193 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 194 :rtype: ``tuple`` 195 """ 196 urilist = "\n".join("<li><a href=\"%(uri)s\">%(uri)s</a></li>" % 197 _webutil.escape_html(uri) for uri in additional) 198 return dict(location=location, uris=additional), dict(uris=urilist)
199
200 - def headers(self, collection):
201 """ Modify response headers """ 202 HTTPResponse.headers(self, collection) 203 collection.set('location', self.param['location'])
204 205
206 -class Accepted(HTTPResponse):
207 """ 202 Accepted (RFC 2616) """ 208 status, reason = 202, "Accepted" 209 message = """ 210 <p>Your request has been accepted and may be processed later.</p> 211 """.strip()
212 213
214 -class NonAuthoritativeInformation(HTTPResponse):
215 """ 203 Non-Authoritative Information (RFC 2616) """ 216 status, reason, message = 203, "Non-Authoritative Information", None
217 218
219 -class NoContent(HTTPResponse):
220 """ 204 No Content (RFC 2616) """ 221 status, reason, message = 204, "No Content", None
222 223
224 -class ResetContent(HTTPResponse):
225 """ 205 Reset Content (RFC 2616) """ 226 status, reason, message = 205, "Reset Content", None
227 228
229 -class PartialContent(HTTPResponse):
230 """ 206 Partial Content (RFC 2616) """ 231 status, reason, message = 206, "Partial Content", None
232 233
234 -class MultiStatus(HTTPResponse):
235 """ 207 Multi-Status (RFC 2518) """ 236 status, reason, message = 207, "Multi-Status", None
237 238
239 -class MultipleChoices(HTTPResponse):
240 """ 300 Multiple Choices (RFC 2616) """ 241 status, reason = 300, "Multiple Choices" 242 message = """ 243 <p>Multiple representations available:</p> 244 <ul> 245 %(variants)s 246 </ul> 247 """.strip() 248
249 - def init(self, variants):
250 """ 251 Add list of variants 252 253 :Parameters: 254 - `variants`: List of choosable variants (``('uri', ...)``) 255 256 :Types: 257 - `variants`: ``iterable`` 258 259 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 260 :rtype: ``tuple`` 261 """ 262 variantlist = "\n".join("<li>%(variant)s</li>" % 263 _webutil.escape_html(variant) for variant in variants) 264 return dict(variants=variants), dict(variants=variantlist)
265 266
267 -class _BaseRedirect(HTTPResponse):
268 """ Base redirect class """ 269 message = """ 270 <p>The document has moved <a href="%(location)s">here</a>.</p> 271 """.strip() 272
273 - def init(self, location):
274 """ 275 Ensure location parameter 276 277 :Parameters: 278 - `location`: New location 279 280 :Types: 281 - `location`: ``str`` 282 283 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 284 :rtype: ``tuple`` 285 """ 286 return dict(location=location), {}
287
288 - def headers(self, collection):
289 """ Modify response headers """ 290 HTTPResponse.headers(self, collection) 291 collection.set('location', self.param['location'])
292 293
294 -class HTTPRedirectResponse(_BaseRedirect):
295 """ Subclass for type identification of automatic redirects """
296 297
298 -class MovedPermanently(HTTPRedirectResponse):
299 """ 301 Moved Permanently (RFC 2616) """ 300 status, reason = 301, "Moved Permanently"
301 302
303 -class Found(HTTPRedirectResponse):
304 """ 302 Found (RFC 2616) """ 305 status, reason = 302, "Found"
306 307
308 -class SeeOther(HTTPRedirectResponse):
309 """ 303 See Other (RFC 2616) """ 310 status, reason = 303, "See Other" 311 message = """ 312 <p>The answer to your request is located <a href="%(location)s">here</a>.</p> 313 """.strip()
314 315
316 -class NotModified(HTTPResponse):
317 """ 304 Not Modified (RFC 2616) """ 318 status, reason, message = 304, "Not Modified", None
319 320
321 -class UseProxy(_BaseRedirect):
322 """ 305 Use Proxy (RFC 2616) """ 323 status, reason = 305, "Use Proxy" 324 message = """ 325 <p>This resource is only accessible through the proxy 326 %(location)s<br> 327 You will need to configure your client to use that proxy.</p> 328 """.strip()
329 330
331 -class Unused306(HTTPResponse):
332 """ 306 (Unused) (RFC 2616) """ 333 status, reason, message = 306, "Unused", "<p>Unused Status Code</p>"
334 335
336 -class TemporaryRedirect(HTTPRedirectResponse):
337 """ 307 Temporary Redirect (RFC 2616) """ 338 status, reason = 307, "Temporary Redirect"
339 340
341 -class BadRequest(HTTPResponse):
342 """ 400 Bad Request (RFC 2616) """ 343 status, reason = 400, "Bad Request" 344 message = """ 345 <p>Your browser sent a request that this server could not understand.<br> 346 %(hint)s</p> 347 """.strip() 348
349 - def init(self, hint=None):
350 """ 351 Add an error hint 352 353 :Parameters: 354 - `hint`: Optional hint describing the error (unescaped HTML) 355 356 :Types: 357 - `hint`: ``str`` 358 359 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 360 :rtype: ``tuple`` 361 """ 362 hint_esc = hint and _webutil.escape_html(hint) or "" 363 return dict(hint=hint), dict(hint=hint_esc)
364 365
366 -class AuthorizationRequired(HTTPResponse):
367 """ 401 Authorization Required (RFC 2616) """ 368 status, reason = 401, "Authorization Required" 369 message = """ 370 <p>This server could not verify that you 371 are authorized to access the document 372 requested. Either you supplied the wrong 373 credentials (e.g., bad password), or your 374 browser doesn't understand how to supply 375 the credentials required.</p> 376 """.strip() 377
378 - def init(self, auth_type, realm):
379 """ 380 Add auth type and realm 381 382 :Parameters: 383 - `auth_type`: Authentication Type (like 'Basic', see RFC 2617) 384 - `realm`: Authentication realm 385 386 :Types: 387 - `auth_type`: ``str`` 388 - `realm`: ``str`` 389 390 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 391 :rtype: ``tuple`` 392 """ 393 assert auth_type.lower() == 'basic', \ 394 'Only basic authentication supported yet' 395 return dict(auth_type=auth_type, realm=realm), {}
396
397 - def headers(self, collection):
398 """ Modify response headers """ 399 HTTPResponse.headers(self, collection) 400 collection.set('WWW-Authenticate', '%s realm="%s"' % ( 401 self.param['auth_type'], self.param['realm'].replace('"', '\\"') 402 ))
403 404
405 -class PaymentRequired(HTTPResponse):
406 """ 402 Payment Required (RFC 2616) """ 407 status, reason = 402, "Payment Required" 408 message = """ 409 <p>This resource requires payment.</p> 410 """.strip()
411 412
413 -class Forbidden(HTTPResponse):
414 """ 403 Forbidden (RFC 2616) """ 415 status, reason = 403, "Forbidden" 416 message = """ 417 <p>You don't have permission to access %(url)s 418 non this server.</p> 419 """.strip()
420 421
422 -class NotFound(HTTPResponse):
423 """ 404 Not Found (RFC 2616) """ 424 status, reason = 404, "Not Found" 425 message = """ 426 <p>The requested URL %(url)s was not found on this server.</p> 427 """.strip()
428 429
430 -class MethodNotAllowed(HTTPResponse):
431 """ 405 Method Not Allowed """ 432 status, reason = 405, "Method Not Allowed" 433 message = """ 434 <p>The requested method %(method)s is not allowed for the URL %(url)s.</p> 435 """.strip() 436
437 - def init(self, allowed):
438 """ 439 Add the allowed method list 440 441 :Parameters: 442 - `allowed`: List of allowed methods (``('method', ...)``) 443 444 :Types: 445 - `allowed`: ``iterable`` 446 447 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 448 :rtype: ``tuple`` 449 """ 450 allowed = list(sorted(set(allowed))) 451 allowed_esc = '\n'.join("<li>%s</li>" % _webutil.escape_html(method) 452 for method in allowed) 453 return dict(allowed=allowed), dict(allowed=allowed_esc)
454
455 - def headers(self, collection):
456 """ Modify response headers """ 457 HTTPResponse.headers(self, collection) 458 collection.set('allow', ', '.join(self.param['allowed']))
459 460
461 -class NotAcceptable(HTTPResponse):
462 """ 406 Not Acceptable (RFC 2616) """ 463 status, reason = 406, "Not Acceptable" 464 message = """ 465 <p>An appropriate representation of the requested resource %(url)s 466 could not be found on this server.</p> 467 Available variants: 468 <ul> 469 %(variants)s 470 </ul> 471 """.strip() 472
473 - def init(self, variants, descriptions=None):
474 """ 475 Add the variant list 476 477 :Parameters: 478 - `variants`: The variant URLs 479 - `descriptions`: variant descriptions, the url is the key 480 481 :Types: 482 - `variants`: ``iterable`` 483 - `descriptions`: ``dict`` 484 485 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 486 :rtype: ``tuple`` 487 """ 488 if descriptions is None: 489 descriptions = {} 490 variants_esc = '\n'.join( 491 '<li><a href="%(var)s">%(var)s</a>%(desc)s</li>' % dict( 492 var=_webutil.escape_html(var), 493 desc=descriptions.get(var) and 494 (", " + _webutil.escape_html(descriptions[var])) or "", 495 ) for var in variants) 496 return ( 497 dict(variants=variants, descriptions=descriptions), 498 dict(variants=variants_esc, descriptions="") 499 )
500 501
502 -class ProxyAuthenticationRequired(AuthorizationRequired):
503 """ 407 Proxy Authentication Required (RFC 2616) """ 504 status, reason = 407, "Proxy Authentication Required"
505 506
507 -class RequestTimeout(HTTPResponse):
508 """ 408 Request Timeout (RFC 2616) """ 509 status, reason = 408, "Request Timeout" 510 message = """ 511 <p>Server timeout waiting for the HTTP request from the client.</p> 512 """.strip()
513 514
515 -class Conflict(HTTPResponse):
516 """ 409 Conflict (RFC 2616) """ 517 status, reason = 409, "Conflict" 518 message = """ 519 <p>The request could not be completed due to a conflict with the current 520 state of the resource.</p>%(desc)s 521 """.strip() 522
523 - def init(self, desc=None):
524 """ 525 Optionally add a conflict description 526 527 :Parameters: 528 - `desc`: Optional conflict description 529 530 :Types: 531 - `desc`: ``str`` 532 533 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 534 :rtype: ``tuple`` 535 """ 536 desc_esc = desc and "\n<p>%s</p>" % _webutil.escape_html(desc) or "" 537 return dict(desc=desc), dict(desc=desc_esc)
538 539
540 -class Gone(HTTPResponse):
541 """ 410 Gone (RFC 2616) """ 542 status, reason = 410, "Gone" 543 message = """ 544 <p>The requested resource<br>%(url)s<br> 545 is no longer available on this server and there is no forwarding address. 546 Please remove all references to this resource.</p> 547 """.strip()
548 549
550 -class LengthRequired(HTTPResponse):
551 """ 411 Length Required (RFC 2616) """ 552 status, reason = 411, "Length Required" 553 message = """ 554 <p>A request of the requested method %(method)s requires a valid 555 Content-length.%(desc)s</p> 556 """.strip() 557
558 - def init(self, desc=None):
559 """ 560 Add optional description 561 562 :Parameters: 563 - `desc`: Optional additional description 564 565 :Types: 566 - `desc`: ``str`` 567 568 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 569 :rtype: ``tuple`` 570 """ 571 desc_esc = desc and ("<br>\n" + _webutil.escape_html(desc)) or "" 572 return dict(desc=desc), dict(desc=desc_esc)
573 574
575 -class PreconditionFailed(HTTPResponse):
576 """ 412 Precondition Failed (RFC 2616) """ 577 status, reason = 412, "Precondition Failed" 578 message = """ 579 <p>The precondition on the request for the URL %(url)s evaluated to false.</p> 580 """.strip()
581 582
583 -class RequestEntityTooLarge(HTTPResponse):
584 """ 413 Request Entity Too Large (RFC 2616) """ 585 status, reason = 413, "Request Entity Too Large" 586 message = """ 587 <p>The requested resource<br>%(url)s<br> 588 does not allow request data with %(method)s requests, or the amount of data 589 provided in the request exceeds the capacity limit.</p> 590 """.strip()
591 592
593 -class RequestURITooLong(HTTPResponse):
594 """ 414 Request-URI Too Long (RFC 2616) """ 595 status, reason = 414, "Request-URI Too Long" 596 message = """ 597 <p>The requested URL's length exceeds the capacity 598 limit for this server.%(desc)s</p> 599 """.strip() 600
601 - def init(self, desc=None):
602 """ 603 Optionally add a error description 604 605 :Parameters: 606 - `desc`: Optional error description 607 608 :Types: 609 - `desc`: ``str`` 610 611 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 612 :rtype: ``tuple`` 613 """ 614 desc_esc = desc and "<br>\n%s" % _webutil.escape_html(desc) or "" 615 return dict(desc=desc), dict(desc=desc_esc)
616 617
618 -class UnsupportedMediaType(HTTPResponse):
619 """ 415 Unsupported Media Type (RFC 2616) """ 620 status, reason = 415, "Unsupported Media Type" 621 message = """ 622 <p>The supplied request data is not in a format 623 acceptable for processing by this resource.</p> 624 """.strip()
625 626
627 -class RequestRangeNotSatisfiable(HTTPResponse):
628 """ 416 Request Range Not Satisfiable (RFC 2616) """ 629 status, reason = 416, "Request Range Not Satisfiable" 630 message = """ 631 <p>None of the range-specifier values in the Range 632 request-header field overlap the current extent 633 of the selected resource.</p> 634 """.strip()
635 636
637 -class ExpectationFailed(HTTPResponse):
638 """ 417 Expectation Failed (RFC 2616) """ 639 status, reason = 417, "Expectation Failed" 640 message = """ 641 <p>The expectation given in an Expect request-header field could not be met 642 by this server.</p> 643 """.strip()
644 645
646 -class UnprocessableEntity(HTTPResponse):
647 """ 422 Unprocessable Entity (RFC 2518) """ 648 status, reason = 422, "Unprocessable Entity" 649 message = """ 650 <p>The server understands the media type of the 651 request entity, but was unable to process the 652 contained instructions.</p> 653 """.strip()
654 655
656 -class Locked(HTTPResponse):
657 """ 423 Locked (RFC 2518) """ 658 status, reason = 423, "Locked" 659 message = """ 660 <p>The requested resource is currently locked. 661 The lock must be released or proper identification 662 given before the method can be applied.</p> 663 """.strip()
664 665
666 -class FailedDependency(HTTPResponse):
667 """ 424 Failed Dependency (RFC 2518) """ 668 status, reason = 424, "Failed Dependency" 669 message = """ 670 <p>The method could not be performed on the resource 671 because the requested action depended on another 672 action and that other action failed.</p> 673 """.strip()
674 675
676 -class UpgradeRequired(HTTPResponse):
677 """ 426 Upgrade Required (RFC 2817) """ 678 status, reason = 426, "Upgrade Required" 679 message = """ 680 <p>The requested resource can only be retrieved 681 using SSL. The server is willing to upgrade the current 682 connection to SSL, but your client doesn't support it. 683 Either upgrade your client, or try requesting the page 684 using https://.</p> 685 """.strip() 686
687 - def init(self, tokens):
688 """ 689 Add upgrade tokens 690 691 :Parameters: 692 - `tokens`: List of upgrade tokens to advertise 693 694 :Types: 695 - `tokens`: ``iterable`` 696 697 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 698 :rtype: ``tuple`` 699 """ 700 tokens_esc = _webutil.escape_html(", ".join(tokens)) 701 return dict(tokens=tokens), dict(tokens=tokens_esc)
702
703 - def headers(self, collection):
704 """ Modify response headers """ 705 HTTPResponse.headers(self, collection) 706 collection.add('connection', 'upgrade') 707 collection.set('upgrade', ', '.join(self.param['tokens']))
708 709
710 -class InternalServerError(HTTPResponse):
711 """ 500 Internal Server Error (RFC 2616) """ 712 status, reason = 500, "Internal Server Error" 713 message = """ 714 <p>The server encountered an internal error or 715 misconfiguration and was unable to complete 716 your request.</p> 717 <p>Please contact the server administrator,%(admin)s and inform them of the 718 time the error occurred, 719 and anything you might have done that may have 720 caused the error.</p> 721 <p>More information about this error may be available 722 in the server error log.</p> 723 """.strip() 724
725 - def init(self, admin=None):
726 """ 727 Add optional admin address 728 729 :Parameters: 730 - `admin`: The optional admin contact address 731 732 :Types: 733 - `admin`: ``str`` 734 735 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 736 :rtype: ``tuple`` 737 """ 738 admin_esc = admin and (" " + _webutil.escape_html(admin)) or "" 739 return dict(admin=admin), dict(admin=admin_esc)
740 741
742 -class NotImplemented(HTTPResponse): # pylint: disable = W0622
743 """ 501 Not Implemented (RFC 2616) """ 744 status, reason = 501, "Not Implemented" 745 message = """ 746 <p>%(method)s to %(url)s not supported.<br> 747 %(desc)s</p> 748 """.strip() 749
750 - def init(self, desc=None):
751 """ 752 Add optional description 753 754 :Parameters: 755 - `desc`: Optional error description 756 757 :Types: 758 - `desc`: ``str`` 759 760 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 761 :rtype: ``tuple`` 762 """ 763 desc_esc = desc and _webutil.escape_html(desc) or "" 764 return dict(desc=desc), dict(desc=desc_esc)
765 766
767 -class BadGateway(HTTPResponse):
768 """ 502 Bad Gateway (RFC 2616) """ 769 status, reason = 502, "Bad Gateway" 770 message = """ 771 <p>The proxy server received an invalid 772 response from an upstream server.<br>%(desc)s</p> 773 """.strip() 774
775 - def init(self, desc=None):
776 """ 777 Optionally add a error description 778 779 :Parameters: 780 - `desc`: Optional error description 781 782 :Types: 783 - `desc`: ``str`` 784 785 :return: Tuple of unescaped and escaped parameters (``(dict, dict)``) 786 :rtype: ``tuple`` 787 """ 788 desc_esc = desc and _webutil.escape_html(desc) or "" 789 return dict(desc=desc), dict(desc=desc_esc)
790 791
792 -class ServiceUnavailable(HTTPResponse):
793 """ 503 Service Unavailable (RFC 2616) """ 794 status, reason = 503, "Service Unavailable" 795 message = """ 796 <p>The server is temporarily unable to service your 797 request due to maintenance downtime or capacity 798 problems. Please try again later.</p> 799 """.strip()
800 801
802 -class GatewayTimeout(HTTPResponse):
803 """ 504 Gateway Timeout (RFC 2616) """ 804 status, reason = 504, "Gateway Timeout" 805 message = """ 806 <p>The proxy server did not receive a timely response 807 from the upstream server.</p> 808 """.strip()
809 810
811 -class HTTPVersionNotSupported(HTTPResponse):
812 """ 505 HTTP Version Not Supported (RFC 2616) """ 813 status, reason = 505, "HTTP Version Not Supported" 814 message = """ 815 <p>The HTTP version used in the request is not suppored.</p> 816 """.strip()
817 818
819 -class VariantAlsoNegotiates(HTTPResponse):
820 """ 506 Variant Also Negotiates (RFC 2295) """ 821 status, reason = 506, "Variant Also Negotiates" 822 message = """ 823 <p>A variant for the requested resource %(url)s is itself a negotiable 824 resource. This indicates a configuration error.</p> 825 """.strip()
826 827
828 -class InsufficientStorage(HTTPResponse):
829 """ 507 Insufficient Storage (RFC 2518) """ 830 status, reason = 507, "Insufficient Storage" 831 message = """ 832 <p>The method could not be performed on the resource 833 because the server is unable to store the 834 representation needed to successfully complete the 835 request. There is insufficient free space left in 836 your storage allocation.</p> 837 """.strip()
838 839
840 -class NotExtended(HTTPResponse):
841 """ 510 Not Extended (RFC 2774) """ 842 status, reason = 510, "Not Extended" 843 message = """ 844 <p>A mandatory extension policy in the request is not 845 accepted by the server for this resource.</p> 846 """.strip()
847 848
849 -def classes(space):
850 """ 851 Compute the mapping status code -> status class 852 853 :Parameters: 854 - `space`: Namespace to inspect 855 856 :Types: 857 - `space`: ``dict`` 858 859 :return: The mapping (``{status: HTTPResponse, ...}``) 860 :rtype: ``dict`` 861 """ 862 def _issubclass(inner, outer): 863 """ Determine subclassness """ 864 try: 865 return issubclass(inner, outer) 866 except TypeError: 867 return False
868 return dict((cls.status, cls) for cls in space.values() 869 if _issubclass(cls, HTTPResponse) and cls.status) 870 classes = classes(globals()) 871 872
873 -def reasons():
874 """ 875 Compute the mapping status code -> reason phrase 876 877 :return: The mapping (``{status: 'reason', ...}``) 878 :rtype: ``dict`` 879 """ 880 return dict((status, cls.reason) 881 for status, cls in classes.iteritems())
882 reasons = reasons() 883