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

root/branches/cherrypy-2.1/cherrypy/_cperror.py

Revision 766 (checked in by fumanchu, 3 years ago)

Fix for #372 (browserUrl and querystrings).

Line 
1 """
2 Copyright (c) 2004, CherryPy Team (team@cherrypy.org)
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8     * Redistributions of source code must retain the above copyright notice,
9       this list of conditions and the following disclaimer.
10     * Redistributions in binary form must reproduce the above copyright notice,
11       this list of conditions and the following disclaimer in the documentation
12       and/or other materials provided with the distribution.
13     * Neither the name of the CherryPy Team nor the names of its contributors
14       may be used to endorse or promote products derived from this software
15       without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 """
28
29 import urllib
30
31 class Error(Exception):
32     pass
33
34 class NotReady(Error):
35     """A request was made before the app server has been started."""
36     pass
37
38 class WrongResponseType(Error):
39     """ Happens when the cherrypy.response.body is not a string """
40     pass
41
42 class WrongUnreprValue(Error):
43     """ Happens when unrepr can't parse a value """
44     pass
45
46 class WrongConfigValue(Error):
47     """ Happens when unrepr can't parse a config value """
48     pass
49
50 class RequestHandled(Exception):
51     """Exception raised when no further request handling should occur."""
52     pass
53
54 class InternalRedirect(Exception):
55     """Exception raised when processing should be handled by a different path.
56     
57     If you supply 'params', it will be used to re-populate paramMap.
58     If 'params' is a dict, it will be used directly.
59     If 'params' is a string, it will be converted to a dict using cgi.parse_qs.
60     
61     If you omit 'params', the paramMap from the original request will
62     remain in effect, including any POST parameters.
63     """
64    
65     def __init__(self, path, params=None):
66         import cherrypy
67         import cgi
68         request = cherrypy.request
69        
70         # Set a 'path' member attribute so that code which traps this
71         # error can have access to it.
72         self.path = path
73        
74         if params is not None:
75             if isinstance(params, basestring):
76                 request.queryString = params
77                 pm = cgi.parse_qs(params, keep_blank_values=True)
78                 for key, val in pm.items():
79                     if len(val) == 1:
80                         pm[key] = val[0]
81                 request.paramMap = pm
82             else:
83                 request.queryString = urllib.urlencode(params)
84                 request.paramMap = params.copy()
85        
86         request.browserUrl = request.base + path
87         if request.queryString:
88             request.browserUrl += '?' + request.queryString
89
90
91
92 class HTTPRedirect(Exception):
93     """Exception raised when the request should be redirected.
94     
95     The new URL must be passed as the first argument to the Exception, e.g.,
96         cperror.HTTPRedirect(newUrl). Multiple URLs are allowed. If a URL
97         is absolute, it will be used as-is. If it is relative, it is assumed
98         to be relative to the current cherrypy.request.path.
99     """
100    
101     def __init__(self, urls, status=None):
102         import urlparse
103         import cherrypy
104        
105         if isinstance(urls, basestring):
106             urls = [urls]
107        
108         abs_urls = []
109         for url in urls:
110             # Note that urljoin will "do the right thing" whether url is:
111             #  1. a complete URL with host (e.g. "http://www.dummy.biz/test")
112             #  2. a URL relative to root (e.g. "/dummy")
113             #  3. a URL relative to the current path
114             # Note that any querystring in browserUrl will be discarded.
115             url = urlparse.urljoin(cherrypy.request.browserUrl, url)
116             abs_urls.append(url)
117         self.urls = abs_urls
118        
119         # RFC 2616 indicates a 301 response code fits our goal; however,
120         # browser support for 301 is quite messy. Do 302 instead. See
121         # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html
122         if status is None:
123             if cherrypy.response.version >= "1.1":
124                 status = 303
125             else:
126                 status = 302
127         else:
128             status = int(status)
129             if status < 300 or status > 399:
130                 raise ValueError("status must be between 300 and 399.")
131        
132         self.status = status
133    
134     def set_response(self):
135         import cherrypy
136         cherrypy.response.status = status = self.status
137         cherrypy.response.headerMap['Content-Type'] = "text/html"
138        
139         if status in (300, 301, 302, 303, 307):
140             # "The ... URI SHOULD be given by the Location field
141             # in the response."
142             cherrypy.response.headerMap['Location'] = self.urls[0]
143            
144             # "Unless the request method was HEAD, the entity of the response
145             # SHOULD contain a short hypertext note with a hyperlink to the
146             # new URI(s)."
147             msg = {300: "This resource can be found at <a href='%s'>%s</a>.",
148                    301: "This resource has permanently moved to <a href='%s'>%s</a>.",
149                    302: "This resource resides temporarily at <a href='%s'>%s</a>.",
150                    303: "This resource can be found at <a href='%s'>%s</a>.",
151                    307: "This resource has moved temporarily to <a href='%s'>%s</a>.",
152                    }[status]
153             cherrypy.response.body = "<br />\n".join([msg % (url, url)
154                                                  for url in self.urls])
155         elif status == 304:
156             # Not Modified.
157             # "The response MUST include the following header fields:
158             # Date, unless its omission is required by section 14.18.1"
159             # The "Date" header should have been set in Request.__init__
160            
161             # "The 304 response MUST NOT contain a message-body."
162             cherrypy.response.body = []
163         elif status == 305:
164             # Use Proxy.
165             # self.urls[0] should be the URI of the proxy.
166             cherrypy.response.headerMap['Location'] = self.urls[0]
167             cherrypy.response.body = []
168         else:
169             raise ValueError("The %s status code is unknown." % status)
170
171
172 class HTTPError(Error):
173     """ Exception used to return an HTTP error code to the client.
174         This exception will automatically set the response status and body.
175         
176         A custom message (a long description to display in the browser)
177         can be provided in place of the default.
178     """
179    
180     def __init__(self, status=500, message=None):
181         self.status = status = int(status)
182         if status < 400 or status > 599:
183             raise ValueError("status must be between 400 and 599.")
184         self.message = message
185    
186     def set_response(self):
187         import cherrypy
188         from cherrypy._cputil import getErrorPage, formatExc
189        
190         tb = formatExc()
191         if cherrypy.config.get('server.logTracebacks', True):
192             cherrypy.log(tb)
193        
194         defaultOn = (cherrypy.config.get('server.environment') == 'development')
195         if not cherrypy.config.get('server.showTracebacks', defaultOn):
196             tb = None
197        
198         # In all cases, finalize will be called after this method,
199         # so don't bother cleaning up response values here.
200         cherrypy.response.status = self.status
201         cherrypy.response.body = getErrorPage(self.status, traceback=tb,
202                                               message=self.message)
203        
204         if cherrypy.response.headerMap.has_key("Content-Encoding"):
205             del cherrypy.response.headerMap['Content-Encoding']
206    
207     def __str__(self):
208         import cherrypy
209         return "%s: %s" % (self.status, self.message or "")
210
211
212 class NotFound(HTTPError):
213     """ Happens when a URL couldn't be mapped to any class.method """
214    
215     def __init__(self, path=None):
216         if path is None:
217             import cherrypy
218             path = cherrypy.request.path
219         self.args = (path,)
220         HTTPError.__init__(self, 404, "The path %s was not found." % repr(path))
221
222
223 class InternalError(HTTPError):
224     """ Error that should never happen """
225    
226     def __init__(self, message=None):
227         HTTPError.__init__(self, 500, message)
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets