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

root/tags/cherrypy-2.1.0-rc1/cherrypy/_cperror.py

Revision 650 (checked in by mikerobi, 3 years ago)

implimented changes needed to re-close ticket:288

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 import os
31
32
33 class Error(Exception):
34     pass
35
36 class InternalError(Error):
37     """ Error that should never happen """
38     pass
39
40 class NotReady(Error):
41     """A request was made before the app server has been started."""
42     pass
43
44 class WrongResponseType(Error):
45     """ Happens when the cherrypy.response.body is not a string """
46     pass
47
48 class WrongUnreprValue(Error):
49     """ Happens when unrepr can't parse a value """
50     pass
51
52 class WrongConfigValue(Error):
53     """ Happens when unrepr can't parse a config value """
54     pass
55
56 class RequestHandled(Exception):
57     """Exception raised when no further request handling should occur."""
58     pass
59
60 class InternalRedirect(Exception):
61     """Exception raised when processing should be handled by a different path.
62     
63     If you supply 'params', it will be used to re-populate paramMap.
64     If 'params' is a dict, it will be used directly.
65     If 'params' is a string, it will be converted to a dict using cgi.parse_qs.
66     
67     If you omit 'params', the paramMap from the original request will
68     remain in effect, including any POST parameters.
69     """
70    
71     def __init__(self, path, params=None):
72         import cherrypy
73         import cgi
74        
75         # Set a 'path' member attribute so that code which traps this
76         # error can have access to it.
77         self.path = path
78        
79         if params is not None:
80             if isinstance(params, basestring):
81                 cherrypy.request.queryString = params
82                 pm = cgi.parse_qs(params, keep_blank_values=True)
83                 for key, val in pm.items():
84                     if len(val) == 1:
85                         pm[key] = val[0]
86                 cherrypy.request.paramMap = pm
87             else:
88                 cherrypy.request.queryString = urllib.urlencode(params)
89                 cherrypy.request.paramMap = params.copy()
90        
91         cherrypy.request.browserUrl = cherrypy.request.base + path
92
93
94
95 class HTTPRedirect(Exception):
96     """Exception raised when the request should be redirected.
97     
98     The new URL must be passed as the first argument to the Exception, e.g.,
99         cperror.HTTPRedirect(newUrl). Multiple URLs are allowed. If a URL
100         is absolute, it will be used as-is. If it is relative, it is assumed
101         to be relative to the current cherrypy.request.path.
102     """
103    
104     def __init__(self, urls, status=None):
105         import urlparse
106         import cherrypy
107        
108         if isinstance(urls, basestring):
109             urls = [urls]
110        
111         abs_urls = []
112         for url in urls:
113             # Note that urljoin will "do the right thing" whether url is:
114             #  1. a complete URL with host (e.g. "http://www.dummy.biz/test")
115             #  2. a URL relative to root (e.g. "/dummy")
116             #  3. a URL relative to the current path
117             url = urlparse.urljoin(cherrypy.request.browserUrl, url)
118             abs_urls.append(url)
119         self.urls = abs_urls
120        
121         # RFC 2616 indicates a 301 response code fits our goal; however,
122         # browser support for 301 is quite messy. Do 302 instead. See
123         # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html
124         if status is None:
125             if cherrypy.request.version >= "1.1":
126                 status = 303
127             else:
128                 status = 302
129         else:
130             status = int(status)
131             if status < 300 or status > 399:
132                 raise ValueError("status must be between 300 and 399.")
133        
134         self.status = status
135    
136     def set_response(self):
137         import cherrypy
138         cherrypy.response.status = status = self.status
139         cherrypy.response.headerMap['Content-Type'] = "text/html"
140        
141         if status in (300, 301, 302, 303, 307):
142             # "The ... URI SHOULD be given by the Location field
143             # in the response."
144             cherrypy.response.headerMap['Location'] = self.urls[0]
145            
146             # "Unless the request method was HEAD, the entity of the response
147             # SHOULD contain a short hypertext note with a hyperlink to the
148             # new URI(s)."
149             msg = {300: "This resource can be found at <a href='%s'>%s</a>.",
150                    301: "This resource has permanently moved to <a href='%s'>%s</a>.",
151                    302: "This resource resides temporarily at <a href='%s'>%s</a>.",
152                    303: "This resource can be found at <a href='%s'>%s</a>.",
153                    307: "This resource has moved temporarily to <a href='%s'>%s</a>.",
154                    }[status]
155             cherrypy.response.body = "<br />\n".join([msg % (url, url)
156                                                  for url in self.urls])
157         elif status == 304:
158             # Not Modified.
159             # "The response MUST include the following header fields:
160             # Date, unless its omission is required by section 14.18.1"
161             # The "Date" header should have been set in Request.__init__
162            
163             # "The 304 response MUST NOT contain a message-body."
164             cherrypy.response.body = []
165         elif status == 305:
166             # Use Proxy.
167             # self.urls[0] should be the URI of the proxy.
168             cherrypy.response.headerMap['Location'] = self.urls[0]
169             cherrypy.response.body = []
170         else:
171             raise ValueError("The %s status code is unknown." % status)
172
173
174 _missing = object()
175
176 class HTTPError(Error):
177     """ Exception used to return an HTTP error code to the client.
178         This exception will automatically set the response status and body.
179         
180         A custom body can be pased to the init method in place of the
181         standard error page.
182     """
183    
184     def __init__(self, status=500, body=_missing):
185         self.status = status = int(status)
186         if status < 400 or status > 599:
187             raise ValueError("status must be between 400 and 599.")
188          
189         self.body = body
190    
191     def set_response(self):
192         import cherrypy
193        
194         # we now now have access to the traceback
195         statusString, defaultBody = cherrypy._cputil.getErrorStatusAndPage(self.status)
196        
197         if self.body is _missing:
198             self.body = defaultBody
199             # try to look up a custom error page in the config map
200             # if there is no error page then use the pageGenerator
201            
202             # The page generator is used because the init method is called
203             # before the exception is raised.  It is impossible to embed the
204             # traceback in the error page at this piont so we use the generator
205             # to render the error page at a later point
206            
207             import cherrypy
208             # try and read the page from a file
209             # we use the default if the page can't be read
210             try:
211                 errorPageFile = cherrypy.config.get('errorPage.%s' % status, '')
212                 self.body = file(errorPageFile, 'r')
213             except:
214                 # we have alread set the body
215                 pass
216
217         cherrypy.response.status = statusString
218         cherrypy.response.body   = self.body
219    
220     def __str__(self):
221         import cherrypy
222         return cherrypy._cputil.getErrorStatusAndPage(self.status)[0]
223
224 class NotFound(HTTPError):
225     """ Happens when a URL couldn't be mapped to any class.method """
226    
227     def __init__(self, path):
228         self.args = (path,)
229         HTTPError.__init__(self, 404)
230
231     def __str__(self):
232         return self.args[0]
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets