1 """Basic tests for the cherrypy.Request object."""
2
3 import os
4 localDir = os.path.dirname(__file__)
5 import sys
6 import types
7 from cherrypy._cpcompat import IncompleteRead, ntob, ntou, unicodestr
8
9 import cherrypy
10 from cherrypy import _cptools, tools
11 from cherrypy.lib import httputil
12
13 defined_http_methods = ("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE",
14 "TRACE", "PROPFIND")
15
16
17
18
19 from cherrypy.test import helper
20
21
23
25 class Root:
26
27 def index(self):
28 return "hello"
29 index.exposed = True
30
31 def scheme(self):
32 return cherrypy.request.scheme
33 scheme.exposed = True
34
35 root = Root()
36
37 class TestType(type):
38 """Metaclass which automatically exposes all functions in each
39 subclass, and adds an instance of the subclass as an attribute
40 of root.
41 """
42 def __init__(cls, name, bases, dct):
43 type.__init__(cls, name, bases, dct)
44 for value in dct.values():
45 if isinstance(value, types.FunctionType):
46 value.exposed = True
47 setattr(root, name.lower(), cls())
48 Test = TestType('Test', (object,), {})
49
50 class PathInfo(Test):
51
52 def default(self, *args):
53 return cherrypy.request.path_info
54
55 class Params(Test):
56
57 def index(self, thing):
58 return repr(thing)
59
60 def ismap(self, x, y):
61 return "Coordinates: %s, %s" % (x, y)
62
63 def default(self, *args, **kwargs):
64 return "args: %s kwargs: %s" % (args, sorted(kwargs.items()))
65 default._cp_config = {'request.query_string_encoding': 'latin1'}
66
67 class ParamErrorsCallable(object):
68 exposed = True
69
70 def __call__(self):
71 return "data"
72
73 class ParamErrors(Test):
74
75 def one_positional(self, param1):
76 return "data"
77 one_positional.exposed = True
78
79 def one_positional_args(self, param1, *args):
80 return "data"
81 one_positional_args.exposed = True
82
83 def one_positional_args_kwargs(self, param1, *args, **kwargs):
84 return "data"
85 one_positional_args_kwargs.exposed = True
86
87 def one_positional_kwargs(self, param1, **kwargs):
88 return "data"
89 one_positional_kwargs.exposed = True
90
91 def no_positional(self):
92 return "data"
93 no_positional.exposed = True
94
95 def no_positional_args(self, *args):
96 return "data"
97 no_positional_args.exposed = True
98
99 def no_positional_args_kwargs(self, *args, **kwargs):
100 return "data"
101 no_positional_args_kwargs.exposed = True
102
103 def no_positional_kwargs(self, **kwargs):
104 return "data"
105 no_positional_kwargs.exposed = True
106
107 callable_object = ParamErrorsCallable()
108
109 def raise_type_error(self, **kwargs):
110 raise TypeError("Client Error")
111 raise_type_error.exposed = True
112
113 def raise_type_error_with_default_param(self, x, y=None):
114 return '%d' % 'a'
115 raise_type_error_with_default_param.exposed = True
116
117 def callable_error_page(status, **kwargs):
118 return "Error %s - Well, I'm very sorry but you haven't paid!" % (
119 status)
120
121 class Error(Test):
122
123 _cp_config = {'tools.log_tracebacks.on': True,
124 }
125
126 def reason_phrase(self):
127 raise cherrypy.HTTPError("410 Gone fishin'")
128
129 def custom(self, err='404'):
130 raise cherrypy.HTTPError(
131 int(err), "No, <b>really</b>, not found!")
132 custom._cp_config = {
133 'error_page.404': os.path.join(localDir, "static/index.html"),
134 'error_page.401': callable_error_page,
135 }
136
137 def custom_default(self):
138 return 1 + 'a'
139 custom_default._cp_config = {
140 'error_page.default': callable_error_page}
141
142 def noexist(self):
143 raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
144 noexist._cp_config = {'error_page.404': "nonexistent.html"}
145
146 def page_method(self):
147 raise ValueError()
148
149 def page_yield(self):
150 yield "howdy"
151 raise ValueError()
152
153 def page_streamed(self):
154 yield "word up"
155 raise ValueError()
156 yield "very oops"
157 page_streamed._cp_config = {"response.stream": True}
158
159 def cause_err_in_finalize(self):
160
161 cherrypy.response.status = "ZOO OK"
162 cause_err_in_finalize._cp_config = {
163 'request.show_tracebacks': False}
164
165 def rethrow(self):
166 """Test that an error raised here will be thrown out to
167 the server.
168 """
169 raise ValueError()
170 rethrow._cp_config = {'request.throw_errors': True}
171
172 class Expect(Test):
173
174 def expectation_failed(self):
175 expect = cherrypy.request.headers.elements("Expect")
176 if expect and expect[0].value != '100-continue':
177 raise cherrypy.HTTPError(400)
178 raise cherrypy.HTTPError(417, 'Expectation Failed')
179
180 class Headers(Test):
181
182 def default(self, headername):
183 """Spit back out the value for the requested header."""
184 return cherrypy.request.headers[headername]
185
186 def doubledheaders(self):
187
188
189
190
191
192
193
194 hMap = cherrypy.response.headers
195 hMap['content-type'] = "text/html"
196 hMap['content-length'] = 18
197 hMap['server'] = 'CherryPy headertest'
198 hMap['location'] = ('%s://%s:%s/headers/'
199 % (cherrypy.request.local.ip,
200 cherrypy.request.local.port,
201 cherrypy.request.scheme))
202
203
204 hMap['Expires'] = 'Thu, 01 Dec 2194 16:00:00 GMT'
205
206 return "double header test"
207
208 def ifmatch(self):
209 val = cherrypy.request.headers['If-Match']
210 assert isinstance(val, unicodestr)
211 cherrypy.response.headers['ETag'] = val
212 return val
213
214 class HeaderElements(Test):
215
216 def get_elements(self, headername):
217 e = cherrypy.request.headers.elements(headername)
218 return "\n".join([unicodestr(x) for x in e])
219
220 class Method(Test):
221
222 def index(self):
223 m = cherrypy.request.method
224 if m in defined_http_methods or m == "CONNECT":
225 return m
226
227 if m == "LINK":
228 raise cherrypy.HTTPError(405)
229 else:
230 raise cherrypy.HTTPError(501)
231
232 def parameterized(self, data):
233 return data
234
235 def request_body(self):
236
237
238 return cherrypy.request.body
239
240 def reachable(self):
241 return "success"
242
243 class Divorce:
244
245 """HTTP Method handlers shouldn't collide with normal method names.
246 For example, a GET-handler shouldn't collide with a method named
247 'get'.
248
249 If you build HTTP method dispatching into CherryPy, rewrite this
250 class to use your new dispatch mechanism and make sure that:
251 "GET /divorce HTTP/1.1" maps to divorce.index() and
252 "GET /divorce/get?ID=13 HTTP/1.1" maps to divorce.get()
253 """
254
255 documents = {}
256
257 def index(self):
258 yield "<h1>Choose your document</h1>\n"
259 yield "<ul>\n"
260 for id, contents in self.documents.items():
261 yield (
262 " <li><a href='/divorce/get?ID=%s'>%s</a>:"
263 " %s</li>\n" % (id, id, contents))
264 yield "</ul>"
265 index.exposed = True
266
267 def get(self, ID):
268 return ("Divorce document %s: %s" %
269 (ID, self.documents.get(ID, "empty")))
270 get.exposed = True
271
272 root.divorce = Divorce()
273
274 class ThreadLocal(Test):
275
276 def index(self):
277 existing = repr(getattr(cherrypy.request, "asdf", None))
278 cherrypy.request.asdf = "rassfrassin"
279 return existing
280
281 appconf = {
282 '/method': {
283 'request.methods_with_bodies': ("POST", "PUT", "PROPFIND")
284 },
285 }
286 cherrypy.tree.mount(root, config=appconf)
287 setup_server = staticmethod(setup_server)
288
292
296
298
299 self.getPage("http://localhost/pathinfo/foo/bar")
300 self.assertBody("/pathinfo/foo/bar")
301
303 self.getPage("/params/?thing=a")
304 self.assertBody(repr(ntou("a")))
305
306 self.getPage("/params/?thing=a&thing=b&thing=c")
307 self.assertBody(repr([ntou('a'), ntou('b'), ntou('c')]))
308
309
310 cherrypy.config.update({"request.show_mismatched_params": True})
311 self.getPage("/params/?notathing=meeting")
312 self.assertInBody("Missing parameters: thing")
313 self.getPage("/params/?thing=meeting¬athing=meeting")
314 self.assertInBody("Unexpected query string parameters: notathing")
315
316
317 cherrypy.config.update({"request.show_mismatched_params": False})
318 self.getPage("/params/?notathing=meeting")
319 self.assertInBody("Not Found")
320 self.getPage("/params/?thing=meeting¬athing=meeting")
321 self.assertInBody("Not Found")
322
323
324 self.getPage("/params/%d4%20%e3/cheese?Gruy%E8re=Bulgn%e9ville")
325 self.assertBody("args: %s kwargs: %s" %
326 (('\xd4 \xe3', 'cheese'),
327 [('Gruy\xe8re', ntou('Bulgn\xe9ville'))]))
328
329
330 self.getPage(
331 "/params/code?url=http%3A//cherrypy.org/index%3Fa%3D1%26b%3D2")
332 self.assertBody("args: %s kwargs: %s" %
333 (('code',),
334 [('url', ntou('http://cherrypy.org/index?a=1&b=2'))]))
335
336
337 self.getPage("/params/ismap?223,114")
338 self.assertBody("Coordinates: 223, 114")
339
340
341 self.getPage("/params/dictlike?a[1]=1&a[2]=2&b=foo&b[bar]=baz")
342 self.assertBody("args: %s kwargs: %s" %
343 (('dictlike',),
344 [('a[1]', ntou('1')), ('a[2]', ntou('2')),
345 ('b', ntou('foo')), ('b[bar]', ntou('baz'))]))
346
348
349
350
351
352 for uri in (
353 '/paramerrors/one_positional?param1=foo',
354 '/paramerrors/one_positional_args?param1=foo',
355 '/paramerrors/one_positional_args/foo',
356 '/paramerrors/one_positional_args/foo/bar/baz',
357 '/paramerrors/one_positional_args_kwargs?'
358 'param1=foo¶m2=bar',
359 '/paramerrors/one_positional_args_kwargs/foo?'
360 'param2=bar¶m3=baz',
361 '/paramerrors/one_positional_args_kwargs/foo/bar/baz?'
362 'param2=bar¶m3=baz',
363 '/paramerrors/one_positional_kwargs?'
364 'param1=foo¶m2=bar¶m3=baz',
365 '/paramerrors/one_positional_kwargs/foo?'
366 'param4=foo¶m2=bar¶m3=baz',
367 '/paramerrors/no_positional',
368 '/paramerrors/no_positional_args/foo',
369 '/paramerrors/no_positional_args/foo/bar/baz',
370 '/paramerrors/no_positional_args_kwargs?param1=foo¶m2=bar',
371 '/paramerrors/no_positional_args_kwargs/foo?param2=bar',
372 '/paramerrors/no_positional_args_kwargs/foo/bar/baz?'
373 'param2=bar¶m3=baz',
374 '/paramerrors/no_positional_kwargs?param1=foo¶m2=bar',
375 '/paramerrors/callable_object',
376 ):
377 self.getPage(uri)
378 self.assertStatus(200)
379
380
381
382 error_msgs = [
383 'Missing parameters',
384 'Nothing matches the given URI',
385 'Multiple values for parameters',
386 'Unexpected query string parameters',
387 'Unexpected body parameters',
388 ]
389 for uri, msg in (
390 ('/paramerrors/one_positional', error_msgs[0]),
391 ('/paramerrors/one_positional?foo=foo', error_msgs[0]),
392 ('/paramerrors/one_positional/foo/bar/baz', error_msgs[1]),
393 ('/paramerrors/one_positional/foo?param1=foo', error_msgs[2]),
394 ('/paramerrors/one_positional/foo?param1=foo¶m2=foo',
395 error_msgs[2]),
396 ('/paramerrors/one_positional_args/foo?param1=foo¶m2=foo',
397 error_msgs[2]),
398 ('/paramerrors/one_positional_args/foo/bar/baz?param2=foo',
399 error_msgs[3]),
400 ('/paramerrors/one_positional_args_kwargs/foo/bar/baz?'
401 'param1=bar¶m3=baz',
402 error_msgs[2]),
403 ('/paramerrors/one_positional_kwargs/foo?'
404 'param1=foo¶m2=bar¶m3=baz',
405 error_msgs[2]),
406 ('/paramerrors/no_positional/boo', error_msgs[1]),
407 ('/paramerrors/no_positional?param1=foo', error_msgs[3]),
408 ('/paramerrors/no_positional_args/boo?param1=foo', error_msgs[3]),
409 ('/paramerrors/no_positional_kwargs/boo?param1=foo',
410 error_msgs[1]),
411 ('/paramerrors/callable_object?param1=foo', error_msgs[3]),
412 ('/paramerrors/callable_object/boo', error_msgs[1]),
413 ):
414 for show_mismatched_params in (True, False):
415 cherrypy.config.update(
416 {'request.show_mismatched_params': show_mismatched_params})
417 self.getPage(uri)
418 self.assertStatus(404)
419 if show_mismatched_params:
420 self.assertInBody(msg)
421 else:
422 self.assertInBody("Not Found")
423
424
425 for uri, body, msg in (
426 ('/paramerrors/one_positional/foo',
427 'param1=foo', error_msgs[2]),
428 ('/paramerrors/one_positional/foo',
429 'param1=foo¶m2=foo', error_msgs[2]),
430 ('/paramerrors/one_positional_args/foo',
431 'param1=foo¶m2=foo', error_msgs[2]),
432 ('/paramerrors/one_positional_args/foo/bar/baz',
433 'param2=foo', error_msgs[4]),
434 ('/paramerrors/one_positional_args_kwargs/foo/bar/baz',
435 'param1=bar¶m3=baz', error_msgs[2]),
436 ('/paramerrors/one_positional_kwargs/foo',
437 'param1=foo¶m2=bar¶m3=baz', error_msgs[2]),
438 ('/paramerrors/no_positional', 'param1=foo', error_msgs[4]),
439 ('/paramerrors/no_positional_args/boo',
440 'param1=foo', error_msgs[4]),
441 ('/paramerrors/callable_object', 'param1=foo', error_msgs[4]),
442 ):
443 for show_mismatched_params in (True, False):
444 cherrypy.config.update(
445 {'request.show_mismatched_params': show_mismatched_params})
446 self.getPage(uri, method='POST', body=body)
447 self.assertStatus(400)
448 if show_mismatched_params:
449 self.assertInBody(msg)
450 else:
451 self.assertInBody("400 Bad")
452
453
454
455 for uri, body, msg in (
456 ('/paramerrors/one_positional?param2=foo',
457 'param1=foo', error_msgs[3]),
458 ('/paramerrors/one_positional/foo/bar',
459 'param2=foo', error_msgs[1]),
460 ('/paramerrors/one_positional_args/foo/bar?param2=foo',
461 'param3=foo', error_msgs[3]),
462 ('/paramerrors/one_positional_kwargs/foo/bar',
463 'param2=bar¶m3=baz', error_msgs[1]),
464 ('/paramerrors/no_positional?param1=foo',
465 'param2=foo', error_msgs[3]),
466 ('/paramerrors/no_positional_args/boo?param2=foo',
467 'param1=foo', error_msgs[3]),
468 ('/paramerrors/callable_object?param2=bar',
469 'param1=foo', error_msgs[3]),
470 ):
471 for show_mismatched_params in (True, False):
472 cherrypy.config.update(
473 {'request.show_mismatched_params': show_mismatched_params})
474 self.getPage(uri, method='POST', body=body)
475 self.assertStatus(404)
476 if show_mismatched_params:
477 self.assertInBody(msg)
478 else:
479 self.assertInBody("Not Found")
480
481
482
483 for uri in (
484 '/paramerrors/raise_type_error',
485 '/paramerrors/raise_type_error_with_default_param?x=0',
486 '/paramerrors/raise_type_error_with_default_param?x=0&y=0',
487 ):
488 self.getPage(uri, method='GET')
489 self.assertStatus(500)
490 self.assertTrue('Client Error', self.body)
491
493 self.getPage("/error/missing")
494 self.assertStatus(404)
495 self.assertErrorPage(404, "The path '/error/missing' was not found.")
496
497 ignore = helper.webtest.ignored_exceptions
498 ignore.append(ValueError)
499 try:
500 valerr = '\n raise ValueError()\nValueError'
501 self.getPage("/error/page_method")
502 self.assertErrorPage(500, pattern=valerr)
503
504 self.getPage("/error/page_yield")
505 self.assertErrorPage(500, pattern=valerr)
506
507 if (cherrypy.server.protocol_version == "HTTP/1.0" or
508 getattr(cherrypy.server, "using_apache", False)):
509 self.getPage("/error/page_streamed")
510
511
512 self.assertStatus(200)
513 self.assertBody("word up")
514 else:
515
516
517 self.assertRaises((ValueError, IncompleteRead), self.getPage,
518 "/error/page_streamed")
519
520
521 self.getPage("/error/cause_err_in_finalize")
522 msg = "Illegal response status from server ('ZOO' is non-numeric)."
523 self.assertErrorPage(500, msg, None)
524 finally:
525 ignore.pop()
526
527
528 self.getPage('/error/reason_phrase')
529 self.assertStatus("410 Gone fishin'")
530
531
532 self.getPage("/error/custom")
533 self.assertStatus(404)
534 self.assertBody("Hello, world\r\n" + (" " * 499))
535
536
537 self.getPage("/error/custom?err=401")
538 self.assertStatus(401)
539 self.assertBody(
540 "Error 401 Unauthorized - "
541 "Well, I'm very sorry but you haven't paid!")
542
543
544 self.getPage("/error/custom_default")
545 self.assertStatus(500)
546 self.assertBody(
547 "Error 500 Internal Server Error - "
548 "Well, I'm very sorry but you haven't paid!".ljust(513))
549
550
551
552 self.getPage("/error/noexist")
553 self.assertStatus(404)
554 if sys.version_info >= (3, 3):
555 exc_name = "FileNotFoundError"
556 else:
557 exc_name = "IOError"
558 msg = ("No, <b>really</b>, not found!<br />"
559 "In addition, the custom error page failed:\n<br />"
560 "%s: [Errno 2] "
561 "No such file or directory: 'nonexistent.html'") % (exc_name,)
562 self.assertInBody(msg)
563
564 if getattr(cherrypy.server, "using_apache", False):
565 pass
566 else:
567
568 self.getPage("/error/rethrow")
569 self.assertInBody("raise ValueError()")
570
572 e = ('Expect', '100-continue')
573 self.getPage("/headerelements/get_elements?headername=Expect", [e])
574 self.assertBody('100-continue')
575
576 self.getPage("/expect/expectation_failed", [e])
577 self.assertStatus(417)
578
580
581 h = [('Accept', 'audio/*; q=0.2, audio/basic')]
582 self.getPage("/headerelements/get_elements?headername=Accept", h)
583 self.assertStatus(200)
584 self.assertBody("audio/basic\n"
585 "audio/*;q=0.2")
586
587 h = [
588 ('Accept',
589 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')
590 ]
591 self.getPage("/headerelements/get_elements?headername=Accept", h)
592 self.assertStatus(200)
593 self.assertBody("text/x-c\n"
594 "text/html\n"
595 "text/x-dvi;q=0.8\n"
596 "text/plain;q=0.5")
597
598
599 h = [('Accept', 'text/*, text/html, text/html;level=1, */*')]
600 self.getPage("/headerelements/get_elements?headername=Accept", h)
601 self.assertStatus(200)
602 self.assertBody("text/html;level=1\n"
603 "text/html\n"
604 "text/*\n"
605 "*/*")
606
607
608 h = [('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8')]
609 self.getPage(
610 "/headerelements/get_elements?headername=Accept-Charset", h)
611 self.assertStatus("200 OK")
612 self.assertBody("iso-8859-5\n"
613 "unicode-1-1;q=0.8")
614
615
616 h = [('Accept-Encoding', 'gzip;q=1.0, identity; q=0.5, *;q=0')]
617 self.getPage(
618 "/headerelements/get_elements?headername=Accept-Encoding", h)
619 self.assertStatus("200 OK")
620 self.assertBody("gzip;q=1.0\n"
621 "identity;q=0.5\n"
622 "*;q=0")
623
624
625 h = [('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7')]
626 self.getPage(
627 "/headerelements/get_elements?headername=Accept-Language", h)
628 self.assertStatus("200 OK")
629 self.assertBody("da\n"
630 "en-gb;q=0.8\n"
631 "en;q=0.7")
632
633
634
635 self.getPage("/headerelements/get_elements?headername=Content-Type",
636
637 headers=[('Content-Type', 'text/html; charset=utf-8;')])
638 self.assertStatus(200)
639 self.assertBody("text/html;charset=utf-8")
640
642
643
644 self.getPage("/headers/Accept-Charset",
645 headers=[("Accept-Charset", "iso-8859-5"),
646 ("Accept-Charset", "unicode-1-1;q=0.8")])
647 self.assertBody("iso-8859-5, unicode-1-1;q=0.8")
648
649
650 self.getPage("/headers/doubledheaders")
651 self.assertBody("double header test")
652 hnames = [name.title() for name, val in self.headers]
653 for key in ['Content-Length', 'Content-Type', 'Date',
654 'Expires', 'Location', 'Server']:
655 self.assertEqual(hnames.count(key), 1, self.headers)
656
658
659 self.assertEqual(
660 httputil.decode_TEXT(ntou("=?utf-8?q?f=C3=BCr?=")), ntou("f\xfcr"))
661
662 if cherrypy.server.protocol_version == "HTTP/1.1":
663
664 u = ntou('\u212bngstr\xf6m', 'escape')
665 c = ntou("=E2=84=ABngstr=C3=B6m")
666 self.getPage("/headers/ifmatch",
667 [('If-Match', ntou('=?utf-8?q?%s?=') % c)])
668
669 self.assertBody(ntob("\xe2\x84\xabngstr\xc3\xb6m"))
670
671 self.assertHeader("ETag", ntou('=?utf-8?b?4oSrbmdzdHLDtm0=?='))
672
673
674 self.getPage("/headers/ifmatch",
675 [('If-Match', ntou('=?utf-8?q?%s?=') % (c * 10))])
676 self.assertBody(ntob("\xe2\x84\xabngstr\xc3\xb6m") * 10)
677
678 etag = self.assertHeader(
679 "ETag",
680 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
681 '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
682 '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
683 '4oSrbmdzdHLDtm0=?=')
684 self.assertEqual(httputil.decode_TEXT(etag), u * 10)
685
698
700 helper.webtest.methods_with_bodies = ("POST", "PUT", "PROPFIND")
701
702
703 for m in defined_http_methods:
704 self.getPage("/method/", method=m)
705
706
707 if m == "HEAD":
708 self.assertBody("")
709 elif m == "TRACE":
710
711 self.assertEqual(self.body[:5], ntob("TRACE"))
712 else:
713 self.assertBody(m)
714
715
716 self.getPage("/method/parameterized", method="PUT",
717 body="data=on+top+of+other+things")
718 self.assertBody("on top of other things")
719
720
721 b = "one thing on top of another"
722 h = [("Content-Type", "text/plain"),
723 ("Content-Length", str(len(b)))]
724 self.getPage("/method/request_body", headers=h, method="PUT", body=b)
725 self.assertStatus(200)
726 self.assertBody(b)
727
728
729
730 b = ntob("one thing on top of another")
731 self.persistent = True
732 try:
733 conn = self.HTTP_CONN
734 conn.putrequest("PUT", "/method/request_body", skip_host=True)
735 conn.putheader("Host", self.HOST)
736 conn.putheader('Content-Length', str(len(b)))
737 conn.endheaders()
738 conn.send(b)
739 response = conn.response_class(conn.sock, method="PUT")
740 response.begin()
741 self.assertEqual(response.status, 200)
742 self.body = response.read()
743 self.assertBody(b)
744 finally:
745 self.persistent = False
746
747
748
749
750 h = [("Content-Type", "text/plain")]
751 self.getPage("/method/reachable", headers=h, method="PUT")
752 self.assertStatus(411)
753
754
755 b = ('<?xml version="1.0" encoding="utf-8" ?>\n\n'
756 '<propfind xmlns="DAV:"><prop><getlastmodified/>'
757 '</prop></propfind>')
758 h = [('Content-Type', 'text/xml'),
759 ('Content-Length', str(len(b)))]
760 self.getPage("/method/request_body", headers=h,
761 method="PROPFIND", body=b)
762 self.assertStatus(200)
763 self.assertBody(b)
764
765
766 self.getPage("/method/", method="LINK")
767 self.assertStatus(405)
768
769
770 self.getPage("/method/", method="SEARCH")
771 self.assertStatus(501)
772
773
774
775
776
777 self.getPage("/divorce/get?ID=13")
778 self.assertBody('Divorce document 13: empty')
779 self.assertStatus(200)
780 self.getPage("/divorce/", method="GET")
781 self.assertBody('<h1>Choose your document</h1>\n<ul>\n</ul>')
782 self.assertStatus(200)
783
785 if getattr(cherrypy.server, "using_apache", False):
786 return self.skip("skipped due to known Apache differences... ")
787
788 self.getPage("/method/", method="CONNECT")
789 self.assertBody("CONNECT")
790
792 results = []
793 for x in range(20):
794 self.getPage("/threadlocal/")
795 results.append(self.body)
796 self.assertEqual(results, [ntob("None")] * 20)
797