Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cp3-wsgi-remix/lib/cptools.py

Revision 1243 (checked in by fumanchu, 2 years ago)

Lots of mixedCase to lower_with_underscores.

  • Property svn:eol-style set to native
Line 
1 """Functions for builtin CherryPy tools."""
2
3 import cherrypy
4 import http as _http
5
6
7 #                     Conditional HTTP request support                     #
8
9 def validate_etags(autotags=False):
10     """Validate the current ETag against If-Match, If-None-Match headers."""
11     # Guard against being run twice.
12     if hasattr(cherrypy.response, "ETag"):
13         return
14    
15     etag = cherrypy.response.headers.get('ETag')
16    
17     if (not etag) and autotags:
18         import md5
19         etag = '"%s"' % md5.new(cherrypy.response.collapse_body()).hexdigest()
20         cherrypy.response.headers['ETag'] = etag
21    
22     if etag:
23         cherrypy.response.ETag = etag
24        
25         status, reason, msg = _http.valid_status(cherrypy.response.status)
26        
27         conditions = cherrypy.request.headers.elements('If-Match') or []
28         conditions = [str(x) for x in conditions]
29         if conditions and not (conditions == ["*"] or etag in conditions):
30             if (status >= 200 and status < 299) or status == 412:
31                 raise cherrypy.HTTPError(412)
32        
33         conditions = cherrypy.request.headers.elements('If-None-Match') or []
34         conditions = [str(x) for x in conditions]
35         if conditions == ["*"] or etag in conditions:
36             if (status >= 200 and status < 299) or status == 304:
37                 if cherrypy.request.method in ("GET", "HEAD"):
38                     raise cherrypy.HTTPRedirect([], 304)
39                 else:
40                     raise cherrypy.HTTPError(412)
41
42 def validate_since():
43     """Validate the current Last-Modified against If-Modified-Since headers."""
44     lastmod = cherrypy.response.headers.get('Last-Modified')
45     if lastmod:
46         status, reason, msg = _http.valid_status(cherrypy.response.status)
47        
48         since = cherrypy.request.headers.get('If-Unmodified-Since')
49         if since and since != lastmod:
50             if (status >= 200 and status < 299) or status == 412:
51                 raise cherrypy.HTTPError(412)
52        
53         since = cherrypy.request.headers.get('If-Modified-Since')
54         if since and since == lastmod:
55             if (status >= 200 and status < 299) or status == 304:
56                 if cherrypy.request.method in ("GET", "HEAD"):
57                     raise cherrypy.HTTPRedirect([], 304)
58                 else:
59                     raise cherrypy.HTTPError(412)
60
61
62 #                                Tool code                                #
63
64 def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For'):
65     """Change the base URL (scheme://host[:port][/path]).
66     
67     Useful when running a CP server behind Apache.
68     
69     If you want the new request.base to include path info (not just the host),
70     you must explicitly set base to the full base path, and ALSO set 'local'
71     to '', so that the X-Forwarded-Host request header (which never includes
72     path info) does not override it.
73     
74     cherrypy.request.remote.ip (the IP address of the client) will be
75     rewritten if the header specified by the 'remote' arg is valid.
76     By default, 'remote' is set to 'X-Forwarded-For'. If you do not
77     want to rewrite remote.ip, set the 'remote' arg to an empty string.
78     """
79    
80     request = cherrypy.request
81    
82     if base is None:
83         port = cherrypy.local.port
84         if port == 80:
85             base = 'http://localhost'
86         else:
87             base = 'http://localhost:%s' % port
88    
89     if local:
90         base = request.headers.get(local, base)
91    
92     if base.find("://") == -1:
93         # add http:// or https:// if needed
94         base = request.base[:request.base.find("://") + 3] + base
95    
96     request.base = base
97    
98     if remote:
99         xff = request.headers.get(remote)
100         if xff:
101             if remote == 'X-Forwarded-For':
102                 # See http://bob.pythonmac.org/archives/2005/09/23/apache-x-forwarded-for-caveat/
103                 xff = xff.split(',')[-1].strip()
104             request.remote.ip = xff
105
106
107 def response_headers(headers=None):
108     """Set headers on the response."""
109     for name, value in (headers or []):
110         cherrypy.response.headers[name] = value
111
112
113 _login_screen = """<html><body>
114 Message: %(error_msg)s
115 <form method="post" action="do_login">
116     Login: <input type="text" name="login" value="%(login)s" size="10" /><br />
117     Password: <input type="password" name="password" size="10" /><br />
118     <input type="hidden" name="from_page" value="%(from_page)s" /><br />
119     <input type="submit" />
120 </form>
121 </body></html>"""
122
123 def session_auth(check_login_and_password=None, not_logged_in=None,
124                  load_user_by_username=None, session_key='username',
125                  on_login=None, on_logout=None, login_screen=None):
126     """Assert that the user is logged in."""
127    
128     if login_screen is None:
129         login_screen = _login_screen
130    
131     request = cherrypy.request
132     tdata = cherrypy.thread_data
133     sess = cherrypy.session
134     request.user = None
135     tdata.user = None
136    
137     if request.path.endswith('login_screen'):
138         return False
139     elif request.path.endswith('do_logout'):
140         login = sess.get(session_key)
141         sess[session_key] = None
142         request.user = None
143         tdata.user = None
144         if login and on_logout:
145             on_logout(login)
146         from_page = request.params.get('from_page', '..')
147         raise cherrypy.HTTPRedirect(from_page)
148     elif request.path.endswith('do_login'):
149         from_page = request.params.get('from_page', '..')
150         login = request.params['login']
151         password = request.params['password']
152         error_msg = check_login_and_password(login, password)
153         if error_msg:
154             kw = {"from_page": from_page,
155                   "login": login, "error_msg": error_msg}
156             cherrypy.response.body = login_screen % kw
157             return True
158        
159         sess[session_key] = login
160         if on_login:
161             on_login(login)
162         raise cherrypy.HTTPRedirect(from_page or "/")
163    
164     # Check if user is logged in
165     temp_user = None
166     if (not sess.get(session_key)) and not_logged_in:
167         # Call not_logged_in so that applications where anonymous user
168         #   is OK can handle it
169         temp_user = not_logged_in()
170     if (not sess.get(session_key)) and not temp_user:
171         kw = {"from_page": request.browser_url, "login": "", "error_msg": ""}
172         cherrypy.response.body = login_screen % kw
173         return True
174    
175     # Everything is OK: user is logged in
176     if load_user_by_username and not tdata.user:
177         username = temp_user or sess[session_key]
178         request.user = load_user_by_username(username)
179         tdata.user = request.user
180    
181     return False
182
183 def virtual_host(use_x_forwarded_host=True, **domains):
184     """Redirect internally based on the Host header.
185     
186     Useful when running multiple sites within one CP server.
187     
188     From http://groups.google.com/group/cherrypy-users/browse_thread/thread/f393540fe278e54d:
189     
190     For various reasons I need several domains to point to different parts of a
191     single website structure as well as to their own "homepage"   EG
192     
193     http://www.mydom1.com  ->  root
194     http://www.mydom2.com  ->  root/mydom2/
195     http://www.mydom3.com  ->  root/mydom3/
196     http://www.mydom4.com  ->  under construction page
197     
198     but also to have  http://www.mydom1.com/mydom2/  etc to be valid pages in
199     their own right.
200     """
201     if hasattr(cherrypy.request, "virtual_prefix"):
202         return
203    
204     domain = cherrypy.request.headers.get('Host', '')
205     if use_x_forwarded_host:
206         domain = cherrypy.request.headers.get("X-Forwarded-Host", domain)
207    
208     cherrypy.request.virtual_prefix = prefix = domains.get(domain, "")
209     if prefix:
210         raise cherrypy.InternalRedirect(_http.urljoin(prefix, cherrypy.request.path_info))
211
212 def log_traceback():
213     """Write the last error's traceback to the cherrypy error log."""
214     from cherrypy import _cperror
215     cherrypy.log(_cperror.format_exc(), "HTTP")
216
217 def log_request_headers():
218     """Write request headers to the cherrypy error log."""
219     h = ["  %s: %s" % (k, v) for k, v in cherrypy.request.header_list]
220     cherrypy.log('\nRequest Headers:\n' + '\n'.join(h), "HTTP")
221
222 def redirect(url=''):
223     """Raise cherrypy.HTTPRedirect to the given url."""
224     raise cherrypy.HTTPRedirect(url)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets