1 """Basic tests for the CherryPy core: request handling."""
2
3 from cherrypy.test import test
4 test.prefer_parent_path()
5
6 import cherrypy
7 from cherrypy import _cptools, tools
8 from cherrypy.lib import http, static
9 import types
10
11 import os
12 localDir = os.path.dirname(__file__)
13 log_file = os.path.join(localDir, "test.log")
14 log_access_file = os.path.join(localDir, "access.log")
15 favicon_path = os.path.join(os.getcwd(), localDir, "../favicon.ico")
16
17 defined_http_methods = ("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE",
18 "TRACE", "CONNECT", "PROPFIND")
19
20
22 class Root:
23
24 def index(self):
25 return "hello"
26 index.exposed = True
27
28 favicon_ico = tools.staticfile.handler(filename=favicon_path)
29
30 def andnow(self):
31 return "the larch"
32 andnow.exposed = True
33
34 def global_(self):
35 pass
36 global_.exposed = True
37
38 def delglobal(self):
39 del self.__class__.__dict__['global_']
40 delglobal.exposed = True
41
42 def defct(self, newct):
43 newct = "text/%s" % newct
44 cherrypy.config.update({'tools.response_headers.on': True,
45 'tools.response_headers.headers':
46 [('Content-Type', newct)]})
47 defct.exposed = True
48
49 def upload(self, file):
50 return "Size: %s" % len(file.file.read())
51 upload.exposed = True
52
53 root = Root()
54
55
56 class TestType(type):
57 """Metaclass which automatically exposes all functions in each subclass,
58 and adds an instance of the subclass as an attribute of root.
59 """
60 def __init__(cls, name, bases, dct):
61 type.__init__(name, bases, dct)
62 for value in dct.itervalues():
63 if isinstance(value, types.FunctionType):
64 value.exposed = True
65 setattr(root, name.lower(), cls())
66 class Test(object):
67 __metaclass__ = TestType
68
69
70 class URL(Test):
71
72 _cp_config = {'tools.trailing_slash.on': False}
73
74 def index(self, path_info, relative=None):
75 return cherrypy.url(path_info, relative=bool(relative))
76
77 def leaf(self, path_info, relative=None):
78 return cherrypy.url(path_info, relative=bool(relative))
79
80
81 class Params(Test):
82
83 def index(self, thing):
84 return repr(thing)
85
86 def ismap(self, x, y):
87 return "Coordinates: %s, %s" % (x, y)
88
89 def default(self, *args, **kwargs):
90 return "args: %s kwargs: %s" % (args, kwargs)
91
92
93 class Status(Test):
94
95 def index(self):
96 return "normal"
97
98 def blank(self):
99 cherrypy.response.status = ""
100
101
102
103
104
105 def illegal(self):
106 cherrypy.response.status = 781
107 return "oops"
108
109
110 def unknown(self):
111 cherrypy.response.status = "431 My custom error"
112 return "funky"
113
114
115 def bad(self):
116 cherrypy.response.status = "error"
117 return "bad news"
118
119
120 class Redirect(Test):
121
122 class Error:
123 _cp_config = {"tools.err_redirect.on": True,
124 "tools.err_redirect.url": "/errpage",
125 "tools.err_redirect.internal": False,
126 }
127
128 def index(self):
129 raise NameError("redirect_test")
130 index.exposed = True
131 error = Error()
132
133 def index(self):
134 return "child"
135
136 def by_code(self, code):
137 raise cherrypy.HTTPRedirect("somewhere else", code)
138 by_code._cp_config = {'tools.trailing_slash.extra': True}
139
140 def nomodify(self):
141 raise cherrypy.HTTPRedirect("", 304)
142
143 def proxy(self):
144 raise cherrypy.HTTPRedirect("proxy", 305)
145
146 def stringify(self):
147 return str(cherrypy.HTTPRedirect("/"))
148
149 def fragment(self, frag):
150 raise cherrypy.HTTPRedirect("/some/url#%s" % frag)
151
152 def login_redir():
153 if not getattr(cherrypy.request, "login", None):
154 raise cherrypy.InternalRedirect("/internalredirect/login")
155 tools.login_redir = _cptools.Tool('before_handler', login_redir)
156
157 def redir_custom():
158 raise cherrypy.InternalRedirect("/internalredirect/custom_err")
159
160 class InternalRedirect(Test):
161
162 def index(self):
163 raise cherrypy.InternalRedirect("/")
164
165 def relative(self, a, b):
166 raise cherrypy.InternalRedirect("cousin?t=6")
167
168 def cousin(self, t):
169 assert cherrypy.request.prev.closed
170 return cherrypy.request.prev.query_string
171
172 def petshop(self, user_id):
173 if user_id == "parrot":
174
175 raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=slug')
176 elif user_id == "terrier":
177
178 raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=fish')
179 else:
180
181 raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=%s' % user_id)
182
183
184
185 def secure(self):
186 return "Welcome!"
187 secure = tools.login_redir()(secure)
188
189
190
191
192 def login(self):
193 return "Please log in"
194 login._cp_config = {'hooks.before_error_response': redir_custom}
195
196 def custom_err(self):
197 return "Something went horribly wrong."
198
199 def early_ir(self, arg):
200 return "whatever"
201 early_ir._cp_config = {'hooks.before_request_body': redir_custom}
202
203 class Image(Test):
204
205 def getImagesByUser(self, user_id):
206 return "0 images for %s" % user_id
207
208
209 class Flatten(Test):
210
211 def as_string(self):
212 return "content"
213
214 def as_list(self):
215 return ["con", "tent"]
216
217 def as_yield(self):
218 yield "content"
219
220 def as_dblyield(self):
221 yield self.as_yield()
222 as_dblyield._cp_config = {'tools.flatten.on': True}
223
224 def as_refyield(self):
225 for chunk in self.as_yield():
226 yield chunk
227
228
229 class Error(Test):
230
231 _cp_config = {'tools.log_tracebacks.on': True,
232 }
233
234 def custom(self):
235 raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
236 custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html")}
237
238 def noexist(self):
239 raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
240 noexist._cp_config = {'error_page.404': "nonexistent.html"}
241
242 def page_method(self):
243 raise ValueError()
244
245 def page_yield(self):
246 yield "howdy"
247 raise ValueError()
248
249 def page_streamed(self):
250 yield "word up"
251 raise ValueError()
252 yield "very oops"
253 page_streamed._cp_config = {"response.stream": True}
254
255 def cause_err_in_finalize(self):
256
257 cherrypy.response.status = "ZOO OK"
258 cause_err_in_finalize._cp_config = {'request.show_tracebacks': False}
259
260 def rethrow(self):
261 """Test that an error raised here will be thrown out to the server."""
262 raise ValueError()
263 rethrow._cp_config = {'request.throw_errors': True}
264
265
266 class Ranges(Test):
267
268 def get_ranges(self, bytes):
269 return repr(http.get_ranges('bytes=%s' % bytes, 8))
270
271 def slice_file(self):
272 path = os.path.join(os.getcwd(), os.path.dirname(__file__))
273 return static.serve_file(os.path.join(path, "static/index.html"))
274
275
276 class Expect(Test):
277
278 def expectation_failed(self):
279 expect = cherrypy.request.headers.elements("Expect")
280 if expect and expect[0].value != '100-continue':
281 raise cherrypy.HTTPError(400)
282 raise cherrypy.HTTPError(417, 'Expectation Failed')
283
284 class Headers(Test):
285
286 def default(self, headername):
287 """Spit back out the value for the requested header."""
288 return cherrypy.request.headers[headername]
289
290 def doubledheaders(self):
291
292
293
294
295
296
297 hMap = cherrypy.response.headers
298 hMap['content-type'] = "text/html"
299 hMap['content-length'] = 18
300 hMap['server'] = 'CherryPy headertest'
301 hMap['location'] = ('%s://%s:%s/headers/'
302 % (cherrypy.request.local.ip,
303 cherrypy.request.local.port,
304 cherrypy.request.scheme))
305
306
307 hMap['Expires'] = 'Thu, 01 Dec 2194 16:00:00 GMT'
308
309 return "double header test"
310
311 def ifmatch(self):
312 val = cherrypy.request.headers['If-Match']
313 cherrypy.response.headers['ETag'] = val
314 return repr(val)
315
316
317 class HeaderElements(Test):
318
319 def get_elements(self, headername):
320 e = cherrypy.request.headers.elements(headername)
321 return "\n".join([unicode(x) for x in e])
322
323
324 class Method(Test):
325
326 def index(self):
327 m = cherrypy.request.method
328 if m in defined_http_methods:
329 return m
330
331 if m == "LINK":
332 raise cherrypy.HTTPError(405)
333 else:
334 raise cherrypy.HTTPError(501)
335
336 def parameterized(self, data):
337 return data
338
339 def request_body(self):
340
341
342 return cherrypy.request.body
343
344 def reachable(self):
345 return "success"
346
347 class Divorce:
348 """HTTP Method handlers shouldn't collide with normal method names.
349 For example, a GET-handler shouldn't collide with a method named 'get'.
350
351 If you build HTTP method dispatching into CherryPy, rewrite this class
352 to use your new dispatch mechanism and make sure that:
353 "GET /divorce HTTP/1.1" maps to divorce.index() and
354 "GET /divorce/get?ID=13 HTTP/1.1" maps to divorce.get()
355 """
356
357 documents = {}
358
359 def index(self):
360 yield "<h1>Choose your document</h1>\n"
361 yield "<ul>\n"
362 for