1
2
3
4 """
5 | This file is part of the web2py Web Framework
6 | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7 | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8
9 HTTP statuses helpers
10 --------------------------------------------
11 """
12
13 import re
14
15 __all__ = ['HTTP', 'redirect']
16
17 defined_status = {
18 200: 'OK',
19 201: 'CREATED',
20 202: 'ACCEPTED',
21 203: 'NON-AUTHORITATIVE INFORMATION',
22 204: 'NO CONTENT',
23 205: 'RESET CONTENT',
24 206: 'PARTIAL CONTENT',
25 301: 'MOVED PERMANENTLY',
26 302: 'FOUND',
27 303: 'SEE OTHER',
28 304: 'NOT MODIFIED',
29 305: 'USE PROXY',
30 307: 'TEMPORARY REDIRECT',
31 400: 'BAD REQUEST',
32 401: 'UNAUTHORIZED',
33 402: 'PAYMENT REQUIRED',
34 403: 'FORBIDDEN',
35 404: 'NOT FOUND',
36 405: 'METHOD NOT ALLOWED',
37 406: 'NOT ACCEPTABLE',
38 407: 'PROXY AUTHENTICATION REQUIRED',
39 408: 'REQUEST TIMEOUT',
40 409: 'CONFLICT',
41 410: 'GONE',
42 411: 'LENGTH REQUIRED',
43 412: 'PRECONDITION FAILED',
44 413: 'REQUEST ENTITY TOO LARGE',
45 414: 'REQUEST-URI TOO LONG',
46 415: 'UNSUPPORTED MEDIA TYPE',
47 416: 'REQUESTED RANGE NOT SATISFIABLE',
48 417: 'EXPECTATION FAILED',
49 422: 'UNPROCESSABLE ENTITY',
50 429: 'TOO MANY REQUESTS',
51 451: 'UNAVAILABLE FOR LEGAL REASONS',
52 500: 'INTERNAL SERVER ERROR',
53 501: 'NOT IMPLEMENTED',
54 502: 'BAD GATEWAY',
55 503: 'SERVICE UNAVAILABLE',
56 504: 'GATEWAY TIMEOUT',
57 505: 'HTTP VERSION NOT SUPPORTED',
58 509: 'BANDWIDTH LIMIT EXCEEDED',
59 }
60
61 regex_status = re.compile('^\d{3} [0-9A-Z ]+$')
62
63 -class HTTP(Exception):
64 """Raises an HTTP response
65
66 Args:
67 status: usually an integer. If it's a well known status code, the ERROR
68 message will be automatically added. A string can also be passed
69 as `510 Foo Bar` and in that case the status code and the error
70 message will be parsed accordingly
71 body: what to return as body. If left as is, will return the error code
72 and the status message in the body itself
73 cookies: pass cookies along (usually not needed)
74 headers: pass headers as usual dict mapping
75 """
76
77 - def __init__(
78 self,
79 status,
80 body='',
81 cookies=None,
82 **headers
83 ):
88
90 if cookies and len(cookies) > 0:
91 self.headers['Set-Cookie'] = [
92 str(cookie)[11:] for cookie in cookies.values()]
93
94 - def to(self, responder, env=None):
95 env = env or {}
96 status = self.status
97 headers = self.headers
98 if status in defined_status:
99 status = '%d %s' % (status, defined_status[status])
100 elif isinstance(status, int):
101 status = '%d UNKNOWN ERROR' % status
102 else:
103 status = str(status)
104 if not regex_status.match(status):
105 status = '500 %s' % (defined_status[500])
106 headers.setdefault('Content-Type', 'text/html; charset=UTF-8')
107 body = self.body
108 if status[:1] == '4':
109 if not body:
110 body = status
111 if isinstance(body, str):
112 headers['Content-Length'] = len(body)
113 rheaders = []
114 for k, v in headers.iteritems():
115 if isinstance(v, list):
116 rheaders += [(k, str(item)) for item in v]
117 elif not v is None:
118 rheaders.append((k, str(v)))
119 responder(status, rheaders)
120 if env.get('request_method', '') == 'HEAD':
121 return ['']
122 elif isinstance(body, str):
123 return [body]
124 elif hasattr(body, '__iter__'):
125 return body
126 else:
127 return [str(body)]
128
129 @property
131 """
132 compose a message describing this exception
133
134 "status defined_status [web2py_error]"
135
136 message elements that are not defined are omitted
137 """
138 msg = '%(status)s'
139 if self.status in defined_status:
140 msg = '%(status)s %(defined_status)s'
141 if 'web2py_error' in self.headers:
142 msg += ' [%(web2py_error)s]'
143 return msg % dict(
144 status=self.status,
145 defined_status=defined_status.get(self.status),
146 web2py_error=self.headers.get('web2py_error'))
147
149 "stringify me"
150 return self.message
151
152
153 -def redirect(location='', how=303, client_side=False):
154 """Raises a redirect (303)
155
156 Args:
157 location: the url where to redirect
158 how: what HTTP status code to use when redirecting
159 client_side: if set to True, it triggers a reload of the entire page
160 when the fragment has been loaded as a component
161 """
162 if location:
163 from gluon import current
164 loc = location.replace('\r', '%0D').replace('\n', '%0A')
165 if client_side and current.request.ajax:
166 raise HTTP(200, **{'web2py-redirect-location': loc})
167 else:
168 raise HTTP(how,
169 'You are being redirected <a href="%s">here</a>' % loc,
170 Location=loc)
171 else:
172 from gluon import current
173 if client_side and current.request.ajax:
174 raise HTTP(200, **{'web2py-component-command': 'window.location.reload(true)'})
175