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 id, contents in self.documents.iteritems():
363 yield (" <li><a href='/divorce/get?ID=%s'>%s</a>: %s</li>\n"
364 % (id, id, contents))
365 yield "</ul>"
366 index.exposed = True
367
368 def get(self, ID):
369 return ("Divorce document %s: %s" %
370 (ID, self.documents.get(ID, "empty")))
371 get.exposed = True
372
373 root.divorce = Divorce()
374
375
376 class Cookies(Test):
377
378 def single(self, name):
379 cookie = cherrypy.request.cookie[name]
380 cherrypy.response.cookie[name] = cookie.value
381
382 def multiple(self, names):
383 for name in names:
384 cookie = cherrypy.request.cookie[name]
385 cherrypy.response.cookie[name] = cookie.value
386
387
388 class ThreadLocal(Test):
389
390 def index(self):
391 existing = repr(getattr(cherrypy.request, "asdf", None))
392 cherrypy.request.asdf = "rassfrassin"
393 return existing
394
395 cherrypy.config.update({
396 'log.error_file': log_file,
397 'environment': 'test_suite',
398 'server.max_request_body_size': 200,
399 'server.max_request_header_size': 500,
400 })
401 appconf = {
402 '/': {'log.access_file': log_access_file},
403 '/method': {'request.methods_with_bodies': ("POST", "PUT", "PROPFIND")},
404 }
405 cherrypy.tree.mount(root, config=appconf)
406
407
408
409
410 from cherrypy.test import helper
411
413
415 self.getPage("/params/?thing=a")
416 self.assertBody("'a'")
417
418 self.getPage("/params/?thing=a&thing=b&thing=c")
419 self.assertBody("['a', 'b', 'c']")
420
421
422 ignore = helper.webtest.ignored_exceptions
423 ignore.append(TypeError)
424 try:
425 self.getPage("/params/?notathing=meeting")
426 self.assertInBody("index() got an unexpected keyword argument 'notathing'")
427 finally:
428 ignore.pop()
429
430
431 self.getPage("/params/%d4%20%e3/cheese?Gruy%E8re=Bulgn%e9ville")
432 self.assertBody(r"args: ('\xd4 \xe3', 'cheese') "
433 r"kwargs: {'Gruy\xe8re': 'Bulgn\xe9ville'}")
434
435
436 self.getPage("/params/code?url=http%3A//cherrypy.org/index%3Fa%3D1%26b%3D2")
437 self.assertBody(r"args: ('code',) "
438 r"kwargs: {'url': 'http://cherrypy.org/index?a=1&b=2'}")
439
440
441 self.getPage("/params/ismap?223,114")
442 self.assertBody("Coordinates: 223, 114")
443
466
468 f = open(log_access_file, "wb")
469 f.write("")
470 f.close()
471 f = open(log_file, "wb")
472 f.write("")
473 f.close()
474
475 self.getPage("/flatten/as_string")
476 self.assertBody('content')
477 self.assertStatus(200)
478
479 self.getPage("/flatten/as_yield")
480 self.assertBody('content')
481 self.assertStatus(200)
482
483 data = open(log_access_file, "rb").readlines()
484
485 host = self.HOST
486 if not host:
487
488
489 host = "127.0.0.1"
490 intro = '%s - - [' % host
491
492 if not data[0].startswith(intro):
493 self.fail("%r doesn't start with %r" % (data[0], intro))
494 haslength = False
495 for k, v in self.headers:
496 if k.lower() == 'content-length':
497 haslength = True
498 line = data[-2].strip()
499 if haslength:
500 if not line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 7 "" ""'
501 % self.prefix()):
502 self.fail(line)
503 else:
504 if not line.endswith('] "GET %s/flatten/as_string HTTP/1.1" 200 - "" ""'
505 % self.prefix()):
506 self.fail(line)
507
508 if not data[-1].startswith(intro):
509 self.fail("%r doesn't start with %r" % (data[-1], intro))
510 haslength = False
511 for k, v in self.headers:
512 if k.lower() == 'content-length':
513 haslength = True
514 line = data[-1].strip()
515 if haslength:
516 self.assert_(line.endswith('] "GET %s/flatten/as_yield HTTP/1.1" 200 7 "" ""'
517 % self.prefix()))
518 else:
519 self.assert_(line.endswith('] "GET %s/flatten/as_yield HTTP/1.1" 200 - "" ""'
520 % self.prefix()))
521
522 ignore = helper.webtest.ignored_exceptions
523 ignore.append(ValueError)
524 try:
525
526 self.getPage("/error/page_method")
527 self.assertInBody("raise ValueError()")
528 data = open(log_file, "rb").readlines()
529 self.assertEqual(data[0].strip().endswith('HTTP Traceback (most recent call last):'), True)
530 self.assertEqual(data[-3].strip().endswith('raise ValueError()'), True)
531 finally:
532 ignore.pop()
533
535
536
537
538 self.getPage("/redirect?id=3")
539 self.assertStatus(('302 Found', '303 See Other'))
540 self.assertInBody("<a href='%s/redirect/?id=3'>"
541 "%s/redirect/?id=3</a>" % (self.base(), self.base()))
542
543 if self.prefix():
544
545
546 self.getPage("")
547 self.assertStatus(('302 Found', '303 See Other'))
548 self.assertInBody("<a href='%s/'>%s/</a>" %
549 (self.base(), self.base()))
550
551
552
553
554 self.getPage("/redirect/by_code/?code=307")
555 self.assertStatus(('302 Found', '303 See Other'))
556 self.assertInBody("<a href='%s/redirect/by_code?code=307'>"
557 "%s/redirect/by_code?code=307</a>"
558 % (self.base(), self.base()))
559
560
561
562
563 self.getPage('/url?path_info=page1')
564 self.assertBody('%s/url/page1' % self.base())
565 self.getPage('/url/leaf/?path_info=page1')
566 self.assertBody('%s/url/page1' % self.base())
567
623
666
668 for url in ["/flatten/as_string", "/flatten/as_list",
669 "/flatten/as_yield", "/flatten/as_dblyield",
670 "/flatten/as_refyield"]:
671 self.getPage(url)
672 self.assertBody('content')
673
675 self.getPage("/error/missing")
676 self.assertStatus(404)
677 self.assertErrorPage(404, "The path '/error/missing' was not found.")
678
679 ignore = helper.webtest.ignored_exceptions
680 ignore.append(ValueError)
681 try:
682 valerr = '\n raise ValueError()\nValueError'
683 self.getPage("/error/page_method")
684 self.assertErrorPage(500, pattern=valerr)
685
686 self.getPage("/error/page_yield")
687 self.assertErrorPage(500, pattern=valerr)
688
689 self.getPage("/error/page_streamed")
690
691
692 self.assertStatus(200)
693 self.assertBody("word upUnrecoverable error in the server.")
694
695
696 self.getPage("/error/cause_err_in_finalize")
697 msg = "Illegal response status from server ('ZOO' is non-numeric)."
698 self.assertErrorPage(500, msg, None)
699 finally:
700 ignore.pop()
701
702
703 self.getPage("/error/custom")
704 self.assertStatus(404)
705 self.assertBody("Hello, world\r\n" + (" " * 499))
706
707
708
709 self.getPage("/error/noexist")
710 self.assertStatus(404)
711 msg = ("No, <b>really</b>, not found!<br />"
712 "In addition, the custom error page failed:\n<br />"
713 "[Errno 2] No such file or directory: 'nonexistent.html'")
714 self.assertInBody(msg)
715
716 if (hasattr(self, 'harness') and
717 "modpython" in self.harness.__class__.__name__.lower()):
718 pass
719 else:
720
721 self.getPage("/error/rethrow")
722 self.assertInBody("raise ValueError()")
723
725 self.getPage("/ranges/get_ranges?bytes=3-6")
726 self.assertBody("[(3, 7)]")
727
728
729 self.getPage("/ranges/get_ranges?bytes=2-4,-1")
730 self.assertBody("[(2, 5), (7, 8)]")
731
732
733 if cherrypy.server.protocol_version == "HTTP/1.1":
734 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')])
735 self.assertStatus(206)
736 self.assertHeader("Content-Type", "text/html")
737 self.assertHeader("Content-Range", "bytes 2-5/14")
738 self.assertBody("llo,")
739
740
741 self.getPage("/ranges/slice_file", [('Range', 'bytes=4-6,2-5')])
742 self.assertStatus(206)
743 ct = self.assertHeader("Content-Type")
744 expected_type = "multipart/byteranges; boundary="
745 self.assert_(ct.startswith(expected_type))
746 boundary = ct[len(expected_type):]
747 expected_body = ("\r\n--%s\r\n"
748 "Content-type: text/html\r\n"
749 "Content-range: bytes 4-6/14\r\n"
750 "\r\n"
751 "o, \r\n"
752 "--%s\r\n"
753 "Content-type: text/html\r\n"
754 "Content-range: bytes 2-5/14\r\n"
755 "\r\n"
756 "llo,\r\n"
757 "--%s--\r\n" % (boundary, boundary, boundary))
758 self.assertBody(expected_body)
759 self.assertHeader("Content-Length")
760
761
762 self.getPage("/ranges/slice_file", [('Range', 'bytes=2300-2900')])
763 self.assertStatus(416)
764
765
766
767 self.assertHeader("Content-Range", "bytes */14")
768 elif cherrypy.server.protocol_version == "HTTP/1.0":
769
770 self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')])
771 self.assertStatus(200)
772 self.assertBody("Hello, world\r\n")
773
775 e = ('Expect', '100-continue')
776 self.getPage("/headerelements/get_elements?headername=Expect", [e])
777 self.assertBody('100-continue')
778
779 self.getPage("/expect/expectation_failed", [('Content-Length', '200'), e])
780 self.assertStatus(417)
781
783
784 h = [('Accept', 'audio/*; q=0.2, audio/basic')]
785 self.getPage("/headerelements/get_elements?headername=Accept", h)
786 self.assertStatus(200)
787 self.assertBody("audio/basic\n"
788 "audio/*;q=0.2")
789
790 h = [('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')]
791 self.getPage("/headerelements/get_elements?headername=Accept", h)
792 self.assertStatus(200)
793 self.assertBody("text/x-c\n"
794 "text/html\n"
795 "text/x-dvi;q=0.8\n"
796 "text/plain;q=0.5")
797
798
799 h = [('Accept', 'text/*, text/html, text/html;level=1, */*')]
800 self.getPage("/headerelements/get_elements?headername=Accept", h)
801 self.assertStatus(200)
802 self.assertBody("text/html;level=1\n"
803 "text/html\n"
804 "text/*\n"
805 "*/*")
806
807
808 h = [('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8')]
809 self.getPage("/headerelements/get_elements?headername=Accept-Charset", h)
810 self.assertStatus("200 OK")
811 self.assertBody("iso-8859-5\n"
812 "unicode-1-1;q=0.8")
813
814
815 h = [('Accept-Encoding', 'gzip;q=1.0, identity; q=0.5, *;q=0')]
816 self.getPage("/headerelements/get_elements?headername=Accept-Encoding", h)
817 self.assertStatus("200 OK")
818 self.assertBody("gzip;q=1.0\n"
819 "identity;q=0.5\n"
820 "*;q=0")
821
822
823 h = [('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7')]
824 self.getPage("/headerelements/get_elements?headername=Accept-Language", h)
825 self.assertStatus("200 OK")
826 self.assertBody("da\n"
827 "en-gb;q=0.8\n"
828 "en;q=0.7")
829
831
832 self.getPage("/headers/doubledheaders")
833 self.assertBody("double header test")
834 hnames = [name.title() for name, val in self.headers]
835 for key in ['Content-Length', 'Content-Type', 'Date',
836 'Expires', 'Location', 'Server']:
837 self.assertEqual(hnames.count(key), 1)
838
839 if cherrypy.server.protocol_version == "HTTP/1.1":
840
841 c = "=E2=84=ABngstr=C3=B6m"
842 self.getPage("/headers/ifmatch", [('If-Match', '=?utf-8?q?%s?=' % c)])
843 self.assertBody("u'\\u212bngstr\\xf6m'")
844 self.assertHeader("ETag", '=?utf-8?b?4oSrbmdzdHLDtm0=?=')
845
846
847 self.getPage("/headers/ifmatch",
848 [('If-Match', '=?utf-8?q?%s?=' % (c * 10))])
849 self.assertBody("u'%s'" % ('\\u212bngstr\\xf6m' * 10))
850 self.assertHeader("ETag",
851 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt4oSrbmdzdHLDtm0=?='
852 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt4oSrbmdzdHLDtm0=?='
853 '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2bQ==?=')
854
855
856
857 self.getPage("/headers/Accept-Charset",
858 headers=[("Accept-Charset", "iso-8859-5"),
859 ("Accept-Charset", "unicode-1-1;q=0.8")])
860 self.assertBody("iso-8859-5, unicode-1-1;q=0.8")
861
862
863
864 self.getPage("/headers/Content-Type",
865 headers=[])
866 self.assertStatus(500)
867
868
869
870 self.getPage("/headers/Content-Type",
871 headers=[("Content-type", "application/json")])
872 self.assertBody("application/json")
873
875 helper.webtest.methods_with_bodies = ("POST", "PUT", "PROPFIND")
876
877
878 for m in defined_http_methods:
879 self.getPage("/method/", method=m)
880
881
882 if m == "HEAD":
883 self.assertBody("")
884 elif m == "TRACE":
885
886 self.assertEqual(self.body[:5], "TRACE")
887 else:
888 self.assertBody(m)
889
890
891 self.getPage("/method/parameterized", method="PUT",
892 body="data=on+top+of+other+things")
893 self.assertBody("on top of other things")
894
895
896 b = "one thing on top of another"
897 h = [("Content-Type", "text/plain"),
898 ("Content-Length", str(len(b)))]
899 self.getPage("/method/request_body", headers=h, method="PUT", body=b)
900 self.assertStatus(200)
901 self.assertBody(b)
902
903
904
905
906 h = [("Content-Type", "text/plain")]
907 self.getPage("/method/reachable", headers=h, method="PUT")
908 self.assertBody("success")
909
910
911 b = ('<?xml version="1.0" encoding="utf-8" ?>\n\n'
912 '<propfind xmlns="DAV:"><prop><getlastmodified/>'
913 '</prop></propfind>')
914 h = [('Content-Type', 'text/xml'),
915 ('Content-Length', str(len(b)))]
916 self.getPage("/method/request_body", headers=h, method="PROPFIND", body=b)
917 self.assertStatus(200)
918 self.assertBody(b)
919
920
921 self.getPage("/method/", method="LINK")
922 self.assertStatus(405)
923
924
925 self.getPage("/method/", method="SEARCH")
926 self.assertStatus(501)
927
928
929
930
931
932 self.getPage("/divorce/get?ID=13")
933 self.assertBody('Divorce document 13: empty')
934 self.assertStatus(200)
935 self.getPage("/divorce/", method="GET")
936 self.assertBody('<h1>Choose your document</h1>\n<ul>\n</ul>')
937 self.assertStatus(200)
938
948
950 import sys
951 if sys.version_info >= (2, 5):
952 self.getPage("/cookies/single?name=First",
953 [('Cookie', 'First=Dinsdale;')])
954 self.assertHeader('Set-Cookie', 'First=Dinsdale')
955
956 self.getPage("/cookies/multiple?names=First&names=Last",
957 [('Cookie', 'First=Dinsdale; Last=Piranha;'),
958 ])
959 self.assertHeader('Set-Cookie', 'First=Dinsdale')
960 self.assertHeader('Set-Cookie', 'Last=Piranha')
961 else:
962 self.getPage("/cookies/single?name=First",
963 [('Cookie', 'First=Dinsdale;')])
964 self.assertHeader('Set-Cookie', 'First=Dinsdale;')
965
966 self.getPage("/cookies/multiple?names=First&names=Last",
967 [('Cookie', 'First=Dinsdale; Last=Piranha;'),
968 ])
969 self.assertHeader('Set-Cookie', 'First=Dinsdale;')
970 self.assertHeader('Set-Cookie', 'Last=Piranha;')
971
973 self.getPage("/", headers=[('From', "x" * 500)])
974 self.assertStatus(413)
975
976
977
978
979 lines256 = "x" * 248
980 self.getPage("/",
981 headers=[('Host', '%s:%s' % (self.HOST, self.PORT)),
982 ('From', lines256)])
983
984
985 body = """--x
986 Content-Disposition: form-data; name="file"; filename="hello.txt"
987 Content-Type: text/plain
988
989 %s
990 --x--
991 """
992 b = body % ("x" * 96)
993 h = [("Content-type", "multipart/form-data; boundary=x"),
994 ("Content-Length", len(b))]
995 self.getPage('/upload', h, "POST", b)
996 self.assertBody('Size: 96')
997
998 b = body % ("x" * 200)
999 h = [("Content-type", "multipart/form-data; boundary=x"),
1000 ("Content-Length", len(b))]
1001 self.getPage('/upload', h, "POST", b)
1002 self.assertStatus(413)
1003
1005 results = []
1006 for x in xrange(20):
1007 self.getPage("/threadlocal/")
1008 results.append(self.body)
1009 self.assertEqual(results, ["None"] * 20)
1010
1012 self.getPage('/')
1013 self.assertHeader('Content-Type', 'text/html')
1014 self.getPage('/defct/plain')
1015 self.getPage('/')
1016 self.assertHeader('Content-Type', 'text/plain')
1017 self.getPage('/defct/html')
1018
1020
1021 self.getPage('/url/leaf?path_info=page1')
1022 self.assertBody('%s/url/page1' % self.base())
1023 self.getPage('/url/?path_info=page1')
1024 self.assertBody('%s/url/page1' % self.base())
1025
1026
1027 self.getPage('/url/leaf?path_info=/page1')
1028 self.assertBody('%s/page1' % self.base())
1029 self.getPage('/url/?path_info=/page1')
1030 self.assertBody('%s/page1' % self.base())
1031
1032
1033 self.getPage('/url/leaf?path_info=./page1')
1034 self.assertBody('%s/url/page1' % self.base())
1035 self.getPage('/url/leaf?path_info=other/./page1')
1036 self.assertBody('%s/url/other/page1' % self.base())
1037 self.getPage('/url/?path_info=/other/./page1')
1038 self.assertBody('%s/other/page1' % self.base())
1039
1040
1041 self.getPage('/url/leaf?path_info=../page1')
1042 self.assertBody('%s/page1' % self.base())
1043 self.getPage('/url/leaf?path_info=other/../page1')
1044 self.assertBody('%s/url/page1' % self.base())
1045 self.getPage('/url/leaf?path_info=/other/../page1')
1046 self.assertBody('%s/page1' % self.base())
1047
1048
1049 self.getPage('/url/?path_info=page1&relative=True')
1050 self.assertBody('page1')
1051 self.getPage('/url/leaf?path_info=/page1&relative=True')
1052 self.assertBody('../page1')
1053 self.getPage('/url/leaf?path_info=../page1&relative=True')
1054 self.assertBody('../page1')
1055 self.getPage('/url/?path_info=other/../page1&relative=True')
1056 self.assertBody('page1')
1057
1058
1059 if __name__ == '__main__':
1060 setup_server()
1061 helper.testmain()
1062