Removed spurious static_path.
[smonitor.git] / monitor / cherrypy / test / test_request_obj.py
blob91ee4fd03ffe3eecb962bd47b0c17a8177af4127
1 """Basic tests for the cherrypy.Request object."""
3 import os
4 localDir = os.path.dirname(__file__)
5 import sys
6 import types
7 from cherrypy._cpcompat import IncompleteRead, ntob, unicodestr
9 import cherrypy
10 from cherrypy import _cptools, tools
11 from cherrypy.lib import httputil
13 defined_http_methods = ("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE",
14 "TRACE", "PROPFIND")
17 # Client-side code #
19 from cherrypy.test import helper
21 class RequestObjectTests(helper.CPWebCase):
23 def setup_server():
24 class Root:
26 def index(self):
27 return "hello"
28 index.exposed = True
30 def scheme(self):
31 return cherrypy.request.scheme
32 scheme.exposed = True
34 root = Root()
37 class TestType(type):
38 """Metaclass which automatically exposes all functions in each subclass,
39 and adds an instance of the subclass as an attribute of root.
40 """
41 def __init__(cls, name, bases, dct):
42 type.__init__(cls, name, bases, dct)
43 for value in dct.values():
44 if isinstance(value, types.FunctionType):
45 value.exposed = True
46 setattr(root, name.lower(), cls())
47 class Test(object):
48 __metaclass__ = TestType
51 class Params(Test):
53 def index(self, thing):
54 return repr(thing)
56 def ismap(self, x, y):
57 return "Coordinates: %s, %s" % (x, y)
59 def default(self, *args, **kwargs):
60 return "args: %s kwargs: %s" % (args, kwargs)
61 default._cp_config = {'request.query_string_encoding': 'latin1'}
64 class ParamErrorsCallable(object):
65 exposed = True
66 def __call__(self):
67 return "data"
69 class ParamErrors(Test):
71 def one_positional(self, param1):
72 return "data"
73 one_positional.exposed = True
75 def one_positional_args(self, param1, *args):
76 return "data"
77 one_positional_args.exposed = True
79 def one_positional_args_kwargs(self, param1, *args, **kwargs):
80 return "data"
81 one_positional_args_kwargs.exposed = True
83 def one_positional_kwargs(self, param1, **kwargs):
84 return "data"
85 one_positional_kwargs.exposed = True
87 def no_positional(self):
88 return "data"
89 no_positional.exposed = True
91 def no_positional_args(self, *args):
92 return "data"
93 no_positional_args.exposed = True
95 def no_positional_args_kwargs(self, *args, **kwargs):
96 return "data"
97 no_positional_args_kwargs.exposed = True
99 def no_positional_kwargs(self, **kwargs):
100 return "data"
101 no_positional_kwargs.exposed = True
103 callable_object = ParamErrorsCallable()
105 def raise_type_error(self, **kwargs):
106 raise TypeError("Client Error")
107 raise_type_error.exposed = True
109 def raise_type_error_with_default_param(self, x, y=None):
110 return '%d' % 'a' # throw an exception
111 raise_type_error_with_default_param.exposed = True
113 def callable_error_page(status, **kwargs):
114 return "Error %s - Well, I'm very sorry but you haven't paid!" % status
117 class Error(Test):
119 _cp_config = {'tools.log_tracebacks.on': True,
122 def reason_phrase(self):
123 raise cherrypy.HTTPError("410 Gone fishin'")
125 def custom(self, err='404'):
126 raise cherrypy.HTTPError(int(err), "No, <b>really</b>, not found!")
127 custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html"),
128 'error_page.401': callable_error_page,
131 def custom_default(self):
132 return 1 + 'a' # raise an unexpected error
133 custom_default._cp_config = {'error_page.default': callable_error_page}
135 def noexist(self):
136 raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
137 noexist._cp_config = {'error_page.404': "nonexistent.html"}
139 def page_method(self):
140 raise ValueError()
142 def page_yield(self):
143 yield "howdy"
144 raise ValueError()
146 def page_streamed(self):
147 yield "word up"
148 raise ValueError()
149 yield "very oops"
150 page_streamed._cp_config = {"response.stream": True}
152 def cause_err_in_finalize(self):
153 # Since status must start with an int, this should error.
154 cherrypy.response.status = "ZOO OK"
155 cause_err_in_finalize._cp_config = {'request.show_tracebacks': False}
157 def rethrow(self):
158 """Test that an error raised here will be thrown out to the server."""
159 raise ValueError()
160 rethrow._cp_config = {'request.throw_errors': True}
163 class Expect(Test):
165 def expectation_failed(self):
166 expect = cherrypy.request.headers.elements("Expect")
167 if expect and expect[0].value != '100-continue':
168 raise cherrypy.HTTPError(400)
169 raise cherrypy.HTTPError(417, 'Expectation Failed')
171 class Headers(Test):
173 def default(self, headername):
174 """Spit back out the value for the requested header."""
175 return cherrypy.request.headers[headername]
177 def doubledheaders(self):
178 # From http://www.cherrypy.org/ticket/165:
179 # "header field names should not be case sensitive sayes the rfc.
180 # if i set a headerfield in complete lowercase i end up with two
181 # header fields, one in lowercase, the other in mixed-case."
183 # Set the most common headers
184 hMap = cherrypy.response.headers
185 hMap['content-type'] = "text/html"
186 hMap['content-length'] = 18
187 hMap['server'] = 'CherryPy headertest'
188 hMap['location'] = ('%s://%s:%s/headers/'
189 % (cherrypy.request.local.ip,
190 cherrypy.request.local.port,
191 cherrypy.request.scheme))
193 # Set a rare header for fun
194 hMap['Expires'] = 'Thu, 01 Dec 2194 16:00:00 GMT'
196 return "double header test"
198 def ifmatch(self):
199 val = cherrypy.request.headers['If-Match']
200 assert isinstance(val, unicodestr)
201 cherrypy.response.headers['ETag'] = val
202 return val
205 class HeaderElements(Test):
207 def get_elements(self, headername):
208 e = cherrypy.request.headers.elements(headername)
209 return "\n".join([unicodestr(x) for x in e])
212 class Method(Test):
214 def index(self):
215 m = cherrypy.request.method
216 if m in defined_http_methods or m == "CONNECT":
217 return m
219 if m == "LINK":
220 raise cherrypy.HTTPError(405)
221 else:
222 raise cherrypy.HTTPError(501)
224 def parameterized(self, data):
225 return data
227 def request_body(self):
228 # This should be a file object (temp file),
229 # which CP will just pipe back out if we tell it to.
230 return cherrypy.request.body
232 def reachable(self):
233 return "success"
235 class Divorce:
236 """HTTP Method handlers shouldn't collide with normal method names.
237 For example, a GET-handler shouldn't collide with a method named 'get'.
239 If you build HTTP method dispatching into CherryPy, rewrite this class
240 to use your new dispatch mechanism and make sure that:
241 "GET /divorce HTTP/1.1" maps to divorce.index() and
242 "GET /divorce/get?ID=13 HTTP/1.1" maps to divorce.get()
245 documents = {}
247 def index(self):
248 yield "<h1>Choose your document</h1>\n"
249 yield "<ul>\n"
250 for id, contents in self.documents.items():
251 yield (" <li><a href='/divorce/get?ID=%s'>%s</a>: %s</li>\n"
252 % (id, id, contents))
253 yield "</ul>"
254 index.exposed = True
256 def get(self, ID):
257 return ("Divorce document %s: %s" %
258 (ID, self.documents.get(ID, "empty")))
259 get.exposed = True
261 root.divorce = Divorce()
264 class ThreadLocal(Test):
266 def index(self):
267 existing = repr(getattr(cherrypy.request, "asdf", None))
268 cherrypy.request.asdf = "rassfrassin"
269 return existing
271 appconf = {
272 '/method': {'request.methods_with_bodies': ("POST", "PUT", "PROPFIND")},
274 cherrypy.tree.mount(root, config=appconf)
275 setup_server = staticmethod(setup_server)
277 def test_scheme(self):
278 self.getPage("/scheme")
279 self.assertBody(self.scheme)
281 def testParams(self):
282 self.getPage("/params/?thing=a")
283 self.assertBody("u'a'")
285 self.getPage("/params/?thing=a&thing=b&thing=c")
286 self.assertBody("[u'a', u'b', u'c']")
288 # Test friendly error message when given params are not accepted.
289 cherrypy.config.update({"request.show_mismatched_params": True})
290 self.getPage("/params/?notathing=meeting")
291 self.assertInBody("Missing parameters: thing")
292 self.getPage("/params/?thing=meeting&notathing=meeting")
293 self.assertInBody("Unexpected query string parameters: notathing")
295 # Test ability to turn off friendly error messages
296 cherrypy.config.update({"request.show_mismatched_params": False})
297 self.getPage("/params/?notathing=meeting")
298 self.assertInBody("Not Found")
299 self.getPage("/params/?thing=meeting&notathing=meeting")
300 self.assertInBody("Not Found")
302 # Test "% HEX HEX"-encoded URL, param keys, and values
303 self.getPage("/params/%d4%20%e3/cheese?Gruy%E8re=Bulgn%e9ville")
304 self.assertBody(r"args: ('\xd4 \xe3', 'cheese') "
305 r"kwargs: {'Gruy\xe8re': u'Bulgn\xe9ville'}")
307 # Make sure that encoded = and & get parsed correctly
308 self.getPage("/params/code?url=http%3A//cherrypy.org/index%3Fa%3D1%26b%3D2")
309 self.assertBody(r"args: ('code',) "
310 r"kwargs: {'url': u'http://cherrypy.org/index?a=1&b=2'}")
312 # Test coordinates sent by <img ismap>
313 self.getPage("/params/ismap?223,114")
314 self.assertBody("Coordinates: 223, 114")
316 # Test "name[key]" dict-like params
317 self.getPage("/params/dictlike?a[1]=1&a[2]=2&b=foo&b[bar]=baz")
318 self.assertBody(
319 "args: ('dictlike',) "
320 "kwargs: {'a[1]': u'1', 'b[bar]': u'baz', 'b': u'foo', 'a[2]': u'2'}")
322 def testParamErrors(self):
324 # test that all of the handlers work when given
325 # the correct parameters in order to ensure that the
326 # errors below aren't coming from some other source.
327 for uri in (
328 '/paramerrors/one_positional?param1=foo',
329 '/paramerrors/one_positional_args?param1=foo',
330 '/paramerrors/one_positional_args/foo',
331 '/paramerrors/one_positional_args/foo/bar/baz',
332 '/paramerrors/one_positional_args_kwargs?param1=foo&param2=bar',
333 '/paramerrors/one_positional_args_kwargs/foo?param2=bar&param3=baz',
334 '/paramerrors/one_positional_args_kwargs/foo/bar/baz?param2=bar&param3=baz',
335 '/paramerrors/one_positional_kwargs?param1=foo&param2=bar&param3=baz',
336 '/paramerrors/one_positional_kwargs/foo?param4=foo&param2=bar&param3=baz',
337 '/paramerrors/no_positional',
338 '/paramerrors/no_positional_args/foo',
339 '/paramerrors/no_positional_args/foo/bar/baz',
340 '/paramerrors/no_positional_args_kwargs?param1=foo&param2=bar',
341 '/paramerrors/no_positional_args_kwargs/foo?param2=bar',
342 '/paramerrors/no_positional_args_kwargs/foo/bar/baz?param2=bar&param3=baz',
343 '/paramerrors/no_positional_kwargs?param1=foo&param2=bar',
344 '/paramerrors/callable_object',
346 self.getPage(uri)
347 self.assertStatus(200)
349 # query string parameters are part of the URI, so if they are wrong
350 # for a particular handler, the status MUST be a 404.
351 error_msgs = [
352 'Missing parameters',
353 'Nothing matches the given URI',
354 'Multiple values for parameters',
355 'Unexpected query string parameters',
356 'Unexpected body parameters',
358 for uri, msg in (
359 ('/paramerrors/one_positional', error_msgs[0]),
360 ('/paramerrors/one_positional?foo=foo', error_msgs[0]),
361 ('/paramerrors/one_positional/foo/bar/baz', error_msgs[1]),
362 ('/paramerrors/one_positional/foo?param1=foo', error_msgs[2]),
363 ('/paramerrors/one_positional/foo?param1=foo&param2=foo', error_msgs[2]),
364 ('/paramerrors/one_positional_args/foo?param1=foo&param2=foo', error_msgs[2]),
365 ('/paramerrors/one_positional_args/foo/bar/baz?param2=foo', error_msgs[3]),
366 ('/paramerrors/one_positional_args_kwargs/foo/bar/baz?param1=bar&param3=baz', error_msgs[2]),
367 ('/paramerrors/one_positional_kwargs/foo?param1=foo&param2=bar&param3=baz', error_msgs[2]),
368 ('/paramerrors/no_positional/boo', error_msgs[1]),
369 ('/paramerrors/no_positional?param1=foo', error_msgs[3]),
370 ('/paramerrors/no_positional_args/boo?param1=foo', error_msgs[3]),
371 ('/paramerrors/no_positional_kwargs/boo?param1=foo', error_msgs[1]),
372 ('/paramerrors/callable_object?param1=foo', error_msgs[3]),
373 ('/paramerrors/callable_object/boo', error_msgs[1]),
375 for show_mismatched_params in (True, False):
376 cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
377 self.getPage(uri)
378 self.assertStatus(404)
379 if show_mismatched_params:
380 self.assertInBody(msg)
381 else:
382 self.assertInBody("Not Found")
384 # if body parameters are wrong, a 400 must be returned.
385 for uri, body, msg in (
386 ('/paramerrors/one_positional/foo', 'param1=foo', error_msgs[2]),
387 ('/paramerrors/one_positional/foo', 'param1=foo&param2=foo', error_msgs[2]),
388 ('/paramerrors/one_positional_args/foo', 'param1=foo&param2=foo', error_msgs[2]),
389 ('/paramerrors/one_positional_args/foo/bar/baz', 'param2=foo', error_msgs[4]),
390 ('/paramerrors/one_positional_args_kwargs/foo/bar/baz', 'param1=bar&param3=baz', error_msgs[2]),
391 ('/paramerrors/one_positional_kwargs/foo', 'param1=foo&param2=bar&param3=baz', error_msgs[2]),
392 ('/paramerrors/no_positional', 'param1=foo', error_msgs[4]),
393 ('/paramerrors/no_positional_args/boo', 'param1=foo', error_msgs[4]),
394 ('/paramerrors/callable_object', 'param1=foo', error_msgs[4]),
396 for show_mismatched_params in (True, False):
397 cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
398 self.getPage(uri, method='POST', body=body)
399 self.assertStatus(400)
400 if show_mismatched_params:
401 self.assertInBody(msg)
402 else:
403 self.assertInBody("Bad Request")
406 # even if body parameters are wrong, if we get the uri wrong, then
407 # it's a 404
408 for uri, body, msg in (
409 ('/paramerrors/one_positional?param2=foo', 'param1=foo', error_msgs[3]),
410 ('/paramerrors/one_positional/foo/bar', 'param2=foo', error_msgs[1]),
411 ('/paramerrors/one_positional_args/foo/bar?param2=foo', 'param3=foo', error_msgs[3]),
412 ('/paramerrors/one_positional_kwargs/foo/bar', 'param2=bar&param3=baz', error_msgs[1]),
413 ('/paramerrors/no_positional?param1=foo', 'param2=foo', error_msgs[3]),
414 ('/paramerrors/no_positional_args/boo?param2=foo', 'param1=foo', error_msgs[3]),
415 ('/paramerrors/callable_object?param2=bar', 'param1=foo', error_msgs[3]),
417 for show_mismatched_params in (True, False):
418 cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
419 self.getPage(uri, method='POST', body=body)
420 self.assertStatus(404)
421 if show_mismatched_params:
422 self.assertInBody(msg)
423 else:
424 self.assertInBody("Not Found")
426 # In the case that a handler raises a TypeError we should
427 # let that type error through.
428 for uri in (
429 '/paramerrors/raise_type_error',
430 '/paramerrors/raise_type_error_with_default_param?x=0',
431 '/paramerrors/raise_type_error_with_default_param?x=0&y=0',
433 self.getPage(uri, method='GET')
434 self.assertStatus(500)
435 self.assertTrue('Client Error', self.body)
437 def testErrorHandling(self):
438 self.getPage("/error/missing")
439 self.assertStatus(404)
440 self.assertErrorPage(404, "The path '/error/missing' was not found.")
442 ignore = helper.webtest.ignored_exceptions
443 ignore.append(ValueError)
444 try:
445 valerr = '\n raise ValueError()\nValueError'
446 self.getPage("/error/page_method")
447 self.assertErrorPage(500, pattern=valerr)
449 self.getPage("/error/page_yield")
450 self.assertErrorPage(500, pattern=valerr)
452 if (cherrypy.server.protocol_version == "HTTP/1.0" or
453 getattr(cherrypy.server, "using_apache", False)):
454 self.getPage("/error/page_streamed")
455 # Because this error is raised after the response body has
456 # started, the status should not change to an error status.
457 self.assertStatus(200)
458 self.assertBody("word up")
459 else:
460 # Under HTTP/1.1, the chunked transfer-coding is used.
461 # The HTTP client will choke when the output is incomplete.
462 self.assertRaises((ValueError, IncompleteRead), self.getPage,
463 "/error/page_streamed")
465 # No traceback should be present
466 self.getPage("/error/cause_err_in_finalize")
467 msg = "Illegal response status from server ('ZOO' is non-numeric)."
468 self.assertErrorPage(500, msg, None)
469 finally:
470 ignore.pop()
472 # Test HTTPError with a reason-phrase in the status arg.
473 self.getPage('/error/reason_phrase')
474 self.assertStatus("410 Gone fishin'")
476 # Test custom error page for a specific error.
477 self.getPage("/error/custom")
478 self.assertStatus(404)
479 self.assertBody("Hello, world\r\n" + (" " * 499))
481 # Test custom error page for a specific error.
482 self.getPage("/error/custom?err=401")
483 self.assertStatus(401)
484 self.assertBody("Error 401 Unauthorized - Well, I'm very sorry but you haven't paid!")
486 # Test default custom error page.
487 self.getPage("/error/custom_default")
488 self.assertStatus(500)
489 self.assertBody("Error 500 Internal Server Error - Well, I'm very sorry but you haven't paid!".ljust(513))
491 # Test error in custom error page (ticket #305).
492 # Note that the message is escaped for HTML (ticket #310).
493 self.getPage("/error/noexist")
494 self.assertStatus(404)
495 msg = ("No, &lt;b&gt;really&lt;/b&gt;, not found!<br />"
496 "In addition, the custom error page failed:\n<br />"
497 "IOError: [Errno 2] No such file or directory: 'nonexistent.html'")
498 self.assertInBody(msg)
500 if getattr(cherrypy.server, "using_apache", False):
501 pass
502 else:
503 # Test throw_errors (ticket #186).
504 self.getPage("/error/rethrow")
505 self.assertInBody("raise ValueError()")
507 def testExpect(self):
508 e = ('Expect', '100-continue')
509 self.getPage("/headerelements/get_elements?headername=Expect", [e])
510 self.assertBody('100-continue')
512 self.getPage("/expect/expectation_failed", [e])
513 self.assertStatus(417)
515 def testHeaderElements(self):
516 # Accept-* header elements should be sorted, with most preferred first.
517 h = [('Accept', 'audio/*; q=0.2, audio/basic')]
518 self.getPage("/headerelements/get_elements?headername=Accept", h)
519 self.assertStatus(200)
520 self.assertBody("audio/basic\n"
521 "audio/*;q=0.2")
523 h = [('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')]
524 self.getPage("/headerelements/get_elements?headername=Accept", h)
525 self.assertStatus(200)
526 self.assertBody("text/x-c\n"
527 "text/html\n"
528 "text/x-dvi;q=0.8\n"
529 "text/plain;q=0.5")
531 # Test that more specific media ranges get priority.
532 h = [('Accept', 'text/*, text/html, text/html;level=1, */*')]
533 self.getPage("/headerelements/get_elements?headername=Accept", h)
534 self.assertStatus(200)
535 self.assertBody("text/html;level=1\n"
536 "text/html\n"
537 "text/*\n"
538 "*/*")
540 # Test Accept-Charset
541 h = [('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8')]
542 self.getPage("/headerelements/get_elements?headername=Accept-Charset", h)
543 self.assertStatus("200 OK")
544 self.assertBody("iso-8859-5\n"
545 "unicode-1-1;q=0.8")
547 # Test Accept-Encoding
548 h = [('Accept-Encoding', 'gzip;q=1.0, identity; q=0.5, *;q=0')]
549 self.getPage("/headerelements/get_elements?headername=Accept-Encoding", h)
550 self.assertStatus("200 OK")
551 self.assertBody("gzip;q=1.0\n"
552 "identity;q=0.5\n"
553 "*;q=0")
555 # Test Accept-Language
556 h = [('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7')]
557 self.getPage("/headerelements/get_elements?headername=Accept-Language", h)
558 self.assertStatus("200 OK")
559 self.assertBody("da\n"
560 "en-gb;q=0.8\n"
561 "en;q=0.7")
563 # Test malformed header parsing. See http://www.cherrypy.org/ticket/763.
564 self.getPage("/headerelements/get_elements?headername=Content-Type",
565 # Note the illegal trailing ";"
566 headers=[('Content-Type', 'text/html; charset=utf-8;')])
567 self.assertStatus(200)
568 self.assertBody("text/html;charset=utf-8")
570 def test_repeated_headers(self):
571 # Test that two request headers are collapsed into one.
572 # See http://www.cherrypy.org/ticket/542.
573 self.getPage("/headers/Accept-Charset",
574 headers=[("Accept-Charset", "iso-8859-5"),
575 ("Accept-Charset", "unicode-1-1;q=0.8")])
576 self.assertBody("iso-8859-5, unicode-1-1;q=0.8")
578 # Tests that each header only appears once, regardless of case.
579 self.getPage("/headers/doubledheaders")
580 self.assertBody("double header test")
581 hnames = [name.title() for name, val in self.headers]
582 for key in ['Content-Length', 'Content-Type', 'Date',
583 'Expires', 'Location', 'Server']:
584 self.assertEqual(hnames.count(key), 1, self.headers)
586 def test_encoded_headers(self):
587 # First, make sure the innards work like expected.
588 self.assertEqual(httputil.decode_TEXT(u"=?utf-8?q?f=C3=BCr?="), u"f\xfcr")
590 if cherrypy.server.protocol_version == "HTTP/1.1":
591 # Test RFC-2047-encoded request and response header values
592 u = u'\u212bngstr\xf6m'
593 c = u"=E2=84=ABngstr=C3=B6m"
594 self.getPage("/headers/ifmatch", [('If-Match', u'=?utf-8?q?%s?=' % c)])
595 # The body should be utf-8 encoded.
596 self.assertBody("\xe2\x84\xabngstr\xc3\xb6m")
597 # But the Etag header should be RFC-2047 encoded (binary)
598 self.assertHeader("ETag", u'=?utf-8?b?4oSrbmdzdHLDtm0=?=')
600 # Test a *LONG* RFC-2047-encoded request and response header value
601 self.getPage("/headers/ifmatch",
602 [('If-Match', u'=?utf-8?q?%s?=' % (c * 10))])
603 self.assertBody("\xe2\x84\xabngstr\xc3\xb6m" * 10)
604 # Note: this is different output for Python3, but it decodes fine.
605 etag = self.assertHeader("ETag",
606 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
607 '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
608 '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
609 '4oSrbmdzdHLDtm0=?=')
610 self.assertEqual(httputil.decode_TEXT(etag), u * 10)
612 def test_header_presence(self):
613 # If we don't pass a Content-Type header, it should not be present
614 # in cherrypy.request.headers
615 self.getPage("/headers/Content-Type",
616 headers=[])
617 self.assertStatus(500)
619 # If Content-Type is present in the request, it should be present in
620 # cherrypy.request.headers
621 self.getPage("/headers/Content-Type",
622 headers=[("Content-type", "application/json")])
623 self.assertBody("application/json")
625 def test_basic_HTTPMethods(self):
626 helper.webtest.methods_with_bodies = ("POST", "PUT", "PROPFIND")
628 # Test that all defined HTTP methods work.
629 for m in defined_http_methods:
630 self.getPage("/method/", method=m)
632 # HEAD requests should not return any body.
633 if m == "HEAD":
634 self.assertBody("")
635 elif m == "TRACE":
636 # Some HTTP servers (like modpy) have their own TRACE support
637 self.assertEqual(self.body[:5], ntob("TRACE"))
638 else:
639 self.assertBody(m)
641 # Request a PUT method with a form-urlencoded body
642 self.getPage("/method/parameterized", method="PUT",
643 body="data=on+top+of+other+things")
644 self.assertBody("on top of other things")
646 # Request a PUT method with a file body
647 b = "one thing on top of another"
648 h = [("Content-Type", "text/plain"),
649 ("Content-Length", str(len(b)))]
650 self.getPage("/method/request_body", headers=h, method="PUT", body=b)
651 self.assertStatus(200)
652 self.assertBody(b)
654 # Request a PUT method with a file body but no Content-Type.
655 # See http://www.cherrypy.org/ticket/790.
656 b = ntob("one thing on top of another")
657 self.persistent = True
658 try:
659 conn = self.HTTP_CONN
660 conn.putrequest("PUT", "/method/request_body", skip_host=True)
661 conn.putheader("Host", self.HOST)
662 conn.putheader('Content-Length', str(len(b)))
663 conn.endheaders()
664 conn.send(b)
665 response = conn.response_class(conn.sock, method="PUT")
666 response.begin()
667 self.assertEqual(response.status, 200)
668 self.body = response.read()
669 self.assertBody(b)
670 finally:
671 self.persistent = False
673 # Request a PUT method with no body whatsoever (not an empty one).
674 # See http://www.cherrypy.org/ticket/650.
675 # Provide a C-T or webtest will provide one (and a C-L) for us.
676 h = [("Content-Type", "text/plain")]
677 self.getPage("/method/reachable", headers=h, method="PUT")
678 self.assertStatus(411)
680 # Request a custom method with a request body
681 b = ('<?xml version="1.0" encoding="utf-8" ?>\n\n'
682 '<propfind xmlns="DAV:"><prop><getlastmodified/>'
683 '</prop></propfind>')
684 h = [('Content-Type', 'text/xml'),
685 ('Content-Length', str(len(b)))]
686 self.getPage("/method/request_body", headers=h, method="PROPFIND", body=b)
687 self.assertStatus(200)
688 self.assertBody(b)
690 # Request a disallowed method
691 self.getPage("/method/", method="LINK")
692 self.assertStatus(405)
694 # Request an unknown method
695 self.getPage("/method/", method="SEARCH")
696 self.assertStatus(501)
698 # For method dispatchers: make sure that an HTTP method doesn't
699 # collide with a virtual path atom. If you build HTTP-method
700 # dispatching into the core, rewrite these handlers to use
701 # your dispatch idioms.
702 self.getPage("/divorce/get?ID=13")
703 self.assertBody('Divorce document 13: empty')
704 self.assertStatus(200)
705 self.getPage("/divorce/", method="GET")
706 self.assertBody('<h1>Choose your document</h1>\n<ul>\n</ul>')
707 self.assertStatus(200)
709 def test_CONNECT_method(self):
710 if getattr(cherrypy.server, "using_apache", False):
711 return self.skip("skipped due to known Apache differences... ")
713 self.getPage("/method/", method="CONNECT")
714 self.assertBody("CONNECT")
716 def testEmptyThreadlocals(self):
717 results = []
718 for x in range(20):
719 self.getPage("/threadlocal/")
720 results.append(self.body)
721 self.assertEqual(results, [ntob("None")] * 20)