Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/cherrypy/test
diff options
context:
space:
mode:
Diffstat (limited to 'cherrypy/test')
-rwxr-xr-xcherrypy/test/__init__.py25
-rwxr-xr-xcherrypy/test/_test_decorators.py41
-rwxr-xr-xcherrypy/test/_test_states_demo.py66
-rwxr-xr-xcherrypy/test/benchmark.py409
-rwxr-xr-xcherrypy/test/checkerdemo.py47
-rwxr-xr-xcherrypy/test/helper.py476
-rwxr-xr-xcherrypy/test/logtest.py181
-rwxr-xr-xcherrypy/test/modfastcgi.py135
-rwxr-xr-xcherrypy/test/modfcgid.py125
-rwxr-xr-xcherrypy/test/modpy.py163
-rwxr-xr-xcherrypy/test/modwsgi.py148
-rwxr-xr-xcherrypy/test/sessiondemo.py153
-rw-r--r--cherrypy/test/static/dirback.jpgbin18238 -> 0 bytes
-rw-r--r--cherrypy/test/static/index.html1
-rw-r--r--cherrypy/test/style.css1
-rw-r--r--cherrypy/test/test.pem38
-rwxr-xr-xcherrypy/test/test_auth_basic.py79
-rwxr-xr-xcherrypy/test/test_auth_digest.py115
-rwxr-xr-xcherrypy/test/test_bus.py263
-rwxr-xr-xcherrypy/test/test_caching.py329
-rwxr-xr-xcherrypy/test/test_config.py249
-rwxr-xr-xcherrypy/test/test_config_server.py121
-rwxr-xr-xcherrypy/test/test_conn.py734
-rwxr-xr-xcherrypy/test/test_core.py617
-rwxr-xr-xcherrypy/test/test_dynamicobjectmapping.py403
-rwxr-xr-xcherrypy/test/test_encoding.py363
-rwxr-xr-xcherrypy/test/test_etags.py81
-rwxr-xr-xcherrypy/test/test_http.py168
-rwxr-xr-xcherrypy/test/test_httpauth.py151
-rwxr-xr-xcherrypy/test/test_httplib.py29
-rwxr-xr-xcherrypy/test/test_json.py79
-rwxr-xr-xcherrypy/test/test_logging.py149
-rwxr-xr-xcherrypy/test/test_mime.py128
-rwxr-xr-xcherrypy/test/test_misc_tools.py202
-rwxr-xr-xcherrypy/test/test_objectmapping.py403
-rwxr-xr-xcherrypy/test/test_proxy.py129
-rwxr-xr-xcherrypy/test/test_refleaks.py119
-rwxr-xr-xcherrypy/test/test_request_obj.py722
-rwxr-xr-xcherrypy/test/test_routes.py69
-rwxr-xr-xcherrypy/test/test_session.py464
-rwxr-xr-xcherrypy/test/test_sessionauthenticate.py62
-rwxr-xr-xcherrypy/test/test_states.py436
-rwxr-xr-xcherrypy/test/test_static.py300
-rwxr-xr-xcherrypy/test/test_tools.py393
-rwxr-xr-xcherrypy/test/test_tutorials.py201
-rwxr-xr-xcherrypy/test/test_virtualhost.py107
-rwxr-xr-xcherrypy/test/test_wsgi_ns.py80
-rwxr-xr-xcherrypy/test/test_wsgi_vhost.py36
-rwxr-xr-xcherrypy/test/test_wsgiapps.py111
-rwxr-xr-xcherrypy/test/test_xmlrpc.py172
-rwxr-xr-xcherrypy/test/webtest.py535
51 files changed, 0 insertions, 10608 deletions
diff --git a/cherrypy/test/__init__.py b/cherrypy/test/__init__.py
deleted file mode 100755
index e4c400d..0000000
--- a/cherrypy/test/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""Regression test suite for CherryPy.
-
-Run 'nosetests -s test/' to exercise all tests.
-
-The '-s' flag instructs nose to output stdout messages, wihch is crucial to
-the 'interactive' mode of webtest.py. If you run these tests without the '-s'
-flag, don't be surprised if the test seems to hang: it's waiting for your
-interactive input.
-"""
-
-import sys
-def newexit():
- raise SystemExit('Exit called')
-
-def setup():
- # We want to monkey patch sys.exit so that we can get some
- # information about where exit is being called.
- newexit._old = sys.exit
- sys.exit = newexit
-
-def teardown():
- try:
- sys.exit = sys.exit._old
- except AttributeError:
- sys.exit = sys._exit
diff --git a/cherrypy/test/_test_decorators.py b/cherrypy/test/_test_decorators.py
deleted file mode 100755
index 5bcbc1e..0000000
--- a/cherrypy/test/_test_decorators.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""Test module for the @-decorator syntax, which is version-specific"""
-
-from cherrypy import expose, tools
-from cherrypy._cpcompat import ntob
-
-
-class ExposeExamples(object):
-
- @expose
- def no_call(self):
- return "Mr E. R. Bradshaw"
-
- @expose()
- def call_empty(self):
- return "Mrs. B.J. Smegma"
-
- @expose("call_alias")
- def nesbitt(self):
- return "Mr Nesbitt"
-
- @expose(["alias1", "alias2"])
- def andrews(self):
- return "Mr Ken Andrews"
-
- @expose(alias="alias3")
- def watson(self):
- return "Mr. and Mrs. Watson"
-
-
-class ToolExamples(object):
-
- @expose
- @tools.response_headers(headers=[('Content-Type', 'application/data')])
- def blah(self):
- yield ntob("blah")
- # This is here to demonstrate that _cp_config = {...} overwrites
- # the _cp_config attribute added by the Tool decorator. You have
- # to write _cp_config[k] = v or _cp_config.update(...) instead.
- blah._cp_config['response.stream'] = True
-
-
diff --git a/cherrypy/test/_test_states_demo.py b/cherrypy/test/_test_states_demo.py
deleted file mode 100755
index 3f8f196..0000000
--- a/cherrypy/test/_test_states_demo.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import os
-import sys
-import time
-starttime = time.time()
-
-import cherrypy
-
-
-class Root:
-
- def index(self):
- return "Hello World"
- index.exposed = True
-
- def mtimes(self):
- return repr(cherrypy.engine.publish("Autoreloader", "mtimes"))
- mtimes.exposed = True
-
- def pid(self):
- return str(os.getpid())
- pid.exposed = True
-
- def start(self):
- return repr(starttime)
- start.exposed = True
-
- def exit(self):
- # This handler might be called before the engine is STARTED if an
- # HTTP worker thread handles it before the HTTP server returns
- # control to engine.start. We avoid that race condition here
- # by waiting for the Bus to be STARTED.
- cherrypy.engine.wait(state=cherrypy.engine.states.STARTED)
- cherrypy.engine.exit()
- exit.exposed = True
-
-
-def unsub_sig():
- cherrypy.log("unsubsig: %s" % cherrypy.config.get('unsubsig', False))
- if cherrypy.config.get('unsubsig', False):
- cherrypy.log("Unsubscribing the default cherrypy signal handler")
- cherrypy.engine.signal_handler.unsubscribe()
- try:
- from signal import signal, SIGTERM
- except ImportError:
- pass
- else:
- def old_term_handler(signum=None, frame=None):
- cherrypy.log("I am an old SIGTERM handler.")
- sys.exit(0)
- cherrypy.log("Subscribing the new one.")
- signal(SIGTERM, old_term_handler)
-cherrypy.engine.subscribe('start', unsub_sig, priority=100)
-
-
-def starterror():
- if cherrypy.config.get('starterror', False):
- zerodiv = 1 / 0
-cherrypy.engine.subscribe('start', starterror, priority=6)
-
-def log_test_case_name():
- if cherrypy.config.get('test_case_name', False):
- cherrypy.log("STARTED FROM: %s" % cherrypy.config.get('test_case_name'))
-cherrypy.engine.subscribe('start', log_test_case_name, priority=6)
-
-
-cherrypy.tree.mount(Root(), '/', {'/': {}})
diff --git a/cherrypy/test/benchmark.py b/cherrypy/test/benchmark.py
deleted file mode 100755
index 90536a5..0000000
--- a/cherrypy/test/benchmark.py
+++ /dev/null
@@ -1,409 +0,0 @@
-"""CherryPy Benchmark Tool
-
- Usage:
- benchmark.py --null --notests --help --cpmodpy --modpython --ab=path --apache=path
-
- --null: use a null Request object (to bench the HTTP server only)
- --notests: start the server but do not run the tests; this allows
- you to check the tested pages with a browser
- --help: show this help message
- --cpmodpy: run tests via apache on 8080 (with the builtin _cpmodpy)
- --modpython: run tests via apache on 8080 (with modpython_gateway)
- --ab=path: Use the ab script/executable at 'path' (see below)
- --apache=path: Use the apache script/exe at 'path' (see below)
-
- To run the benchmarks, the Apache Benchmark tool "ab" must either be on
- your system path, or specified via the --ab=path option.
-
- To run the modpython tests, the "apache" executable or script must be
- on your system path, or provided via the --apache=path option. On some
- platforms, "apache" may be called "apachectl" or "apache2ctl"--create
- a symlink to them if needed.
-"""
-
-import getopt
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-import re
-import sys
-import time
-import traceback
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-from cherrypy import _cperror, _cpmodpy
-from cherrypy.lib import httputil
-
-
-AB_PATH = ""
-APACHE_PATH = "apache"
-SCRIPT_NAME = "/cpbench/users/rdelon/apps/blog"
-
-__all__ = ['ABSession', 'Root', 'print_report',
- 'run_standard_benchmarks', 'safe_threads',
- 'size_report', 'startup', 'thread_report',
- ]
-
-size_cache = {}
-
-class Root:
-
- def index(self):
- return """<html>
-<head>
- <title>CherryPy Benchmark</title>
-</head>
-<body>
- <ul>
- <li><a href="hello">Hello, world! (14 byte dynamic)</a></li>
- <li><a href="static/index.html">Static file (14 bytes static)</a></li>
- <li><form action="sizer">Response of length:
- <input type='text' name='size' value='10' /></form>
- </li>
- </ul>
-</body>
-</html>"""
- index.exposed = True
-
- def hello(self):
- return "Hello, world\r\n"
- hello.exposed = True
-
- def sizer(self, size):
- resp = size_cache.get(size, None)
- if resp is None:
- size_cache[size] = resp = "X" * int(size)
- return resp
- sizer.exposed = True
-
-
-cherrypy.config.update({
- 'log.error.file': '',
- 'environment': 'production',
- 'server.socket_host': '127.0.0.1',
- 'server.socket_port': 8080,
- 'server.max_request_header_size': 0,
- 'server.max_request_body_size': 0,
- 'engine.deadlock_poll_freq': 0,
- })
-
-# Cheat mode on ;)
-del cherrypy.config['tools.log_tracebacks.on']
-del cherrypy.config['tools.log_headers.on']
-del cherrypy.config['tools.trailing_slash.on']
-
-appconf = {
- '/static': {
- 'tools.staticdir.on': True,
- 'tools.staticdir.dir': 'static',
- 'tools.staticdir.root': curdir,
- },
- }
-app = cherrypy.tree.mount(Root(), SCRIPT_NAME, appconf)
-
-
-class NullRequest:
- """A null HTTP request class, returning 200 and an empty body."""
-
- def __init__(self, local, remote, scheme="http"):
- pass
-
- def close(self):
- pass
-
- def run(self, method, path, query_string, protocol, headers, rfile):
- cherrypy.response.status = "200 OK"
- cherrypy.response.header_list = [("Content-Type", 'text/html'),
- ("Server", "Null CherryPy"),
- ("Date", httputil.HTTPDate()),
- ("Content-Length", "0"),
- ]
- cherrypy.response.body = [""]
- return cherrypy.response
-
-
-class NullResponse:
- pass
-
-
-class ABSession:
- """A session of 'ab', the Apache HTTP server benchmarking tool.
-
-Example output from ab:
-
-This is ApacheBench, Version 2.0.40-dev <$Revision: 1.121.2.1 $> apache-2.0
-Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
-Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
-
-Benchmarking 127.0.0.1 (be patient)
-Completed 100 requests
-Completed 200 requests
-Completed 300 requests
-Completed 400 requests
-Completed 500 requests
-Completed 600 requests
-Completed 700 requests
-Completed 800 requests
-Completed 900 requests
-
-
-Server Software: CherryPy/3.1beta
-Server Hostname: 127.0.0.1
-Server Port: 8080
-
-Document Path: /static/index.html
-Document Length: 14 bytes
-
-Concurrency Level: 10
-Time taken for tests: 9.643867 seconds
-Complete requests: 1000
-Failed requests: 0
-Write errors: 0
-Total transferred: 189000 bytes
-HTML transferred: 14000 bytes
-Requests per second: 103.69 [#/sec] (mean)
-Time per request: 96.439 [ms] (mean)
-Time per request: 9.644 [ms] (mean, across all concurrent requests)
-Transfer rate: 19.08 [Kbytes/sec] received
-
-Connection Times (ms)
- min mean[+/-sd] median max
-Connect: 0 0 2.9 0 10
-Processing: 20 94 7.3 90 130
-Waiting: 0 43 28.1 40 100
-Total: 20 95 7.3 100 130
-
-Percentage of the requests served within a certain time (ms)
- 50% 100
- 66% 100
- 75% 100
- 80% 100
- 90% 100
- 95% 100
- 98% 100
- 99% 110
- 100% 130 (longest request)
-Finished 1000 requests
-"""
-
- parse_patterns = [('complete_requests', 'Completed',
- ntob(r'^Complete requests:\s*(\d+)')),
- ('failed_requests', 'Failed',
- ntob(r'^Failed requests:\s*(\d+)')),
- ('requests_per_second', 'req/sec',
- ntob(r'^Requests per second:\s*([0-9.]+)')),
- ('time_per_request_concurrent', 'msec/req',
- ntob(r'^Time per request:\s*([0-9.]+).*concurrent requests\)$')),
- ('transfer_rate', 'KB/sec',
- ntob(r'^Transfer rate:\s*([0-9.]+)')),
- ]
-
- def __init__(self, path=SCRIPT_NAME + "/hello", requests=1000, concurrency=10):
- self.path = path
- self.requests = requests
- self.concurrency = concurrency
-
- def args(self):
- port = cherrypy.server.socket_port
- assert self.concurrency > 0
- assert self.requests > 0
- # Don't use "localhost".
- # Cf http://mail.python.org/pipermail/python-win32/2008-March/007050.html
- return ("-k -n %s -c %s http://127.0.0.1:%s%s" %
- (self.requests, self.concurrency, port, self.path))
-
- def run(self):
- # Parse output of ab, setting attributes on self
- try:
- self.output = _cpmodpy.read_process(AB_PATH or "ab", self.args())
- except:
- print(_cperror.format_exc())
- raise
-
- for attr, name, pattern in self.parse_patterns:
- val = re.search(pattern, self.output, re.MULTILINE)
- if val:
- val = val.group(1)
- setattr(self, attr, val)
- else:
- setattr(self, attr, None)
-
-
-safe_threads = (25, 50, 100, 200, 400)
-if sys.platform in ("win32",):
- # For some reason, ab crashes with > 50 threads on my Win2k laptop.
- safe_threads = (10, 20, 30, 40, 50)
-
-
-def thread_report(path=SCRIPT_NAME + "/hello", concurrency=safe_threads):
- sess = ABSession(path)
- attrs, names, patterns = list(zip(*sess.parse_patterns))
- avg = dict.fromkeys(attrs, 0.0)
-
- yield ('threads',) + names
- for c in concurrency:
- sess.concurrency = c
- sess.run()
- row = [c]
- for attr in attrs:
- val = getattr(sess, attr)
- if val is None:
- print(sess.output)
- row = None
- break
- val = float(val)
- avg[attr] += float(val)
- row.append(val)
- if row:
- yield row
-
- # Add a row of averages.
- yield ["Average"] + [str(avg[attr] / len(concurrency)) for attr in attrs]
-
-def size_report(sizes=(10, 100, 1000, 10000, 100000, 100000000),
- concurrency=50):
- sess = ABSession(concurrency=concurrency)
- attrs, names, patterns = list(zip(*sess.parse_patterns))
- yield ('bytes',) + names
- for sz in sizes:
- sess.path = "%s/sizer?size=%s" % (SCRIPT_NAME, sz)
- sess.run()
- yield [sz] + [getattr(sess, attr) for attr in attrs]
-
-def print_report(rows):
- for row in rows:
- print("")
- for i, val in enumerate(row):
- sys.stdout.write(str(val).rjust(10) + " | ")
- print("")
-
-
-def run_standard_benchmarks():
- print("")
- print("Client Thread Report (1000 requests, 14 byte response body, "
- "%s server threads):" % cherrypy.server.thread_pool)
- print_report(thread_report())
-
- print("")
- print("Client Thread Report (1000 requests, 14 bytes via staticdir, "
- "%s server threads):" % cherrypy.server.thread_pool)
- print_report(thread_report("%s/static/index.html" % SCRIPT_NAME))
-
- print("")
- print("Size Report (1000 requests, 50 client threads, "
- "%s server threads):" % cherrypy.server.thread_pool)
- print_report(size_report())
-
-
-# modpython and other WSGI #
-
-def startup_modpython(req=None):
- """Start the CherryPy app server in 'serverless' mode (for modpython/WSGI)."""
- if cherrypy.engine.state == cherrypy._cpengine.STOPPED:
- if req:
- if "nullreq" in req.get_options():
- cherrypy.engine.request_class = NullRequest
- cherrypy.engine.response_class = NullResponse
- ab_opt = req.get_options().get("ab", "")
- if ab_opt:
- global AB_PATH
- AB_PATH = ab_opt
- cherrypy.engine.start()
- if cherrypy.engine.state == cherrypy._cpengine.STARTING:
- cherrypy.engine.wait()
- return 0 # apache.OK
-
-
-def run_modpython(use_wsgi=False):
- print("Starting mod_python...")
- pyopts = []
-
- # Pass the null and ab=path options through Apache
- if "--null" in opts:
- pyopts.append(("nullreq", ""))
-
- if "--ab" in opts:
- pyopts.append(("ab", opts["--ab"]))
-
- s = _cpmodpy.ModPythonServer
- if use_wsgi:
- pyopts.append(("wsgi.application", "cherrypy::tree"))
- pyopts.append(("wsgi.startup", "cherrypy.test.benchmark::startup_modpython"))
- handler = "modpython_gateway::handler"
- s = s(port=8080, opts=pyopts, apache_path=APACHE_PATH, handler=handler)
- else:
- pyopts.append(("cherrypy.setup", "cherrypy.test.benchmark::startup_modpython"))
- s = s(port=8080, opts=pyopts, apache_path=APACHE_PATH)
-
- try:
- s.start()
- run()
- finally:
- s.stop()
-
-
-
-if __name__ == '__main__':
- longopts = ['cpmodpy', 'modpython', 'null', 'notests',
- 'help', 'ab=', 'apache=']
- try:
- switches, args = getopt.getopt(sys.argv[1:], "", longopts)
- opts = dict(switches)
- except getopt.GetoptError:
- print(__doc__)
- sys.exit(2)
-
- if "--help" in opts:
- print(__doc__)
- sys.exit(0)
-
- if "--ab" in opts:
- AB_PATH = opts['--ab']
-
- if "--notests" in opts:
- # Return without stopping the server, so that the pages
- # can be tested from a standard web browser.
- def run():
- port = cherrypy.server.socket_port
- print("You may now open http://127.0.0.1:%s%s/" %
- (port, SCRIPT_NAME))
-
- if "--null" in opts:
- print("Using null Request object")
- else:
- def run():
- end = time.time() - start
- print("Started in %s seconds" % end)
- if "--null" in opts:
- print("\nUsing null Request object")
- try:
- try:
- run_standard_benchmarks()
- except:
- print(_cperror.format_exc())
- raise
- finally:
- cherrypy.engine.exit()
-
- print("Starting CherryPy app server...")
-
- class NullWriter(object):
- """Suppresses the printing of socket errors."""
- def write(self, data):
- pass
- sys.stderr = NullWriter()
-
- start = time.time()
-
- if "--cpmodpy" in opts:
- run_modpython()
- elif "--modpython" in opts:
- run_modpython(use_wsgi=True)
- else:
- if "--null" in opts:
- cherrypy.server.request_class = NullRequest
- cherrypy.server.response_class = NullResponse
-
- cherrypy.engine.start_with_callback(run)
- cherrypy.engine.block()
diff --git a/cherrypy/test/checkerdemo.py b/cherrypy/test/checkerdemo.py
deleted file mode 100755
index 32a7dee..0000000
--- a/cherrypy/test/checkerdemo.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""Demonstration app for cherrypy.checker.
-
-This application is intentionally broken and badly designed.
-To demonstrate the output of the CherryPy Checker, simply execute
-this module.
-"""
-
-import os
-import cherrypy
-thisdir = os.path.dirname(os.path.abspath(__file__))
-
-class Root:
- pass
-
-if __name__ == '__main__':
- conf = {'/base': {'tools.staticdir.root': thisdir,
- # Obsolete key.
- 'throw_errors': True,
- },
- # This entry should be OK.
- '/base/static': {'tools.staticdir.on': True,
- 'tools.staticdir.dir': 'static'},
- # Warn on missing folder.
- '/base/js': {'tools.staticdir.on': True,
- 'tools.staticdir.dir': 'js'},
- # Warn on dir with an abs path even though we provide root.
- '/base/static2': {'tools.staticdir.on': True,
- 'tools.staticdir.dir': '/static'},
- # Warn on dir with a relative path with no root.
- '/static3': {'tools.staticdir.on': True,
- 'tools.staticdir.dir': 'static'},
- # Warn on unknown namespace
- '/unknown': {'toobles.gzip.on': True},
- # Warn special on cherrypy.<known ns>.*
- '/cpknown': {'cherrypy.tools.encode.on': True},
- # Warn on mismatched types
- '/conftype': {'request.show_tracebacks': 14},
- # Warn on unknown tool.
- '/web': {'tools.unknown.on': True},
- # Warn on server.* in app config.
- '/app1': {'server.socket_host': '0.0.0.0'},
- # Warn on 'localhost'
- 'global': {'server.socket_host': 'localhost'},
- # Warn on '[name]'
- '[/extra_brackets]': {},
- }
- cherrypy.quickstart(Root(), config=conf)
diff --git a/cherrypy/test/helper.py b/cherrypy/test/helper.py
deleted file mode 100755
index ff9e06c..0000000
--- a/cherrypy/test/helper.py
+++ /dev/null
@@ -1,476 +0,0 @@
-"""A library of helper functions for the CherryPy test suite."""
-
-import datetime
-import logging
-log = logging.getLogger(__name__)
-import os
-thisdir = os.path.abspath(os.path.dirname(__file__))
-serverpem = os.path.join(os.getcwd(), thisdir, 'test.pem')
-
-import re
-import sys
-import time
-import warnings
-
-import cherrypy
-from cherrypy._cpcompat import basestring, copyitems, HTTPSConnection, ntob
-from cherrypy.lib import httputil
-from cherrypy.lib.reprconf import unrepr
-from cherrypy.test import webtest
-
-import nose
-
-_testconfig = None
-
-def get_tst_config(overconf = {}):
- global _testconfig
- if _testconfig is None:
- conf = {
- 'scheme': 'http',
- 'protocol': "HTTP/1.1",
- 'port': 8080,
- 'host': '127.0.0.1',
- 'validate': False,
- 'conquer': False,
- 'server': 'wsgi',
- }
- try:
- import testconfig
- _conf = testconfig.config.get('supervisor', None)
- if _conf is not None:
- for k, v in _conf.items():
- if isinstance(v, basestring):
- _conf[k] = unrepr(v)
- conf.update(_conf)
- except ImportError:
- pass
- _testconfig = conf
- conf = _testconfig.copy()
- conf.update(overconf)
-
- return conf
-
-class Supervisor(object):
- """Base class for modeling and controlling servers during testing."""
-
- def __init__(self, **kwargs):
- for k, v in kwargs.items():
- if k == 'port':
- setattr(self, k, int(v))
- setattr(self, k, v)
-
-
-log_to_stderr = lambda msg, level: sys.stderr.write(msg + os.linesep)
-
-class LocalSupervisor(Supervisor):
- """Base class for modeling/controlling servers which run in the same process.
-
- When the server side runs in a different process, start/stop can dump all
- state between each test module easily. When the server side runs in the
- same process as the client, however, we have to do a bit more work to ensure
- config and mounted apps are reset between tests.
- """
-
- using_apache = False
- using_wsgi = False
-
- def __init__(self, **kwargs):
- for k, v in kwargs.items():
- setattr(self, k, v)
-
- cherrypy.server.httpserver = self.httpserver_class
-
- engine = cherrypy.engine
- if hasattr(engine, "signal_handler"):
- engine.signal_handler.subscribe()
- if hasattr(engine, "console_control_handler"):
- engine.console_control_handler.subscribe()
- #engine.subscribe('log', log_to_stderr)
-
- def start(self, modulename=None):
- """Load and start the HTTP server."""
- if modulename:
- # Unhook httpserver so cherrypy.server.start() creates a new
- # one (with config from setup_server, if declared).
- cherrypy.server.httpserver = None
-
- cherrypy.engine.start()
-
- self.sync_apps()
-
- def sync_apps(self):
- """Tell the server about any apps which the setup functions mounted."""
- pass
-
- def stop(self):
- td = getattr(self, 'teardown', None)
- if td:
- td()
-
- cherrypy.engine.exit()
-
- for name, server in copyitems(getattr(cherrypy, 'servers', {})):
- server.unsubscribe()
- del cherrypy.servers[name]
-
-
-class NativeServerSupervisor(LocalSupervisor):
- """Server supervisor for the builtin HTTP server."""
-
- httpserver_class = "cherrypy._cpnative_server.CPHTTPServer"
- using_apache = False
- using_wsgi = False
-
- def __str__(self):
- return "Builtin HTTP Server on %s:%s" % (self.host, self.port)
-
-
-class LocalWSGISupervisor(LocalSupervisor):
- """Server supervisor for the builtin WSGI server."""
-
- httpserver_class = "cherrypy._cpwsgi_server.CPWSGIServer"
- using_apache = False
- using_wsgi = True
-
- def __str__(self):
- return "Builtin WSGI Server on %s:%s" % (self.host, self.port)
-
- def sync_apps(self):
- """Hook a new WSGI app into the origin server."""
- cherrypy.server.httpserver.wsgi_app = self.get_app()
-
- def get_app(self, app=None):
- """Obtain a new (decorated) WSGI app to hook into the origin server."""
- if app is None:
- app = cherrypy.tree
-
- if self.conquer:
- try:
- import wsgiconq
- except ImportError:
- warnings.warn("Error importing wsgiconq. pyconquer will not run.")
- else:
- app = wsgiconq.WSGILogger(app, c_calls=True)
-
- if self.validate:
- try:
- from wsgiref import validate
- except ImportError:
- warnings.warn("Error importing wsgiref. The validator will not run.")
- else:
- #wraps the app in the validator
- app = validate.validator(app)
-
- return app
-
-
-def get_cpmodpy_supervisor(**options):
- from cherrypy.test import modpy
- sup = modpy.ModPythonSupervisor(**options)
- sup.template = modpy.conf_cpmodpy
- return sup
-
-def get_modpygw_supervisor(**options):
- from cherrypy.test import modpy
- sup = modpy.ModPythonSupervisor(**options)
- sup.template = modpy.conf_modpython_gateway
- sup.using_wsgi = True
- return sup
-
-def get_modwsgi_supervisor(**options):
- from cherrypy.test import modwsgi
- return modwsgi.ModWSGISupervisor(**options)
-
-def get_modfcgid_supervisor(**options):
- from cherrypy.test import modfcgid
- return modfcgid.ModFCGISupervisor(**options)
-
-def get_modfastcgi_supervisor(**options):
- from cherrypy.test import modfastcgi
- return modfastcgi.ModFCGISupervisor(**options)
-
-def get_wsgi_u_supervisor(**options):
- cherrypy.server.wsgi_version = ('u', 0)
- return LocalWSGISupervisor(**options)
-
-
-class CPWebCase(webtest.WebCase):
-
- script_name = ""
- scheme = "http"
-
- available_servers = {'wsgi': LocalWSGISupervisor,
- 'wsgi_u': get_wsgi_u_supervisor,
- 'native': NativeServerSupervisor,
- 'cpmodpy': get_cpmodpy_supervisor,
- 'modpygw': get_modpygw_supervisor,
- 'modwsgi': get_modwsgi_supervisor,
- 'modfcgid': get_modfcgid_supervisor,
- 'modfastcgi': get_modfastcgi_supervisor,
- }
- default_server = "wsgi"
-
- def _setup_server(cls, supervisor, conf):
- v = sys.version.split()[0]
- log.info("Python version used to run this test script: %s" % v)
- log.info("CherryPy version: %s" % cherrypy.__version__)
- if supervisor.scheme == "https":
- ssl = " (ssl)"
- else:
- ssl = ""
- log.info("HTTP server version: %s%s" % (supervisor.protocol, ssl))
- log.info("PID: %s" % os.getpid())
-
- cherrypy.server.using_apache = supervisor.using_apache
- cherrypy.server.using_wsgi = supervisor.using_wsgi
-
- if sys.platform[:4] == 'java':
- cherrypy.config.update({'server.nodelay': False})
-
- if isinstance(conf, basestring):
- parser = cherrypy.lib.reprconf.Parser()
- conf = parser.dict_from_file(conf).get('global', {})
- else:
- conf = conf or {}
- baseconf = conf.copy()
- baseconf.update({'server.socket_host': supervisor.host,
- 'server.socket_port': supervisor.port,
- 'server.protocol_version': supervisor.protocol,
- 'environment': "test_suite",
- })
- if supervisor.scheme == "https":
- #baseconf['server.ssl_module'] = 'builtin'
- baseconf['server.ssl_certificate'] = serverpem
- baseconf['server.ssl_private_key'] = serverpem
-
- # helper must be imported lazily so the coverage tool
- # can run against module-level statements within cherrypy.
- # Also, we have to do "from cherrypy.test import helper",
- # exactly like each test module does, because a relative import
- # would stick a second instance of webtest in sys.modules,
- # and we wouldn't be able to globally override the port anymore.
- if supervisor.scheme == "https":
- webtest.WebCase.HTTP_CONN = HTTPSConnection
- return baseconf
- _setup_server = classmethod(_setup_server)
-
- def setup_class(cls):
- ''
- #Creates a server
- conf = get_tst_config()
- supervisor_factory = cls.available_servers.get(conf.get('server', 'wsgi'))
- if supervisor_factory is None:
- raise RuntimeError('Unknown server in config: %s' % conf['server'])
- supervisor = supervisor_factory(**conf)
-
- #Copied from "run_test_suite"
- cherrypy.config.reset()
- baseconf = cls._setup_server(supervisor, conf)
- cherrypy.config.update(baseconf)
- setup_client()
-
- if hasattr(cls, 'setup_server'):
- # Clear the cherrypy tree and clear the wsgi server so that
- # it can be updated with the new root
- cherrypy.tree = cherrypy._cptree.Tree()
- cherrypy.server.httpserver = None
- cls.setup_server()
- supervisor.start(cls.__module__)
-
- cls.supervisor = supervisor
- setup_class = classmethod(setup_class)
-
- def teardown_class(cls):
- ''
- if hasattr(cls, 'setup_server'):
- cls.supervisor.stop()
- teardown_class = classmethod(teardown_class)
-
- def prefix(self):
- return self.script_name.rstrip("/")
-
- def base(self):
- if ((self.scheme == "http" and self.PORT == 80) or
- (self.scheme == "https" and self.PORT == 443)):
- port = ""
- else:
- port = ":%s" % self.PORT
-
- return "%s://%s%s%s" % (self.scheme, self.HOST, port,
- self.script_name.rstrip("/"))
-
- def exit(self):
- sys.exit()
-
- def getPage(self, url, headers=None, method="GET", body=None, protocol=None):
- """Open the url. Return status, headers, body."""
- if self.script_name:
- url = httputil.urljoin(self.script_name, url)
- return webtest.WebCase.getPage(self, url, headers, method, body, protocol)
-
- def skip(self, msg='skipped '):
- raise nose.SkipTest(msg)
-
- def assertErrorPage(self, status, message=None, pattern=''):
- """Compare the response body with a built in error page.
-
- The function will optionally look for the regexp pattern,
- within the exception embedded in the error page."""
-
- # This will never contain a traceback
- page = cherrypy._cperror.get_error_page(status, message=message)
-
- # First, test the response body without checking the traceback.
- # Stick a match-all group (.*) in to grab the traceback.
- esc = re.escape
- epage = esc(page)
- epage = epage.replace(esc('<pre id="traceback"></pre>'),
- esc('<pre id="traceback">') + '(.*)' + esc('</pre>'))
- m = re.match(ntob(epage, self.encoding), self.body, re.DOTALL)
- if not m:
- self._handlewebError('Error page does not match; expected:\n' + page)
- return
-
- # Now test the pattern against the traceback
- if pattern is None:
- # Special-case None to mean that there should be *no* traceback.
- if m and m.group(1):
- self._handlewebError('Error page contains traceback')
- else:
- if (m is None) or (
- not re.search(ntob(re.escape(pattern), self.encoding),
- m.group(1))):
- msg = 'Error page does not contain %s in traceback'
- self._handlewebError(msg % repr(pattern))
-
- date_tolerance = 2
-
- def assertEqualDates(self, dt1, dt2, seconds=None):
- """Assert abs(dt1 - dt2) is within Y seconds."""
- if seconds is None:
- seconds = self.date_tolerance
-
- if dt1 > dt2:
- diff = dt1 - dt2
- else:
- diff = dt2 - dt1
- if not diff < datetime.timedelta(seconds=seconds):
- raise AssertionError('%r and %r are not within %r seconds.' %
- (dt1, dt2, seconds))
-
-
-def setup_client():
- """Set up the WebCase classes to match the server's socket settings."""
- webtest.WebCase.PORT = cherrypy.server.socket_port
- webtest.WebCase.HOST = cherrypy.server.socket_host
- if cherrypy.server.ssl_certificate:
- CPWebCase.scheme = 'https'
-
-# --------------------------- Spawning helpers --------------------------- #
-
-
-class CPProcess(object):
-
- pid_file = os.path.join(thisdir, 'test.pid')
- config_file = os.path.join(thisdir, 'test.conf')
- config_template = """[global]
-server.socket_host: '%(host)s'
-server.socket_port: %(port)s
-checker.on: False
-log.screen: False
-log.error_file: r'%(error_log)s'
-log.access_file: r'%(access_log)s'
-%(ssl)s
-%(extra)s
-"""
- error_log = os.path.join(thisdir, 'test.error.log')
- access_log = os.path.join(thisdir, 'test.access.log')
-
- def __init__(self, wait=False, daemonize=False, ssl=False, socket_host=None, socket_port=None):
- self.wait = wait
- self.daemonize = daemonize
- self.ssl = ssl
- self.host = socket_host or cherrypy.server.socket_host
- self.port = socket_port or cherrypy.server.socket_port
-
- def write_conf(self, extra=""):
- if self.ssl:
- serverpem = os.path.join(thisdir, 'test.pem')
- ssl = """
-server.ssl_certificate: r'%s'
-server.ssl_private_key: r'%s'
-""" % (serverpem, serverpem)
- else:
- ssl = ""
-
- conf = self.config_template % {
- 'host': self.host,
- 'port': self.port,
- 'error_log': self.error_log,
- 'access_log': self.access_log,
- 'ssl': ssl,
- 'extra': extra,
- }
- f = open(self.config_file, 'wb')
- f.write(ntob(conf, 'utf-8'))
- f.close()
-
- def start(self, imports=None):
- """Start cherryd in a subprocess."""
- cherrypy._cpserver.wait_for_free_port(self.host, self.port)
-
- args = [sys.executable, os.path.join(thisdir, '..', 'cherryd'),
- '-c', self.config_file, '-p', self.pid_file]
-
- if not isinstance(imports, (list, tuple)):
- imports = [imports]
- for i in imports:
- if i:
- args.append('-i')
- args.append(i)
-
- if self.daemonize:
- args.append('-d')
-
- env = os.environ.copy()
- # Make sure we import the cherrypy package in which this module is defined.
- grandparentdir = os.path.abspath(os.path.join(thisdir, '..', '..'))
- if env.get('PYTHONPATH', ''):
- env['PYTHONPATH'] = os.pathsep.join((grandparentdir, env['PYTHONPATH']))
- else:
- env['PYTHONPATH'] = grandparentdir
- if self.wait:
- self.exit_code = os.spawnve(os.P_WAIT, sys.executable, args, env)
- else:
- os.spawnve(os.P_NOWAIT, sys.executable, args, env)
- cherrypy._cpserver.wait_for_occupied_port(self.host, self.port)
-
- # Give the engine a wee bit more time to finish STARTING
- if self.daemonize:
- time.sleep(2)
- else:
- time.sleep(1)
-
- def get_pid(self):
- return int(open(self.pid_file, 'rb').read())
-
- def join(self):
- """Wait for the process to exit."""
- try:
- try:
- # Mac, UNIX
- os.wait()
- except AttributeError:
- # Windows
- try:
- pid = self.get_pid()
- except IOError:
- # Assume the subprocess deleted the pidfile on shutdown.
- pass
- else:
- os.waitpid(pid, 0)
- except OSError:
- x = sys.exc_info()[1]
- if x.args != (10, 'No child processes'):
- raise
-
diff --git a/cherrypy/test/logtest.py b/cherrypy/test/logtest.py
deleted file mode 100755
index c093da2..0000000
--- a/cherrypy/test/logtest.py
+++ /dev/null
@@ -1,181 +0,0 @@
-"""logtest, a unittest.TestCase helper for testing log output."""
-
-import sys
-import time
-
-import cherrypy
-
-
-try:
- # On Windows, msvcrt.getch reads a single char without output.
- import msvcrt
- def getchar():
- return msvcrt.getch()
-except ImportError:
- # Unix getchr
- import tty, termios
- def getchar():
- fd = sys.stdin.fileno()
- old_settings = termios.tcgetattr(fd)
- try:
- tty.setraw(sys.stdin.fileno())
- ch = sys.stdin.read(1)
- finally:
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
- return ch
-
-
-class LogCase(object):
- """unittest.TestCase mixin for testing log messages.
-
- logfile: a filename for the desired log. Yes, I know modes are evil,
- but it makes the test functions so much cleaner to set this once.
-
- lastmarker: the last marker in the log. This can be used to search for
- messages since the last marker.
-
- markerPrefix: a string with which to prefix log markers. This should be
- unique enough from normal log output to use for marker identification.
- """
-
- logfile = None
- lastmarker = None
- markerPrefix = "test suite marker: "
-
- def _handleLogError(self, msg, data, marker, pattern):
- print("")
- print(" ERROR: %s" % msg)
-
- if not self.interactive:
- raise self.failureException(msg)
-
- p = " Show: [L]og [M]arker [P]attern; [I]gnore, [R]aise, or sys.e[X]it >> "
- print p,
- # ARGH
- sys.stdout.flush()
- while True:
- i = getchar().upper()
- if i not in "MPLIRX":
- continue
- print(i.upper()) # Also prints new line
- if i == "L":
- for x, line in enumerate(data):
- if (x + 1) % self.console_height == 0:
- # The \r and comma should make the next line overwrite
- print "<-- More -->\r",
- m = getchar().lower()
- # Erase our "More" prompt
- print " \r",
- if m == "q":
- break
- print(line.rstrip())
- elif i == "M":
- print(repr(marker or self.lastmarker))
- elif i == "P":
- print(repr(pattern))
- elif i == "I":
- # return without raising the normal exception
- return
- elif i == "R":
- raise self.failureException(msg)
- elif i == "X":
- self.exit()
- print p,
-
- def exit(self):
- sys.exit()
-
- def emptyLog(self):
- """Overwrite self.logfile with 0 bytes."""
- open(self.logfile, 'wb').write("")
-
- def markLog(self, key=None):
- """Insert a marker line into the log and set self.lastmarker."""
- if key is None:
- key = str(time.time())
- self.lastmarker = key
-
- open(self.logfile, 'ab+').write("%s%s\n" % (self.markerPrefix, key))
-
- def _read_marked_region(self, marker=None):
- """Return lines from self.logfile in the marked region.
-
- If marker is None, self.lastmarker is used. If the log hasn't
- been marked (using self.markLog), the entire log will be returned.
- """
-## # Give the logger time to finish writing?
-## time.sleep(0.5)
-
- logfile = self.logfile
- marker = marker or self.lastmarker
- if marker is None:
- return open(logfile, 'rb').readlines()
-
- data = []
- in_region = False
- for line in open(logfile, 'rb'):
- if in_region:
- if (line.startswith(self.markerPrefix) and not marker in line):
- break
- else:
- data.append(line)
- elif marker in line:
- in_region = True
- return data
-
- def assertInLog(self, line, marker=None):
- """Fail if the given (partial) line is not in the log.
-
- The log will be searched from the given marker to the next marker.
- If marker is None, self.lastmarker is used. If the log hasn't
- been marked (using self.markLog), the entire log will be searched.
- """
- data = self._read_marked_region(marker)
- for logline in data:
- if line in logline:
- return
- msg = "%r not found in log" % line
- self._handleLogError(msg, data, marker, line)
-
- def assertNotInLog(self, line, marker=None):
- """Fail if the given (partial) line is in the log.
-
- The log will be searched from the given marker to the next marker.
- If marker is None, self.lastmarker is used. If the log hasn't
- been marked (using self.markLog), the entire log will be searched.
- """
- data = self._read_marked_region(marker)
- for logline in data:
- if line in logline:
- msg = "%r found in log" % line
- self._handleLogError(msg, data, marker, line)
-
- def assertLog(self, sliceargs, lines, marker=None):
- """Fail if log.readlines()[sliceargs] is not contained in 'lines'.
-
- The log will be searched from the given marker to the next marker.
- If marker is None, self.lastmarker is used. If the log hasn't
- been marked (using self.markLog), the entire log will be searched.
- """
- data = self._read_marked_region(marker)
- if isinstance(sliceargs, int):
- # Single arg. Use __getitem__ and allow lines to be str or list.
- if isinstance(lines, (tuple, list)):
- lines = lines[0]
- if lines not in data[sliceargs]:
- msg = "%r not found on log line %r" % (lines, sliceargs)
- self._handleLogError(msg, [data[sliceargs]], marker, lines)
- else:
- # Multiple args. Use __getslice__ and require lines to be list.
- if isinstance(lines, tuple):
- lines = list(lines)
- elif isinstance(lines, basestring):
- raise TypeError("The 'lines' arg must be a list when "
- "'sliceargs' is a tuple.")
-
- start, stop = sliceargs
- for line, logline in zip(lines, data[start:stop]):
- if line not in logline:
- msg = "%r not found in log" % line
- self._handleLogError(msg, data[start:stop], marker, line)
-
diff --git a/cherrypy/test/modfastcgi.py b/cherrypy/test/modfastcgi.py
deleted file mode 100755
index 95acf14..0000000
--- a/cherrypy/test/modfastcgi.py
+++ /dev/null
@@ -1,135 +0,0 @@
-"""Wrapper for mod_fastcgi, for use as a CherryPy HTTP server when testing.
-
-To autostart fastcgi, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl", "apache2ctl",
-or "httpd"--create a symlink to them if needed.
-
-You'll also need the WSGIServer from flup.servers.
-See http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
- output is then truncated again by Apache. See test_core.testRanges.
- This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
- See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
- CherryPy may set "304 Not modified" but Apache will write out
- "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
- Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
- transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
- instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
- mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
- no response entity body. This isn't really a bug; it just differs from
- the CherryPy default.
-"""
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import re
-import sys
-import time
-
-import cherrypy
-from cherrypy.process import plugins, servers
-from cherrypy.test import helper
-
-
-def read_process(cmd, args=""):
- pipein, pipeout = os.popen4("%s %s" % (cmd, args))
- try:
- firstline = pipeout.readline()
- if (re.search(r"(not recognized|No such file|not found)", firstline,
- re.IGNORECASE)):
- raise IOError('%s must be on your system path.' % cmd)
- output = firstline + pipeout.read()
- finally:
- pipeout.close()
- return output
-
-
-APACHE_PATH = "apache2ctl"
-CONF_PATH = "fastcgi.conf"
-
-conf_fastcgi = """
-# Apache2 server conf file for testing CherryPy with mod_fastcgi.
-# fumanchu: I had to hard-code paths due to crazy Debian layouts :(
-ServerRoot /usr/lib/apache2
-User #1000
-ErrorLog %(root)s/mod_fastcgi.error.log
-
-DocumentRoot "%(root)s"
-ServerName 127.0.0.1
-Listen %(port)s
-LoadModule fastcgi_module modules/mod_fastcgi.so
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options +ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "%(server)s" -host 127.0.0.1:4000
-"""
-
-def erase_script_name(environ, start_response):
- environ['SCRIPT_NAME'] = ''
- return cherrypy.tree(environ, start_response)
-
-class ModFCGISupervisor(helper.LocalWSGISupervisor):
-
- httpserver_class = "cherrypy.process.servers.FlupFCGIServer"
- using_apache = True
- using_wsgi = True
- template = conf_fastcgi
-
- def __str__(self):
- return "FCGI Server on %s:%s" % (self.host, self.port)
-
- def start(self, modulename):
- cherrypy.server.httpserver = servers.FlupFCGIServer(
- application=erase_script_name, bindAddress=('127.0.0.1', 4000))
- cherrypy.server.httpserver.bind_addr = ('127.0.0.1', 4000)
- cherrypy.server.socket_port = 4000
- # For FCGI, we both start apache...
- self.start_apache()
- # ...and our local server
- cherrypy.engine.start()
- self.sync_apps()
-
- def start_apache(self):
- fcgiconf = CONF_PATH
- if not os.path.isabs(fcgiconf):
- fcgiconf = os.path.join(curdir, fcgiconf)
-
- # Write the Apache conf file.
- f = open(fcgiconf, 'wb')
- try:
- server = repr(os.path.join(curdir, 'fastcgi.pyc'))[1:-1]
- output = self.template % {'port': self.port, 'root': curdir,
- 'server': server}
- output = output.replace('\r\n', '\n')
- f.write(output)
- finally:
- f.close()
-
- result = read_process(APACHE_PATH, "-k start -f %s" % fcgiconf)
- if result:
- print(result)
-
- def stop(self):
- """Gracefully shutdown a server that is serving forever."""
- read_process(APACHE_PATH, "-k stop")
- helper.LocalWSGISupervisor.stop(self)
-
- def sync_apps(self):
- cherrypy.server.httpserver.fcgiserver.application = self.get_app(erase_script_name)
-
diff --git a/cherrypy/test/modfcgid.py b/cherrypy/test/modfcgid.py
deleted file mode 100755
index 736aa4c..0000000
--- a/cherrypy/test/modfcgid.py
+++ /dev/null
@@ -1,125 +0,0 @@
-"""Wrapper for mod_fcgid, for use as a CherryPy HTTP server when testing.
-
-To autostart fcgid, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl", "apache2ctl",
-or "httpd"--create a symlink to them if needed.
-
-You'll also need the WSGIServer from flup.servers.
-See http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
- output is then truncated again by Apache. See test_core.testRanges.
- This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
- See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
- CherryPy may set "304 Not modified" but Apache will write out
- "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
- Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
- transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
- instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
- mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
- no response entity body. This isn't really a bug; it just differs from
- the CherryPy default.
-"""
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import re
-import sys
-import time
-
-import cherrypy
-from cherrypy._cpcompat import ntob
-from cherrypy.process import plugins, servers
-from cherrypy.test import helper
-
-
-def read_process(cmd, args=""):
- pipein, pipeout = os.popen4("%s %s" % (cmd, args))
- try:
- firstline = pipeout.readline()
- if (re.search(r"(not recognized|No such file|not found)", firstline,
- re.IGNORECASE)):
- raise IOError('%s must be on your system path.' % cmd)
- output = firstline + pipeout.read()
- finally:
- pipeout.close()
- return output
-
-
-APACHE_PATH = "httpd"
-CONF_PATH = "fcgi.conf"
-
-conf_fcgid = """
-# Apache2 server conf file for testing CherryPy with mod_fcgid.
-
-DocumentRoot "%(root)s"
-ServerName 127.0.0.1
-Listen %(port)s
-LoadModule fastcgi_module modules/mod_fastcgi.dll
-LoadModule rewrite_module modules/mod_rewrite.so
-
-Options ExecCGI
-SetHandler fastcgi-script
-RewriteEngine On
-RewriteRule ^(.*)$ /fastcgi.pyc [L]
-FastCgiExternalServer "%(server)s" -host 127.0.0.1:4000
-"""
-
-class ModFCGISupervisor(helper.LocalSupervisor):
-
- using_apache = True
- using_wsgi = True
- template = conf_fcgid
-
- def __str__(self):
- return "FCGI Server on %s:%s" % (self.host, self.port)
-
- def start(self, modulename):
- cherrypy.server.httpserver = servers.FlupFCGIServer(
- application=cherrypy.tree, bindAddress=('127.0.0.1', 4000))
- cherrypy.server.httpserver.bind_addr = ('127.0.0.1', 4000)
- # For FCGI, we both start apache...
- self.start_apache()
- # ...and our local server
- helper.LocalServer.start(self, modulename)
-
- def start_apache(self):
- fcgiconf = CONF_PATH
- if not os.path.isabs(fcgiconf):
- fcgiconf = os.path.join(curdir, fcgiconf)
-
- # Write the Apache conf file.
- f = open(fcgiconf, 'wb')
- try:
- server = repr(os.path.join(curdir, 'fastcgi.pyc'))[1:-1]
- output = self.template % {'port': self.port, 'root': curdir,
- 'server': server}
- output = ntob(output.replace('\r\n', '\n'))
- f.write(output)
- finally:
- f.close()
-
- result = read_process(APACHE_PATH, "-k start -f %s" % fcgiconf)
- if result:
- print(result)
-
- def stop(self):
- """Gracefully shutdown a server that is serving forever."""
- read_process(APACHE_PATH, "-k stop")
- helper.LocalServer.stop(self)
-
- def sync_apps(self):
- cherrypy.server.httpserver.fcgiserver.application = self.get_app()
-
diff --git a/cherrypy/test/modpy.py b/cherrypy/test/modpy.py
deleted file mode 100755
index 519571f..0000000
--- a/cherrypy/test/modpy.py
+++ /dev/null
@@ -1,163 +0,0 @@
-"""Wrapper for mod_python, for use as a CherryPy HTTP server when testing.
-
-To autostart modpython, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl" or "apache2ctl"--
-create a symlink to them if needed.
-
-If you wish to test the WSGI interface instead of our _cpmodpy interface,
-you also need the 'modpython_gateway' module at:
-http://projects.amor.org/misc/wiki/ModPythonGateway
-
-
-KNOWN BUGS
-==========
-
-1. Apache processes Range headers automatically; CherryPy's truncated
- output is then truncated again by Apache. See test_core.testRanges.
- This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
- See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-4. Apache replaces status "reason phrases" automatically. For example,
- CherryPy may set "304 Not modified" but Apache will write out
- "304 Not Modified" (capital "M").
-5. Apache does not allow custom error codes as per the spec.
-6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
- Request-URI too early.
-7. mod_python will not read request bodies which use the "chunked"
- transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
- instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
- mod_python's requestobject.c).
-8. Apache will output a "Content-Length: 0" response header even if there's
- no response entity body. This isn't really a bug; it just differs from
- the CherryPy default.
-"""
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import re
-import time
-
-from cherrypy.test import helper
-
-
-def read_process(cmd, args=""):
- pipein, pipeout = os.popen4("%s %s" % (cmd, args))
- try:
- firstline = pipeout.readline()
- if (re.search(r"(not recognized|No such file|not found)", firstline,
- re.IGNORECASE)):
- raise IOError('%s must be on your system path.' % cmd)
- output = firstline + pipeout.read()
- finally:
- pipeout.close()
- return output
-
-
-APACHE_PATH = "httpd"
-CONF_PATH = "test_mp.conf"
-
-conf_modpython_gateway = """
-# Apache2 server conf file for testing CherryPy with modpython_gateway.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-SetHandler python-program
-PythonFixupHandler cherrypy.test.modpy::wsgisetup
-PythonOption testmod %(modulename)s
-PythonHandler modpython_gateway::handler
-PythonOption wsgi.application cherrypy::tree
-PythonOption socket_host %(host)s
-PythonDebug On
-"""
-
-conf_cpmodpy = """
-# Apache2 server conf file for testing CherryPy with _cpmodpy.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-LoadModule python_module modules/mod_python.so
-
-SetHandler python-program
-PythonFixupHandler cherrypy.test.modpy::cpmodpysetup
-PythonHandler cherrypy._cpmodpy::handler
-PythonOption cherrypy.setup cherrypy.test.%(modulename)s::setup_server
-PythonOption socket_host %(host)s
-PythonDebug On
-"""
-
-class ModPythonSupervisor(helper.Supervisor):
-
- using_apache = True
- using_wsgi = False
- template = None
-
- def __str__(self):
- return "ModPython Server on %s:%s" % (self.host, self.port)
-
- def start(self, modulename):
- mpconf = CONF_PATH
- if not os.path.isabs(mpconf):
- mpconf = os.path.join(curdir, mpconf)
-
- f = open(mpconf, 'wb')
- try:
- f.write(self.template %
- {'port': self.port, 'modulename': modulename,
- 'host': self.host})
- finally:
- f.close()
-
- result = read_process(APACHE_PATH, "-k start -f %s" % mpconf)
- if result:
- print(result)
-
- def stop(self):
- """Gracefully shutdown a server that is serving forever."""
- read_process(APACHE_PATH, "-k stop")
-
-
-loaded = False
-def wsgisetup(req):
- global loaded
- if not loaded:
- loaded = True
- options = req.get_options()
-
- import cherrypy
- cherrypy.config.update({
- "log.error_file": os.path.join(curdir, "test.log"),
- "environment": "test_suite",
- "server.socket_host": options['socket_host'],
- })
-
- modname = options['testmod']
- mod = __import__(modname, globals(), locals(), [''])
- mod.setup_server()
-
- cherrypy.server.unsubscribe()
- cherrypy.engine.start()
- from mod_python import apache
- return apache.OK
-
-
-def cpmodpysetup(req):
- global loaded
- if not loaded:
- loaded = True
- options = req.get_options()
-
- import cherrypy
- cherrypy.config.update({
- "log.error_file": os.path.join(curdir, "test.log"),
- "environment": "test_suite",
- "server.socket_host": options['socket_host'],
- })
- from mod_python import apache
- return apache.OK
-
diff --git a/cherrypy/test/modwsgi.py b/cherrypy/test/modwsgi.py
deleted file mode 100755
index 309a541..0000000
--- a/cherrypy/test/modwsgi.py
+++ /dev/null
@@ -1,148 +0,0 @@
-"""Wrapper for mod_wsgi, for use as a CherryPy HTTP server.
-
-To autostart modwsgi, the "apache" executable or script must be
-on your system path, or you must override the global APACHE_PATH.
-On some platforms, "apache" may be called "apachectl" or "apache2ctl"--
-create a symlink to them if needed.
-
-
-KNOWN BUGS
-==========
-
-##1. Apache processes Range headers automatically; CherryPy's truncated
-## output is then truncated again by Apache. See test_core.testRanges.
-## This was worked around in http://www.cherrypy.org/changeset/1319.
-2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
- See test_core.testHTTPMethods.
-3. Max request header and body settings do not work with Apache.
-##4. Apache replaces status "reason phrases" automatically. For example,
-## CherryPy may set "304 Not modified" but Apache will write out
-## "304 Not Modified" (capital "M").
-##5. Apache does not allow custom error codes as per the spec.
-##6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
-## Request-URI too early.
-7. mod_wsgi will not read request bodies which use the "chunked"
- transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block
- instead of REQUEST_CHUNKED_DECHUNK, see Apache2's http_protocol.c and
- mod_python's requestobject.c).
-8. When responding with 204 No Content, mod_wsgi adds a Content-Length
- header for you.
-9. When an error is raised, mod_wsgi has no facility for printing a
- traceback as the response content (it's sent to the Apache log instead).
-10. Startup and shutdown of Apache when running mod_wsgi seems slow.
-"""
-
-import os
-curdir = os.path.abspath(os.path.dirname(__file__))
-import re
-import sys
-import time
-
-import cherrypy
-from cherrypy.test import helper, webtest
-
-
-def read_process(cmd, args=""):
- pipein, pipeout = os.popen4("%s %s" % (cmd, args))
- try:
- firstline = pipeout.readline()
- if (re.search(r"(not recognized|No such file|not found)", firstline,
- re.IGNORECASE)):
- raise IOError('%s must be on your system path.' % cmd)
- output = firstline + pipeout.read()
- finally:
- pipeout.close()
- return output
-
-
-if sys.platform == 'win32':
- APACHE_PATH = "httpd"
-else:
- APACHE_PATH = "apache"
-
-CONF_PATH = "test_mw.conf"
-
-conf_modwsgi = r"""
-# Apache2 server conf file for testing CherryPy with modpython_gateway.
-
-ServerName 127.0.0.1
-DocumentRoot "/"
-Listen %(port)s
-
-AllowEncodedSlashes On
-LoadModule rewrite_module modules/mod_rewrite.so
-RewriteEngine on
-RewriteMap escaping int:escape
-
-LoadModule log_config_module modules/mod_log_config.so
-LogFormat "%%h %%l %%u %%t \"%%r\" %%>s %%b \"%%{Referer}i\" \"%%{User-agent}i\"" combined
-CustomLog "%(curdir)s/apache.access.log" combined
-ErrorLog "%(curdir)s/apache.error.log"
-LogLevel debug
-
-LoadModule wsgi_module modules/mod_wsgi.so
-LoadModule env_module modules/mod_env.so
-
-WSGIScriptAlias / "%(curdir)s/modwsgi.py"
-SetEnv testmod %(testmod)s
-"""
-
-
-class ModWSGISupervisor(helper.Supervisor):
- """Server Controller for ModWSGI and CherryPy."""
-
- using_apache = True
- using_wsgi = True
- template=conf_modwsgi
-
- def __str__(self):
- return "ModWSGI Server on %s:%s" % (self.host, self.port)
-
- def start(self, modulename):
- mpconf = CONF_PATH
- if not os.path.isabs(mpconf):
- mpconf = os.path.join(curdir, mpconf)
-
- f = open(mpconf, 'wb')
- try:
- output = (self.template %
- {'port': self.port, 'testmod': modulename,
- 'curdir': curdir})
- f.write(output)
- finally:
- f.close()
-
- result = read_process(APACHE_PATH, "-k start -f %s" % mpconf)
- if result:
- print(result)
-
- # Make a request so mod_wsgi starts up our app.
- # If we don't, concurrent initial requests will 404.
- cherrypy._cpserver.wait_for_occupied_port("127.0.0.1", self.port)
- webtest.openURL('/ihopetheresnodefault', port=self.port)
- time.sleep(1)
-
- def stop(self):
- """Gracefully shutdown a server that is serving forever."""
- read_process(APACHE_PATH, "-k stop")
-
-
-loaded = False
-def application(environ, start_response):
- import cherrypy
- global loaded
- if not loaded:
- loaded = True
- modname = "cherrypy.test." + environ['testmod']
- mod = __import__(modname, globals(), locals(), [''])
- mod.setup_server()
-
- cherrypy.config.update({
- "log.error_file": os.path.join(curdir, "test.error.log"),
- "log.access_file": os.path.join(curdir, "test.access.log"),
- "environment": "test_suite",
- "engine.SIGHUP": None,
- "engine.SIGTERM": None,
- })
- return cherrypy.tree(environ, start_response)
-
diff --git a/cherrypy/test/sessiondemo.py b/cherrypy/test/sessiondemo.py
deleted file mode 100755
index 342e5b5..0000000
--- a/cherrypy/test/sessiondemo.py
+++ /dev/null
@@ -1,153 +0,0 @@
-#!/usr/bin/python
-"""A session demonstration app."""
-
-import calendar
-from datetime import datetime
-import sys
-import cherrypy
-from cherrypy.lib import sessions
-from cherrypy._cpcompat import copyitems
-
-
-page = """
-<html>
-<head>
-<style type='text/css'>
-table { border-collapse: collapse; border: 1px solid #663333; }
-th { text-align: right; background-color: #663333; color: white; padding: 0.5em; }
-td { white-space: pre-wrap; font-family: monospace; padding: 0.5em;
- border: 1px solid #663333; }
-.warn { font-family: serif; color: #990000; }
-</style>
-<script type="text/javascript">
-<!--
-function twodigit(d) { return d < 10 ? "0" + d : d; }
-function formattime(t) {
- var month = t.getUTCMonth() + 1;
- var day = t.getUTCDate();
- var year = t.getUTCFullYear();
- var hours = t.getUTCHours();
- var minutes = t.getUTCMinutes();
- return (year + "/" + twodigit(month) + "/" + twodigit(day) + " " +
- hours + ":" + twodigit(minutes) + " UTC");
-}
-
-function interval(s) {
- // Return the given interval (in seconds) as an English phrase
- var seconds = s %% 60;
- s = Math.floor(s / 60);
- var minutes = s %% 60;
- s = Math.floor(s / 60);
- var hours = s %% 24;
- var v = twodigit(hours) + ":" + twodigit(minutes) + ":" + twodigit(seconds);
- var days = Math.floor(s / 24);
- if (days != 0) v = days + ' days, ' + v;
- return v;
-}
-
-var fudge_seconds = 5;
-
-function init() {
- // Set the content of the 'btime' cell.
- var currentTime = new Date();
- var bunixtime = Math.floor(currentTime.getTime() / 1000);
-
- var v = formattime(currentTime);
- v += " (Unix time: " + bunixtime + ")";
-
- var diff = Math.abs(%(serverunixtime)s - bunixtime);
- if (diff > fudge_seconds) v += "<p class='warn'>Browser and Server times disagree.</p>";
-
- document.getElementById('btime').innerHTML = v;
-
- // Warn if response cookie expires is not close to one hour in the future.
- // Yes, we want this to happen when wit hit the 'Expire' link, too.
- var expires = Date.parse("%(expires)s") / 1000;
- var onehour = (60 * 60);
- if (Math.abs(expires - (bunixtime + onehour)) > fudge_seconds) {
- diff = Math.floor(expires - bunixtime);
- if (expires > (bunixtime + onehour)) {
- var msg = "Response cookie 'expires' date is " + interval(diff) + " in the future.";
- } else {
- var msg = "Response cookie 'expires' date is " + interval(0 - diff) + " in the past.";
- }
- document.getElementById('respcookiewarn').innerHTML = msg;
- }
-}
-//-->
-</script>
-</head>
-
-<body onload='init()'>
-<h2>Session Demo</h2>
-<p>Reload this page. The session ID should not change from one reload to the next</p>
-<p><a href='../'>Index</a> | <a href='expire'>Expire</a> | <a href='regen'>Regenerate</a></p>
-<table>
- <tr><th>Session ID:</th><td>%(sessionid)s<p class='warn'>%(changemsg)s</p></td></tr>
- <tr><th>Request Cookie</th><td>%(reqcookie)s</td></tr>
- <tr><th>Response Cookie</th><td>%(respcookie)s<p id='respcookiewarn' class='warn'></p></td></tr>
- <tr><th>Session Data</th><td>%(sessiondata)s</td></tr>
- <tr><th>Server Time</th><td id='stime'>%(servertime)s (Unix time: %(serverunixtime)s)</td></tr>
- <tr><th>Browser Time</th><td id='btime'>&nbsp;</td></tr>
- <tr><th>Cherrypy Version:</th><td>%(cpversion)s</td></tr>
- <tr><th>Python Version:</th><td>%(pyversion)s</td></tr>
-</table>
-</body></html>
-"""
-
-class Root(object):
-
- def page(self):
- changemsg = []
- if cherrypy.session.id != cherrypy.session.originalid:
- if cherrypy.session.originalid is None:
- changemsg.append('Created new session because no session id was given.')
- if cherrypy.session.missing:
- changemsg.append('Created new session due to missing (expired or malicious) session.')
- if cherrypy.session.regenerated:
- changemsg.append('Application generated a new session.')
-
- try:
- expires = cherrypy.response.cookie['session_id']['expires']
- except KeyError:
- expires = ''
-
- return page % {
- 'sessionid': cherrypy.session.id,
- 'changemsg': '<br>'.join(changemsg),
- 'respcookie': cherrypy.response.cookie.output(),
- 'reqcookie': cherrypy.request.cookie.output(),
- 'sessiondata': copyitems(cherrypy.session),
- 'servertime': datetime.utcnow().strftime("%Y/%m/%d %H:%M") + " UTC",
- 'serverunixtime': calendar.timegm(datetime.utcnow().timetuple()),
- 'cpversion': cherrypy.__version__,
- 'pyversion': sys.version,
- 'expires': expires,
- }
-
- def index(self):
- # Must modify data or the session will not be saved.
- cherrypy.session['color'] = 'green'
- return self.page()
- index.exposed = True
-
- def expire(self):
- sessions.expire()
- return self.page()
- expire.exposed = True
-
- def regen(self):
- cherrypy.session.regenerate()
- # Must modify data or the session will not be saved.
- cherrypy.session['color'] = 'yellow'
- return self.page()
- regen.exposed = True
-
-if __name__ == '__main__':
- cherrypy.config.update({
- #'environment': 'production',
- 'log.screen': True,
- 'tools.sessions.on': True,
- })
- cherrypy.quickstart(Root())
-
diff --git a/cherrypy/test/static/dirback.jpg b/cherrypy/test/static/dirback.jpg
deleted file mode 100644
index 530e6d6..0000000
--- a/cherrypy/test/static/dirback.jpg
+++ /dev/null
Binary files differ
diff --git a/cherrypy/test/static/index.html b/cherrypy/test/static/index.html
deleted file mode 100644
index b9f5f09..0000000
--- a/cherrypy/test/static/index.html
+++ /dev/null
@@ -1 +0,0 @@
-Hello, world
diff --git a/cherrypy/test/style.css b/cherrypy/test/style.css
deleted file mode 100644
index b266e93..0000000
--- a/cherrypy/test/style.css
+++ /dev/null
@@ -1 +0,0 @@
-Dummy stylesheet
diff --git a/cherrypy/test/test.pem b/cherrypy/test/test.pem
deleted file mode 100644
index 47a4704..0000000
--- a/cherrypy/test/test.pem
+++ /dev/null
@@ -1,38 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDBKo554mzIMY+AByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZ
-R9L4WtImEew05FY3Izerfm3MN3+MC0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Kn
-da+O6xldVSosu8Ev3z9VZ94iC/ZgKzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQAB
-AoGAWOCF0ZrWxn3XMucWq2LNwPKqlvVGwbIwX3cDmX22zmnM4Fy6arXbYh4XlyCj
-9+ofqRrxIFz5k/7tFriTmZ0xag5+Jdx+Kwg0/twiP7XCNKipFogwe1Hznw8OFAoT
-enKBdj2+/n2o0Bvo/tDB59m9L/538d46JGQUmJlzMyqYikECQQDyoq+8CtMNvE18
-8VgHcR/KtApxWAjj4HpaHYL637ATjThetUZkW92mgDgowyplthusxdNqhHWyv7E8
-tWNdYErZAkEAy85ShTR0M5aWmrE7o0r0SpWInAkNBH9aXQRRARFYsdBtNfRu6I0i
-0lvU9wiu3eF57FMEC86yViZ5UBnQfTu7vQJAVesj/Zt7pwaCDfdMa740OsxMUlyR
-MVhhGx4OLpYdPJ8qUecxGQKq13XZ7R1HGyNEY4bd2X80Smq08UFuATfC6QJAH8UB
-yBHtKz2GLIcELOg6PIYizW/7v3+6rlVF60yw7sb2vzpjL40QqIn4IKoR2DSVtOkb
-8FtAIX3N21aq0VrGYQJBAIPiaEc2AZ8Bq2GC4F3wOz/BxJ/izvnkiotR12QK4fh5
-yjZMhTjWCas5zwHR5PDjlD88AWGDMsZ1PicD4348xJQ=
------END RSA PRIVATE KEY-----
------BEGIN CERTIFICATE-----
-MIIDxTCCAy6gAwIBAgIJAI18BD7eQxlGMA0GCSqGSIb3DQEBBAUAMIGeMQswCQYD
-VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU2FuIERpZWdv
-MRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0MREwDwYDVQQLEwhkZXYtdGVzdDEW
-MBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4GCSqGSIb3DQEJARYRcmVtaUBjaGVy
-cnlweS5vcmcwHhcNMDYwOTA5MTkyMDIwWhcNMzQwMTI0MTkyMDIwWjCBnjELMAkG
-A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNVBAcTCVNhbiBEaWVn
-bzEZMBcGA1UEChMQQ2hlcnJ5UHkgUHJvamVjdDERMA8GA1UECxMIZGV2LXRlc3Qx
-FjAUBgNVBAMTDUNoZXJyeVB5IFRlYW0xIDAeBgkqhkiG9w0BCQEWEXJlbWlAY2hl
-cnJ5cHkub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBKo554mzIMY+A
-ByUNpaUOP9bJnQ7ZLQe9XgHwoLJR4VzpyZZZR9L4WtImEew05FY3Izerfm3MN3+M
-C0tJ6yQU9sOiU3vBW6RrLIMlfKsnRwBRZ0Knda+O6xldVSosu8Ev3z9VZ94iC/Zg
-KzrH7Mjj/U8/MQO7RBS/LAqee8bFNQIDAQABo4IBBzCCAQMwHQYDVR0OBBYEFDIQ
-2feb71tVZCWpU0qJ/Tw+wdtoMIHTBgNVHSMEgcswgciAFDIQ2feb71tVZCWpU0qJ
-/Tw+wdtooYGkpIGhMIGeMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5p
-YTESMBAGA1UEBxMJU2FuIERpZWdvMRkwFwYDVQQKExBDaGVycnlQeSBQcm9qZWN0
-MREwDwYDVQQLEwhkZXYtdGVzdDEWMBQGA1UEAxMNQ2hlcnJ5UHkgVGVhbTEgMB4G
-CSqGSIb3DQEJARYRcmVtaUBjaGVycnlweS5vcmeCCQCNfAQ+3kMZRjAMBgNVHRME
-BTADAQH/MA0GCSqGSIb3DQEBBAUAA4GBAL7AAQz7IePV48ZTAFHKr88ntPALsL5S
-8vHCZPNMevNkLTj3DYUw2BcnENxMjm1kou2F2BkvheBPNZKIhc6z4hAml3ed1xa2
-D7w6e6OTcstdK/+KrPDDHeOP1dhMWNs2JE1bNlfF1LiXzYKSXpe88eCKjCXsCT/T
-NluCaWQys3MS
------END CERTIFICATE-----
diff --git a/cherrypy/test/test_auth_basic.py b/cherrypy/test/test_auth_basic.py
deleted file mode 100755
index 3a9781d..0000000
--- a/cherrypy/test/test_auth_basic.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-import cherrypy
-from cherrypy._cpcompat import md5, ntob
-from cherrypy.lib import auth_basic
-from cherrypy.test import helper
-
-
-class BasicAuthTest(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def index(self):
- return "This is public."
- index.exposed = True
-
- class BasicProtected:
- def index(self):
- return "Hello %s, you've been authorized." % cherrypy.request.login
- index.exposed = True
-
- class BasicProtected2:
- def index(self):
- return "Hello %s, you've been authorized." % cherrypy.request.login
- index.exposed = True
-
- userpassdict = {'xuser' : 'xpassword'}
- userhashdict = {'xuser' : md5(ntob('xpassword')).hexdigest()}
-
- def checkpasshash(realm, user, password):
- p = userhashdict.get(user)
- return p and p == md5(ntob(password)).hexdigest() or False
-
- conf = {'/basic': {'tools.auth_basic.on': True,
- 'tools.auth_basic.realm': 'wonderland',
- 'tools.auth_basic.checkpassword': auth_basic.checkpassword_dict(userpassdict)},
- '/basic2': {'tools.auth_basic.on': True,
- 'tools.auth_basic.realm': 'wonderland',
- 'tools.auth_basic.checkpassword': checkpasshash},
- }
-
- root = Root()
- root.basic = BasicProtected()
- root.basic2 = BasicProtected2()
- cherrypy.tree.mount(root, config=conf)
- setup_server = staticmethod(setup_server)
-
- def testPublic(self):
- self.getPage("/")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html;charset=utf-8')
- self.assertBody('This is public.')
-
- def testBasic(self):
- self.getPage("/basic/")
- self.assertStatus(401)
- self.assertHeader('WWW-Authenticate', 'Basic realm="wonderland"')
-
- self.getPage('/basic/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3JX')])
- self.assertStatus(401)
-
- self.getPage('/basic/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3Jk')])
- self.assertStatus('200 OK')
- self.assertBody("Hello xuser, you've been authorized.")
-
- def testBasic2(self):
- self.getPage("/basic2/")
- self.assertStatus(401)
- self.assertHeader('WWW-Authenticate', 'Basic realm="wonderland"')
-
- self.getPage('/basic2/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3JX')])
- self.assertStatus(401)
-
- self.getPage('/basic2/', [('Authorization', 'Basic eHVzZXI6eHBhc3N3b3Jk')])
- self.assertStatus('200 OK')
- self.assertBody("Hello xuser, you've been authorized.")
-
diff --git a/cherrypy/test/test_auth_digest.py b/cherrypy/test/test_auth_digest.py
deleted file mode 100755
index 1960fa8..0000000
--- a/cherrypy/test/test_auth_digest.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# This file is part of CherryPy <http://www.cherrypy.org/>
-# -*- coding: utf-8 -*-
-# vim:ts=4:sw=4:expandtab:fileencoding=utf-8
-
-
-import cherrypy
-from cherrypy.lib import auth_digest
-
-from cherrypy.test import helper
-
-class DigestAuthTest(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def index(self):
- return "This is public."
- index.exposed = True
-
- class DigestProtected:
- def index(self):
- return "Hello %s, you've been authorized." % cherrypy.request.login
- index.exposed = True
-
- def fetch_users():
- return {'test': 'test'}
-
-
- get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain(fetch_users())
- conf = {'/digest': {'tools.auth_digest.on': True,
- 'tools.auth_digest.realm': 'localhost',
- 'tools.auth_digest.get_ha1': get_ha1,
- 'tools.auth_digest.key': 'a565c27146791cfb',
- 'tools.auth_digest.debug': 'True'}}
-
- root = Root()
- root.digest = DigestProtected()
- cherrypy.tree.mount(root, config=conf)
- setup_server = staticmethod(setup_server)
-
- def testPublic(self):
- self.getPage("/")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html;charset=utf-8')
- self.assertBody('This is public.')
-
- def testDigest(self):
- self.getPage("/digest/")
- self.assertStatus(401)
-
- value = None
- for k, v in self.headers:
- if k.lower() == "www-authenticate":
- if v.startswith("Digest"):
- value = v
- break
-
- if value is None:
- self._handlewebError("Digest authentification scheme was not found")
-
- value = value[7:]
- items = value.split(', ')
- tokens = {}
- for item in items:
- key, value = item.split('=')
- tokens[key.lower()] = value
-
- missing_msg = "%s is missing"
- bad_value_msg = "'%s' was expecting '%s' but found '%s'"
- nonce = None
- if 'realm' not in tokens:
- self._handlewebError(missing_msg % 'realm')
- elif tokens['realm'] != '"localhost"':
- self._handlewebError(bad_value_msg % ('realm', '"localhost"', tokens['realm']))
- if 'nonce' not in tokens:
- self._handlewebError(missing_msg % 'nonce')
- else:
- nonce = tokens['nonce'].strip('"')
- if 'algorithm' not in tokens:
- self._handlewebError(missing_msg % 'algorithm')
- elif tokens['algorithm'] != '"MD5"':
- self._handlewebError(bad_value_msg % ('algorithm', '"MD5"', tokens['algorithm']))
- if 'qop' not in tokens:
- self._handlewebError(missing_msg % 'qop')
- elif tokens['qop'] != '"auth"':
- self._handlewebError(bad_value_msg % ('qop', '"auth"', tokens['qop']))
-
- get_ha1 = auth_digest.get_ha1_dict_plain({'test' : 'test'})
-
- # Test user agent response with a wrong value for 'realm'
- base_auth = 'Digest username="test", realm="wrong realm", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
- auth_header = base_auth % (nonce, '11111111111111111111111111111111', '00000001')
- auth = auth_digest.HttpDigestAuthorization(auth_header, 'GET')
- # calculate the response digest
- ha1 = get_ha1(auth.realm, 'test')
- response = auth.request_digest(ha1)
- # send response with correct response digest, but wrong realm
- auth_header = base_auth % (nonce, response, '00000001')
- self.getPage('/digest/', [('Authorization', auth_header)])
- self.assertStatus(401)
-
- # Test that must pass
- base_auth = 'Digest username="test", realm="localhost", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
- auth_header = base_auth % (nonce, '11111111111111111111111111111111', '00000001')
- auth = auth_digest.HttpDigestAuthorization(auth_header, 'GET')
- # calculate the response digest
- ha1 = get_ha1('localhost', 'test')
- response = auth.request_digest(ha1)
- # send response with correct response digest
- auth_header = base_auth % (nonce, response, '00000001')
- self.getPage('/digest/', [('Authorization', auth_header)])
- self.assertStatus('200 OK')
- self.assertBody("Hello test, you've been authorized.")
-
diff --git a/cherrypy/test/test_bus.py b/cherrypy/test/test_bus.py
deleted file mode 100755
index 51c1022..0000000
--- a/cherrypy/test/test_bus.py
+++ /dev/null
@@ -1,263 +0,0 @@
-import threading
-import time
-import unittest
-
-import cherrypy
-from cherrypy._cpcompat import get_daemon, set
-from cherrypy.process import wspbus
-
-
-msg = "Listener %d on channel %s: %s."
-
-
-class PublishSubscribeTests(unittest.TestCase):
-
- def get_listener(self, channel, index):
- def listener(arg=None):
- self.responses.append(msg % (index, channel, arg))
- return listener
-
- def test_builtin_channels(self):
- b = wspbus.Bus()
-
- self.responses, expected = [], []
-
- for channel in b.listeners:
- for index, priority in enumerate([100, 50, 0, 51]):
- b.subscribe(channel, self.get_listener(channel, index), priority)
-
- for channel in b.listeners:
- b.publish(channel)
- expected.extend([msg % (i, channel, None) for i in (2, 1, 3, 0)])
- b.publish(channel, arg=79347)
- expected.extend([msg % (i, channel, 79347) for i in (2, 1, 3, 0)])
-
- self.assertEqual(self.responses, expected)
-
- def test_custom_channels(self):
- b = wspbus.Bus()
-
- self.responses, expected = [], []
-
- custom_listeners = ('hugh', 'louis', 'dewey')
- for channel in custom_listeners:
- for index, priority in enumerate([None, 10, 60, 40]):
- b.subscribe(channel, self.get_listener(channel, index), priority)
-
- for channel in custom_listeners:
- b.publish(channel, 'ah so')
- expected.extend([msg % (i, channel, 'ah so') for i in (1, 3, 0, 2)])
- b.publish(channel)
- expected.extend([msg % (i, channel, None) for i in (1, 3, 0, 2)])
-
- self.assertEqual(self.responses, expected)
-
- def test_listener_errors(self):
- b = wspbus.Bus()
-
- self.responses, expected = [], []
- channels = [c for c in b.listeners if c != 'log']
-
- for channel in channels:
- b.subscribe(channel, self.get_listener(channel, 1))
- # This will break since the lambda takes no args.
- b.subscribe(channel, lambda: None, priority=20)
-
- for channel in channels:
- self.assertRaises(wspbus.ChannelFailures, b.publish, channel, 123)
- expected.append(msg % (1, channel, 123))
-
- self.assertEqual(self.responses, expected)
-
-
-class BusMethodTests(unittest.TestCase):
-
- def log(self, bus):
- self._log_entries = []
- def logit(msg, level):
- self._log_entries.append(msg)
- bus.subscribe('log', logit)
-
- def assertLog(self, entries):
- self.assertEqual(self._log_entries, entries)
-
- def get_listener(self, channel, index):
- def listener(arg=None):
- self.responses.append(msg % (index, channel, arg))
- return listener
-
- def test_start(self):
- b = wspbus.Bus()
- self.log(b)
-
- self.responses = []
- num = 3
- for index in range(num):
- b.subscribe('start', self.get_listener('start', index))
-
- b.start()
- try:
- # The start method MUST call all 'start' listeners.
- self.assertEqual(set(self.responses),
- set([msg % (i, 'start', None) for i in range(num)]))
- # The start method MUST move the state to STARTED
- # (or EXITING, if errors occur)
- self.assertEqual(b.state, b.states.STARTED)
- # The start method MUST log its states.
- self.assertLog(['Bus STARTING', 'Bus STARTED'])
- finally:
- # Exit so the atexit handler doesn't complain.
- b.exit()
-
- def test_stop(self):
- b = wspbus.Bus()
- self.log(b)
-
- self.responses = []
- num = 3
- for index in range(num):
- b.subscribe('stop', self.get_listener('stop', index))
-
- b.stop()
-
- # The stop method MUST call all 'stop' listeners.
- self.assertEqual(set(self.responses),
- set([msg % (i, 'stop', None) for i in range(num)]))
- # The stop method MUST move the state to STOPPED
- self.assertEqual(b.state, b.states.STOPPED)
- # The stop method MUST log its states.
- self.assertLog(['Bus STOPPING', 'Bus STOPPED'])
-
- def test_graceful(self):
- b = wspbus.Bus()
- self.log(b)
-
- self.responses = []
- num = 3
- for index in range(num):
- b.subscribe('graceful', self.get_listener('graceful', index))
-
- b.graceful()
-
- # The graceful method MUST call all 'graceful' listeners.
- self.assertEqual(set(self.responses),
- set([msg % (i, 'graceful', None) for i in range(num)]))
- # The graceful method MUST log its states.
- self.assertLog(['Bus graceful'])
-
- def test_exit(self):
- b = wspbus.Bus()
- self.log(b)
-
- self.responses = []
- num = 3
- for index in range(num):
- b.subscribe('stop', self.get_listener('stop', index))
- b.subscribe('exit', self.get_listener('exit', index))
-
- b.exit()
-
- # The exit method MUST call all 'stop' listeners,
- # and then all 'exit' listeners.
- self.assertEqual(set(self.responses),
- set([msg % (i, 'stop', None) for i in range(num)] +
- [msg % (i, 'exit', None) for i in range(num)]))
- # The exit method MUST move the state to EXITING
- self.assertEqual(b.state, b.states.EXITING)
- # The exit method MUST log its states.
- self.assertLog(['Bus STOPPING', 'Bus STOPPED', 'Bus EXITING', 'Bus EXITED'])
-
- def test_wait(self):
- b = wspbus.Bus()
-
- def f(method):
- time.sleep(0.2)
- getattr(b, method)()
-
- for method, states in [('start', [b.states.STARTED]),
- ('stop', [b.states.STOPPED]),
- ('start', [b.states.STARTING, b.states.STARTED]),
- ('exit', [b.states.EXITING]),
- ]:
- threading.Thread(target=f, args=(method,)).start()
- b.wait(states)
-
- # The wait method MUST wait for the given state(s).
- if b.state not in states:
- self.fail("State %r not in %r" % (b.state, states))
-
- def test_block(self):
- b = wspbus.Bus()
- self.log(b)
-
- def f():
- time.sleep(0.2)
- b.exit()
- def g():
- time.sleep(0.4)
- threading.Thread(target=f).start()
- threading.Thread(target=g).start()
- threads = [t for t in threading.enumerate() if not get_daemon(t)]
- self.assertEqual(len(threads), 3)
-
- b.block()
-
- # The block method MUST wait for the EXITING state.
- self.assertEqual(b.state, b.states.EXITING)
- # The block method MUST wait for ALL non-main, non-daemon threads to finish.
- threads = [t for t in threading.enumerate() if not get_daemon(t)]
- self.assertEqual(len(threads), 1)
- # The last message will mention an indeterminable thread name; ignore it
- self.assertEqual(self._log_entries[:-1],
- ['Bus STOPPING', 'Bus STOPPED',
- 'Bus EXITING', 'Bus EXITED',
- 'Waiting for child threads to terminate...'])
-
- def test_start_with_callback(self):
- b = wspbus.Bus()
- self.log(b)
- try:
- events = []
- def f(*args, **kwargs):
- events.append(("f", args, kwargs))
- def g():
- events.append("g")
- b.subscribe("start", g)
- b.start_with_callback(f, (1, 3, 5), {"foo": "bar"})
- # Give wait() time to run f()
- time.sleep(0.2)
-
- # The callback method MUST wait for the STARTED state.
- self.assertEqual(b.state, b.states.STARTED)
- # The callback method MUST run after all start methods.
- self.assertEqual(events, ["g", ("f", (1, 3, 5), {"foo": "bar"})])
- finally:
- b.exit()
-
- def test_log(self):
- b = wspbus.Bus()
- self.log(b)
- self.assertLog([])
-
- # Try a normal message.
- expected = []
- for msg in ["O mah darlin'"] * 3 + ["Clementiiiiiiiine"]:
- b.log(msg)
- expected.append(msg)
- self.assertLog(expected)
-
- # Try an error message
- try:
- foo
- except NameError:
- b.log("You are lost and gone forever", traceback=True)
- lastmsg = self._log_entries[-1]
- if "Traceback" not in lastmsg or "NameError" not in lastmsg:
- self.fail("Last log message %r did not contain "
- "the expected traceback." % lastmsg)
- else:
- self.fail("NameError was not raised as expected.")
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/cherrypy/test/test_caching.py b/cherrypy/test/test_caching.py
deleted file mode 100755
index 720a933..0000000
--- a/cherrypy/test/test_caching.py
+++ /dev/null
@@ -1,329 +0,0 @@
-import datetime
-import gzip
-from itertools import count
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import sys
-import threading
-import time
-import urllib
-
-import cherrypy
-from cherrypy._cpcompat import next, ntob, quote, xrange
-from cherrypy.lib import httputil
-
-gif_bytes = ntob('GIF89a\x01\x00\x01\x00\x82\x00\x01\x99"\x1e\x00\x00\x00\x00\x00'
- '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
- '\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x02\x03\x02\x08\t\x00;')
-
-
-
-from cherrypy.test import helper
-
-class CacheTest(helper.CPWebCase):
-
- def setup_server():
-
- class Root:
-
- _cp_config = {'tools.caching.on': True}
-
- def __init__(self):
- self.counter = 0
- self.control_counter = 0
- self.longlock = threading.Lock()
-
- def index(self):
- self.counter += 1
- msg = "visit #%s" % self.counter
- return msg
- index.exposed = True
-
- def control(self):
- self.control_counter += 1
- return "visit #%s" % self.control_counter
- control.exposed = True
-
- def a_gif(self):
- cherrypy.response.headers['Last-Modified'] = httputil.HTTPDate()
- return gif_bytes
- a_gif.exposed = True
-
- def long_process(self, seconds='1'):
- try:
- self.longlock.acquire()
- time.sleep(float(seconds))
- finally:
- self.longlock.release()
- return 'success!'
- long_process.exposed = True
-
- def clear_cache(self, path):
- cherrypy._cache.store[cherrypy.request.base + path].clear()
- clear_cache.exposed = True
-
- class VaryHeaderCachingServer(object):
-
- _cp_config = {'tools.caching.on': True,
- 'tools.response_headers.on': True,
- 'tools.response_headers.headers': [('Vary', 'Our-Varying-Header')],
- }
-
- def __init__(self):
- self.counter = count(1)
-
- def index(self):
- return "visit #%s" % next(self.counter)
- index.exposed = True
-
- class UnCached(object):
- _cp_config = {'tools.expires.on': True,
- 'tools.expires.secs': 60,
- 'tools.staticdir.on': True,
- 'tools.staticdir.dir': 'static',
- 'tools.staticdir.root': curdir,
- }
-
- def force(self):
- cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
- self._cp_config['tools.expires.force'] = True
- self._cp_config['tools.expires.secs'] = 0
- return "being forceful"
- force.exposed = True
- force._cp_config = {'tools.expires.secs': 0}
-
- def dynamic(self):
- cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
- cherrypy.response.headers['Cache-Control'] = 'private'
- return "D-d-d-dynamic!"
- dynamic.exposed = True
-
- def cacheable(self):
- cherrypy.response.headers['Etag'] = 'bibbitybobbityboo'
- return "Hi, I'm cacheable."
- cacheable.exposed = True
-
- def specific(self):
- cherrypy.response.headers['Etag'] = 'need_this_to_make_me_cacheable'
- return "I am being specific"
- specific.exposed = True
- specific._cp_config = {'tools.expires.secs': 86400}
-
- class Foo(object):pass
-
- def wrongtype(self):
- cherrypy.response.headers['Etag'] = 'need_this_to_make_me_cacheable'
- return "Woops"
- wrongtype.exposed = True
- wrongtype._cp_config = {'tools.expires.secs': Foo()}
-
- cherrypy.tree.mount(Root())
- cherrypy.tree.mount(UnCached(), "/expires")
- cherrypy.tree.mount(VaryHeaderCachingServer(), "/varying_headers")
- cherrypy.config.update({'tools.gzip.on': True})
- setup_server = staticmethod(setup_server)
-
- def testCaching(self):
- elapsed = 0.0
- for trial in range(10):
- self.getPage("/")
- # The response should be the same every time,
- # except for the Age response header.
- self.assertBody('visit #1')
- if trial != 0:
- age = int(self.assertHeader("Age"))
- self.assert_(age >= elapsed)
- elapsed = age
-
- # POST, PUT, DELETE should not be cached.
- self.getPage("/", method="POST")
- self.assertBody('visit #2')
- # Because gzip is turned on, the Vary header should always Vary for content-encoding
- self.assertHeader('Vary', 'Accept-Encoding')
- # The previous request should have invalidated the cache,
- # so this request will recalc the response.
- self.getPage("/", method="GET")
- self.assertBody('visit #3')
- # ...but this request should get the cached copy.
- self.getPage("/", method="GET")
- self.assertBody('visit #3')
- self.getPage("/", method="DELETE")
- self.assertBody('visit #4')
-
- # The previous request should have invalidated the cache,
- # so this request will recalc the response.
- self.getPage("/", method="GET", headers=[('Accept-Encoding', 'gzip')])
- self.assertHeader('Content-Encoding', 'gzip')
- self.assertHeader('Vary')
- self.assertEqual(cherrypy.lib.encoding.decompress(self.body), ntob("visit #5"))
-
- # Now check that a second request gets the gzip header and gzipped body
- # This also tests a bug in 3.0 to 3.0.2 whereby the cached, gzipped
- # response body was being gzipped a second time.
- self.getPage("/", method="GET", headers=[('Accept-Encoding', 'gzip')])
- self.assertHeader('Content-Encoding', 'gzip')
- self.assertEqual(cherrypy.lib.encoding.decompress(self.body), ntob("visit #5"))
-
- # Now check that a third request that doesn't accept gzip
- # skips the cache (because the 'Vary' header denies it).
- self.getPage("/", method="GET")
- self.assertNoHeader('Content-Encoding')
- self.assertBody('visit #6')
-
- def testVaryHeader(self):
- self.getPage("/varying_headers/")
- self.assertStatus("200 OK")
- self.assertHeaderItemValue('Vary', 'Our-Varying-Header')
- self.assertBody('visit #1')
-
- # Now check that different 'Vary'-fields don't evict each other.
- # This test creates 2 requests with different 'Our-Varying-Header'
- # and then tests if the first one still exists.
- self.getPage("/varying_headers/", headers=[('Our-Varying-Header', 'request 2')])
- self.assertStatus("200 OK")
- self.assertBody('visit #2')
-
- self.getPage("/varying_headers/", headers=[('Our-Varying-Header', 'request 2')])
- self.assertStatus("200 OK")
- self.assertBody('visit #2')
-
- self.getPage("/varying_headers/")
- self.assertStatus("200 OK")
- self.assertBody('visit #1')
-
- def testExpiresTool(self):
- # test setting an expires header
- self.getPage("/expires/specific")
- self.assertStatus("200 OK")
- self.assertHeader("Expires")
-
- # test exceptions for bad time values
- self.getPage("/expires/wrongtype")
- self.assertStatus(500)
- self.assertInBody("TypeError")
-
- # static content should not have "cache prevention" headers
- self.getPage("/expires/index.html")
- self.assertStatus("200 OK")
- self.assertNoHeader("Pragma")
- self.assertNoHeader("Cache-Control")
- self.assertHeader("Expires")
-
- # dynamic content that sets indicators should not have
- # "cache prevention" headers
- self.getPage("/expires/cacheable")
- self.assertStatus("200 OK")
- self.assertNoHeader("Pragma")
- self.assertNoHeader("Cache-Control")
- self.assertHeader("Expires")
-
- self.getPage('/expires/dynamic')
- self.assertBody("D-d-d-dynamic!")
- # the Cache-Control header should be untouched
- self.assertHeader("Cache-Control", "private")
- self.assertHeader("Expires")
-
- # configure the tool to ignore indicators and replace existing headers
- self.getPage("/expires/force")
- self.assertStatus("200 OK")
- # This also gives us a chance to test 0 expiry with no other headers
- self.assertHeader("Pragma", "no-cache")
- if cherrypy.server.protocol_version == "HTTP/1.1":
- self.assertHeader("Cache-Control", "no-cache, must-revalidate")
- self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-
- # static content should now have "cache prevention" headers
- self.getPage("/expires/index.html")
- self.assertStatus("200 OK")
- self.assertHeader("Pragma", "no-cache")
- if cherrypy.server.protocol_version == "HTTP/1.1":
- self.assertHeader("Cache-Control", "no-cache, must-revalidate")
- self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-
- # the cacheable handler should now have "cache prevention" headers
- self.getPage("/expires/cacheable")
- self.assertStatus("200 OK")
- self.assertHeader("Pragma", "no-cache")
- if cherrypy.server.protocol_version == "HTTP/1.1":
- self.assertHeader("Cache-Control", "no-cache, must-revalidate")
- self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-
- self.getPage('/expires/dynamic')
- self.assertBody("D-d-d-dynamic!")
- # dynamic sets Cache-Control to private but it should be
- # overwritten here ...
- self.assertHeader("Pragma", "no-cache")
- if cherrypy.server.protocol_version == "HTTP/1.1":
- self.assertHeader("Cache-Control", "no-cache, must-revalidate")
- self.assertHeader("Expires", "Sun, 28 Jan 2007 00:00:00 GMT")
-
- def testLastModified(self):
- self.getPage("/a.gif")
- self.assertStatus(200)
- self.assertBody(gif_bytes)
- lm1 = self.assertHeader("Last-Modified")
-
- # this request should get the cached copy.
- self.getPage("/a.gif")
- self.assertStatus(200)
- self.assertBody(gif_bytes)
- self.assertHeader("Age")
- lm2 = self.assertHeader("Last-Modified")
- self.assertEqual(lm1, lm2)
-
- # this request should match the cached copy, but raise 304.
- self.getPage("/a.gif", [('If-Modified-Since', lm1)])
- self.assertStatus(304)
- self.assertNoHeader("Last-Modified")
- if not getattr(cherrypy.server, "using_apache", False):
- self.assertHeader("Age")
-
- def test_antistampede(self):
- SECONDS = 4
- # We MUST make an initial synchronous request in order to create the
- # AntiStampedeCache object, and populate its selecting_headers,
- # before the actual stampede.
- self.getPage("/long_process?seconds=%d" % SECONDS)
- self.assertBody('success!')
- self.getPage("/clear_cache?path=" +
- quote('/long_process?seconds=%d' % SECONDS, safe=''))
- self.assertStatus(200)
- sys.stdout.write("prepped... ")
- sys.stdout.flush()
-
- start = datetime.datetime.now()
- def run():
- self.getPage("/long_process?seconds=%d" % SECONDS)
- # The response should be the same every time
- self.assertBody('success!')
- ts = [threading.Thread(target=run) for i in xrange(100)]
- for t in ts:
- t.start()
- for t in ts:
- t.join()
- self.assertEqualDates(start, datetime.datetime.now(),
- # Allow a second for our thread/TCP overhead etc.
- seconds=SECONDS + 1.1)
-
- def test_cache_control(self):
- self.getPage("/control")
- self.assertBody('visit #1')
- self.getPage("/control")
- self.assertBody('visit #1')
-
- self.getPage("/control", headers=[('Cache-Control', 'no-cache')])
- self.assertBody('visit #2')
- self.getPage("/control")
- self.assertBody('visit #2')
-
- self.getPage("/control", headers=[('Pragma', 'no-cache')])
- self.assertBody('visit #3')
- self.getPage("/control")
- self.assertBody('visit #3')
-
- time.sleep(1)
- self.getPage("/control", headers=[('Cache-Control', 'max-age=0')])
- self.assertBody('visit #4')
- self.getPage("/control")
- self.assertBody('visit #4')
-
diff --git a/cherrypy/test/test_config.py b/cherrypy/test/test_config.py
deleted file mode 100755
index a0bd8ab..0000000
--- a/cherrypy/test/test_config.py
+++ /dev/null
@@ -1,249 +0,0 @@
-"""Tests for the CherryPy configuration system."""
-
-import os, sys
-localDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-from cherrypy._cpcompat import ntob, StringIO
-import unittest
-
-import cherrypy
-
-def setup_server():
-
- class Root:
-
- _cp_config = {'foo': 'this',
- 'bar': 'that'}
-
- def __init__(self):
- cherrypy.config.namespaces['db'] = self.db_namespace
-
- def db_namespace(self, k, v):
- if k == "scheme":
- self.db = v
-
- # @cherrypy.expose(alias=('global_', 'xyz'))
- def index(self, key):
- return cherrypy.request.config.get(key, "None")
- index = cherrypy.expose(index, alias=('global_', 'xyz'))
-
- def repr(self, key):
- return repr(cherrypy.request.config.get(key, None))
- repr.exposed = True
-
- def dbscheme(self):
- return self.db
- dbscheme.exposed = True
-
- def plain(self, x):
- return x
- plain.exposed = True
- plain._cp_config = {'request.body.attempt_charsets': ['utf-16']}
-
- favicon_ico = cherrypy.tools.staticfile.handler(
- filename=os.path.join(localDir, '../favicon.ico'))
-
- class Foo:
-
- _cp_config = {'foo': 'this2',
- 'baz': 'that2'}
-
- def index(self, key):
- return cherrypy.request.config.get(key, "None")
- index.exposed = True
- nex = index
-
- def silly(self):
- return 'Hello world'
- silly.exposed = True
- silly._cp_config = {'response.headers.X-silly': 'sillyval'}
-
- def bar(self, key):
- return repr(cherrypy.request.config.get(key, None))
- bar.exposed = True
- bar._cp_config = {'foo': 'this3', 'bax': 'this4'}
-
- class Another:
-
- def index(self, key):
- return str(cherrypy.request.config.get(key, "None"))
- index.exposed = True
-
-
- def raw_namespace(key, value):
- if key == 'input.map':
- handler = cherrypy.request.handler
- def wrapper():
- params = cherrypy.request.params
- for name, coercer in list(value.items()):
- try:
- params[name] = coercer(params[name])
- except KeyError:
- pass
- return handler()
- cherrypy.request.handler = wrapper
- elif key == 'output':
- handler = cherrypy.request.handler
- def wrapper():
- # 'value' is a type (like int or str).
- return value(handler())
- cherrypy.request.handler = wrapper
-
- class Raw:
-
- _cp_config = {'raw.output': repr}
-
- def incr(self, num):
- return num + 1
- incr.exposed = True
- incr._cp_config = {'raw.input.map': {'num': int}}
-
- ioconf = StringIO("""
-[/]
-neg: -1234
-filename: os.path.join(sys.prefix, "hello.py")
-thing1: cherrypy.lib.httputil.response_codes[404]
-thing2: __import__('cherrypy.tutorial', globals(), locals(), ['']).thing2
-complex: 3+2j
-ones: "11"
-twos: "22"
-stradd: %%(ones)s + %%(twos)s + "33"
-
-[/favicon.ico]
-tools.staticfile.filename = %r
-""" % os.path.join(localDir, 'static/dirback.jpg'))
-
- root = Root()
- root.foo = Foo()
- root.raw = Raw()
- app = cherrypy.tree.mount(root, config=ioconf)
- app.request_class.namespaces['raw'] = raw_namespace
-
- cherrypy.tree.mount(Another(), "/another")
- cherrypy.config.update({'luxuryyacht': 'throatwobblermangrove',
- 'db.scheme': r"sqlite///memory",
- })
-
-
-# Client-side code #
-
-from cherrypy.test import helper
-
-class ConfigTests(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def testConfig(self):
- tests = [
- ('/', 'nex', 'None'),
- ('/', 'foo', 'this'),
- ('/', 'bar', 'that'),
- ('/xyz', 'foo', 'this'),
- ('/foo/', 'foo', 'this2'),
- ('/foo/', 'bar', 'that'),
- ('/foo/', 'bax', 'None'),
- ('/foo/bar', 'baz', "'that2'"),
- ('/foo/nex', 'baz', 'that2'),
- # If 'foo' == 'this', then the mount point '/another' leaks into '/'.
- ('/another/','foo', 'None'),
- ]
- for path, key, expected in tests:
- self.getPage(path + "?key=" + key)
- self.assertBody(expected)
-
- expectedconf = {
- # From CP defaults
- 'tools.log_headers.on': False,
- 'tools.log_tracebacks.on': True,
- 'request.show_tracebacks': True,
- 'log.screen': False,
- 'environment': 'test_suite',
- 'engine.autoreload_on': False,
- # From global config
- 'luxuryyacht': 'throatwobblermangrove',
- # From Root._cp_config
- 'bar': 'that',
- # From Foo._cp_config
- 'baz': 'that2',
- # From Foo.bar._cp_config
- 'foo': 'this3',
- 'bax': 'this4',
- }
- for key, expected in expectedconf.items():
- self.getPage("/foo/bar?key=" + key)
- self.assertBody(repr(expected))
-
- def testUnrepr(self):
- self.getPage("/repr?key=neg")
- self.assertBody("-1234")
-
- self.getPage("/repr?key=filename")
- self.assertBody(repr(os.path.join(sys.prefix, "hello.py")))
-
- self.getPage("/repr?key=thing1")
- self.assertBody(repr(cherrypy.lib.httputil.response_codes[404]))
-
- if not getattr(cherrypy.server, "using_apache", False):
- # The object ID's won't match up when using Apache, since the
- # server and client are running in different processes.
- self.getPage("/repr?key=thing2")
- from cherrypy.tutorial import thing2
- self.assertBody(repr(thing2))
-
- self.getPage("/repr?key=complex")
- self.assertBody("(3+2j)")
-
- self.getPage("/repr?key=stradd")
- self.assertBody(repr("112233"))
-
- def testRespNamespaces(self):
- self.getPage("/foo/silly")
- self.assertHeader('X-silly', 'sillyval')
- self.assertBody('Hello world')
-
- def testCustomNamespaces(self):
- self.getPage("/raw/incr?num=12")
- self.assertBody("13")
-
- self.getPage("/dbscheme")
- self.assertBody(r"sqlite///memory")
-
- def testHandlerToolConfigOverride(self):
- # Assert that config overrides tool constructor args. Above, we set
- # the favicon in the page handler to be '../favicon.ico',
- # but then overrode it in config to be './static/dirback.jpg'.
- self.getPage("/favicon.ico")
- self.assertBody(open(os.path.join(localDir, "static/dirback.jpg"),
- "rb").read())
-
- def test_request_body_namespace(self):
- self.getPage("/plain", method='POST', headers=[
- ('Content-Type', 'application/x-www-form-urlencoded'),
- ('Content-Length', '13')],
- body=ntob('\xff\xfex\x00=\xff\xfea\x00b\x00c\x00'))
- self.assertBody("abc")
-
-
-class VariableSubstitutionTests(unittest.TestCase):
- setup_server = staticmethod(setup_server)
-
- def test_config(self):
- from textwrap import dedent
-
- # variable substitution with [DEFAULT]
- conf = dedent("""
- [DEFAULT]
- dir = "/some/dir"
- my.dir = %(dir)s + "/sub"
-
- [my]
- my.dir = %(dir)s + "/my/dir"
- my.dir2 = %(my.dir)s + '/dir2'
-
- """)
-
- fp = StringIO(conf)
-
- cherrypy.config.update(fp)
- self.assertEqual(cherrypy.config["my"]["my.dir"], "/some/dir/my/dir")
- self.assertEqual(cherrypy.config["my"]["my.dir2"], "/some/dir/my/dir/dir2")
-
diff --git a/cherrypy/test/test_config_server.py b/cherrypy/test/test_config_server.py
deleted file mode 100755
index 0b9718d..0000000
--- a/cherrypy/test/test_config_server.py
+++ /dev/null
@@ -1,121 +0,0 @@
-"""Tests for the CherryPy configuration system."""
-
-import os, sys
-localDir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-import socket
-import time
-
-import cherrypy
-
-
-# Client-side code #
-
-from cherrypy.test import helper
-
-class ServerConfigTests(helper.CPWebCase):
-
- def setup_server():
-
- class Root:
- def index(self):
- return cherrypy.request.wsgi_environ['SERVER_PORT']
- index.exposed = True
-
- def upload(self, file):
- return "Size: %s" % len(file.file.read())
- upload.exposed = True
-
- def tinyupload(self):
- return cherrypy.request.body.read()
- tinyupload.exposed = True
- tinyupload._cp_config = {'request.body.maxbytes': 100}
-
- cherrypy.tree.mount(Root())
-
- cherrypy.config.update({
- 'server.socket_host': '0.0.0.0',
- 'server.socket_port': 9876,
- 'server.max_request_body_size': 200,
- 'server.max_request_header_size': 500,
- 'server.socket_timeout': 0.5,
-
- # Test explicit server.instance
- 'server.2.instance': 'cherrypy._cpwsgi_server.CPWSGIServer',
- 'server.2.socket_port': 9877,
-
- # Test non-numeric <servername>
- # Also test default server.instance = builtin server
- 'server.yetanother.socket_port': 9878,
- })
- setup_server = staticmethod(setup_server)
-
- PORT = 9876
-
- def testBasicConfig(self):
- self.getPage("/")
- self.assertBody(str(self.PORT))
-
- def testAdditionalServers(self):
- if self.scheme == 'https':
- return self.skip("not available under ssl")
- self.PORT = 9877
- self.getPage("/")
- self.assertBody(str(self.PORT))
- self.PORT = 9878
- self.getPage("/")
- self.assertBody(str(self.PORT))
-
- def testMaxRequestSizePerHandler(self):
- if getattr(cherrypy.server, "using_apache", False):
- return self.skip("skipped due to known Apache differences... ")
-
- self.getPage('/tinyupload', method="POST",
- headers=[('Content-Type', 'text/plain'),
- ('Content-Length', '100')],
- body="x" * 100)
- self.assertStatus(200)
- self.assertBody("x" * 100)
-
- self.getPage('/tinyupload', method="POST",
- headers=[('Content-Type', 'text/plain'),
- ('Content-Length', '101')],
- body="x" * 101)
- self.assertStatus(413)
-
- def testMaxRequestSize(self):
- if getattr(cherrypy.server, "using_apache", False):
- return self.skip("skipped due to known Apache differences... ")
-
- for size in (500, 5000, 50000):
- self.getPage("/", headers=[('From', "x" * 500)])
- self.assertStatus(413)
-
- # Test for http://www.cherrypy.org/ticket/421
- # (Incorrect border condition in readline of SizeCheckWrapper).
- # This hangs in rev 891 and earlier.
- lines256 = "x" * 248
- self.getPage("/",
- headers=[('Host', '%s:%s' % (self.HOST, self.PORT)),
- ('From', lines256)])
-
- # Test upload
- body = '\r\n'.join([
- '--x',
- 'Content-Disposition: form-data; name="file"; filename="hello.txt"',
- 'Content-Type: text/plain',
- '',
- '%s',
- '--x--'])
- partlen = 200 - len(body)
- b = body % ("x" * partlen)
- h = [("Content-type", "multipart/form-data; boundary=x"),
- ("Content-Length", "%s" % len(b))]
- self.getPage('/upload', h, "POST", b)
- self.assertBody('Size: %d' % partlen)
-
- b = body % ("x" * 200)
- h = [("Content-type", "multipart/form-data; boundary=x"),
- ("Content-Length", "%s" % len(b))]
- self.getPage('/upload', h, "POST", b)
- self.assertStatus(413)
-
diff --git a/cherrypy/test/test_conn.py b/cherrypy/test/test_conn.py
deleted file mode 100755
index 1346f59..0000000
--- a/cherrypy/test/test_conn.py
+++ /dev/null
@@ -1,734 +0,0 @@
-"""Tests for TCP connection handling, including proper and timely close."""
-
-import socket
-import sys
-import time
-timeout = 1
-
-
-import cherrypy
-from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, NotConnected, BadStatusLine
-from cherrypy._cpcompat import ntob, urlopen, unicodestr
-from cherrypy.test import webtest
-from cherrypy import _cperror
-
-
-pov = 'pPeErRsSiIsStTeEnNcCeE oOfF vViIsSiIoOnN'
-
-def setup_server():
-
- def raise500():
- raise cherrypy.HTTPError(500)
-
- class Root:
-
- def index(self):
- return pov
- index.exposed = True
- page1 = index
- page2 = index
- page3 = index
-
- def hello(self):
- return "Hello, world!"
- hello.exposed = True
-
- def timeout(self, t):
- return str(cherrypy.server.httpserver.timeout)
- timeout.exposed = True
-
- def stream(self, set_cl=False):
- if set_cl:
- cherrypy.response.headers['Content-Length'] = 10
-
- def content():
- for x in range(10):
- yield str(x)
-
- return content()
- stream.exposed = True
- stream._cp_config = {'response.stream': True}
-
- def error(self, code=500):
- raise cherrypy.HTTPError(code)
- error.exposed = True
-
- def upload(self):
- if not cherrypy.request.method == 'POST':
- raise AssertionError("'POST' != request.method %r" %
- cherrypy.request.method)
- return "thanks for '%s'" % cherrypy.request.body.read()
- upload.exposed = True
-
- def custom(self, response_code):
- cherrypy.response.status = response_code
- return "Code = %s" % response_code
- custom.exposed = True
-
- def err_before_read(self):
- return "ok"
- err_before_read.exposed = True
- err_before_read._cp_config = {'hooks.on_start_resource': raise500}
-
- def one_megabyte_of_a(self):
- return ["a" * 1024] * 1024
- one_megabyte_of_a.exposed = True
-
- def custom_cl(self, body, cl):
- cherrypy.response.headers['Content-Length'] = cl
- if not isinstance(body, list):
- body = [body]
- newbody = []
- for chunk in body:
- if isinstance(chunk, unicodestr):
- chunk = chunk.encode('ISO-8859-1')
- newbody.append(chunk)
- return newbody
- custom_cl.exposed = True
- # Turn off the encoding tool so it doens't collapse
- # our response body and reclaculate the Content-Length.
- custom_cl._cp_config = {'tools.encode.on': False}
-
- cherrypy.tree.mount(Root())
- cherrypy.config.update({
- 'server.max_request_body_size': 1001,
- 'server.socket_timeout': timeout,
- })
-
-
-from cherrypy.test import helper
-
-class ConnectionCloseTests(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_HTTP11(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- self.persistent = True
-
- # Make the first request and assert there's no "Connection: close".
- self.getPage("/")
- self.assertStatus('200 OK')
- self.assertBody(pov)
- self.assertNoHeader("Connection")
-
- # Make another request on the same connection.
- self.getPage("/page1")
- self.assertStatus('200 OK')
- self.assertBody(pov)
- self.assertNoHeader("Connection")
-
- # Test client-side close.
- self.getPage("/page2", headers=[("Connection", "close")])
- self.assertStatus('200 OK')
- self.assertBody(pov)
- self.assertHeader("Connection", "close")
-
- # Make another request on the same connection, which should error.
- self.assertRaises(NotConnected, self.getPage, "/")
-
- def test_Streaming_no_len(self):
- self._streaming(set_cl=False)
-
- def test_Streaming_with_len(self):
- self._streaming(set_cl=True)
-
- def _streaming(self, set_cl):
- if cherrypy.server.protocol_version == "HTTP/1.1":
- self.PROTOCOL = "HTTP/1.1"
-
- self.persistent = True
-
- # Make the first request and assert there's no "Connection: close".
- self.getPage("/")
- self.assertStatus('200 OK')
- self.assertBody(pov)
- self.assertNoHeader("Connection")
-
- # Make another, streamed request on the same connection.
- if set_cl:
- # When a Content-Length is provided, the content should stream
- # without closing the connection.
- self.getPage("/stream?set_cl=Yes")
- self.assertHeader("Content-Length")
- self.assertNoHeader("Connection", "close")
- self.assertNoHeader("Transfer-Encoding")
-
- self.assertStatus('200 OK')
- self.assertBody('0123456789')
- else:
- # When no Content-Length response header is provided,
- # streamed output will either close the connection, or use
- # chunked encoding, to determine transfer-length.
- self.getPage("/stream")
- self.assertNoHeader("Content-Length")
- self.assertStatus('200 OK')
- self.assertBody('0123456789')
-
- chunked_response = False
- for k, v in self.headers:
- if k.lower() == "transfer-encoding":
- if str(v) == "chunked":
- chunked_response = True
-
- if chunked_response:
- self.assertNoHeader("Connection", "close")
- else:
- self.assertHeader("Connection", "close")
-
- # Make another request on the same connection, which should error.
- self.assertRaises(NotConnected, self.getPage, "/")
-
- # Try HEAD. See http://www.cherrypy.org/ticket/864.
- self.getPage("/stream", method='HEAD')
- self.assertStatus('200 OK')
- self.assertBody('')
- self.assertNoHeader("Transfer-Encoding")
- else:
- self.PROTOCOL = "HTTP/1.0"
-
- self.persistent = True
-
- # Make the first request and assert Keep-Alive.
- self.getPage("/", headers=[("Connection", "Keep-Alive")])
- self.assertStatus('200 OK')
- self.assertBody(pov)
- self.assertHeader("Connection", "Keep-Alive")
-
- # Make another, streamed request on the same connection.
- if set_cl:
- # When a Content-Length is provided, the content should
- # stream without closing the connection.
- self.getPage("/stream?set_cl=Yes",
- headers=[("Connection", "Keep-Alive")])
- self.assertHeader("Content-Length")
- self.assertHeader("Connection", "Keep-Alive")
- self.assertNoHeader("Transfer-Encoding")
- self.assertStatus('200 OK')
- self.assertBody('0123456789')
- else:
- # When a Content-Length is not provided,
- # the server should close the connection.
- self.getPage("/stream", headers=[("Connection", "Keep-Alive")])
- self.assertStatus('200 OK')
- self.assertBody('0123456789')
-
- self.assertNoHeader("Content-Length")
- self.assertNoHeader("Connection", "Keep-Alive")
- self.assertNoHeader("Transfer-Encoding")
-
- # Make another request on the same connection, which should error.
- self.assertRaises(NotConnected, self.getPage, "/")
-
- def test_HTTP10_KeepAlive(self):
- self.PROTOCOL = "HTTP/1.0"
- if self.scheme == "https":
- self.HTTP_CONN = HTTPSConnection
- else:
- self.HTTP_CONN = HTTPConnection
-
- # Test a normal HTTP/1.0 request.
- self.getPage("/page2")
- self.assertStatus('200 OK')
- self.assertBody(pov)
- # Apache, for example, may emit a Connection header even for HTTP/1.0
-## self.assertNoHeader("Connection")
-
- # Test a keep-alive HTTP/1.0 request.
- self.persistent = True
-
- self.getPage("/page3", headers=[("Connection", "Keep-Alive")])
- self.assertStatus('200 OK')
- self.assertBody(pov)
- self.assertHeader("Connection", "Keep-Alive")
-
- # Remove the keep-alive header again.
- self.getPage("/page3")
- self.assertStatus('200 OK')
- self.assertBody(pov)
- # Apache, for example, may emit a Connection header even for HTTP/1.0
-## self.assertNoHeader("Connection")
-
-
-class PipelineTests(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_HTTP11_Timeout(self):
- # If we timeout without sending any data,
- # the server will close the conn with a 408.
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- # Connect but send nothing.
- self.persistent = True
- conn = self.HTTP_CONN
- conn.auto_open = False
- conn.connect()
-
- # Wait for our socket timeout
- time.sleep(timeout * 2)
-
- # The request should have returned 408 already.
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 408)
- conn.close()
-
- # Connect but send half the headers only.
- self.persistent = True
- conn = self.HTTP_CONN
- conn.auto_open = False
- conn.connect()
- conn.send(ntob('GET /hello HTTP/1.1'))
- conn.send(("Host: %s" % self.HOST).encode('ascii'))
-
- # Wait for our socket timeout
- time.sleep(timeout * 2)
-
- # The conn should have already sent 408.
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 408)
- conn.close()
-
- def test_HTTP11_Timeout_after_request(self):
- # If we timeout after at least one request has succeeded,
- # the server will close the conn without 408.
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- # Make an initial request
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/timeout?t=%s" % timeout, skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 200)
- self.body = response.read()
- self.assertBody(str(timeout))
-
- # Make a second request on the same socket
- conn._output(ntob('GET /hello HTTP/1.1'))
- conn._output(ntob("Host: %s" % self.HOST, 'ascii'))
- conn._send_output()
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 200)
- self.body = response.read()
- self.assertBody("Hello, world!")
-
- # Wait for our socket timeout
- time.sleep(timeout * 2)
-
- # Make another request on the same socket, which should error
- conn._output(ntob('GET /hello HTTP/1.1'))
- conn._output(ntob("Host: %s" % self.HOST, 'ascii'))
- conn._send_output()
- response = conn.response_class(conn.sock, method="GET")
- try:
- response.begin()
- except:
- if not isinstance(sys.exc_info()[1],
- (socket.error, BadStatusLine)):
- self.fail("Writing to timed out socket didn't fail"
- " as it should have: %s" % sys.exc_info()[1])
- else:
- if response.status != 408:
- self.fail("Writing to timed out socket didn't fail"
- " as it should have: %s" %
- response.read())
-
- conn.close()
-
- # Make another request on a new socket, which should work
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 200)
- self.body = response.read()
- self.assertBody(pov)
-
-
- # Make another request on the same socket,
- # but timeout on the headers
- conn.send(ntob('GET /hello HTTP/1.1'))
- # Wait for our socket timeout
- time.sleep(timeout * 2)
- response = conn.response_class(conn.sock, method="GET")
- try:
- response.begin()
- except:
- if not isinstance(sys.exc_info()[1],
- (socket.error, BadStatusLine)):
- self.fail("Writing to timed out socket didn't fail"
- " as it should have: %s" % sys.exc_info()[1])
- else:
- self.fail("Writing to timed out socket didn't fail"
- " as it should have: %s" %
- response.read())
-
- conn.close()
-
- # Retry the request on a new connection, which should work
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 200)
- self.body = response.read()
- self.assertBody(pov)
- conn.close()
-
- def test_HTTP11_pipelining(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- # Test pipelining. httplib doesn't support this directly.
- self.persistent = True
- conn = self.HTTP_CONN
-
- # Put request 1
- conn.putrequest("GET", "/hello", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
-
- for trial in range(5):
- # Put next request
- conn._output(ntob('GET /hello HTTP/1.1'))
- conn._output(ntob("Host: %s" % self.HOST, 'ascii'))
- conn._send_output()
-
- # Retrieve previous response
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- body = response.read(13)
- self.assertEqual(response.status, 200)
- self.assertEqual(body, ntob("Hello, world!"))
-
- # Retrieve final response
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- body = response.read()
- self.assertEqual(response.status, 200)
- self.assertEqual(body, ntob("Hello, world!"))
-
- conn.close()
-
- def test_100_Continue(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- self.persistent = True
- conn = self.HTTP_CONN
-
- # Try a page without an Expect request header first.
- # Note that httplib's response.begin automatically ignores
- # 100 Continue responses, so we must manually check for it.
- conn.putrequest("POST", "/upload", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.putheader("Content-Type", "text/plain")
- conn.putheader("Content-Length", "4")
- conn.endheaders()
- conn.send(ntob("d'oh"))
- response = conn.response_class(conn.sock, method="POST")
- version, status, reason = response._read_status()
- self.assertNotEqual(status, 100)
- conn.close()
-
- # Now try a page with an Expect header...
- conn.connect()
- conn.putrequest("POST", "/upload", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.putheader("Content-Type", "text/plain")
- conn.putheader("Content-Length", "17")
- conn.putheader("Expect", "100-continue")
- conn.endheaders()
- response = conn.response_class(conn.sock, method="POST")
-
- # ...assert and then skip the 100 response
- version, status, reason = response._read_status()
- self.assertEqual(status, 100)
- while True:
- line = response.fp.readline().strip()
- if line:
- self.fail("100 Continue should not output any headers. Got %r" % line)
- else:
- break
-
- # ...send the body
- body = ntob("I am a small file")
- conn.send(body)
-
- # ...get the final response
- response.begin()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus(200)
- self.assertBody("thanks for '%s'" % body)
- conn.close()
-
-
-class ConnectionTests(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_readall_or_close(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- if self.scheme == "https":
- self.HTTP_CONN = HTTPSConnection
- else:
- self.HTTP_CONN = HTTPConnection
-
- # Test a max of 0 (the default) and then reset to what it was above.
- old_max = cherrypy.server.max_request_body_size
- for new_max in (0, old_max):
- cherrypy.server.max_request_body_size = new_max
-
- self.persistent = True
- conn = self.HTTP_CONN
-
- # Get a POST page with an error
- conn.putrequest("POST", "/err_before_read", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.putheader("Content-Type", "text/plain")
- conn.putheader("Content-Length", "1000")
- conn.putheader("Expect", "100-continue")
- conn.endheaders()
- response = conn.response_class(conn.sock, method="POST")
-
- # ...assert and then skip the 100 response
- version, status, reason = response._read_status()
- self.assertEqual(status, 100)
- while True:
- skip = response.fp.readline().strip()
- if not skip:
- break
-
- # ...send the body
- conn.send(ntob("x" * 1000))
-
- # ...get the final response
- response.begin()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus(500)
-
- # Now try a working page with an Expect header...
- conn._output(ntob('POST /upload HTTP/1.1'))
- conn._output(ntob("Host: %s" % self.HOST, 'ascii'))
- conn._output(ntob("Content-Type: text/plain"))
- conn._output(ntob("Content-Length: 17"))
- conn._output(ntob("Expect: 100-continue"))
- conn._send_output()
- response = conn.response_class(conn.sock, method="POST")
-
- # ...assert and then skip the 100 response
- version, status, reason = response._read_status()
- self.assertEqual(status, 100)
- while True:
- skip = response.fp.readline().strip()
- if not skip:
- break
-
- # ...send the body
- body = ntob("I am a small file")
- conn.send(body)
-
- # ...get the final response
- response.begin()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus(200)
- self.assertBody("thanks for '%s'" % body)
- conn.close()
-
- def test_No_Message_Body(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- # Set our HTTP_CONN to an instance so it persists between requests.
- self.persistent = True
-
- # Make the first request and assert there's no "Connection: close".
- self.getPage("/")
- self.assertStatus('200 OK')
- self.assertBody(pov)
- self.assertNoHeader("Connection")
-
- # Make a 204 request on the same connection.
- self.getPage("/custom/204")
- self.assertStatus(204)
- self.assertNoHeader("Content-Length")
- self.assertBody("")
- self.assertNoHeader("Connection")
-
- # Make a 304 request on the same connection.
- self.getPage("/custom/304")
- self.assertStatus(304)
- self.assertNoHeader("Content-Length")
- self.assertBody("")
- self.assertNoHeader("Connection")
-
- def test_Chunked_Encoding(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- if (hasattr(self, 'harness') and
- "modpython" in self.harness.__class__.__name__.lower()):
- # mod_python forbids chunked encoding
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- # Set our HTTP_CONN to an instance so it persists between requests.
- self.persistent = True
- conn = self.HTTP_CONN
-
- # Try a normal chunked request (with extensions)
- body = ntob("8;key=value\r\nxx\r\nxxxx\r\n5\r\nyyyyy\r\n0\r\n"
- "Content-Type: application/json\r\n"
- "\r\n")
- conn.putrequest("POST", "/upload", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.putheader("Transfer-Encoding", "chunked")
- conn.putheader("Trailer", "Content-Type")
- # Note that this is somewhat malformed:
- # we shouldn't be sending Content-Length.
- # RFC 2616 says the server should ignore it.
- conn.putheader("Content-Length", "3")
- conn.endheaders()
- conn.send(body)
- response = conn.getresponse()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus('200 OK')
- self.assertBody("thanks for '%s'" % ntob('xx\r\nxxxxyyyyy'))
-
- # Try a chunked request that exceeds server.max_request_body_size.
- # Note that the delimiters and trailer are included.
- body = ntob("3e3\r\n" + ("x" * 995) + "\r\n0\r\n\r\n")
- conn.putrequest("POST", "/upload", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.putheader("Transfer-Encoding", "chunked")
- conn.putheader("Content-Type", "text/plain")
- # Chunked requests don't need a content-length
-## conn.putheader("Content-Length", len(body))
- conn.endheaders()
- conn.send(body)
- response = conn.getresponse()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus(413)
- conn.close()
-
- def test_Content_Length_in(self):
- # Try a non-chunked request where Content-Length exceeds
- # server.max_request_body_size. Assert error before body send.
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("POST", "/upload", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.putheader("Content-Type", "text/plain")
- conn.putheader("Content-Length", "9999")
- conn.endheaders()
- response = conn.getresponse()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus(413)
- self.assertBody("The entity sent with the request exceeds "
- "the maximum allowed bytes.")
- conn.close()
-
- def test_Content_Length_out_preheaders(self):
- # Try a non-chunked response where Content-Length is less than
- # the actual bytes in the response body.
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/custom_cl?body=I+have+too+many+bytes&cl=5",
- skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- response = conn.getresponse()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus(500)
- self.assertBody(
- "The requested resource returned more bytes than the "
- "declared Content-Length.")
- conn.close()
-
- def test_Content_Length_out_postheaders(self):
- # Try a non-chunked response where Content-Length is less than
- # the actual bytes in the response body.
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/custom_cl?body=I+too&body=+have+too+many&cl=5",
- skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- response = conn.getresponse()
- self.status, self.headers, self.body = webtest.shb(response)
- self.assertStatus(200)
- self.assertBody("I too")
- conn.close()
-
- def test_598(self):
- remote_data_conn = urlopen('%s://%s:%s/one_megabyte_of_a/' %
- (self.scheme, self.HOST, self.PORT,))
- buf = remote_data_conn.read(512)
- time.sleep(timeout * 0.6)
- remaining = (1024 * 1024) - 512
- while remaining:
- data = remote_data_conn.read(remaining)
- if not data:
- break
- else:
- buf += data
- remaining -= len(data)
-
- self.assertEqual(len(buf), 1024 * 1024)
- self.assertEqual(buf, ntob("a" * 1024 * 1024))
- self.assertEqual(remaining, 0)
- remote_data_conn.close()
-
-
-class BadRequestTests(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_No_CRLF(self):
- self.persistent = True
-
- conn = self.HTTP_CONN
- conn.send(ntob('GET /hello HTTP/1.1\n\n'))
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.body = response.read()
- self.assertBody("HTTP requires CRLF terminators")
- conn.close()
-
- conn.connect()
- conn.send(ntob('GET /hello HTTP/1.1\r\n\n'))
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.body = response.read()
- self.assertBody("HTTP requires CRLF terminators")
- conn.close()
-
diff --git a/cherrypy/test/test_core.py b/cherrypy/test/test_core.py
deleted file mode 100755
index 09544e3..0000000
--- a/cherrypy/test/test_core.py
+++ /dev/null
@@ -1,617 +0,0 @@
-"""Basic tests for the CherryPy core: request handling."""
-
-import os
-localDir = os.path.dirname(__file__)
-import sys
-import types
-
-import cherrypy
-from cherrypy._cpcompat import IncompleteRead, itervalues, ntob
-from cherrypy import _cptools, tools
-from cherrypy.lib import httputil, static
-
-
-favicon_path = os.path.join(os.getcwd(), localDir, "../favicon.ico")
-
-# Client-side code #
-
-from cherrypy.test import helper
-
-class CoreRequestHandlingTest(helper.CPWebCase):
-
- def setup_server():
- class Root:
-
- def index(self):
- return "hello"
- index.exposed = True
-
- favicon_ico = tools.staticfile.handler(filename=favicon_path)
-
- def defct(self, newct):
- newct = "text/%s" % newct
- cherrypy.config.update({'tools.response_headers.on': True,
- 'tools.response_headers.headers':
- [('Content-Type', newct)]})
- defct.exposed = True
-
- def baseurl(self, path_info, relative=None):
- return cherrypy.url(path_info, relative=bool(relative))
- baseurl.exposed = True
-
- root = Root()
-
- if sys.version_info >= (2, 5):
- from cherrypy.test._test_decorators import ExposeExamples
- root.expose_dec = ExposeExamples()
-
-
- class TestType(type):
- """Metaclass which automatically exposes all functions in each subclass,
- and adds an instance of the subclass as an attribute of root.
- """
- def __init__(cls, name, bases, dct):
- type.__init__(cls, name, bases, dct)
- for value in itervalues(dct):
- if isinstance(value, types.FunctionType):
- value.exposed = True
- setattr(root, name.lower(), cls())
- class Test(object):
- __metaclass__ = TestType
-
-
- class URL(Test):
-
- _cp_config = {'tools.trailing_slash.on': False}
-
- def index(self, path_info, relative=None):
- if relative != 'server':
- relative = bool(relative)
- return cherrypy.url(path_info, relative=relative)
-
- def leaf(self, path_info, relative=None):
- if relative != 'server':
- relative = bool(relative)
- return cherrypy.url(path_info, relative=relative)
-
-
- class Status(Test):
-
- def index(self):
- return "normal"
-
- def blank(self):
- cherrypy.response.status = ""
-
- # According to RFC 2616, new status codes are OK as long as they
- # are between 100 and 599.
-
- # Here is an illegal code...
- def illegal(self):
- cherrypy.response.status = 781
- return "oops"
-
- # ...and here is an unknown but legal code.
- def unknown(self):
- cherrypy.response.status = "431 My custom error"
- return "funky"
-
- # Non-numeric code
- def bad(self):
- cherrypy.response.status = "error"
- return "bad news"
-
-
- class Redirect(Test):
-
- class Error:
- _cp_config = {"tools.err_redirect.on": True,
- "tools.err_redirect.url": "/errpage",
- "tools.err_redirect.internal": False,
- }
-
- def index(self):
- raise NameError("redirect_test")
- index.exposed = True
- error = Error()
-
- def index(self):
- return "child"
-
- def custom(self, url, code):
- raise cherrypy.HTTPRedirect(url, code)
-
- def by_code(self, code):
- raise cherrypy.HTTPRedirect("somewhere%20else", code)
- by_code._cp_config = {'tools.trailing_slash.extra': True}
-
- def nomodify(self):
- raise cherrypy.HTTPRedirect("", 304)
-
- def proxy(self):
- raise cherrypy.HTTPRedirect("proxy", 305)
-
- def stringify(self):
- return str(cherrypy.HTTPRedirect("/"))
-
- def fragment(self, frag):
- raise cherrypy.HTTPRedirect("/some/url#%s" % frag)
-
- def login_redir():
- if not getattr(cherrypy.request, "login", None):
- raise cherrypy.InternalRedirect("/internalredirect/login")
- tools.login_redir = _cptools.Tool('before_handler', login_redir)
-
- def redir_custom():
- raise cherrypy.InternalRedirect("/internalredirect/custom_err")
-
- class InternalRedirect(Test):
-
- def index(self):
- raise cherrypy.InternalRedirect("/")
-
- def choke(self):
- return 3 / 0
- choke.exposed = True
- choke._cp_config = {'hooks.before_error_response': redir_custom}
-
- def relative(self, a, b):
- raise cherrypy.InternalRedirect("cousin?t=6")
-
- def cousin(self, t):
- assert cherrypy.request.prev.closed
- return cherrypy.request.prev.query_string
-
- def petshop(self, user_id):
- if user_id == "parrot":
- # Trade it for a slug when redirecting
- raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=slug')
- elif user_id == "terrier":
- # Trade it for a fish when redirecting
- raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=fish')
- else:
- # This should pass the user_id through to getImagesByUser
- raise cherrypy.InternalRedirect(
- '/image/getImagesByUser?user_id=%s' % str(user_id))
-
- # We support Python 2.3, but the @-deco syntax would look like this:
- # @tools.login_redir()
- def secure(self):
- return "Welcome!"
- secure = tools.login_redir()(secure)
- # Since calling the tool returns the same function you pass in,
- # you could skip binding the return value, and just write:
- # tools.login_redir()(secure)
-
- def login(self):
- return "Please log in"
-
- def custom_err(self):
- return "Something went horribly wrong."
-
- def early_ir(self, arg):
- return "whatever"
- early_ir._cp_config = {'hooks.before_request_body': redir_custom}
-
-
- class Image(Test):
-
- def getImagesByUser(self, user_id):
- return "0 images for %s" % user_id
-
-
- class Flatten(Test):
-
- def as_string(self):
- return "content"
-
- def as_list(self):
- return ["con", "tent"]
-
- def as_yield(self):
- yield ntob("content")
-
- def as_dblyield(self):
- yield self.as_yield()
- as_dblyield._cp_config = {'tools.flatten.on': True}
-
- def as_refyield(self):
- for chunk in self.as_yield():
- yield chunk
-
-
- class Ranges(Test):
-
- def get_ranges(self, bytes):
- return repr(httputil.get_ranges('bytes=%s' % bytes, 8))
-
- def slice_file(self):
- path = os.path.join(os.getcwd(), os.path.dirname(__file__))
- return static.serve_file(os.path.join(path, "static/index.html"))
-
-
- class Cookies(Test):
-
- def single(self, name):
- cookie = cherrypy.request.cookie[name]
- # Python2's SimpleCookie.__setitem__ won't take unicode keys.
- cherrypy.response.cookie[str(name)] = cookie.value
-
- def multiple(self, names):
- for name in names:
- cookie = cherrypy.request.cookie[name]
- # Python2's SimpleCookie.__setitem__ won't take unicode keys.
- cherrypy.response.cookie[str(name)] = cookie.value
-
-
- cherrypy.tree.mount(root)
- setup_server = staticmethod(setup_server)
-
-
- def testStatus(self):
- self.getPage("/status/")
- self.assertBody('normal')
- self.assertStatus(200)
-
- self.getPage("/status/blank")
- self.assertBody('')
- self.assertStatus(200)
-
- self.getPage("/status/illegal")
- self.assertStatus(500)
- msg = "Illegal response status from server (781 is out of range)."
- self.assertErrorPage(500, msg)
-
- if not getattr(cherrypy.server, 'using_apache', False):
- self.getPage("/status/unknown")
- self.assertBody('funky')
- self.assertStatus(431)
-
- self.getPage("/status/bad")
- self.assertStatus(500)
- msg = "Illegal response status from server ('error' is non-numeric)."
- self.assertErrorPage(500, msg)
-
- def testSlashes(self):
- # Test that requests for index methods without a trailing slash
- # get redirected to the same URI path with a trailing slash.
- # Make sure GET params are preserved.
- self.getPage("/redirect?id=3")
- self.assertStatus(301)
- self.assertInBody("<a href='%s/redirect/?id=3'>"
- "%s/redirect/?id=3</a>" % (self.base(), self.base()))
-
- if self.prefix():
- # Corner case: the "trailing slash" redirect could be tricky if
- # we're using a virtual root and the URI is "/vroot" (no slash).
- self.getPage("")
- self.assertStatus(301)
- self.assertInBody("<a href='%s/'>%s/</a>" %
- (self.base(), self.base()))
-
- # Test that requests for NON-index methods WITH a trailing slash
- # get redirected to the same URI path WITHOUT a trailing slash.
- # Make sure GET params are preserved.
- self.getPage("/redirect/by_code/?code=307")
- self.assertStatus(301)
- self.assertInBody("<a href='%s/redirect/by_code?code=307'>"
- "%s/redirect/by_code?code=307</a>"
- % (self.base(), self.base()))
-
- # If the trailing_slash tool is off, CP should just continue
- # as if the slashes were correct. But it needs some help
- # inside cherrypy.url to form correct output.
- self.getPage('/url?path_info=page1')
- self.assertBody('%s/url/page1' % self.base())
- self.getPage('/url/leaf/?path_info=page1')
- self.assertBody('%s/url/page1' % self.base())
-
- def testRedirect(self):
- self.getPage("/redirect/")
- self.assertBody('child')
- self.assertStatus(200)
-
- self.getPage("/redirect/by_code?code=300")
- self.assertMatchesBody(r"<a href='(.*)somewhere%20else'>\1somewhere%20else</a>")
- self.assertStatus(300)
-
- self.getPage("/redirect/by_code?code=301")
- self.assertMatchesBody(r"<a href='(.*)somewhere%20else'>\1somewhere%20else</a>")
- self.assertStatus(301)
-
- self.getPage("/redirect/by_code?code=302")
- self.assertMatchesBody(r"<a href='(.*)somewhere%20else'>\1somewhere%20else</a>")
- self.assertStatus(302)
-
- self.getPage("/redirect/by_code?code=303")
- self.assertMatchesBody(r"<a href='(.*)somewhere%20else'>\1somewhere%20else</a>")
- self.assertStatus(303)
-
- self.getPage("/redirect/by_code?code=307")
- self.assertMatchesBody(r"<a href='(.*)somewhere%20else'>\1somewhere%20else</a>")
- self.assertStatus(307)
-
- self.getPage("/redirect/nomodify")
- self.assertBody('')
- self.assertStatus(304)
-
- self.getPage("/redirect/proxy")
- self.assertBody('')
- self.assertStatus(305)
-
- # HTTPRedirect on error
- self.getPage("/redirect/error/")
- self.assertStatus(('302 Found', '303 See Other'))
- self.assertInBody('/errpage')
-
- # Make sure str(HTTPRedirect()) works.
- self.getPage("/redirect/stringify", protocol="HTTP/1.0")
- self.assertStatus(200)
- self.assertBody("(['%s/'], 302)" % self.base())
- if cherrypy.server.protocol_version == "HTTP/1.1":
- self.getPage("/redirect/stringify", protocol="HTTP/1.1")
- self.assertStatus(200)
- self.assertBody("(['%s/'], 303)" % self.base())
-
- # check that #fragments are handled properly
- # http://skrb.org/ietf/http_errata.html#location-fragments
- frag = "foo"
- self.getPage("/redirect/fragment/%s" % frag)
- self.assertMatchesBody(r"<a href='(.*)\/some\/url\#%s'>\1\/some\/url\#%s</a>" % (frag, frag))
- loc = self.assertHeader('Location')
- assert loc.endswith("#%s" % frag)
- self.assertStatus(('302 Found', '303 See Other'))
-
- # check injection protection
- # See http://www.cherrypy.org/ticket/1003
- self.getPage("/redirect/custom?code=303&url=/foobar/%0d%0aSet-Cookie:%20somecookie=someval")
- self.assertStatus(303)
- loc = self.assertHeader('Location')
- assert 'Set-Cookie' in loc
- self.assertNoHeader('Set-Cookie')
-
- def test_InternalRedirect(self):
- # InternalRedirect
- self.getPage("/internalredirect/")
- self.assertBody('hello')
- self.assertStatus(200)
-
- # Test passthrough
- self.getPage("/internalredirect/petshop?user_id=Sir-not-appearing-in-this-film")
- self.assertBody('0 images for Sir-not-appearing-in-this-film')
- self.assertStatus(200)
-
- # Test args
- self.getPage("/internalredirect/petshop?user_id=parrot")
- self.assertBody('0 images for slug')
- self.assertStatus(200)
-
- # Test POST
- self.getPage("/internalredirect/petshop", method="POST",
- body="user_id=terrier")
- self.assertBody('0 images for fish')
- self.assertStatus(200)
-
- # Test ir before body read
- self.getPage("/internalredirect/early_ir", method="POST",
- body="arg=aha!")
- self.assertBody("Something went horribly wrong.")
- self.assertStatus(200)
-
- self.getPage("/internalredirect/secure")
- self.assertBody('Please log in')
- self.assertStatus(200)
-
- # Relative path in InternalRedirect.
- # Also tests request.prev.
- self.getPage("/internalredirect/relative?a=3&b=5")
- self.assertBody("a=3&b=5")
- self.assertStatus(200)
-
- # InternalRedirect on error
- self.getPage("/internalredirect/choke")
- self.assertStatus(200)
- self.assertBody("Something went horribly wrong.")
-
- def testFlatten(self):
- for url in ["/flatten/as_string", "/flatten/as_list",
- "/flatten/as_yield", "/flatten/as_dblyield",
- "/flatten/as_refyield"]:
- self.getPage(url)
- self.assertBody('content')
-
- def testRanges(self):
- self.getPage("/ranges/get_ranges?bytes=3-6")
- self.assertBody("[(3, 7)]")
-
- # Test multiple ranges and a suffix-byte-range-spec, for good measure.
- self.getPage("/ranges/get_ranges?bytes=2-4,-1")
- self.assertBody("[(2, 5), (7, 8)]")
-
- # Get a partial file.
- if cherrypy.server.protocol_version == "HTTP/1.1":
- self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')])
- self.assertStatus(206)
- self.assertHeader("Content-Type", "text/html;charset=utf-8")
- self.assertHeader("Content-Range", "bytes 2-5/14")
- self.assertBody("llo,")
-
- # What happens with overlapping ranges (and out of order, too)?
- self.getPage("/ranges/slice_file", [('Range', 'bytes=4-6,2-5')])
- self.assertStatus(206)
- ct = self.assertHeader("Content-Type")
- expected_type = "multipart/byteranges; boundary="
- self.assert_(ct.startswith(expected_type))
- boundary = ct[len(expected_type):]
- expected_body = ("\r\n--%s\r\n"
- "Content-type: text/html\r\n"
- "Content-range: bytes 4-6/14\r\n"
- "\r\n"
- "o, \r\n"
- "--%s\r\n"
- "Content-type: text/html\r\n"
- "Content-range: bytes 2-5/14\r\n"
- "\r\n"
- "llo,\r\n"
- "--%s--\r\n" % (boundary, boundary, boundary))
- self.assertBody(expected_body)
- self.assertHeader("Content-Length")
-
- # Test "416 Requested Range Not Satisfiable"
- self.getPage("/ranges/slice_file", [('Range', 'bytes=2300-2900')])
- self.assertStatus(416)
- # "When this status code is returned for a byte-range request,
- # the response SHOULD include a Content-Range entity-header
- # field specifying the current length of the selected resource"
- self.assertHeader("Content-Range", "bytes */14")
- elif cherrypy.server.protocol_version == "HTTP/1.0":
- # Test Range behavior with HTTP/1.0 request
- self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')])
- self.assertStatus(200)
- self.assertBody("Hello, world\r\n")
-
- def testFavicon(self):
- # favicon.ico is served by staticfile.
- icofilename = os.path.join(localDir, "../favicon.ico")
- icofile = open(icofilename, "rb")
- data = icofile.read()
- icofile.close()
-
- self.getPage("/favicon.ico")
- self.assertBody(data)
-
- def testCookies(self):
- if sys.version_info >= (2, 5):
- header_value = lambda x: x
- else:
- header_value = lambda x: x+';'
-
- self.getPage("/cookies/single?name=First",
- [('Cookie', 'First=Dinsdale;')])
- self.assertHeader('Set-Cookie', header_value('First=Dinsdale'))
-
- self.getPage("/cookies/multiple?names=First&names=Last",
- [('Cookie', 'First=Dinsdale; Last=Piranha;'),
- ])
- self.assertHeader('Set-Cookie', header_value('First=Dinsdale'))
- self.assertHeader('Set-Cookie', header_value('Last=Piranha'))
-
- self.getPage("/cookies/single?name=Something-With:Colon",
- [('Cookie', 'Something-With:Colon=some-value')])
- self.assertStatus(400)
-
- def testDefaultContentType(self):
- self.getPage('/')
- self.assertHeader('Content-Type', 'text/html;charset=utf-8')
- self.getPage('/defct/plain')
- self.getPage('/')
- self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
- self.getPage('/defct/html')
-
- def test_cherrypy_url(self):
- # Input relative to current
- self.getPage('/url/leaf?path_info=page1')
- self.assertBody('%s/url/page1' % self.base())
- self.getPage('/url/?path_info=page1')
- self.assertBody('%s/url/page1' % self.base())
- # Other host header
- host = 'www.mydomain.example'
- self.getPage('/url/leaf?path_info=page1',
- headers=[('Host', host)])
- self.assertBody('%s://%s/url/page1' % (self.scheme, host))
-
- # Input is 'absolute'; that is, relative to script_name
- self.getPage('/url/leaf?path_info=/page1')
- self.assertBody('%s/page1' % self.base())
- self.getPage('/url/?path_info=/page1')
- self.assertBody('%s/page1' % self.base())
-
- # Single dots
- self.getPage('/url/leaf?path_info=./page1')
- self.assertBody('%s/url/page1' % self.base())
- self.getPage('/url/leaf?path_info=other/./page1')
- self.assertBody('%s/url/other/page1' % self.base())
- self.getPage('/url/?path_info=/other/./page1')
- self.assertBody('%s/other/page1' % self.base())
-
- # Double dots
- self.getPage('/url/leaf?path_info=../page1')
- self.assertBody('%s/page1' % self.base())
- self.getPage('/url/leaf?path_info=other/../page1')
- self.assertBody('%s/url/page1' % self.base())
- self.getPage('/url/leaf?path_info=/other/../page1')
- self.assertBody('%s/page1' % self.base())
-
- # Output relative to current path or script_name
- self.getPage('/url/?path_info=page1&relative=True')
- self.assertBody('page1')
- self.getPage('/url/leaf?path_info=/page1&relative=True')
- self.assertBody('../page1')
- self.getPage('/url/leaf?path_info=page1&relative=True')
- self.assertBody('page1')
- self.getPage('/url/leaf?path_info=leaf/page1&relative=True')
- self.assertBody('leaf/page1')
- self.getPage('/url/leaf?path_info=../page1&relative=True')
- self.assertBody('../page1')
- self.getPage('/url/?path_info=other/../page1&relative=True')
- self.assertBody('page1')
-
- # Output relative to /
- self.getPage('/baseurl?path_info=ab&relative=True')
- self.assertBody('ab')
- # Output relative to /
- self.getPage('/baseurl?path_info=/ab&relative=True')
- self.assertBody('ab')
-
- # absolute-path references ("server-relative")
- # Input relative to current
- self.getPage('/url/leaf?path_info=page1&relative=server')
- self.assertBody('/url/page1')
- self.getPage('/url/?path_info=page1&relative=server')
- self.assertBody('/url/page1')
- # Input is 'absolute'; that is, relative to script_name
- self.getPage('/url/leaf?path_info=/page1&relative=server')
- self.assertBody('/page1')
- self.getPage('/url/?path_info=/page1&relative=server')
- self.assertBody('/page1')
-
- def test_expose_decorator(self):
- if not sys.version_info >= (2, 5):
- return self.skip("skipped (Python 2.5+ only) ")
-
- # Test @expose
- self.getPage("/expose_dec/no_call")
- self.assertStatus(200)
- self.assertBody("Mr E. R. Bradshaw")
-
- # Test @expose()
- self.getPage("/expose_dec/call_empty")
- self.assertStatus(200)
- self.assertBody("Mrs. B.J. Smegma")
-
- # Test @expose("alias")
- self.getPage("/expose_dec/call_alias")
- self.assertStatus(200)
- self.assertBody("Mr Nesbitt")
- # Does the original name work?
- self.getPage("/expose_dec/nesbitt")
- self.assertStatus(200)
- self.assertBody("Mr Nesbitt")
-
- # Test @expose(["alias1", "alias2"])
- self.getPage("/expose_dec/alias1")
- self.assertStatus(200)
- self.assertBody("Mr Ken Andrews")
- self.getPage("/expose_dec/alias2")
- self.assertStatus(200)
- self.assertBody("Mr Ken Andrews")
- # Does the original name work?
- self.getPage("/expose_dec/andrews")
- self.assertStatus(200)
- self.assertBody("Mr Ken Andrews")
-
- # Test @expose(alias="alias")
- self.getPage("/expose_dec/alias3")
- self.assertStatus(200)
- self.assertBody("Mr. and Mrs. Watson")
-
diff --git a/cherrypy/test/test_dynamicobjectmapping.py b/cherrypy/test/test_dynamicobjectmapping.py
deleted file mode 100755
index 1e04d08..0000000
--- a/cherrypy/test/test_dynamicobjectmapping.py
+++ /dev/null
@@ -1,403 +0,0 @@
-import cherrypy
-from cherrypy._cptree import Application
-from cherrypy.test import helper
-
-script_names = ["", "/foo", "/users/fred/blog", "/corp/blog"]
-
-
-
-def setup_server():
- class SubSubRoot:
- def index(self):
- return "SubSubRoot index"
- index.exposed = True
-
- def default(self, *args):
- return "SubSubRoot default"
- default.exposed = True
-
- def handler(self):
- return "SubSubRoot handler"
- handler.exposed = True
-
- def dispatch(self):
- return "SubSubRoot dispatch"
- dispatch.exposed = True
-
- subsubnodes = {
- '1': SubSubRoot(),
- '2': SubSubRoot(),
- }
-
- class SubRoot:
- def index(self):
- return "SubRoot index"
- index.exposed = True
-
- def default(self, *args):
- return "SubRoot %s" % (args,)
- default.exposed = True
-
- def handler(self):
- return "SubRoot handler"
- handler.exposed = True
-
- def _cp_dispatch(self, vpath):
- return subsubnodes.get(vpath[0], None)
-
- subnodes = {
- '1': SubRoot(),
- '2': SubRoot(),
- }
- class Root:
- def index(self):
- return "index"
- index.exposed = True
-
- def default(self, *args):
- return "default %s" % (args,)
- default.exposed = True
-
- def handler(self):
- return "handler"
- handler.exposed = True
-
- def _cp_dispatch(self, vpath):
- return subnodes.get(vpath[0])
-
- #--------------------------------------------------------------------------
- # DynamicNodeAndMethodDispatcher example.
- # This example exposes a fairly naive HTTP api
- class User(object):
- def __init__(self, id, name):
- self.id = id
- self.name = name
-
- def __unicode__(self):
- return unicode(self.name)
-
- user_lookup = {
- 1: User(1, 'foo'),
- 2: User(2, 'bar'),
- }
-
- def make_user(name, id=None):
- if not id:
- id = max(*user_lookup.keys()) + 1
- user_lookup[id] = User(id, name)
- return id
-
- class UserContainerNode(object):
- exposed = True
-
- def POST(self, name):
- """
- Allow the creation of a new Object
- """
- return "POST %d" % make_user(name)
-
- def GET(self):
- keys = user_lookup.keys()
- keys.sort()
- return unicode(keys)
-
- def dynamic_dispatch(self, vpath):
- try:
- id = int(vpath[0])
- except (ValueError, IndexError):
- return None
- return UserInstanceNode(id)
-
- class UserInstanceNode(object):
- exposed = True
- def __init__(self, id):
- self.id = id
- self.user = user_lookup.get(id, None)
-
- # For all but PUT methods there MUST be a valid user identified
- # by self.id
- if not self.user and cherrypy.request.method != 'PUT':
- raise cherrypy.HTTPError(404)
-
- def GET(self, *args, **kwargs):
- """
- Return the appropriate representation of the instance.
- """
- return unicode(self.user)
-
- def POST(self, name):
- """
- Update the fields of the user instance.
- """
- self.user.name = name
- return "POST %d" % self.user.id
-
- def PUT(self, name):
- """
- Create a new user with the specified id, or edit it if it already exists
- """
- if self.user:
- # Edit the current user
- self.user.name = name
- return "PUT %d" % self.user.id
- else:
- # Make a new user with said attributes.
- return "PUT %d" % make_user(name, self.id)
-
- def DELETE(self):
- """
- Delete the user specified at the id.
- """
- id = self.user.id
- del user_lookup[self.user.id]
- del self.user
- return "DELETE %d" % id
-
-
- class ABHandler:
- class CustomDispatch:
- def index(self, a, b):
- return "custom"
- index.exposed = True
-
- def _cp_dispatch(self, vpath):
- """Make sure that if we don't pop anything from vpath,
- processing still works.
- """
- return self.CustomDispatch()
-
- def index(self, a, b=None):
- body = [ 'a:' + str(a) ]
- if b is not None:
- body.append(',b:' + str(b))
- return ''.join(body)
- index.exposed = True
-
- def delete(self, a, b):
- return 'deleting ' + str(a) + ' and ' + str(b)
- delete.exposed = True
-
- class IndexOnly:
- def _cp_dispatch(self, vpath):
- """Make sure that popping ALL of vpath still shows the index
- handler.
- """
- while vpath:
- vpath.pop()
- return self
-
- def index(self):
- return "IndexOnly index"
- index.exposed = True
-
- class DecoratedPopArgs:
- """Test _cp_dispatch with @cherrypy.popargs."""
- def index(self):
- return "no params"
- index.exposed = True
-
- def hi(self):
- return "hi was not interpreted as 'a' param"
- hi.exposed = True
- DecoratedPopArgs = cherrypy.popargs('a', 'b', handler=ABHandler())(DecoratedPopArgs)
-
- class NonDecoratedPopArgs:
- """Test _cp_dispatch = cherrypy.popargs()"""
-
- _cp_dispatch = cherrypy.popargs('a')
-
- def index(self, a):
- return "index: " + str(a)
- index.exposed = True
-
- class ParameterizedHandler:
- """Special handler created for each request"""
-
- def __init__(self, a):
- self.a = a
-
- def index(self):
- if 'a' in cherrypy.request.params:
- raise Exception("Parameterized handler argument ended up in request.params")
- return self.a
- index.exposed = True
-
- class ParameterizedPopArgs:
- """Test cherrypy.popargs() with a function call handler"""
- ParameterizedPopArgs = cherrypy.popargs('a', handler=ParameterizedHandler)(ParameterizedPopArgs)
-
- Root.decorated = DecoratedPopArgs()
- Root.undecorated = NonDecoratedPopArgs()
- Root.index_only = IndexOnly()
- Root.parameter_test = ParameterizedPopArgs()
-
- Root.users = UserContainerNode()
-
- md = cherrypy.dispatch.MethodDispatcher('dynamic_dispatch')
- for url in script_names:
- conf = {'/': {
- 'user': (url or "/").split("/")[-2],
- },
- '/users': {
- 'request.dispatch': md
- },
- }
- cherrypy.tree.mount(Root(), url, conf)
-
-class DynamicObjectMappingTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def testObjectMapping(self):
- for url in script_names:
- prefix = self.script_name = url
-
- self.getPage('/')
- self.assertBody('index')
-
- self.getPage('/handler')
- self.assertBody('handler')
-
- # Dynamic dispatch will succeed here for the subnodes
- # so the subroot gets called
- self.getPage('/1/')
- self.assertBody('SubRoot index')
-
- self.getPage('/2/')
- self.assertBody('SubRoot index')
-
- self.getPage('/1/handler')
- self.assertBody('SubRoot handler')
-
- self.getPage('/2/handler')
- self.assertBody('SubRoot handler')
-
- # Dynamic dispatch will fail here for the subnodes
- # so the default gets called
- self.getPage('/asdf/')
- self.assertBody("default ('asdf',)")
-
- self.getPage('/asdf/asdf')
- self.assertBody("default ('asdf', 'asdf')")
-
- self.getPage('/asdf/handler')
- self.assertBody("default ('asdf', 'handler')")
-
- # Dynamic dispatch will succeed here for the subsubnodes
- # so the subsubroot gets called
- self.getPage('/1/1/')
- self.assertBody('SubSubRoot index')
-
- self.getPage('/2/2/')
- self.assertBody('SubSubRoot index')
-
- self.getPage('/1/1/handler')
- self.assertBody('SubSubRoot handler')
-
- self.getPage('/2/2/handler')
- self.assertBody('SubSubRoot handler')
-
- self.getPage('/2/2/dispatch')
- self.assertBody('SubSubRoot dispatch')
-
- # The exposed dispatch will not be called as a dispatch
- # method.
- self.getPage('/2/2/foo/foo')
- self.assertBody("SubSubRoot default")
-
- # Dynamic dispatch will fail here for the subsubnodes
- # so the SubRoot gets called
- self.getPage('/1/asdf/')
- self.assertBody("SubRoot ('asdf',)")
-
- self.getPage('/1/asdf/asdf')
- self.assertBody("SubRoot ('asdf', 'asdf')")
-
- self.getPage('/1/asdf/handler')
- self.assertBody("SubRoot ('asdf', 'handler')")
-
- def testMethodDispatch(self):
- # GET acts like a container
- self.getPage("/users")
- self.assertBody("[1, 2]")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- # POST to the container URI allows creation
- self.getPage("/users", method="POST", body="name=baz")
- self.assertBody("POST 3")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- # POST to a specific instanct URI results in a 404
- # as the resource does not exit.
- self.getPage("/users/5", method="POST", body="name=baz")
- self.assertStatus(404)
-
- # PUT to a specific instanct URI results in creation
- self.getPage("/users/5", method="PUT", body="name=boris")
- self.assertBody("PUT 5")
- self.assertHeader('Allow', 'DELETE, GET, HEAD, POST, PUT')
-
- # GET acts like a container
- self.getPage("/users")
- self.assertBody("[1, 2, 3, 5]")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- test_cases = (
- (1, 'foo', 'fooupdated', 'DELETE, GET, HEAD, POST, PUT'),
- (2, 'bar', 'barupdated', 'DELETE, GET, HEAD, POST, PUT'),
- (3, 'baz', 'bazupdated', 'DELETE, GET, HEAD, POST, PUT'),
- (5, 'boris', 'borisupdated', 'DELETE, GET, HEAD, POST, PUT'),
- )
- for id, name, updatedname, headers in test_cases:
- self.getPage("/users/%d" % id)
- self.assertBody(name)
- self.assertHeader('Allow', headers)
-
- # Make sure POSTs update already existings resources
- self.getPage("/users/%d" % id, method='POST', body="name=%s" % updatedname)
- self.assertBody("POST %d" % id)
- self.assertHeader('Allow', headers)
-
- # Make sure PUTs Update already existing resources.
- self.getPage("/users/%d" % id, method='PUT', body="name=%s" % updatedname)
- self.assertBody("PUT %d" % id)
- self.assertHeader('Allow', headers)
-
- # Make sure DELETES Remove already existing resources.
- self.getPage("/users/%d" % id, method='DELETE')
- self.assertBody("DELETE %d" % id)
- self.assertHeader('Allow', headers)
-
-
- # GET acts like a container
- self.getPage("/users")
- self.assertBody("[]")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- def testVpathDispatch(self):
- self.getPage("/decorated/")
- self.assertBody("no params")
-
- self.getPage("/decorated/hi")
- self.assertBody("hi was not interpreted as 'a' param")
-
- self.getPage("/decorated/yo/")
- self.assertBody("a:yo")
-
- self.getPage("/decorated/yo/there/")
- self.assertBody("a:yo,b:there")
-
- self.getPage("/decorated/yo/there/delete")
- self.assertBody("deleting yo and there")
-
- self.getPage("/decorated/yo/there/handled_by_dispatch/")
- self.assertBody("custom")
-
- self.getPage("/undecorated/blah/")
- self.assertBody("index: blah")
-
- self.getPage("/index_only/a/b/c/d/e/f/g/")
- self.assertBody("IndexOnly index")
-
- self.getPage("/parameter_test/argument2/")
- self.assertBody("argument2")
-
diff --git a/cherrypy/test/test_encoding.py b/cherrypy/test/test_encoding.py
deleted file mode 100755
index 67b28ed..0000000
--- a/cherrypy/test/test_encoding.py
+++ /dev/null
@@ -1,363 +0,0 @@
-
-import gzip
-import sys
-
-import cherrypy
-from cherrypy._cpcompat import BytesIO, IncompleteRead, ntob, ntou
-
-europoundUnicode = ntou('\x80\xa3')
-sing = u"\u6bdb\u6cfd\u4e1c: Sing, Little Birdie?"
-sing8 = sing.encode('utf-8')
-sing16 = sing.encode('utf-16')
-
-
-from cherrypy.test import helper
-
-
-class EncodingTests(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def index(self, param):
- assert param == europoundUnicode, "%r != %r" % (param, europoundUnicode)
- yield europoundUnicode
- index.exposed = True
-
- def mao_zedong(self):
- return sing
- mao_zedong.exposed = True
-
- def utf8(self):
- return sing8
- utf8.exposed = True
- utf8._cp_config = {'tools.encode.encoding': 'utf-8'}
-
- def cookies_and_headers(self):
- # if the headers have non-ascii characters and a cookie has
- # any part which is unicode (even ascii), the response
- # should not fail.
- cherrypy.response.cookie['candy'] = 'bar'
- cherrypy.response.cookie['candy']['domain'] = 'cherrypy.org'
- cherrypy.response.headers['Some-Header'] = 'My d\xc3\xb6g has fleas'
- return 'Any content'
- cookies_and_headers.exposed = True
-
- def reqparams(self, *args, **kwargs):
- return ntob(', ').join([": ".join((k, v)).encode('utf8')
- for k, v in cherrypy.request.params.items()])
- reqparams.exposed = True
-
- def nontext(self, *args, **kwargs):
- cherrypy.response.headers['Content-Type'] = 'application/binary'
- return '\x00\x01\x02\x03'
- nontext.exposed = True
- nontext._cp_config = {'tools.encode.text_only': False,
- 'tools.encode.add_charset': True,
- }
-
- class GZIP:
- def index(self):
- yield "Hello, world"
- index.exposed = True
-
- def noshow(self):
- # Test for ticket #147, where yield showed no exceptions (content-
- # encoding was still gzip even though traceback wasn't zipped).
- raise IndexError()
- yield "Here be dragons"
- noshow.exposed = True
- # Turn encoding off so the gzip tool is the one doing the collapse.
- noshow._cp_config = {'tools.encode.on': False}
-
- def noshow_stream(self):
- # Test for ticket #147, where yield showed no exceptions (content-
- # encoding was still gzip even though traceback wasn't zipped).
- raise IndexError()
- yield "Here be dragons"
- noshow_stream.exposed = True
- noshow_stream._cp_config = {'response.stream': True}
-
- class Decode:
- def extra_charset(self, *args, **kwargs):
- return ', '.join([": ".join((k, v))
- for k, v in cherrypy.request.params.items()])
- extra_charset.exposed = True
- extra_charset._cp_config = {
- 'tools.decode.on': True,
- 'tools.decode.default_encoding': ['utf-16'],
- }
-
- def force_charset(self, *args, **kwargs):
- return ', '.join([": ".join((k, v))
- for k, v in cherrypy.request.params.items()])
- force_charset.exposed = True
- force_charset._cp_config = {
- 'tools.decode.on': True,
- 'tools.decode.encoding': 'utf-16',
- }
-
- root = Root()
- root.gzip = GZIP()
- root.decode = Decode()
- cherrypy.tree.mount(root, config={'/gzip': {'tools.gzip.on': True}})
- setup_server = staticmethod(setup_server)
-
- def test_query_string_decoding(self):
- europoundUtf8 = europoundUnicode.encode('utf-8')
- self.getPage(ntob('/?param=') + europoundUtf8)
- self.assertBody(europoundUtf8)
-
- # Encoded utf8 query strings MUST be parsed correctly.
- # Here, q is the POUND SIGN U+00A3 encoded in utf8 and then %HEX
- self.getPage("/reqparams?q=%C2%A3")
- # The return value will be encoded as utf8.
- self.assertBody(ntob("q: \xc2\xa3"))
-
- # Query strings that are incorrectly encoded MUST raise 404.
- # Here, q is the POUND SIGN U+00A3 encoded in latin1 and then %HEX
- self.getPage("/reqparams?q=%A3")
- self.assertStatus(404)
- self.assertErrorPage(404,
- "The given query string could not be processed. Query "
- "strings for this resource must be encoded with 'utf8'.")
-
- def test_urlencoded_decoding(self):
- # Test the decoding of an application/x-www-form-urlencoded entity.
- europoundUtf8 = europoundUnicode.encode('utf-8')
- body=ntob("param=") + europoundUtf8
- self.getPage('/', method='POST',
- headers=[("Content-Type", "application/x-www-form-urlencoded"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(europoundUtf8)
-
- # Encoded utf8 entities MUST be parsed and decoded correctly.
- # Here, q is the POUND SIGN U+00A3 encoded in utf8
- body = ntob("q=\xc2\xa3")
- self.getPage('/reqparams', method='POST',
- headers=[("Content-Type", "application/x-www-form-urlencoded"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(ntob("q: \xc2\xa3"))
-
- # ...and in utf16, which is not in the default attempt_charsets list:
- body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00")
- self.getPage('/reqparams', method='POST',
- headers=[("Content-Type", "application/x-www-form-urlencoded;charset=utf-16"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(ntob("q: \xc2\xa3"))
-
- # Entities that are incorrectly encoded MUST raise 400.
- # Here, q is the POUND SIGN U+00A3 encoded in utf16, but
- # the Content-Type incorrectly labels it utf-8.
- body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00")
- self.getPage('/reqparams', method='POST',
- headers=[("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertStatus(400)
- self.assertErrorPage(400,
- "The request entity could not be decoded. The following charsets "
- "were attempted: ['utf-8']")
-
- def test_decode_tool(self):
- # An extra charset should be tried first, and succeed if it matches.
- # Here, we add utf-16 as a charset and pass a utf-16 body.
- body = ntob("\xff\xfeq\x00=\xff\xfe\xa3\x00")
- self.getPage('/decode/extra_charset', method='POST',
- headers=[("Content-Type", "application/x-www-form-urlencoded"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(ntob("q: \xc2\xa3"))
-
- # An extra charset should be tried first, and continue to other default
- # charsets if it doesn't match.
- # Here, we add utf-16 as a charset but still pass a utf-8 body.
- body = ntob("q=\xc2\xa3")
- self.getPage('/decode/extra_charset', method='POST',
- headers=[("Content-Type", "application/x-www-form-urlencoded"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(ntob("q: \xc2\xa3"))
-
- # An extra charset should error if force is True and it doesn't match.
- # Here, we force utf-16 as a charset but still pass a utf-8 body.
- body = ntob("q=\xc2\xa3")
- self.getPage('/decode/force_charset', method='POST',
- headers=[("Content-Type", "application/x-www-form-urlencoded"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertErrorPage(400,
- "The request entity could not be decoded. The following charsets "
- "were attempted: ['utf-16']")
-
- def test_multipart_decoding(self):
- # Test the decoding of a multipart entity when the charset (utf16) is
- # explicitly given.
- body=ntob('\r\n'.join(['--X',
- 'Content-Type: text/plain;charset=utf-16',
- 'Content-Disposition: form-data; name="text"',
- '',
- '\xff\xfea\x00b\x00\x1c c\x00',
- '--X',
- 'Content-Type: text/plain;charset=utf-16',
- 'Content-Disposition: form-data; name="submit"',
- '',
- '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
- '--X--']))
- self.getPage('/reqparams', method='POST',
- headers=[("Content-Type", "multipart/form-data;boundary=X"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(ntob("text: ab\xe2\x80\x9cc, submit: Create"))
-
- def test_multipart_decoding_no_charset(self):
- # Test the decoding of a multipart entity when the charset (utf8) is
- # NOT explicitly given, but is in the list of charsets to attempt.
- body=ntob('\r\n'.join(['--X',
- 'Content-Disposition: form-data; name="text"',
- '',
- '\xe2\x80\x9c',
- '--X',
- 'Content-Disposition: form-data; name="submit"',
- '',
- 'Create',
- '--X--']))
- self.getPage('/reqparams', method='POST',
- headers=[("Content-Type", "multipart/form-data;boundary=X"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(ntob("text: \xe2\x80\x9c, submit: Create"))
-
- def test_multipart_decoding_no_successful_charset(self):
- # Test the decoding of a multipart entity when the charset (utf16) is
- # NOT explicitly given, and is NOT in the list of charsets to attempt.
- body=ntob('\r\n'.join(['--X',
- 'Content-Disposition: form-data; name="text"',
- '',
- '\xff\xfea\x00b\x00\x1c c\x00',
- '--X',
- 'Content-Disposition: form-data; name="submit"',
- '',
- '\xff\xfeC\x00r\x00e\x00a\x00t\x00e\x00',
- '--X--']))
- self.getPage('/reqparams', method='POST',
- headers=[("Content-Type", "multipart/form-data;boundary=X"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertStatus(400)
- self.assertErrorPage(400,
- "The request entity could not be decoded. The following charsets "
- "were attempted: ['us-ascii', 'utf-8']")
-
- def test_nontext(self):
- self.getPage('/nontext')
- self.assertHeader('Content-Type', 'application/binary;charset=utf-8')
- self.assertBody('\x00\x01\x02\x03')
-
- def testEncoding(self):
- # Default encoding should be utf-8
- self.getPage('/mao_zedong')
- self.assertBody(sing8)
-
- # Ask for utf-16.
- self.getPage('/mao_zedong', [('Accept-Charset', 'utf-16')])
- self.assertHeader('Content-Type', 'text/html;charset=utf-16')
- self.assertBody(sing16)
-
- # Ask for multiple encodings. ISO-8859-1 should fail, and utf-16
- # should be produced.
- self.getPage('/mao_zedong', [('Accept-Charset',
- 'iso-8859-1;q=1, utf-16;q=0.5')])
- self.assertBody(sing16)
-
- # The "*" value should default to our default_encoding, utf-8
- self.getPage('/mao_zedong', [('Accept-Charset', '*;q=1, utf-7;q=.2')])
- self.assertBody(sing8)
-
- # Only allow iso-8859-1, which should fail and raise 406.
- self.getPage('/mao_zedong', [('Accept-Charset', 'iso-8859-1, *;q=0')])
- self.assertStatus("406 Not Acceptable")
- self.assertInBody("Your client sent this Accept-Charset header: "
- "iso-8859-1, *;q=0. We tried these charsets: "
- "iso-8859-1.")
-
- # Ask for x-mac-ce, which should be unknown. See ticket #569.
- self.getPage('/mao_zedong', [('Accept-Charset',
- 'us-ascii, ISO-8859-1, x-mac-ce')])
- self.assertStatus("406 Not Acceptable")
- self.assertInBody("Your client sent this Accept-Charset header: "
- "us-ascii, ISO-8859-1, x-mac-ce. We tried these "
- "charsets: ISO-8859-1, us-ascii, x-mac-ce.")
-
- # Test the 'encoding' arg to encode.
- self.getPage('/utf8')
- self.assertBody(sing8)
- self.getPage('/utf8', [('Accept-Charset', 'us-ascii, ISO-8859-1')])
- self.assertStatus("406 Not Acceptable")
-
- def testGzip(self):
- zbuf = BytesIO()
- zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=9)
- zfile.write(ntob("Hello, world"))
- zfile.close()
-
- self.getPage('/gzip/', headers=[("Accept-Encoding", "gzip")])
- self.assertInBody(zbuf.getvalue()[:3])
- self.assertHeader("Vary", "Accept-Encoding")
- self.assertHeader("Content-Encoding", "gzip")
-
- # Test when gzip is denied.
- self.getPage('/gzip/', headers=[("Accept-Encoding", "identity")])
- self.assertHeader("Vary", "Accept-Encoding")
- self.assertNoHeader("Content-Encoding")
- self.assertBody("Hello, world")
-
- self.getPage('/gzip/', headers=[("Accept-Encoding", "gzip;q=0")])
- self.assertHeader("Vary", "Accept-Encoding")
- self.assertNoHeader("Content-Encoding")
- self.assertBody("Hello, world")
-
- self.getPage('/gzip/', headers=[("Accept-Encoding", "*;q=0")])
- self.assertStatus(406)
- self.assertNoHeader("Content-Encoding")
- self.assertErrorPage(406, "identity, gzip")
-
- # Test for ticket #147
- self.getPage('/gzip/noshow', headers=[("Accept-Encoding", "gzip")])
- self.assertNoHeader('Content-Encoding')
- self.assertStatus(500)
- self.assertErrorPage(500, pattern="IndexError\n")
-
- # In this case, there's nothing we can do to deliver a
- # readable page, since 1) the gzip header is already set,
- # and 2) we may have already written some of the body.
- # The fix is to never stream yields when using gzip.
- if (cherrypy.server.protocol_version == "HTTP/1.0" or
- getattr(cherrypy.server, "using_apache", False)):
- self.getPage('/gzip/noshow_stream',
- headers=[("Accept-Encoding", "gzip")])
- self.assertHeader('Content-Encoding', 'gzip')
- self.assertInBody('\x1f\x8b\x08\x00')
- else:
- # The wsgiserver will simply stop sending data, and the HTTP client
- # will error due to an incomplete chunk-encoded stream.
- self.assertRaises((ValueError, IncompleteRead), self.getPage,
- '/gzip/noshow_stream',
- headers=[("Accept-Encoding", "gzip")])
-
- def test_UnicodeHeaders(self):
- self.getPage('/cookies_and_headers')
- self.assertBody('Any content')
-
diff --git a/cherrypy/test/test_etags.py b/cherrypy/test/test_etags.py
deleted file mode 100755
index 026f9d6..0000000
--- a/cherrypy/test/test_etags.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-
-class ETagTest(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def resource(self):
- return "Oh wah ta goo Siam."
- resource.exposed = True
-
- def fail(self, code):
- code = int(code)
- if 300 <= code <= 399:
- raise cherrypy.HTTPRedirect([], code)
- else:
- raise cherrypy.HTTPError(code)
- fail.exposed = True
-
- def unicoded(self):
- return u'I am a \u1ee4nicode string.'
- unicoded.exposed = True
- unicoded._cp_config = {'tools.encode.on': True}
-
- conf = {'/': {'tools.etags.on': True,
- 'tools.etags.autotags': True,
- }}
- cherrypy.tree.mount(Root(), config=conf)
- setup_server = staticmethod(setup_server)
-
- def test_etags(self):
- self.getPage("/resource")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html;charset=utf-8')
- self.assertBody('Oh wah ta goo Siam.')
- etag = self.assertHeader('ETag')
-
- # Test If-Match (both valid and invalid)
- self.getPage("/resource", headers=[('If-Match', etag)])
- self.assertStatus("200 OK")
- self.getPage("/resource", headers=[('If-Match', "*")])
- self.assertStatus("200 OK")
- self.getPage("/resource", headers=[('If-Match', "*")], method="POST")
- self.assertStatus("200 OK")
- self.getPage("/resource", headers=[('If-Match', "a bogus tag")])
- self.assertStatus("412 Precondition Failed")
-
- # Test If-None-Match (both valid and invalid)
- self.getPage("/resource", headers=[('If-None-Match', etag)])
- self.assertStatus(304)
- self.getPage("/resource", method='POST', headers=[('If-None-Match', etag)])
- self.assertStatus("412 Precondition Failed")
- self.getPage("/resource", headers=[('If-None-Match', "*")])
- self.assertStatus(304)
- self.getPage("/resource", headers=[('If-None-Match', "a bogus tag")])
- self.assertStatus("200 OK")
-
- def test_errors(self):
- self.getPage("/resource")
- self.assertStatus(200)
- etag = self.assertHeader('ETag')
-
- # Test raising errors in page handler
- self.getPage("/fail/412", headers=[('If-Match', etag)])
- self.assertStatus(412)
- self.getPage("/fail/304", headers=[('If-Match', etag)])
- self.assertStatus(304)
- self.getPage("/fail/412", headers=[('If-None-Match', "*")])
- self.assertStatus(412)
- self.getPage("/fail/304", headers=[('If-None-Match', "*")])
- self.assertStatus(304)
-
- def test_unicode_body(self):
- self.getPage("/unicoded")
- self.assertStatus(200)
- etag1 = self.assertHeader('ETag')
- self.getPage("/unicoded", headers=[('If-Match', etag1)])
- self.assertStatus(200)
- self.assertHeader('ETag', etag1)
-
diff --git a/cherrypy/test/test_http.py b/cherrypy/test/test_http.py
deleted file mode 100755
index eb72b5b..0000000
--- a/cherrypy/test/test_http.py
+++ /dev/null
@@ -1,168 +0,0 @@
-"""Tests for managing HTTP issues (malformed requests, etc)."""
-
-import mimetypes
-
-import cherrypy
-from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, ntob
-
-
-def encode_multipart_formdata(files):
- """Return (content_type, body) ready for httplib.HTTP instance.
-
- files: a sequence of (name, filename, value) tuples for multipart uploads.
- """
- BOUNDARY = '________ThIs_Is_tHe_bouNdaRY_$'
- L = []
- for key, filename, value in files:
- L.append('--' + BOUNDARY)
- L.append('Content-Disposition: form-data; name="%s"; filename="%s"' %
- (key, filename))
- ct = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
- L.append('Content-Type: %s' % ct)
- L.append('')
- L.append(value)
- L.append('--' + BOUNDARY + '--')
- L.append('')
- body = '\r\n'.join(L)
- content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
- return content_type, body
-
-
-
-
-from cherrypy.test import helper
-
-class HTTPTests(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def index(self, *args, **kwargs):
- return "Hello world!"
- index.exposed = True
-
- def no_body(self, *args, **kwargs):
- return "Hello world!"
- no_body.exposed = True
- no_body._cp_config = {'request.process_request_body': False}
-
- def post_multipart(self, file):
- """Return a summary ("a * 65536\nb * 65536") of the uploaded file."""
- contents = file.file.read()
- summary = []
- curchar = ""
- count = 0
- for c in contents:
- if c == curchar:
- count += 1
- else:
- if count:
- summary.append("%s * %d" % (curchar, count))
- count = 1
- curchar = c
- if count:
- summary.append("%s * %d" % (curchar, count))
- return ", ".join(summary)
- post_multipart.exposed = True
-
- cherrypy.tree.mount(Root())
- cherrypy.config.update({'server.max_request_body_size': 30000000})
- setup_server = staticmethod(setup_server)
-
- def test_no_content_length(self):
- # "The presence of a message-body in a request is signaled by the
- # inclusion of a Content-Length or Transfer-Encoding header field in
- # the request's message-headers."
- #
- # Send a message with neither header and no body. Even though
- # the request is of method POST, this should be OK because we set
- # request.process_request_body to False for our handler.
- if self.scheme == "https":
- c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
- else:
- c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
- c.request("POST", "/no_body")
- response = c.getresponse()
- self.body = response.fp.read()
- self.status = str(response.status)
- self.assertStatus(200)
- self.assertBody(ntob('Hello world!'))
-
- # Now send a message that has no Content-Length, but does send a body.
- # Verify that CP times out the socket and responds
- # with 411 Length Required.
- if self.scheme == "https":
- c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
- else:
- c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
- c.request("POST", "/")
- response = c.getresponse()
- self.body = response.fp.read()
- self.status = str(response.status)
- self.assertStatus(411)
-
- def test_post_multipart(self):
- alphabet = "abcdefghijklmnopqrstuvwxyz"
- # generate file contents for a large post
- contents = "".join([c * 65536 for c in alphabet])
-
- # encode as multipart form data
- files=[('file', 'file.txt', contents)]
- content_type, body = encode_multipart_formdata(files)
- body = body.encode('Latin-1')
-
- # post file
- if self.scheme == 'https':
- c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
- else:
- c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
- c.putrequest('POST', '/post_multipart')
- c.putheader('Content-Type', content_type)
- c.putheader('Content-Length', str(len(body)))
- c.endheaders()
- c.send(body)
-
- response = c.getresponse()
- self.body = response.fp.read()
- self.status = str(response.status)
- self.assertStatus(200)
- self.assertBody(", ".join(["%s * 65536" % c for c in alphabet]))
-
- def test_malformed_request_line(self):
- if getattr(cherrypy.server, "using_apache", False):
- return self.skip("skipped due to known Apache differences...")
-
- # Test missing version in Request-Line
- if self.scheme == 'https':
- c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
- else:
- c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
- c._output(ntob('GET /'))
- c._send_output()
- if hasattr(c, 'strict'):
- response = c.response_class(c.sock, strict=c.strict, method='GET')
- else:
- # Python 3.2 removed the 'strict' feature, saying:
- # "http.client now always assumes HTTP/1.x compliant servers."
- response = c.response_class(c.sock, method='GET')
- response.begin()
- self.assertEqual(response.status, 400)
- self.assertEqual(response.fp.read(22), ntob("Malformed Request-Line"))
- c.close()
-
- def test_malformed_header(self):
- if self.scheme == 'https':
- c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
- else:
- c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
- c.putrequest('GET', '/')
- c.putheader('Content-Type', 'text/plain')
- # See http://www.cherrypy.org/ticket/941
- c._output(ntob('Re, 1.2.3.4#015#012'))
- c.endheaders()
-
- response = c.getresponse()
- self.status = str(response.status)
- self.assertStatus(400)
- self.body = response.fp.read(20)
- self.assertBody("Illegal header line.")
-
diff --git a/cherrypy/test/test_httpauth.py b/cherrypy/test/test_httpauth.py
deleted file mode 100755
index 9d0eecb..0000000
--- a/cherrypy/test/test_httpauth.py
+++ /dev/null
@@ -1,151 +0,0 @@
-import cherrypy
-from cherrypy._cpcompat import md5, sha, ntob
-from cherrypy.lib import httpauth
-
-from cherrypy.test import helper
-
-class HTTPAuthTest(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def index(self):
- return "This is public."
- index.exposed = True
-
- class DigestProtected:
- def index(self):
- return "Hello %s, you've been authorized." % cherrypy.request.login
- index.exposed = True
-
- class BasicProtected:
- def index(self):
- return "Hello %s, you've been authorized." % cherrypy.request.login
- index.exposed = True
-
- class BasicProtected2:
- def index(self):
- return "Hello %s, you've been authorized." % cherrypy.request.login
- index.exposed = True
-
- def fetch_users():
- return {'test': 'test'}
-
- def sha_password_encrypter(password):
- return sha(ntob(password)).hexdigest()
-
- def fetch_password(username):
- return sha(ntob('test')).hexdigest()
-
- conf = {'/digest': {'tools.digest_auth.on': True,
- 'tools.digest_auth.realm': 'localhost',
- 'tools.digest_auth.users': fetch_users},
- '/basic': {'tools.basic_auth.on': True,
- 'tools.basic_auth.realm': 'localhost',
- 'tools.basic_auth.users': {'test': md5(ntob('test')).hexdigest()}},
- '/basic2': {'tools.basic_auth.on': True,
- 'tools.basic_auth.realm': 'localhost',
- 'tools.basic_auth.users': fetch_password,
- 'tools.basic_auth.encrypt': sha_password_encrypter}}
-
- root = Root()
- root.digest = DigestProtected()
- root.basic = BasicProtected()
- root.basic2 = BasicProtected2()
- cherrypy.tree.mount(root, config=conf)
- setup_server = staticmethod(setup_server)
-
-
- def testPublic(self):
- self.getPage("/")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html;charset=utf-8')
- self.assertBody('This is public.')
-
- def testBasic(self):
- self.getPage("/basic/")
- self.assertStatus(401)
- self.assertHeader('WWW-Authenticate', 'Basic realm="localhost"')
-
- self.getPage('/basic/', [('Authorization', 'Basic dGVzdDp0ZX60')])
- self.assertStatus(401)
-
- self.getPage('/basic/', [('Authorization', 'Basic dGVzdDp0ZXN0')])
- self.assertStatus('200 OK')
- self.assertBody("Hello test, you've been authorized.")
-
- def testBasic2(self):
- self.getPage("/basic2/")
- self.assertStatus(401)
- self.assertHeader('WWW-Authenticate', 'Basic realm="localhost"')
-
- self.getPage('/basic2/', [('Authorization', 'Basic dGVzdDp0ZX60')])
- self.assertStatus(401)
-
- self.getPage('/basic2/', [('Authorization', 'Basic dGVzdDp0ZXN0')])
- self.assertStatus('200 OK')
- self.assertBody("Hello test, you've been authorized.")
-
- def testDigest(self):
- self.getPage("/digest/")
- self.assertStatus(401)
-
- value = None
- for k, v in self.headers:
- if k.lower() == "www-authenticate":
- if v.startswith("Digest"):
- value = v
- break
-
- if value is None:
- self._handlewebError("Digest authentification scheme was not found")
-
- value = value[7:]
- items = value.split(', ')
- tokens = {}
- for item in items:
- key, value = item.split('=')
- tokens[key.lower()] = value
-
- missing_msg = "%s is missing"
- bad_value_msg = "'%s' was expecting '%s' but found '%s'"
- nonce = None
- if 'realm' not in tokens:
- self._handlewebError(missing_msg % 'realm')
- elif tokens['realm'] != '"localhost"':
- self._handlewebError(bad_value_msg % ('realm', '"localhost"', tokens['realm']))
- if 'nonce' not in tokens:
- self._handlewebError(missing_msg % 'nonce')
- else:
- nonce = tokens['nonce'].strip('"')
- if 'algorithm' not in tokens:
- self._handlewebError(missing_msg % 'algorithm')
- elif tokens['algorithm'] != '"MD5"':
- self._handlewebError(bad_value_msg % ('algorithm', '"MD5"', tokens['algorithm']))
- if 'qop' not in tokens:
- self._handlewebError(missing_msg % 'qop')
- elif tokens['qop'] != '"auth"':
- self._handlewebError(bad_value_msg % ('qop', '"auth"', tokens['qop']))
-
- # Test a wrong 'realm' value
- base_auth = 'Digest username="test", realm="wrong realm", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
- auth = base_auth % (nonce, '', '00000001')
- params = httpauth.parseAuthorization(auth)
- response = httpauth._computeDigestResponse(params, 'test')
-
- auth = base_auth % (nonce, response, '00000001')
- self.getPage('/digest/', [('Authorization', auth)])
- self.assertStatus(401)
-
- # Test that must pass
- base_auth = 'Digest username="test", realm="localhost", nonce="%s", uri="/digest/", algorithm=MD5, response="%s", qop=auth, nc=%s, cnonce="1522e61005789929"'
-
- auth = base_auth % (nonce, '', '00000001')
- params = httpauth.parseAuthorization(auth)
- response = httpauth._computeDigestResponse(params, 'test')
-
- auth = base_auth % (nonce, response, '00000001')
- self.getPage('/digest/', [('Authorization', auth)])
- self.assertStatus('200 OK')
- self.assertBody("Hello test, you've been authorized.")
-
diff --git a/cherrypy/test/test_httplib.py b/cherrypy/test/test_httplib.py
deleted file mode 100755
index 5dc40fd..0000000
--- a/cherrypy/test/test_httplib.py
+++ /dev/null
@@ -1,29 +0,0 @@
-"""Tests for cherrypy/lib/httputil.py."""
-
-import unittest
-from cherrypy.lib import httputil
-
-
-class UtilityTests(unittest.TestCase):
-
- def test_urljoin(self):
- # Test all slash+atom combinations for SCRIPT_NAME and PATH_INFO
- self.assertEqual(httputil.urljoin("/sn/", "/pi/"), "/sn/pi/")
- self.assertEqual(httputil.urljoin("/sn/", "/pi"), "/sn/pi")
- self.assertEqual(httputil.urljoin("/sn/", "/"), "/sn/")
- self.assertEqual(httputil.urljoin("/sn/", ""), "/sn/")
- self.assertEqual(httputil.urljoin("/sn", "/pi/"), "/sn/pi/")
- self.assertEqual(httputil.urljoin("/sn", "/pi"), "/sn/pi")
- self.assertEqual(httputil.urljoin("/sn", "/"), "/sn/")
- self.assertEqual(httputil.urljoin("/sn", ""), "/sn")
- self.assertEqual(httputil.urljoin("/", "/pi/"), "/pi/")
- self.assertEqual(httputil.urljoin("/", "/pi"), "/pi")
- self.assertEqual(httputil.urljoin("/", "/"), "/")
- self.assertEqual(httputil.urljoin("/", ""), "/")
- self.assertEqual(httputil.urljoin("", "/pi/"), "/pi/")
- self.assertEqual(httputil.urljoin("", "/pi"), "/pi")
- self.assertEqual(httputil.urljoin("", "/"), "/")
- self.assertEqual(httputil.urljoin("", ""), "/")
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/cherrypy/test/test_json.py b/cherrypy/test/test_json.py
deleted file mode 100755
index a02c076..0000000
--- a/cherrypy/test/test_json.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-from cherrypy._cpcompat import json
-
-class JsonTest(helper.CPWebCase):
- def setup_server():
- class Root(object):
- def plain(self):
- return 'hello'
- plain.exposed = True
-
- def json_string(self):
- return 'hello'
- json_string.exposed = True
- json_string._cp_config = {'tools.json_out.on': True}
-
- def json_list(self):
- return ['a', 'b', 42]
- json_list.exposed = True
- json_list._cp_config = {'tools.json_out.on': True}
-
- def json_dict(self):
- return {'answer': 42}
- json_dict.exposed = True
- json_dict._cp_config = {'tools.json_out.on': True}
-
- def json_post(self):
- if cherrypy.request.json == [13, 'c']:
- return 'ok'
- else:
- return 'nok'
- json_post.exposed = True
- json_post._cp_config = {'tools.json_in.on': True}
-
- root = Root()
- cherrypy.tree.mount(root)
- setup_server = staticmethod(setup_server)
-
- def test_json_output(self):
- if json is None:
- self.skip("json not found ")
- return
-
- self.getPage("/plain")
- self.assertBody("hello")
-
- self.getPage("/json_string")
- self.assertBody('"hello"')
-
- self.getPage("/json_list")
- self.assertBody('["a", "b", 42]')
-
- self.getPage("/json_dict")
- self.assertBody('{"answer": 42}')
-
- def test_json_input(self):
- if json is None:
- self.skip("json not found ")
- return
-
- body = '[13, "c"]'
- headers = [('Content-Type', 'application/json'),
- ('Content-Length', str(len(body)))]
- self.getPage("/json_post", method="POST", headers=headers, body=body)
- self.assertBody('ok')
-
- body = '[13, "c"]'
- headers = [('Content-Type', 'text/plain'),
- ('Content-Length', str(len(body)))]
- self.getPage("/json_post", method="POST", headers=headers, body=body)
- self.assertStatus(415, 'Expected an application/json content type')
-
- body = '[13, -]'
- headers = [('Content-Type', 'application/json'),
- ('Content-Length', str(len(body)))]
- self.getPage("/json_post", method="POST", headers=headers, body=body)
- self.assertStatus(400, 'Invalid JSON document')
-
diff --git a/cherrypy/test/test_logging.py b/cherrypy/test/test_logging.py
deleted file mode 100755
index 5a13cd4..0000000
--- a/cherrypy/test/test_logging.py
+++ /dev/null
@@ -1,149 +0,0 @@
-"""Basic tests for the CherryPy core: request handling."""
-
-import os
-localDir = os.path.dirname(__file__)
-
-import cherrypy
-
-access_log = os.path.join(localDir, "access.log")
-error_log = os.path.join(localDir, "error.log")
-
-# Some unicode strings.
-tartaros = u'\u03a4\u1f71\u03c1\u03c4\u03b1\u03c1\u03bf\u03c2'
-erebos = u'\u0388\u03c1\u03b5\u03b2\u03bf\u03c2.com'
-
-
-def setup_server():
- class Root:
-
- def index(self):
- return "hello"
- index.exposed = True
-
- def uni_code(self):
- cherrypy.request.login = tartaros
- cherrypy.request.remote.name = erebos
- uni_code.exposed = True
-
- def slashes(self):
- cherrypy.request.request_line = r'GET /slashed\path HTTP/1.1'
- slashes.exposed = True
-
- def whitespace(self):
- # User-Agent = "User-Agent" ":" 1*( product | comment )
- # comment = "(" *( ctext | quoted-pair | comment ) ")"
- # ctext = <any TEXT excluding "(" and ")">
- # TEXT = <any OCTET except CTLs, but including LWS>
- # LWS = [CRLF] 1*( SP | HT )
- cherrypy.request.headers['User-Agent'] = 'Browzuh (1.0\r\n\t\t.3)'
- whitespace.exposed = True
-
- def as_string(self):
- return "content"
- as_string.exposed = True
-
- def as_yield(self):
- yield "content"
- as_yield.exposed = True
-
- def error(self):
- raise ValueError()
- error.exposed = True
- error._cp_config = {'tools.log_tracebacks.on': True}
-
- root = Root()
-
-
- cherrypy.config.update({'log.error_file': error_log,
- 'log.access_file': access_log,
- })
- cherrypy.tree.mount(root)
-
-
-
-from cherrypy.test import helper, logtest
-
-class AccessLogTests(helper.CPWebCase, logtest.LogCase):
- setup_server = staticmethod(setup_server)
-
- logfile = access_log
-
- def testNormalReturn(self):
- self.markLog()
- self.getPage("/as_string",
- headers=[('Referer', 'http://www.cherrypy.org/'),
- ('User-Agent', 'Mozilla/5.0')])
- self.assertBody('content')
- self.assertStatus(200)
-
- intro = '%s - - [' % self.interface()
-
- self.assertLog(-1, intro)
-
- if [k for k, v in self.headers if k.lower() == 'content-length']:
- self.assertLog(-1, '] "GET %s/as_string HTTP/1.1" 200 7 '
- '"http://www.cherrypy.org/" "Mozilla/5.0"'
- % self.prefix())
- else:
- self.assertLog(-1, '] "GET %s/as_string HTTP/1.1" 200 - '
- '"http://www.cherrypy.org/" "Mozilla/5.0"'
- % self.prefix())
-
- def testNormalYield(self):
- self.markLog()
- self.getPage("/as_yield")
- self.assertBody('content')
- self.assertStatus(200)
-
- intro = '%s - - [' % self.interface()
-
- self.assertLog(-1, intro)
- if [k for k, v in self.headers if k.lower() == 'content-length']:
- self.assertLog(-1, '] "GET %s/as_yield HTTP/1.1" 200 7 "" ""' %
- self.prefix())
- else:
- self.assertLog(-1, '] "GET %s/as_yield HTTP/1.1" 200 - "" ""'
- % self.prefix())
-
- def testEscapedOutput(self):
- # Test unicode in access log pieces.
- self.markLog()
- self.getPage("/uni_code")
- self.assertStatus(200)
- self.assertLog(-1, repr(tartaros.encode('utf8'))[1:-1])
- # Test the erebos value. Included inline for your enlightenment.
- # Note the 'r' prefix--those backslashes are literals.
- self.assertLog(-1, r'\xce\x88\xcf\x81\xce\xb5\xce\xb2\xce\xbf\xcf\x82')
-
- # Test backslashes in output.
- self.markLog()
- self.getPage("/slashes")
- self.assertStatus(200)
- self.assertLog(-1, r'"GET /slashed\\path HTTP/1.1"')
-
- # Test whitespace in output.
- self.markLog()
- self.getPage("/whitespace")
- self.assertStatus(200)
- # Again, note the 'r' prefix.
- self.assertLog(-1, r'"Browzuh (1.0\r\n\t\t.3)"')
-
-
-class ErrorLogTests(helper.CPWebCase, logtest.LogCase):
- setup_server = staticmethod(setup_server)
-
- logfile = error_log
-
- def testTracebacks(self):
- # Test that tracebacks get written to the error log.
- self.markLog()
- ignore = helper.webtest.ignored_exceptions
- ignore.append(ValueError)
- try:
- self.getPage("/error")
- self.assertInBody("raise ValueError()")
- self.assertLog(0, 'HTTP Traceback (most recent call last):')
- self.assertLog(-3, 'raise ValueError()')
- finally:
- ignore.pop()
-
diff --git a/cherrypy/test/test_mime.py b/cherrypy/test/test_mime.py
deleted file mode 100755
index 605071b..0000000
--- a/cherrypy/test/test_mime.py
+++ /dev/null
@@ -1,128 +0,0 @@
-"""Tests for various MIME issues, including the safe_multipart Tool."""
-
-import cherrypy
-from cherrypy._cpcompat import ntob, ntou, sorted
-
-def setup_server():
-
- class Root:
-
- def multipart(self, parts):
- return repr(parts)
- multipart.exposed = True
-
- def multipart_form_data(self, **kwargs):
- return repr(list(sorted(kwargs.items())))
- multipart_form_data.exposed = True
-
- def flashupload(self, Filedata, Upload, Filename):
- return ("Upload: %r, Filename: %r, Filedata: %r" %
- (Upload, Filename, Filedata.file.read()))
- flashupload.exposed = True
-
- cherrypy.config.update({'server.max_request_body_size': 0})
- cherrypy.tree.mount(Root())
-
-
-# Client-side code #
-
-from cherrypy.test import helper
-
-class MultipartTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_multipart(self):
- text_part = ntou("This is the text version")
- html_part = ntou("""<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-<html>
-<head>
- <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
-</head>
-<body bgcolor="#ffffff" text="#000000">
-
-This is the <strong>HTML</strong> version
-</body>
-</html>
-""")
- body = '\r\n'.join([
- "--123456789",
- "Content-Type: text/plain; charset='ISO-8859-1'",
- "Content-Transfer-Encoding: 7bit",
- "",
- text_part,
- "--123456789",
- "Content-Type: text/html; charset='ISO-8859-1'",
- "",
- html_part,
- "--123456789--"])
- headers = [
- ('Content-Type', 'multipart/mixed; boundary=123456789'),
- ('Content-Length', str(len(body))),
- ]
- self.getPage('/multipart', headers, "POST", body)
- self.assertBody(repr([text_part, html_part]))
-
- def test_multipart_form_data(self):
- body='\r\n'.join(['--X',
- 'Content-Disposition: form-data; name="foo"',
- '',
- 'bar',
- '--X',
- # Test a param with more than one value.
- # See http://www.cherrypy.org/ticket/1028
- 'Content-Disposition: form-data; name="baz"',
- '',
- '111',
- '--X',
- 'Content-Disposition: form-data; name="baz"',
- '',
- '333',
- '--X--'])
- self.getPage('/multipart_form_data', method='POST',
- headers=[("Content-Type", "multipart/form-data;boundary=X"),
- ("Content-Length", str(len(body))),
- ],
- body=body),
- self.assertBody(repr([('baz', [u'111', u'333']), ('foo', u'bar')]))
-
-
-class SafeMultipartHandlingTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_Flash_Upload(self):
- headers = [
- ('Accept', 'text/*'),
- ('Content-Type', 'multipart/form-data; '
- 'boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'),
- ('User-Agent', 'Shockwave Flash'),
- ('Host', 'www.example.com:8080'),
- ('Content-Length', '499'),
- ('Connection', 'Keep-Alive'),
- ('Cache-Control', 'no-cache'),
- ]
- filedata = ntob('<?xml version="1.0" encoding="UTF-8"?>\r\n'
- '<projectDescription>\r\n'
- '</projectDescription>\r\n')
- body = (ntob(
- '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
- 'Content-Disposition: form-data; name="Filename"\r\n'
- '\r\n'
- '.project\r\n'
- '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
- 'Content-Disposition: form-data; '
- 'name="Filedata"; filename=".project"\r\n'
- 'Content-Type: application/octet-stream\r\n'
- '\r\n')
- + filedata +
- ntob('\r\n'
- '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
- 'Content-Disposition: form-data; name="Upload"\r\n'
- '\r\n'
- 'Submit Query\r\n'
- # Flash apps omit the trailing \r\n on the last line:
- '------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--'
- ))
- self.getPage('/flashupload', headers, "POST", body)
- self.assertBody("Upload: u'Submit Query', Filename: u'.project', "
- "Filedata: %r" % filedata)
-
diff --git a/cherrypy/test/test_misc_tools.py b/cherrypy/test/test_misc_tools.py
deleted file mode 100755
index fb94e86..0000000
--- a/cherrypy/test/test_misc_tools.py
+++ /dev/null
@@ -1,202 +0,0 @@
-import os
-localDir = os.path.dirname(__file__)
-logfile = os.path.join(localDir, "test_misc_tools.log")
-
-import cherrypy
-from cherrypy import tools
-
-
-def setup_server():
- class Root:
- def index(self):
- yield "Hello, world"
- index.exposed = True
- h = [("Content-Language", "en-GB"), ('Content-Type', 'text/plain')]
- tools.response_headers(headers=h)(index)
-
- def other(self):
- return "salut"
- other.exposed = True
- other._cp_config = {
- 'tools.response_headers.on': True,
- 'tools.response_headers.headers': [("Content-Language", "fr"),
- ('Content-Type', 'text/plain')],
- 'tools.log_hooks.on': True,
- }
-
-
- class Accept:
- _cp_config = {'tools.accept.on': True}
-
- def index(self):
- return '<a href="feed">Atom feed</a>'
- index.exposed = True
-
- # In Python 2.4+, we could use a decorator instead:
- # @tools.accept('application/atom+xml')
- def feed(self):
- return """<?xml version="1.0" encoding="utf-8"?>
-<feed xmlns="http://www.w3.org/2005/Atom">
- <title>Unknown Blog</title>
-</feed>"""
- feed.exposed = True
- feed._cp_config = {'tools.accept.media': 'application/atom+xml'}
-
- def select(self):
- # We could also write this: mtype = cherrypy.lib.accept.accept(...)
- mtype = tools.accept.callable(['text/html', 'text/plain'])
- if mtype == 'text/html':
- return "<h2>Page Title</h2>"
- else:
- return "PAGE TITLE"
- select.exposed = True
-
- class Referer:
- def accept(self):
- return "Accepted!"
- accept.exposed = True
- reject = accept
-
- class AutoVary:
- def index(self):
- # Read a header directly with 'get'
- ae = cherrypy.request.headers.get('Accept-Encoding')
- # Read a header directly with '__getitem__'
- cl = cherrypy.request.headers['Host']
- # Read a header directly with '__contains__'
- hasif = 'If-Modified-Since' in cherrypy.request.headers
- # Read a header directly with 'has_key'
- has = cherrypy.request.headers.has_key('Range')
- # Call a lib function
- mtype = tools.accept.callable(['text/html', 'text/plain'])
- return "Hello, world!"
- index.exposed = True
-
- conf = {'/referer': {'tools.referer.on': True,
- 'tools.referer.pattern': r'http://[^/]*example\.com',
- },
- '/referer/reject': {'tools.referer.accept': False,
- 'tools.referer.accept_missing': True,
- },
- '/autovary': {'tools.autovary.on': True},
- }
-
- root = Root()
- root.referer = Referer()
- root.accept = Accept()
- root.autovary = AutoVary()
- cherrypy.tree.mount(root, config=conf)
- cherrypy.config.update({'log.error_file': logfile})
-
-
-from cherrypy.test import helper
-
-class ResponseHeadersTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def testResponseHeadersDecorator(self):
- self.getPage('/')
- self.assertHeader("Content-Language", "en-GB")
- self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-
- def testResponseHeaders(self):
- self.getPage('/other')
- self.assertHeader("Content-Language", "fr")
- self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
-
-
-class RefererTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def testReferer(self):
- self.getPage('/referer/accept')
- self.assertErrorPage(403, 'Forbidden Referer header.')
-
- self.getPage('/referer/accept',
- headers=[('Referer', 'http://www.example.com/')])
- self.assertStatus(200)
- self.assertBody('Accepted!')
-
- # Reject
- self.getPage('/referer/reject')
- self.assertStatus(200)
- self.assertBody('Accepted!')
-
- self.getPage('/referer/reject',
- headers=[('Referer', 'http://www.example.com/')])
- self.assertErrorPage(403, 'Forbidden Referer header.')
-
-
-class AcceptTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_Accept_Tool(self):
- # Test with no header provided
- self.getPage('/accept/feed')
- self.assertStatus(200)
- self.assertInBody('<title>Unknown Blog</title>')
-
- # Specify exact media type
- self.getPage('/accept/feed', headers=[('Accept', 'application/atom+xml')])
- self.assertStatus(200)
- self.assertInBody('<title>Unknown Blog</title>')
-
- # Specify matching media range
- self.getPage('/accept/feed', headers=[('Accept', 'application/*')])
- self.assertStatus(200)
- self.assertInBody('<title>Unknown Blog</title>')
-
- # Specify all media ranges
- self.getPage('/accept/feed', headers=[('Accept', '*/*')])
- self.assertStatus(200)
- self.assertInBody('<title>Unknown Blog</title>')
-
- # Specify unacceptable media types
- self.getPage('/accept/feed', headers=[('Accept', 'text/html')])
- self.assertErrorPage(406,
- "Your client sent this Accept header: text/html. "
- "But this resource only emits these media types: "
- "application/atom+xml.")
-
- # Test resource where tool is 'on' but media is None (not set).
- self.getPage('/accept/')
- self.assertStatus(200)
- self.assertBody('<a href="feed">Atom feed</a>')
-
- def test_accept_selection(self):
- # Try both our expected media types
- self.getPage('/accept/select', [('Accept', 'text/html')])
- self.assertStatus(200)
- self.assertBody('<h2>Page Title</h2>')
- self.getPage('/accept/select', [('Accept', 'text/plain')])
- self.assertStatus(200)
- self.assertBody('PAGE TITLE')
- self.getPage('/accept/select', [('Accept', 'text/plain, text/*;q=0.5')])
- self.assertStatus(200)
- self.assertBody('PAGE TITLE')
-
- # text/* and */* should prefer text/html since it comes first
- # in our 'media' argument to tools.accept
- self.getPage('/accept/select', [('Accept', 'text/*')])
- self.assertStatus(200)
- self.assertBody('<h2>Page Title</h2>')
- self.getPage('/accept/select', [('Accept', '*/*')])
- self.assertStatus(200)
- self.assertBody('<h2>Page Title</h2>')
-
- # Try unacceptable media types
- self.getPage('/accept/select', [('Accept', 'application/xml')])
- self.assertErrorPage(406,
- "Your client sent this Accept header: application/xml. "
- "But this resource only emits these media types: "
- "text/html, text/plain.")
-
-
-class AutoVaryTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def testAutoVary(self):
- self.getPage('/autovary/')
- self.assertHeader(
- "Vary", 'Accept, Accept-Charset, Accept-Encoding, Host, If-Modified-Since, Range')
-
diff --git a/cherrypy/test/test_objectmapping.py b/cherrypy/test/test_objectmapping.py
deleted file mode 100755
index 46816fc..0000000
--- a/cherrypy/test/test_objectmapping.py
+++ /dev/null
@@ -1,403 +0,0 @@
-import cherrypy
-from cherrypy._cptree import Application
-from cherrypy.test import helper
-
-script_names = ["", "/foo", "/users/fred/blog", "/corp/blog"]
-
-
-class ObjectMappingTest(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def index(self, name="world"):
- return name
- index.exposed = True
-
- def foobar(self):
- return "bar"
- foobar.exposed = True
-
- def default(self, *params, **kwargs):
- return "default:" + repr(params)
- default.exposed = True
-
- def other(self):
- return "other"
- other.exposed = True
-
- def extra(self, *p):
- return repr(p)
- extra.exposed = True
-
- def redirect(self):
- raise cherrypy.HTTPRedirect('dir1/', 302)
- redirect.exposed = True
-
- def notExposed(self):
- return "not exposed"
-
- def confvalue(self):
- return cherrypy.request.config.get("user")
- confvalue.exposed = True
-
- def redirect_via_url(self, path):
- raise cherrypy.HTTPRedirect(cherrypy.url(path))
- redirect_via_url.exposed = True
-
- def translate_html(self):
- return "OK"
- translate_html.exposed = True
-
- def mapped_func(self, ID=None):
- return "ID is %s" % ID
- mapped_func.exposed = True
- setattr(Root, "Von B\xfclow", mapped_func)
-
-
- class Exposing:
- def base(self):
- return "expose works!"
- cherrypy.expose(base)
- cherrypy.expose(base, "1")
- cherrypy.expose(base, "2")
-
- class ExposingNewStyle(object):
- def base(self):
- return "expose works!"
- cherrypy.expose(base)
- cherrypy.expose(base, "1")
- cherrypy.expose(base, "2")
-
-
- class Dir1:
- def index(self):
- return "index for dir1"
- index.exposed = True
-
- def myMethod(self):
- return "myMethod from dir1, path_info is:" + repr(cherrypy.request.path_info)
- myMethod.exposed = True
- myMethod._cp_config = {'tools.trailing_slash.extra': True}
-
- def default(self, *params):
- return "default for dir1, param is:" + repr(params)
- default.exposed = True
-
-
- class Dir2:
- def index(self):
- return "index for dir2, path is:" + cherrypy.request.path_info
- index.exposed = True
-
- def script_name(self):
- return cherrypy.tree.script_name()
- script_name.exposed = True
-
- def cherrypy_url(self):
- return cherrypy.url("/extra")
- cherrypy_url.exposed = True
-
- def posparam(self, *vpath):
- return "/".join(vpath)
- posparam.exposed = True
-
-
- class Dir3:
- def default(self):
- return "default for dir3, not exposed"
-
- class Dir4:
- def index(self):
- return "index for dir4, not exposed"
-
- class DefNoIndex:
- def default(self, *args):
- raise cherrypy.HTTPRedirect("contact")
- default.exposed = True
-
- # MethodDispatcher code
- class ByMethod:
- exposed = True
-
- def __init__(self, *things):
- self.things = list(things)
-
- def GET(self):
- return repr(self.things)
-
- def POST(self, thing):
- self.things.append(thing)
-
- class Collection:
- default = ByMethod('a', 'bit')
-
- Root.exposing = Exposing()
- Root.exposingnew = ExposingNewStyle()
- Root.dir1 = Dir1()
- Root.dir1.dir2 = Dir2()
- Root.dir1.dir2.dir3 = Dir3()
- Root.dir1.dir2.dir3.dir4 = Dir4()
- Root.defnoindex = DefNoIndex()
- Root.bymethod = ByMethod('another')
- Root.collection = Collection()
-
- d = cherrypy.dispatch.MethodDispatcher()
- for url in script_names:
- conf = {'/': {'user': (url or "/").split("/")[-2]},
- '/bymethod': {'request.dispatch': d},
- '/collection': {'request.dispatch': d},
- }
- cherrypy.tree.mount(Root(), url, conf)
-
-
- class Isolated:
- def index(self):
- return "made it!"
- index.exposed = True
-
- cherrypy.tree.mount(Isolated(), "/isolated")
-
- class AnotherApp:
-
- exposed = True
-
- def GET(self):
- return "milk"
-
- cherrypy.tree.mount(AnotherApp(), "/app", {'/': {'request.dispatch': d}})
- setup_server = staticmethod(setup_server)
-
-
- def testObjectMapping(self):
- for url in script_names:
- prefix = self.script_name = url
-
- self.getPage('/')
- self.assertBody('world')
-
- self.getPage("/dir1/myMethod")
- self.assertBody("myMethod from dir1, path_info is:'/dir1/myMethod'")
-
- self.getPage("/this/method/does/not/exist")
- self.assertBody("default:('this', 'method', 'does', 'not', 'exist')")
-
- self.getPage("/extra/too/much")
- self.assertBody("('too', 'much')")
-
- self.getPage("/other")
- self.assertBody('other')
-
- self.getPage("/notExposed")
- self.assertBody("default:('notExposed',)")
-
- self.getPage("/dir1/dir2/")
- self.assertBody('index for dir2, path is:/dir1/dir2/')
-
- # Test omitted trailing slash (should be redirected by default).
- self.getPage("/dir1/dir2")
- self.assertStatus(301)
- self.assertHeader('Location', '%s/dir1/dir2/' % self.base())
-
- # Test extra trailing slash (should be redirected if configured).
- self.getPage("/dir1/myMethod/")
- self.assertStatus(301)
- self.assertHeader('Location', '%s/dir1/myMethod' % self.base())
-
- # Test that default method must be exposed in order to match.
- self.getPage("/dir1/dir2/dir3/dir4/index")
- self.assertBody("default for dir1, param is:('dir2', 'dir3', 'dir4', 'index')")
-
- # Test *vpath when default() is defined but not index()
- # This also tests HTTPRedirect with default.
- self.getPage("/defnoindex")
- self.assertStatus((302, 303))
- self.assertHeader('Location', '%s/contact' % self.base())
- self.getPage("/defnoindex/")
- self.assertStatus((302, 303))
- self.assertHeader('Location', '%s/defnoindex/contact' % self.base())
- self.getPage("/defnoindex/page")
- self.assertStatus((302, 303))
- self.assertHeader('Location', '%s/defnoindex/contact' % self.base())
-
- self.getPage("/redirect")
- self.assertStatus('302 Found')
- self.assertHeader('Location', '%s/dir1/' % self.base())
-
- if not getattr(cherrypy.server, "using_apache", False):
- # Test that we can use URL's which aren't all valid Python identifiers
- # This should also test the %XX-unquoting of URL's.
- self.getPage("/Von%20B%fclow?ID=14")
- self.assertBody("ID is 14")
-
- # Test that %2F in the path doesn't get unquoted too early;
- # that is, it should not be used to separate path components.
- # See ticket #393.
- self.getPage("/page%2Fname")
- self.assertBody("default:('page/name',)")
-
- self.getPage("/dir1/dir2/script_name")
- self.assertBody(url)
- self.getPage("/dir1/dir2/cherrypy_url")
- self.assertBody("%s/extra" % self.base())
-
- # Test that configs don't overwrite each other from diferent apps
- self.getPage("/confvalue")
- self.assertBody((url or "/").split("/")[-2])
-
- self.script_name = ""
-
- # Test absoluteURI's in the Request-Line
- self.getPage('http://%s:%s/' % (self.interface(), self.PORT))
- self.assertBody('world')
-
- self.getPage('http://%s:%s/abs/?service=http://192.168.0.1/x/y/z' %
- (self.interface(), self.PORT))
- self.assertBody("default:('abs',)")
-
- self.getPage('/rel/?service=http://192.168.120.121:8000/x/y/z')
- self.assertBody("default:('rel',)")
-
- # Test that the "isolated" app doesn't leak url's into the root app.
- # If it did leak, Root.default() would answer with
- # "default:('isolated', 'doesnt', 'exist')".
- self.getPage("/isolated/")
- self.assertStatus("200 OK")
- self.assertBody("made it!")
- self.getPage("/isolated/doesnt/exist")
- self.assertStatus("404 Not Found")
-
- # Make sure /foobar maps to Root.foobar and not to the app
- # mounted at /foo. See http://www.cherrypy.org/ticket/573
- self.getPage("/foobar")
- self.assertBody("bar")
-
- def test_translate(self):
- self.getPage("/translate_html")
- self.assertStatus("200 OK")
- self.assertBody("OK")
-
- self.getPage("/translate.html")
- self.assertStatus("200 OK")
- self.assertBody("OK")
-
- self.getPage("/translate-html")
- self.assertStatus("200 OK")
- self.assertBody("OK")
-
- def test_redir_using_url(self):
- for url in script_names:
- prefix = self.script_name = url
-
- # Test the absolute path to the parent (leading slash)
- self.getPage('/redirect_via_url?path=./')
- self.assertStatus(('302 Found', '303 See Other'))
- self.assertHeader('Location', '%s/' % self.base())
-
- # Test the relative path to the parent (no leading slash)
- self.getPage('/redirect_via_url?path=./')
- self.assertStatus(('302 Found', '303 See Other'))
- self.assertHeader('Location', '%s/' % self.base())
-
- # Test the absolute path to the parent (leading slash)
- self.getPage('/redirect_via_url/?path=./')
- self.assertStatus(('302 Found', '303 See Other'))
- self.assertHeader('Location', '%s/' % self.base())
-
- # Test the relative path to the parent (no leading slash)
- self.getPage('/redirect_via_url/?path=./')
- self.assertStatus(('302 Found', '303 See Other'))
- self.assertHeader('Location', '%s/' % self.base())
-
- def testPositionalParams(self):
- self.getPage("/dir1/dir2/posparam/18/24/hut/hike")
- self.assertBody("18/24/hut/hike")
-
- # intermediate index methods should not receive posparams;
- # only the "final" index method should do so.
- self.getPage("/dir1/dir2/5/3/sir")
- self.assertBody("default for dir1, param is:('dir2', '5', '3', 'sir')")
-
- # test that extra positional args raises an 404 Not Found
- # See http://www.cherrypy.org/ticket/733.
- self.getPage("/dir1/dir2/script_name/extra/stuff")
- self.assertStatus(404)
-
- def testExpose(self):
- # Test the cherrypy.expose function/decorator
- self.getPage("/exposing/base")
- self.assertBody("expose works!")
-
- self.getPage("/exposing/1")
- self.assertBody("expose works!")
-
- self.getPage("/exposing/2")
- self.assertBody("expose works!")
-
- self.getPage("/exposingnew/base")
- self.assertBody("expose works!")
-
- self.getPage("/exposingnew/1")
- self.assertBody("expose works!")
-
- self.getPage("/exposingnew/2")
- self.assertBody("expose works!")
-
- def testMethodDispatch(self):
- self.getPage("/bymethod")
- self.assertBody("['another']")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- self.getPage("/bymethod", method="HEAD")
- self.assertBody("")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- self.getPage("/bymethod", method="POST", body="thing=one")
- self.assertBody("")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- self.getPage("/bymethod")
- self.assertBody("['another', u'one']")
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- self.getPage("/bymethod", method="PUT")
- self.assertErrorPage(405)
- self.assertHeader('Allow', 'GET, HEAD, POST')
-
- # Test default with posparams
- self.getPage("/collection/silly", method="POST")
- self.getPage("/collection", method="GET")
- self.assertBody("['a', 'bit', 'silly']")
-
- # Test custom dispatcher set on app root (see #737).
- self.getPage("/app")
- self.assertBody("milk")
-
- def testTreeMounting(self):
- class Root(object):
- def hello(self):
- return "Hello world!"
- hello.exposed = True
-
- # When mounting an application instance,
- # we can't specify a different script name in the call to mount.
- a = Application(Root(), '/somewhere')
- self.assertRaises(ValueError, cherrypy.tree.mount, a, '/somewhereelse')
-
- # When mounting an application instance...
- a = Application(Root(), '/somewhere')
- # ...we MUST allow in identical script name in the call to mount...
- cherrypy.tree.mount(a, '/somewhere')
- self.getPage('/somewhere/hello')
- self.assertStatus(200)
- # ...and MUST allow a missing script_name.
- del cherrypy.tree.apps['/somewhere']
- cherrypy.tree.mount(a)
- self.getPage('/somewhere/hello')
- self.assertStatus(200)
-
- # In addition, we MUST be able to create an Application using
- # script_name == None for access to the wsgi_environ.
- a = Application(Root(), script_name=None)
- # However, this does not apply to tree.mount
- self.assertRaises(TypeError, cherrypy.tree.mount, a, None)
-
diff --git a/cherrypy/test/test_proxy.py b/cherrypy/test/test_proxy.py
deleted file mode 100755
index 2fbb619..0000000
--- a/cherrypy/test/test_proxy.py
+++ /dev/null
@@ -1,129 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-script_names = ["", "/path/to/myapp"]
-
-
-class ProxyTest(helper.CPWebCase):
-
- def setup_server():
-
- # Set up site
- cherrypy.config.update({
- 'tools.proxy.on': True,
- 'tools.proxy.base': 'www.mydomain.test',
- })
-
- # Set up application
-
- class Root:
-
- def __init__(self, sn):
- # Calculate a URL outside of any requests.
- self.thisnewpage = cherrypy.url("/this/new/page", script_name=sn)
-
- def pageurl(self):
- return self.thisnewpage
- pageurl.exposed = True
-
- def index(self):
- raise cherrypy.HTTPRedirect('dummy')
- index.exposed = True
-
- def remoteip(self):
- return cherrypy.request.remote.ip
- remoteip.exposed = True
-
- def xhost(self):
- raise cherrypy.HTTPRedirect('blah')
- xhost.exposed = True
- xhost._cp_config = {'tools.proxy.local': 'X-Host',
- 'tools.trailing_slash.extra': True,
- }
-
- def base(self):
- return cherrypy.request.base
- base.exposed = True
-
- def ssl(self):
- return cherrypy.request.base
- ssl.exposed = True
- ssl._cp_config = {'tools.proxy.scheme': 'X-Forwarded-Ssl'}
-
- def newurl(self):
- return ("Browse to <a href='%s'>this page</a>."
- % cherrypy.url("/this/new/page"))
- newurl.exposed = True
-
- for sn in script_names:
- cherrypy.tree.mount(Root(sn), sn)
- setup_server = staticmethod(setup_server)
-
- def testProxy(self):
- self.getPage("/")
- self.assertHeader('Location',
- "%s://www.mydomain.test%s/dummy" %
- (self.scheme, self.prefix()))
-
- # Test X-Forwarded-Host (Apache 1.3.33+ and Apache 2)
- self.getPage("/", headers=[('X-Forwarded-Host', 'http://www.example.test')])
- self.assertHeader('Location', "http://www.example.test/dummy")
- self.getPage("/", headers=[('X-Forwarded-Host', 'www.example.test')])
- self.assertHeader('Location', "%s://www.example.test/dummy" % self.scheme)
- # Test multiple X-Forwarded-Host headers
- self.getPage("/", headers=[
- ('X-Forwarded-Host', 'http://www.example.test, www.cherrypy.test'),
- ])
- self.assertHeader('Location', "http://www.example.test/dummy")
-
- # Test X-Forwarded-For (Apache2)
- self.getPage("/remoteip",
- headers=[('X-Forwarded-For', '192.168.0.20')])
- self.assertBody("192.168.0.20")
- self.getPage("/remoteip",
- headers=[('X-Forwarded-For', '67.15.36.43, 192.168.0.20')])
- self.assertBody("192.168.0.20")
-
- # Test X-Host (lighttpd; see https://trac.lighttpd.net/trac/ticket/418)
- self.getPage("/xhost", headers=[('X-Host', 'www.example.test')])
- self.assertHeader('Location', "%s://www.example.test/blah" % self.scheme)
-
- # Test X-Forwarded-Proto (lighttpd)
- self.getPage("/base", headers=[('X-Forwarded-Proto', 'https')])
- self.assertBody("https://www.mydomain.test")
-
- # Test X-Forwarded-Ssl (webfaction?)
- self.getPage("/ssl", headers=[('X-Forwarded-Ssl', 'on')])
- self.assertBody("https://www.mydomain.test")
-
- # Test cherrypy.url()
- for sn in script_names:
- # Test the value inside requests
- self.getPage(sn + "/newurl")
- self.assertBody("Browse to <a href='%s://www.mydomain.test" % self.scheme
- + sn + "/this/new/page'>this page</a>.")
- self.getPage(sn + "/newurl", headers=[('X-Forwarded-Host',
- 'http://www.example.test')])
- self.assertBody("Browse to <a href='http://www.example.test"
- + sn + "/this/new/page'>this page</a>.")
-
- # Test the value outside requests
- port = ""
- if self.scheme == "http" and self.PORT != 80:
- port = ":%s" % self.PORT
- elif self.scheme == "https" and self.PORT != 443:
- port = ":%s" % self.PORT
- host = self.HOST
- if host in ('0.0.0.0', '::'):
- import socket
- host = socket.gethostname()
- expected = ("%s://%s%s%s/this/new/page"
- % (self.scheme, host, port, sn))
- self.getPage(sn + "/pageurl")
- self.assertBody(expected)
-
- # Test trailing slash (see http://www.cherrypy.org/ticket/562).
- self.getPage("/xhost/", headers=[('X-Host', 'www.example.test')])
- self.assertHeader('Location', "%s://www.example.test/xhost"
- % self.scheme)
-
diff --git a/cherrypy/test/test_refleaks.py b/cherrypy/test/test_refleaks.py
deleted file mode 100755
index 4df1f08..0000000
--- a/cherrypy/test/test_refleaks.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""Tests for refleaks."""
-
-import gc
-from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, ntob
-import threading
-
-import cherrypy
-from cherrypy import _cprequest
-
-
-data = object()
-
-def get_instances(cls):
- return [x for x in gc.get_objects() if isinstance(x, cls)]
-
-
-from cherrypy.test import helper
-
-
-class ReferenceTests(helper.CPWebCase):
-
- def setup_server():
-
- class Root:
- def index(self, *args, **kwargs):
- cherrypy.request.thing = data
- return "Hello world!"
- index.exposed = True
-
- def gc_stats(self):
- output = ["Statistics:"]
-
- # Uncollectable garbage
-
- # gc_collect isn't perfectly synchronous, because it may
- # break reference cycles that then take time to fully
- # finalize. Call it twice and hope for the best.
- gc.collect()
- unreachable = gc.collect()
- if unreachable:
- output.append("\n%s unreachable objects:" % unreachable)
- trash = {}
- for x in gc.garbage:
- trash[type(x)] = trash.get(type(x), 0) + 1
- trash = [(v, k) for k, v in trash.items()]
- trash.sort()
- for pair in trash:
- output.append(" " + repr(pair))
-
- # Request references
- reqs = get_instances(_cprequest.Request)
- lenreqs = len(reqs)
- if lenreqs < 2:
- output.append("\nMissing Request reference. Should be 1 in "
- "this request thread and 1 in the main thread.")
- elif lenreqs > 2:
- output.append("\nToo many Request references (%r)." % lenreqs)
- for req in reqs:
- output.append("Referrers for %s:" % repr(req))
- for ref in gc.get_referrers(req):
- if ref is not reqs:
- output.append(" %s" % repr(ref))
-
- # Response references
- resps = get_instances(_cprequest.Response)
- lenresps = len(resps)
- if lenresps < 2:
- output.append("\nMissing Response reference. Should be 1 in "
- "this request thread and 1 in the main thread.")
- elif lenresps > 2:
- output.append("\nToo many Response references (%r)." % lenresps)
- for resp in resps:
- output.append("Referrers for %s:" % repr(resp))
- for ref in gc.get_referrers(resp):
- if ref is not resps:
- output.append(" %s" % repr(ref))
-
- return "\n".join(output)
- gc_stats.exposed = True
-
- cherrypy.tree.mount(Root())
- setup_server = staticmethod(setup_server)
-
-
- def test_threadlocal_garbage(self):
- success = []
-
- def getpage():
- host = '%s:%s' % (self.interface(), self.PORT)
- if self.scheme == 'https':
- c = HTTPSConnection(host)
- else:
- c = HTTPConnection(host)
- try:
- c.putrequest('GET', '/')
- c.endheaders()
- response = c.getresponse()
- body = response.read()
- self.assertEqual(response.status, 200)
- self.assertEqual(body, ntob("Hello world!"))
- finally:
- c.close()
- success.append(True)
-
- ITERATIONS = 25
- ts = []
- for _ in range(ITERATIONS):
- t = threading.Thread(target=getpage)
- ts.append(t)
- t.start()
-
- for t in ts:
- t.join()
-
- self.assertEqual(len(success), ITERATIONS)
-
- self.getPage("/gc_stats")
- self.assertBody("Statistics:")
-
diff --git a/cherrypy/test/test_request_obj.py b/cherrypy/test/test_request_obj.py
deleted file mode 100755
index 91ee4fd..0000000
--- a/cherrypy/test/test_request_obj.py
+++ /dev/null
@@ -1,722 +0,0 @@
-"""Basic tests for the cherrypy.Request object."""
-
-import os
-localDir = os.path.dirname(__file__)
-import sys
-import types
-from cherrypy._cpcompat import IncompleteRead, ntob, unicodestr
-
-import cherrypy
-from cherrypy import _cptools, tools
-from cherrypy.lib import httputil
-
-defined_http_methods = ("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE",
- "TRACE", "PROPFIND")
-
-
-# Client-side code #
-
-from cherrypy.test import helper
-
-class RequestObjectTests(helper.CPWebCase):
-
- def setup_server():
- class Root:
-
- def index(self):
- return "hello"
- index.exposed = True
-
- def scheme(self):
- return cherrypy.request.scheme
- scheme.exposed = True
-
- root = Root()
-
-
- class TestType(type):
- """Metaclass which automatically exposes all functions in each subclass,
- and adds an instance of the subclass as an attribute of root.
- """
- def __init__(cls, name, bases, dct):
- type.__init__(cls, name, bases, dct)
- for value in dct.values():
- if isinstance(value, types.FunctionType):
- value.exposed = True
- setattr(root, name.lower(), cls())
- class Test(object):
- __metaclass__ = TestType
-
-
- class Params(Test):
-
- def index(self, thing):
- return repr(thing)
-
- def ismap(self, x, y):
- return "Coordinates: %s, %s" % (x, y)
-
- def default(self, *args, **kwargs):
- return "args: %s kwargs: %s" % (args, kwargs)
- default._cp_config = {'request.query_string_encoding': 'latin1'}
-
-
- class ParamErrorsCallable(object):
- exposed = True
- def __call__(self):
- return "data"
-
- class ParamErrors(Test):
-
- def one_positional(self, param1):
- return "data"
- one_positional.exposed = True
-
- def one_positional_args(self, param1, *args):
- return "data"
- one_positional_args.exposed = True
-
- def one_positional_args_kwargs(self, param1, *args, **kwargs):
- return "data"
- one_positional_args_kwargs.exposed = True
-
- def one_positional_kwargs(self, param1, **kwargs):
- return "data"
- one_positional_kwargs.exposed = True
-
- def no_positional(self):
- return "data"
- no_positional.exposed = True
-
- def no_positional_args(self, *args):
- return "data"
- no_positional_args.exposed = True
-
- def no_positional_args_kwargs(self, *args, **kwargs):
- return "data"
- no_positional_args_kwargs.exposed = True
-
- def no_positional_kwargs(self, **kwargs):
- return "data"
- no_positional_kwargs.exposed = True
-
- callable_object = ParamErrorsCallable()
-
- def raise_type_error(self, **kwargs):
- raise TypeError("Client Error")
- raise_type_error.exposed = True
-
- def raise_type_error_with_default_param(self, x, y=None):
- return '%d' % 'a' # throw an exception
- raise_type_error_with_default_param.exposed = True
-
- def callable_error_page(status, **kwargs):
- return "Error %s - Well, I'm very sorry but you haven't paid!" % status
-
-
- class Error(Test):
-
- _cp_config = {'tools.log_tracebacks.on': True,
- }
-
- def reason_phrase(self):
- raise cherrypy.HTTPError("410 Gone fishin'")
-
- def custom(self, err='404'):
- raise cherrypy.HTTPError(int(err), "No, <b>really</b>, not found!")
- custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html"),
- 'error_page.401': callable_error_page,
- }
-
- def custom_default(self):
- return 1 + 'a' # raise an unexpected error
- custom_default._cp_config = {'error_page.default': callable_error_page}
-
- def noexist(self):
- raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!")
- noexist._cp_config = {'error_page.404': "nonexistent.html"}
-
- def page_method(self):
- raise ValueError()
-
- def page_yield(self):
- yield "howdy"
- raise ValueError()
-
- def page_streamed(self):
- yield "word up"
- raise ValueError()
- yield "very oops"
- page_streamed._cp_config = {"response.stream": True}
-
- def cause_err_in_finalize(self):
- # Since status must start with an int, this should error.
- cherrypy.response.status = "ZOO OK"
- cause_err_in_finalize._cp_config = {'request.show_tracebacks': False}
-
- def rethrow(self):
- """Test that an error raised here will be thrown out to the server."""
- raise ValueError()
- rethrow._cp_config = {'request.throw_errors': True}
-
-
- class Expect(Test):
-
- def expectation_failed(self):
- expect = cherrypy.request.headers.elements("Expect")
- if expect and expect[0].value != '100-continue':
- raise cherrypy.HTTPError(400)
- raise cherrypy.HTTPError(417, 'Expectation Failed')
-
- class Headers(Test):
-
- def default(self, headername):
- """Spit back out the value for the requested header."""
- return cherrypy.request.headers[headername]
-
- def doubledheaders(self):
- # From http://www.cherrypy.org/ticket/165:
- # "header field names should not be case sensitive sayes the rfc.
- # if i set a headerfield in complete lowercase i end up with two
- # header fields, one in lowercase, the other in mixed-case."
-
- # Set the most common headers
- hMap = cherrypy.response.headers
- hMap['content-type'] = "text/html"
- hMap['content-length'] = 18
- hMap['server'] = 'CherryPy headertest'
- hMap['location'] = ('%s://%s:%s/headers/'
- % (cherrypy.request.local.ip,
- cherrypy.request.local.port,
- cherrypy.request.scheme))
-
- # Set a rare header for fun
- hMap['Expires'] = 'Thu, 01 Dec 2194 16:00:00 GMT'
-
- return "double header test"
-
- def ifmatch(self):
- val = cherrypy.request.headers['If-Match']
- assert isinstance(val, unicodestr)
- cherrypy.response.headers['ETag'] = val
- return val
-
-
- class HeaderElements(Test):
-
- def get_elements(self, headername):
- e = cherrypy.request.headers.elements(headername)
- return "\n".join([unicodestr(x) for x in e])
-
-
- class Method(Test):
-
- def index(self):
- m = cherrypy.request.method
- if m in defined_http_methods or m == "CONNECT":
- return m
-
- if m == "LINK":
- raise cherrypy.HTTPError(405)
- else:
- raise cherrypy.HTTPError(501)
-
- def parameterized(self, data):
- return data
-
- def request_body(self):
- # This should be a file object (temp file),
- # which CP will just pipe back out if we tell it to.
- return cherrypy.request.body
-
- def reachable(self):
- return "success"
-
- class Divorce:
- """HTTP Method handlers shouldn't collide with normal method names.
- For example, a GET-handler shouldn't collide with a method named 'get'.
-
- If you build HTTP method dispatching into CherryPy, rewrite this class
- to use your new dispatch mechanism and make sure that:
- "GET /divorce HTTP/1.1" maps to divorce.index() and
- "GET /divorce/get?ID=13 HTTP/1.1" maps to divorce.get()
- """
-
- documents = {}
-
- def index(self):
- yield "<h1>Choose your document</h1>\n"
- yield "<ul>\n"
- for id, contents in self.documents.items():
- yield (" <li><a href='/divorce/get?ID=%s'>%s</a>: %s</li>\n"
- % (id, id, contents))
- yield "</ul>"
- index.exposed = True
-
- def get(self, ID):
- return ("Divorce document %s: %s" %
- (ID, self.documents.get(ID, "empty")))
- get.exposed = True
-
- root.divorce = Divorce()
-
-
- class ThreadLocal(Test):
-
- def index(self):
- existing = repr(getattr(cherrypy.request, "asdf", None))
- cherrypy.request.asdf = "rassfrassin"
- return existing
-
- appconf = {
- '/method': {'request.methods_with_bodies': ("POST", "PUT", "PROPFIND")},
- }
- cherrypy.tree.mount(root, config=appconf)
- setup_server = staticmethod(setup_server)
-
- def test_scheme(self):
- self.getPage("/scheme")
- self.assertBody(self.scheme)
-
- def testParams(self):
- self.getPage("/params/?thing=a")
- self.assertBody("u'a'")
-
- self.getPage("/params/?thing=a&thing=b&thing=c")
- self.assertBody("[u'a', u'b', u'c']")
-
- # Test friendly error message when given params are not accepted.
- cherrypy.config.update({"request.show_mismatched_params": True})
- self.getPage("/params/?notathing=meeting")
- self.assertInBody("Missing parameters: thing")
- self.getPage("/params/?thing=meeting&notathing=meeting")
- self.assertInBody("Unexpected query string parameters: notathing")
-
- # Test ability to turn off friendly error messages
- cherrypy.config.update({"request.show_mismatched_params": False})
- self.getPage("/params/?notathing=meeting")
- self.assertInBody("Not Found")
- self.getPage("/params/?thing=meeting&notathing=meeting")
- self.assertInBody("Not Found")
-
- # Test "% HEX HEX"-encoded URL, param keys, and values
- self.getPage("/params/%d4%20%e3/cheese?Gruy%E8re=Bulgn%e9ville")
- self.assertBody(r"args: ('\xd4 \xe3', 'cheese') "
- r"kwargs: {'Gruy\xe8re': u'Bulgn\xe9ville'}")
-
- # Make sure that encoded = and & get parsed correctly
- self.getPage("/params/code?url=http%3A//cherrypy.org/index%3Fa%3D1%26b%3D2")
- self.assertBody(r"args: ('code',) "
- r"kwargs: {'url': u'http://cherrypy.org/index?a=1&b=2'}")
-
- # Test coordinates sent by <img ismap>
- self.getPage("/params/ismap?223,114")
- self.assertBody("Coordinates: 223, 114")
-
- # Test "name[key]" dict-like params
- self.getPage("/params/dictlike?a[1]=1&a[2]=2&b=foo&b[bar]=baz")
- self.assertBody(
- "args: ('dictlike',) "
- "kwargs: {'a[1]': u'1', 'b[bar]': u'baz', 'b': u'foo', 'a[2]': u'2'}")
-
- def testParamErrors(self):
-
- # test that all of the handlers work when given
- # the correct parameters in order to ensure that the
- # errors below aren't coming from some other source.
- for uri in (
- '/paramerrors/one_positional?param1=foo',
- '/paramerrors/one_positional_args?param1=foo',
- '/paramerrors/one_positional_args/foo',
- '/paramerrors/one_positional_args/foo/bar/baz',
- '/paramerrors/one_positional_args_kwargs?param1=foo&param2=bar',
- '/paramerrors/one_positional_args_kwargs/foo?param2=bar&param3=baz',
- '/paramerrors/one_positional_args_kwargs/foo/bar/baz?param2=bar&param3=baz',
- '/paramerrors/one_positional_kwargs?param1=foo&param2=bar&param3=baz',
- '/paramerrors/one_positional_kwargs/foo?param4=foo&param2=bar&param3=baz',
- '/paramerrors/no_positional',
- '/paramerrors/no_positional_args/foo',
- '/paramerrors/no_positional_args/foo/bar/baz',
- '/paramerrors/no_positional_args_kwargs?param1=foo&param2=bar',
- '/paramerrors/no_positional_args_kwargs/foo?param2=bar',
- '/paramerrors/no_positional_args_kwargs/foo/bar/baz?param2=bar&param3=baz',
- '/paramerrors/no_positional_kwargs?param1=foo&param2=bar',
- '/paramerrors/callable_object',
- ):
- self.getPage(uri)
- self.assertStatus(200)
-
- # query string parameters are part of the URI, so if they are wrong
- # for a particular handler, the status MUST be a 404.
- error_msgs = [
- 'Missing parameters',
- 'Nothing matches the given URI',
- 'Multiple values for parameters',
- 'Unexpected query string parameters',
- 'Unexpected body parameters',
- ]
- for uri, msg in (
- ('/paramerrors/one_positional', error_msgs[0]),
- ('/paramerrors/one_positional?foo=foo', error_msgs[0]),
- ('/paramerrors/one_positional/foo/bar/baz', error_msgs[1]),
- ('/paramerrors/one_positional/foo?param1=foo', error_msgs[2]),
- ('/paramerrors/one_positional/foo?param1=foo&param2=foo', error_msgs[2]),
- ('/paramerrors/one_positional_args/foo?param1=foo&param2=foo', error_msgs[2]),
- ('/paramerrors/one_positional_args/foo/bar/baz?param2=foo', error_msgs[3]),
- ('/paramerrors/one_positional_args_kwargs/foo/bar/baz?param1=bar&param3=baz', error_msgs[2]),
- ('/paramerrors/one_positional_kwargs/foo?param1=foo&param2=bar&param3=baz', error_msgs[2]),
- ('/paramerrors/no_positional/boo', error_msgs[1]),
- ('/paramerrors/no_positional?param1=foo', error_msgs[3]),
- ('/paramerrors/no_positional_args/boo?param1=foo', error_msgs[3]),
- ('/paramerrors/no_positional_kwargs/boo?param1=foo', error_msgs[1]),
- ('/paramerrors/callable_object?param1=foo', error_msgs[3]),
- ('/paramerrors/callable_object/boo', error_msgs[1]),
- ):
- for show_mismatched_params in (True, False):
- cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
- self.getPage(uri)
- self.assertStatus(404)
- if show_mismatched_params:
- self.assertInBody(msg)
- else:
- self.assertInBody("Not Found")
-
- # if body parameters are wrong, a 400 must be returned.
- for uri, body, msg in (
- ('/paramerrors/one_positional/foo', 'param1=foo', error_msgs[2]),
- ('/paramerrors/one_positional/foo', 'param1=foo&param2=foo', error_msgs[2]),
- ('/paramerrors/one_positional_args/foo', 'param1=foo&param2=foo', error_msgs[2]),
- ('/paramerrors/one_positional_args/foo/bar/baz', 'param2=foo', error_msgs[4]),
- ('/paramerrors/one_positional_args_kwargs/foo/bar/baz', 'param1=bar&param3=baz', error_msgs[2]),
- ('/paramerrors/one_positional_kwargs/foo', 'param1=foo&param2=bar&param3=baz', error_msgs[2]),
- ('/paramerrors/no_positional', 'param1=foo', error_msgs[4]),
- ('/paramerrors/no_positional_args/boo', 'param1=foo', error_msgs[4]),
- ('/paramerrors/callable_object', 'param1=foo', error_msgs[4]),
- ):
- for show_mismatched_params in (True, False):
- cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
- self.getPage(uri, method='POST', body=body)
- self.assertStatus(400)
- if show_mismatched_params:
- self.assertInBody(msg)
- else:
- self.assertInBody("Bad Request")
-
-
- # even if body parameters are wrong, if we get the uri wrong, then
- # it's a 404
- for uri, body, msg in (
- ('/paramerrors/one_positional?param2=foo', 'param1=foo', error_msgs[3]),
- ('/paramerrors/one_positional/foo/bar', 'param2=foo', error_msgs[1]),
- ('/paramerrors/one_positional_args/foo/bar?param2=foo', 'param3=foo', error_msgs[3]),
- ('/paramerrors/one_positional_kwargs/foo/bar', 'param2=bar&param3=baz', error_msgs[1]),
- ('/paramerrors/no_positional?param1=foo', 'param2=foo', error_msgs[3]),
- ('/paramerrors/no_positional_args/boo?param2=foo', 'param1=foo', error_msgs[3]),
- ('/paramerrors/callable_object?param2=bar', 'param1=foo', error_msgs[3]),
- ):
- for show_mismatched_params in (True, False):
- cherrypy.config.update({'request.show_mismatched_params': show_mismatched_params})
- self.getPage(uri, method='POST', body=body)
- self.assertStatus(404)
- if show_mismatched_params:
- self.assertInBody(msg)
- else:
- self.assertInBody("Not Found")
-
- # In the case that a handler raises a TypeError we should
- # let that type error through.
- for uri in (
- '/paramerrors/raise_type_error',
- '/paramerrors/raise_type_error_with_default_param?x=0',
- '/paramerrors/raise_type_error_with_default_param?x=0&y=0',
- ):
- self.getPage(uri, method='GET')
- self.assertStatus(500)
- self.assertTrue('Client Error', self.body)
-
- def testErrorHandling(self):
- self.getPage("/error/missing")
- self.assertStatus(404)
- self.assertErrorPage(404, "The path '/error/missing' was not found.")
-
- ignore = helper.webtest.ignored_exceptions
- ignore.append(ValueError)
- try:
- valerr = '\n raise ValueError()\nValueError'
- self.getPage("/error/page_method")
- self.assertErrorPage(500, pattern=valerr)
-
- self.getPage("/error/page_yield")
- self.assertErrorPage(500, pattern=valerr)
-
- if (cherrypy.server.protocol_version == "HTTP/1.0" or
- getattr(cherrypy.server, "using_apache", False)):
- self.getPage("/error/page_streamed")
- # Because this error is raised after the response body has
- # started, the status should not change to an error status.
- self.assertStatus(200)
- self.assertBody("word up")
- else:
- # Under HTTP/1.1, the chunked transfer-coding is used.
- # The HTTP client will choke when the output is incomplete.
- self.assertRaises((ValueError, IncompleteRead), self.getPage,
- "/error/page_streamed")
-
- # No traceback should be present
- self.getPage("/error/cause_err_in_finalize")
- msg = "Illegal response status from server ('ZOO' is non-numeric)."
- self.assertErrorPage(500, msg, None)
- finally:
- ignore.pop()
-
- # Test HTTPError with a reason-phrase in the status arg.
- self.getPage('/error/reason_phrase')
- self.assertStatus("410 Gone fishin'")
-
- # Test custom error page for a specific error.
- self.getPage("/error/custom")
- self.assertStatus(404)
- self.assertBody("Hello, world\r\n" + (" " * 499))
-
- # Test custom error page for a specific error.
- self.getPage("/error/custom?err=401")
- self.assertStatus(401)
- self.assertBody("Error 401 Unauthorized - Well, I'm very sorry but you haven't paid!")
-
- # Test default custom error page.
- self.getPage("/error/custom_default")
- self.assertStatus(500)
- self.assertBody("Error 500 Internal Server Error - Well, I'm very sorry but you haven't paid!".ljust(513))
-
- # Test error in custom error page (ticket #305).
- # Note that the message is escaped for HTML (ticket #310).
- self.getPage("/error/noexist")
- self.assertStatus(404)
- msg = ("No, &lt;b&gt;really&lt;/b&gt;, not found!<br />"
- "In addition, the custom error page failed:\n<br />"
- "IOError: [Errno 2] No such file or directory: 'nonexistent.html'")
- self.assertInBody(msg)
-
- if getattr(cherrypy.server, "using_apache", False):
- pass
- else:
- # Test throw_errors (ticket #186).
- self.getPage("/error/rethrow")
- self.assertInBody("raise ValueError()")
-
- def testExpect(self):
- e = ('Expect', '100-continue')
- self.getPage("/headerelements/get_elements?headername=Expect", [e])
- self.assertBody('100-continue')
-
- self.getPage("/expect/expectation_failed", [e])
- self.assertStatus(417)
-
- def testHeaderElements(self):
- # Accept-* header elements should be sorted, with most preferred first.
- h = [('Accept', 'audio/*; q=0.2, audio/basic')]
- self.getPage("/headerelements/get_elements?headername=Accept", h)
- self.assertStatus(200)
- self.assertBody("audio/basic\n"
- "audio/*;q=0.2")
-
- h = [('Accept', 'text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')]
- self.getPage("/headerelements/get_elements?headername=Accept", h)
- self.assertStatus(200)
- self.assertBody("text/x-c\n"
- "text/html\n"
- "text/x-dvi;q=0.8\n"
- "text/plain;q=0.5")
-
- # Test that more specific media ranges get priority.
- h = [('Accept', 'text/*, text/html, text/html;level=1, */*')]
- self.getPage("/headerelements/get_elements?headername=Accept", h)
- self.assertStatus(200)
- self.assertBody("text/html;level=1\n"
- "text/html\n"
- "text/*\n"
- "*/*")
-
- # Test Accept-Charset
- h = [('Accept-Charset', 'iso-8859-5, unicode-1-1;q=0.8')]
- self.getPage("/headerelements/get_elements?headername=Accept-Charset", h)
- self.assertStatus("200 OK")
- self.assertBody("iso-8859-5\n"
- "unicode-1-1;q=0.8")
-
- # Test Accept-Encoding
- h = [('Accept-Encoding', 'gzip;q=1.0, identity; q=0.5, *;q=0')]
- self.getPage("/headerelements/get_elements?headername=Accept-Encoding", h)
- self.assertStatus("200 OK")
- self.assertBody("gzip;q=1.0\n"
- "identity;q=0.5\n"
- "*;q=0")
-
- # Test Accept-Language
- h = [('Accept-Language', 'da, en-gb;q=0.8, en;q=0.7')]
- self.getPage("/headerelements/get_elements?headername=Accept-Language", h)
- self.assertStatus("200 OK")
- self.assertBody("da\n"
- "en-gb;q=0.8\n"
- "en;q=0.7")
-
- # Test malformed header parsing. See http://www.cherrypy.org/ticket/763.
- self.getPage("/headerelements/get_elements?headername=Content-Type",
- # Note the illegal trailing ";"
- headers=[('Content-Type', 'text/html; charset=utf-8;')])
- self.assertStatus(200)
- self.assertBody("text/html;charset=utf-8")
-
- def test_repeated_headers(self):
- # Test that two request headers are collapsed into one.
- # See http://www.cherrypy.org/ticket/542.
- self.getPage("/headers/Accept-Charset",
- headers=[("Accept-Charset", "iso-8859-5"),
- ("Accept-Charset", "unicode-1-1;q=0.8")])
- self.assertBody("iso-8859-5, unicode-1-1;q=0.8")
-
- # Tests that each header only appears once, regardless of case.
- self.getPage("/headers/doubledheaders")
- self.assertBody("double header test")
- hnames = [name.title() for name, val in self.headers]
- for key in ['Content-Length', 'Content-Type', 'Date',
- 'Expires', 'Location', 'Server']:
- self.assertEqual(hnames.count(key), 1, self.headers)
-
- def test_encoded_headers(self):
- # First, make sure the innards work like expected.
- self.assertEqual(httputil.decode_TEXT(u"=?utf-8?q?f=C3=BCr?="), u"f\xfcr")
-
- if cherrypy.server.protocol_version == "HTTP/1.1":
- # Test RFC-2047-encoded request and response header values
- u = u'\u212bngstr\xf6m'
- c = u"=E2=84=ABngstr=C3=B6m"
- self.getPage("/headers/ifmatch", [('If-Match', u'=?utf-8?q?%s?=' % c)])
- # The body should be utf-8 encoded.
- self.assertBody("\xe2\x84\xabngstr\xc3\xb6m")
- # But the Etag header should be RFC-2047 encoded (binary)
- self.assertHeader("ETag", u'=?utf-8?b?4oSrbmdzdHLDtm0=?=')
-
- # Test a *LONG* RFC-2047-encoded request and response header value
- self.getPage("/headers/ifmatch",
- [('If-Match', u'=?utf-8?q?%s?=' % (c * 10))])
- self.assertBody("\xe2\x84\xabngstr\xc3\xb6m" * 10)
- # Note: this is different output for Python3, but it decodes fine.
- etag = self.assertHeader("ETag",
- '=?utf-8?b?4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
- '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
- '4oSrbmdzdHLDtm3ihKtuZ3N0csO2beKEq25nc3Ryw7Zt'
- '4oSrbmdzdHLDtm0=?=')
- self.assertEqual(httputil.decode_TEXT(etag), u * 10)
-
- def test_header_presence(self):
- # If we don't pass a Content-Type header, it should not be present
- # in cherrypy.request.headers
- self.getPage("/headers/Content-Type",
- headers=[])
- self.assertStatus(500)
-
- # If Content-Type is present in the request, it should be present in
- # cherrypy.request.headers
- self.getPage("/headers/Content-Type",
- headers=[("Content-type", "application/json")])
- self.assertBody("application/json")
-
- def test_basic_HTTPMethods(self):
- helper.webtest.methods_with_bodies = ("POST", "PUT", "PROPFIND")
-
- # Test that all defined HTTP methods work.
- for m in defined_http_methods:
- self.getPage("/method/", method=m)
-
- # HEAD requests should not return any body.
- if m == "HEAD":
- self.assertBody("")
- elif m == "TRACE":
- # Some HTTP servers (like modpy) have their own TRACE support
- self.assertEqual(self.body[:5], ntob("TRACE"))
- else:
- self.assertBody(m)
-
- # Request a PUT method with a form-urlencoded body
- self.getPage("/method/parameterized", method="PUT",
- body="data=on+top+of+other+things")
- self.assertBody("on top of other things")
-
- # Request a PUT method with a file body
- b = "one thing on top of another"
- h = [("Content-Type", "text/plain"),
- ("Content-Length", str(len(b)))]
- self.getPage("/method/request_body", headers=h, method="PUT", body=b)
- self.assertStatus(200)
- self.assertBody(b)
-
- # Request a PUT method with a file body but no Content-Type.
- # See http://www.cherrypy.org/ticket/790.
- b = ntob("one thing on top of another")
- self.persistent = True
- try:
- conn = self.HTTP_CONN
- conn.putrequest("PUT", "/method/request_body", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.putheader('Content-Length', str(len(b)))
- conn.endheaders()
- conn.send(b)
- response = conn.response_class(conn.sock, method="PUT")
- response.begin()
- self.assertEqual(response.status, 200)
- self.body = response.read()
- self.assertBody(b)
- finally:
- self.persistent = False
-
- # Request a PUT method with no body whatsoever (not an empty one).
- # See http://www.cherrypy.org/ticket/650.
- # Provide a C-T or webtest will provide one (and a C-L) for us.
- h = [("Content-Type", "text/plain")]
- self.getPage("/method/reachable", headers=h, method="PUT")
- self.assertStatus(411)
-
- # Request a custom method with a request body
- b = ('<?xml version="1.0" encoding="utf-8" ?>\n\n'
- '<propfind xmlns="DAV:"><prop><getlastmodified/>'
- '</prop></propfind>')
- h = [('Content-Type', 'text/xml'),
- ('Content-Length', str(len(b)))]
- self.getPage("/method/request_body", headers=h, method="PROPFIND", body=b)
- self.assertStatus(200)
- self.assertBody(b)
-
- # Request a disallowed method
- self.getPage("/method/", method="LINK")
- self.assertStatus(405)
-
- # Request an unknown method
- self.getPage("/method/", method="SEARCH")
- self.assertStatus(501)
-
- # For method dispatchers: make sure that an HTTP method doesn't
- # collide with a virtual path atom. If you build HTTP-method
- # dispatching into the core, rewrite these handlers to use
- # your dispatch idioms.
- self.getPage("/divorce/get?ID=13")
- self.assertBody('Divorce document 13: empty')
- self.assertStatus(200)
- self.getPage("/divorce/", method="GET")
- self.assertBody('<h1>Choose your document</h1>\n<ul>\n</ul>')
- self.assertStatus(200)
-
- def test_CONNECT_method(self):
- if getattr(cherrypy.server, "using_apache", False):
- return self.skip("skipped due to known Apache differences... ")
-
- self.getPage("/method/", method="CONNECT")
- self.assertBody("CONNECT")
-
- def testEmptyThreadlocals(self):
- results = []
- for x in range(20):
- self.getPage("/threadlocal/")
- results.append(self.body)
- self.assertEqual(results, [ntob("None")] * 20)
-
diff --git a/cherrypy/test/test_routes.py b/cherrypy/test/test_routes.py
deleted file mode 100755
index a8062f8..0000000
--- a/cherrypy/test/test_routes.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-import cherrypy
-
-from cherrypy.test import helper
-import nose
-
-class RoutesDispatchTest(helper.CPWebCase):
-
- def setup_server():
-
- try:
- import routes
- except ImportError:
- raise nose.SkipTest('Install routes to test RoutesDispatcher code')
-
- class Dummy:
- def index(self):
- return "I said good day!"
-
- class City:
-
- def __init__(self, name):
- self.name = name
- self.population = 10000
-
- def index(self, **kwargs):
- return "Welcome to %s, pop. %s" % (self.name, self.population)
- index._cp_config = {'tools.response_headers.on': True,
- 'tools.response_headers.headers': [('Content-Language', 'en-GB')]}
-
- def update(self, **kwargs):
- self.population = kwargs['pop']
- return "OK"
-
- d = cherrypy.dispatch.RoutesDispatcher()
- d.connect(action='index', name='hounslow', route='/hounslow',
- controller=City('Hounslow'))
- d.connect(name='surbiton', route='/surbiton', controller=City('Surbiton'),
- action='index', conditions=dict(method=['GET']))
- d.mapper.connect('/surbiton', controller='surbiton',
- action='update', conditions=dict(method=['POST']))
- d.connect('main', ':action', controller=Dummy())
-
- conf = {'/': {'request.dispatch': d}}
- cherrypy.tree.mount(root=None, config=conf)
- setup_server = staticmethod(setup_server)
-
- def test_Routes_Dispatch(self):
- self.getPage("/hounslow")
- self.assertStatus("200 OK")
- self.assertBody("Welcome to Hounslow, pop. 10000")
-
- self.getPage("/foo")
- self.assertStatus("404 Not Found")
-
- self.getPage("/surbiton")
- self.assertStatus("200 OK")
- self.assertBody("Welcome to Surbiton, pop. 10000")
-
- self.getPage("/surbiton", method="POST", body="pop=1327")
- self.assertStatus("200 OK")
- self.assertBody("OK")
- self.getPage("/surbiton")
- self.assertStatus("200 OK")
- self.assertHeader("Content-Language", "en-GB")
- self.assertBody("Welcome to Surbiton, pop. 1327")
-
diff --git a/cherrypy/test/test_session.py b/cherrypy/test/test_session.py
deleted file mode 100755
index 874023e..0000000
--- a/cherrypy/test/test_session.py
+++ /dev/null
@@ -1,464 +0,0 @@
-import os
-localDir = os.path.dirname(__file__)
-import sys
-import threading
-import time
-
-import cherrypy
-from cherrypy._cpcompat import copykeys, HTTPConnection, HTTPSConnection
-from cherrypy.lib import sessions
-from cherrypy.lib.httputil import response_codes
-
-def http_methods_allowed(methods=['GET', 'HEAD']):
- method = cherrypy.request.method.upper()
- if method not in methods:
- cherrypy.response.headers['Allow'] = ", ".join(methods)
- raise cherrypy.HTTPError(405)
-
-cherrypy.tools.allow = cherrypy.Tool('on_start_resource', http_methods_allowed)
-
-
-def setup_server():
-
- class Root:
-
- _cp_config = {'tools.sessions.on': True,
- 'tools.sessions.storage_type' : 'ram',
- 'tools.sessions.storage_path' : localDir,
- 'tools.sessions.timeout': (1.0 / 60),
- 'tools.sessions.clean_freq': (1.0 / 60),
- }
-
- def clear(self):
- cherrypy.session.cache.clear()
- clear.exposed = True
-
- def data(self):
- cherrypy.session['aha'] = 'foo'
- return repr(cherrypy.session._data)
- data.exposed = True
-
- def testGen(self):
- counter = cherrypy.session.get('counter', 0) + 1
- cherrypy.session['counter'] = counter
- yield str(counter)
- testGen.exposed = True
-
- def testStr(self):
- counter = cherrypy.session.get('counter', 0) + 1
- cherrypy.session['counter'] = counter
- return str(counter)
- testStr.exposed = True
-
- def setsessiontype(self, newtype):
- self.__class__._cp_config.update({'tools.sessions.storage_type': newtype})
- if hasattr(cherrypy, "session"):
- del cherrypy.session
- cls = getattr(sessions, newtype.title() + 'Session')
- if cls.clean_thread:
- cls.clean_thread.stop()
- cls.clean_thread.unsubscribe()
- del cls.clean_thread
- setsessiontype.exposed = True
- setsessiontype._cp_config = {'tools.sessions.on': False}
-
- def index(self):
- sess = cherrypy.session
- c = sess.get('counter', 0) + 1
- time.sleep(0.01)
- sess['counter'] = c
- return str(c)
- index.exposed = True
-
- def keyin(self, key):
- return str(key in cherrypy.session)
- keyin.exposed = True
-
- def delete(self):
- cherrypy.session.delete()
- sessions.expire()
- return "done"
- delete.exposed = True
-
- def delkey(self, key):
- del cherrypy.session[key]
- return "OK"
- delkey.exposed = True
-
- def blah(self):
- return self._cp_config['tools.sessions.storage_type']
- blah.exposed = True
-
- def iredir(self):
- raise cherrypy.InternalRedirect('/blah')
- iredir.exposed = True
-
- def restricted(self):
- return cherrypy.request.method
- restricted.exposed = True
- restricted._cp_config = {'tools.allow.on': True,
- 'tools.allow.methods': ['GET']}
-
- def regen(self):
- cherrypy.tools.sessions.regenerate()
- return "logged in"
- regen.exposed = True
-
- def length(self):
- return str(len(cherrypy.session))
- length.exposed = True
-
- def session_cookie(self):
- # Must load() to start the clean thread.
- cherrypy.session.load()
- return cherrypy.session.id
- session_cookie.exposed = True
- session_cookie._cp_config = {
- 'tools.sessions.path': '/session_cookie',
- 'tools.sessions.name': 'temp',
- 'tools.sessions.persistent': False}
-
- cherrypy.tree.mount(Root())
-
-
-from cherrypy.test import helper
-
-class SessionTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def tearDown(self):
- # Clean up sessions.
- for fname in os.listdir(localDir):
- if fname.startswith(sessions.FileSession.SESSION_PREFIX):
- os.unlink(os.path.join(localDir, fname))
-
- def test_0_Session(self):
- self.getPage('/setsessiontype/ram')
- self.getPage('/clear')
-
- # Test that a normal request gets the same id in the cookies.
- # Note: this wouldn't work if /data didn't load the session.
- self.getPage('/data')
- self.assertBody("{'aha': 'foo'}")
- c = self.cookies[0]
- self.getPage('/data', self.cookies)
- self.assertEqual(self.cookies[0], c)
-
- self.getPage('/testStr')
- self.assertBody('1')
- cookie_parts = dict([p.strip().split('=')
- for p in self.cookies[0][1].split(";")])
- # Assert there is an 'expires' param
- self.assertEqual(set(cookie_parts.keys()),
- set(['session_id', 'expires', 'Path']))
- self.getPage('/testGen', self.cookies)
- self.assertBody('2')
- self.getPage('/testStr', self.cookies)
- self.assertBody('3')
- self.getPage('/data', self.cookies)
- self.assertBody("{'aha': 'foo', 'counter': 3}")
- self.getPage('/length', self.cookies)
- self.assertBody('2')
- self.getPage('/delkey?key=counter', self.cookies)
- self.assertStatus(200)
-
- self.getPage('/setsessiontype/file')
- self.getPage('/testStr')
- self.assertBody('1')
- self.getPage('/testGen', self.cookies)
- self.assertBody('2')
- self.getPage('/testStr', self.cookies)
- self.assertBody('3')
- self.getPage('/delkey?key=counter', self.cookies)
- self.assertStatus(200)
-
- # Wait for the session.timeout (1 second)
- time.sleep(2)
- self.getPage('/')
- self.assertBody('1')
- self.getPage('/length', self.cookies)
- self.assertBody('1')
-
- # Test session __contains__
- self.getPage('/keyin?key=counter', self.cookies)
- self.assertBody("True")
- cookieset1 = self.cookies
-
- # Make a new session and test __len__ again
- self.getPage('/')
- self.getPage('/length', self.cookies)
- self.assertBody('2')
-
- # Test session delete
- self.getPage('/delete', self.cookies)
- self.assertBody("done")
- self.getPage('/delete', cookieset1)
- self.assertBody("done")
- f = lambda: [x for x in os.listdir(localDir) if x.startswith('session-')]
- self.assertEqual(f(), [])
-
- # Wait for the cleanup thread to delete remaining session files
- self.getPage('/')
- f = lambda: [x for x in os.listdir(localDir) if x.startswith('session-')]
- self.assertNotEqual(f(), [])
- time.sleep(2)
- self.assertEqual(f(), [])
-
- def test_1_Ram_Concurrency(self):
- self.getPage('/setsessiontype/ram')
- self._test_Concurrency()
-
- def test_2_File_Concurrency(self):
- self.getPage('/setsessiontype/file')
- self._test_Concurrency()
-
- def _test_Concurrency(self):
- client_thread_count = 5
- request_count = 30
-
- # Get initial cookie
- self.getPage("/")
- self.assertBody("1")
- cookies = self.cookies
-
- data_dict = {}
- errors = []
-
- def request(index):
- if self.scheme == 'https':
- c = HTTPSConnection('%s:%s' % (self.interface(), self.PORT))
- else:
- c = HTTPConnection('%s:%s' % (self.interface(), self.PORT))
- for i in range(request_count):
- c.putrequest('GET', '/')
- for k, v in cookies:
- c.putheader(k, v)
- c.endheaders()
- response = c.getresponse()
- body = response.read()
- if response.status != 200 or not body.isdigit():
- errors.append((response.status, body))
- else:
- data_dict[index] = max(data_dict[index], int(body))
- # Uncomment the following line to prove threads overlap.
-## sys.stdout.write("%d " % index)
-
- # Start <request_count> requests from each of
- # <client_thread_count> concurrent clients
- ts = []
- for c in range(client_thread_count):
- data_dict[c] = 0
- t = threading.Thread(target=request, args=(c,))
- ts.append(t)
- t.start()
-
- for t in ts:
- t.join()
-
- hitcount = max(data_dict.values())
- expected = 1 + (client_thread_count * request_count)
-
- for e in errors:
- print(e)
- self.assertEqual(hitcount, expected)
-
- def test_3_Redirect(self):
- # Start a new session
- self.getPage('/testStr')
- self.getPage('/iredir', self.cookies)
- self.assertBody("file")
-
- def test_4_File_deletion(self):
- # Start a new session
- self.getPage('/testStr')
- # Delete the session file manually and retry.
- id = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
- path = os.path.join(localDir, "session-" + id)
- os.unlink(path)
- self.getPage('/testStr', self.cookies)
-
- def test_5_Error_paths(self):
- self.getPage('/unknown/page')
- self.assertErrorPage(404, "The path '/unknown/page' was not found.")
-
- # Note: this path is *not* the same as above. The above
- # takes a normal route through the session code; this one
- # skips the session code's before_handler and only calls
- # before_finalize (save) and on_end (close). So the session
- # code has to survive calling save/close without init.
- self.getPage('/restricted', self.cookies, method='POST')
- self.assertErrorPage(405, response_codes[405])
-
- def test_6_regenerate(self):
- self.getPage('/testStr')
- # grab the cookie ID
- id1 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
- self.getPage('/regen')
- self.assertBody('logged in')
- id2 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
- self.assertNotEqual(id1, id2)
-
- self.getPage('/testStr')
- # grab the cookie ID
- id1 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
- self.getPage('/testStr',
- headers=[('Cookie',
- 'session_id=maliciousid; '
- 'expires=Sat, 27 Oct 2017 04:18:28 GMT; Path=/;')])
- id2 = self.cookies[0][1].split(";", 1)[0].split("=", 1)[1]
- self.assertNotEqual(id1, id2)
- self.assertNotEqual(id2, 'maliciousid')
-
- def test_7_session_cookies(self):
- self.getPage('/setsessiontype/ram')
- self.getPage('/clear')
- self.getPage('/session_cookie')
- # grab the cookie ID
- cookie_parts = dict([p.strip().split('=') for p in self.cookies[0][1].split(";")])
- # Assert there is no 'expires' param
- self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
- id1 = cookie_parts['temp']
- self.assertEqual(copykeys(sessions.RamSession.cache), [id1])
-
- # Send another request in the same "browser session".
- self.getPage('/session_cookie', self.cookies)
- cookie_parts = dict([p.strip().split('=') for p in self.cookies[0][1].split(";")])
- # Assert there is no 'expires' param
- self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
- self.assertBody(id1)
- self.assertEqual(copykeys(sessions.RamSession.cache), [id1])
-
- # Simulate a browser close by just not sending the cookies
- self.getPage('/session_cookie')
- # grab the cookie ID
- cookie_parts = dict([p.strip().split('=') for p in self.cookies[0][1].split(";")])
- # Assert there is no 'expires' param
- self.assertEqual(set(cookie_parts.keys()), set(['temp', 'Path']))
- # Assert a new id has been generated...
- id2 = cookie_parts['temp']
- self.assertNotEqual(id1, id2)
- self.assertEqual(set(sessions.RamSession.cache.keys()), set([id1, id2]))
-
- # Wait for the session.timeout on both sessions
- time.sleep(2.5)
- cache = copykeys(sessions.RamSession.cache)
- if cache:
- if cache == [id2]:
- self.fail("The second session did not time out.")
- else:
- self.fail("Unknown session id in cache: %r", cache)
-
-
-import socket
-try:
- import memcache
-
- host, port = '127.0.0.1', 11211
- for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
- socket.SOCK_STREAM):
- af, socktype, proto, canonname, sa = res
- s = None
- try:
- s = socket.socket(af, socktype, proto)
- # See http://groups.google.com/group/cherrypy-users/
- # browse_frm/thread/bbfe5eb39c904fe0
- s.settimeout(1.0)
- s.connect((host, port))
- s.close()
- except socket.error:
- if s:
- s.close()
- raise
- break
-except (ImportError, socket.error):
- class MemcachedSessionTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test(self):
- return self.skip("memcached not reachable ")
-else:
- class MemcachedSessionTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def test_0_Session(self):
- self.getPage('/setsessiontype/memcached')
-
- self.getPage('/testStr')
- self.assertBody('1')
- self.getPage('/testGen', self.cookies)
- self.assertBody('2')
- self.getPage('/testStr', self.cookies)
- self.assertBody('3')
- self.getPage('/length', self.cookies)
- self.assertErrorPage(500)
- self.assertInBody("NotImplementedError")
- self.getPage('/delkey?key=counter', self.cookies)
- self.assertStatus(200)
-
- # Wait for the session.timeout (1 second)
- time.sleep(1.25)
- self.getPage('/')
- self.assertBody('1')
-
- # Test session __contains__
- self.getPage('/keyin?key=counter', self.cookies)
- self.assertBody("True")
-
- # Test session delete
- self.getPage('/delete', self.cookies)
- self.assertBody("done")
-
- def test_1_Concurrency(self):
- client_thread_count = 5
- request_count = 30
-
- # Get initial cookie
- self.getPage("/")
- self.assertBody("1")
- cookies = self.cookies
-
- data_dict = {}
-
- def request(index):
- for i in range(request_count):
- self.getPage("/", cookies)
- # Uncomment the following line to prove threads overlap.
-## sys.stdout.write("%d " % index)
- if not self.body.isdigit():
- self.fail(self.body)
- data_dict[index] = v = int(self.body)
-
- # Start <request_count> concurrent requests from
- # each of <client_thread_count> clients
- ts = []
- for c in range(client_thread_count):
- data_dict[c] = 0
- t = threading.Thread(target=request, args=(c,))
- ts.append(t)
- t.start()
-
- for t in ts:
- t.join()
-
- hitcount = max(data_dict.values())
- expected = 1 + (client_thread_count * request_count)
- self.assertEqual(hitcount, expected)
-
- def test_3_Redirect(self):
- # Start a new session
- self.getPage('/testStr')
- self.getPage('/iredir', self.cookies)
- self.assertBody("memcached")
-
- def test_5_Error_paths(self):
- self.getPage('/unknown/page')
- self.assertErrorPage(404, "The path '/unknown/page' was not found.")
-
- # Note: this path is *not* the same as above. The above
- # takes a normal route through the session code; this one
- # skips the session code's before_handler and only calls
- # before_finalize (save) and on_end (close). So the session
- # code has to survive calling save/close without init.
- self.getPage('/restricted', self.cookies, method='POST')
- self.assertErrorPage(405, response_codes[405])
-
diff --git a/cherrypy/test/test_sessionauthenticate.py b/cherrypy/test/test_sessionauthenticate.py
deleted file mode 100755
index ab1fe51..0000000
--- a/cherrypy/test/test_sessionauthenticate.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-
-class SessionAuthenticateTest(helper.CPWebCase):
-
- def setup_server():
-
- def check(username, password):
- # Dummy check_username_and_password function
- if username != 'test' or password != 'password':
- return 'Wrong login/password'
-
- def augment_params():
- # A simple tool to add some things to request.params
- # This is to check to make sure that session_auth can handle request
- # params (ticket #780)
- cherrypy.request.params["test"] = "test"
-
- cherrypy.tools.augment_params = cherrypy.Tool('before_handler',
- augment_params, None, priority=30)
-
- class Test:
-
- _cp_config = {'tools.sessions.on': True,
- 'tools.session_auth.on': True,
- 'tools.session_auth.check_username_and_password': check,
- 'tools.augment_params.on': True,
- }
-
- def index(self, **kwargs):
- return "Hi %s, you are logged in" % cherrypy.request.login
- index.exposed = True
-
- cherrypy.tree.mount(Test())
- setup_server = staticmethod(setup_server)
-
-
- def testSessionAuthenticate(self):
- # request a page and check for login form
- self.getPage('/')
- self.assertInBody('<form method="post" action="do_login">')
-
- # setup credentials
- login_body = 'username=test&password=password&from_page=/'
-
- # attempt a login
- self.getPage('/do_login', method='POST', body=login_body)
- self.assertStatus((302, 303))
-
- # get the page now that we are logged in
- self.getPage('/', self.cookies)
- self.assertBody('Hi test, you are logged in')
-
- # do a logout
- self.getPage('/do_logout', self.cookies, method='POST')
- self.assertStatus((302, 303))
-
- # verify we are logged out
- self.getPage('/', self.cookies)
- self.assertInBody('<form method="post" action="do_login">')
-
diff --git a/cherrypy/test/test_states.py b/cherrypy/test/test_states.py
deleted file mode 100755
index 0f97337..0000000
--- a/cherrypy/test/test_states.py
+++ /dev/null
@@ -1,436 +0,0 @@
-from cherrypy._cpcompat import BadStatusLine, ntob
-import os
-import sys
-import threading
-import time
-
-import cherrypy
-engine = cherrypy.engine
-thisdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-
-class Dependency:
-
- def __init__(self, bus):
- self.bus = bus
- self.running = False
- self.startcount = 0
- self.gracecount = 0
- self.threads = {}
-
- def subscribe(self):
- self.bus.subscribe('start', self.start)
- self.bus.subscribe('stop', self.stop)
- self.bus.subscribe('graceful', self.graceful)
- self.bus.subscribe('start_thread', self.startthread)
- self.bus.subscribe('stop_thread', self.stopthread)
-
- def start(self):
- self.running = True
- self.startcount += 1
-
- def stop(self):
- self.running = False
-
- def graceful(self):
- self.gracecount += 1
-
- def startthread(self, thread_id):
- self.threads[thread_id] = None
-
- def stopthread(self, thread_id):
- del self.threads[thread_id]
-
-db_connection = Dependency(engine)
-
-def setup_server():
- class Root:
- def index(self):
- return "Hello World"
- index.exposed = True
-
- def ctrlc(self):
- raise KeyboardInterrupt()
- ctrlc.exposed = True
-
- def graceful(self):
- engine.graceful()
- return "app was (gracefully) restarted succesfully"
- graceful.exposed = True
-
- def block_explicit(self):
- while True:
- if cherrypy.response.timed_out:
- cherrypy.response.timed_out = False
- return "broken!"
- time.sleep(0.01)
- block_explicit.exposed = True
-
- def block_implicit(self):
- time.sleep(0.5)
- return "response.timeout = %s" % cherrypy.response.timeout
- block_implicit.exposed = True
-
- cherrypy.tree.mount(Root())
- cherrypy.config.update({
- 'environment': 'test_suite',
- 'engine.deadlock_poll_freq': 0.1,
- })
-
- db_connection.subscribe()
-
-
-
-# ------------ Enough helpers. Time for real live test cases. ------------ #
-
-
-from cherrypy.test import helper
-
-class ServerStateTests(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
-
- def setUp(self):
- cherrypy.server.socket_timeout = 0.1
-
- def test_0_NormalStateFlow(self):
- engine.stop()
- # Our db_connection should not be running
- self.assertEqual(db_connection.running, False)
- self.assertEqual(db_connection.startcount, 1)
- self.assertEqual(len(db_connection.threads), 0)
-
- # Test server start
- engine.start()
- self.assertEqual(engine.state, engine.states.STARTED)
-
- host = cherrypy.server.socket_host
- port = cherrypy.server.socket_port
- self.assertRaises(IOError, cherrypy._cpserver.check_port, host, port)
-
- # The db_connection should be running now
- self.assertEqual(db_connection.running, True)
- self.assertEqual(db_connection.startcount, 2)
- self.assertEqual(len(db_connection.threads), 0)
-
- self.getPage("/")
- self.assertBody("Hello World")
- self.assertEqual(len(db_connection.threads), 1)
-
- # Test engine stop. This will also stop the HTTP server.
- engine.stop()
- self.assertEqual(engine.state, engine.states.STOPPED)
-
- # Verify that our custom stop function was called
- self.assertEqual(db_connection.running, False)
- self.assertEqual(len(db_connection.threads), 0)
-
- # Block the main thread now and verify that exit() works.
- def exittest():
- self.getPage("/")
- self.assertBody("Hello World")
- engine.exit()
- cherrypy.server.start()
- engine.start_with_callback(exittest)
- engine.block()
- self.assertEqual(engine.state, engine.states.EXITING)
-
- def test_1_Restart(self):
- cherrypy.server.start()
- engine.start()
-
- # The db_connection should be running now
- self.assertEqual(db_connection.running, True)
- grace = db_connection.gracecount
-
- self.getPage("/")
- self.assertBody("Hello World")
- self.assertEqual(len(db_connection.threads), 1)
-
- # Test server restart from this thread
- engine.graceful()
- self.assertEqual(engine.state, engine.states.STARTED)
- self.getPage("/")
- self.assertBody("Hello World")
- self.assertEqual(db_connection.running, True)
- self.assertEqual(db_connection.gracecount, grace + 1)
- self.assertEqual(len(db_connection.threads), 1)
-
- # Test server restart from inside a page handler
- self.getPage("/graceful")
- self.assertEqual(engine.state, engine.states.STARTED)
- self.assertBody("app was (gracefully) restarted succesfully")
- self.assertEqual(db_connection.running, True)
- self.assertEqual(db_connection.gracecount, grace + 2)
- # Since we are requesting synchronously, is only one thread used?
- # Note that the "/graceful" request has been flushed.
- self.assertEqual(len(db_connection.threads), 0)
-
- engine.stop()
- self.assertEqual(engine.state, engine.states.STOPPED)
- self.assertEqual(db_connection.running, False)
- self.assertEqual(len(db_connection.threads), 0)
-
- def test_2_KeyboardInterrupt(self):
- # Raise a keyboard interrupt in the HTTP server's main thread.
- # We must start the server in this, the main thread
- engine.start()
- cherrypy.server.start()
-
- self.persistent = True
- try:
- # Make the first request and assert there's no "Connection: close".
- self.getPage("/")
- self.assertStatus('200 OK')
- self.assertBody("Hello World")
- self.assertNoHeader("Connection")
-
- cherrypy.server.httpserver.interrupt = KeyboardInterrupt
- engine.block()
-
- self.assertEqual(db_connection.running, False)
- self.assertEqual(len(db_connection.threads), 0)
- self.assertEqual(engine.state, engine.states.EXITING)
- finally:
- self.persistent = False
-
- # Raise a keyboard interrupt in a page handler; on multithreaded
- # servers, this should occur in one of the worker threads.
- # This should raise a BadStatusLine error, since the worker
- # thread will just die without writing a response.
- engine.start()
- cherrypy.server.start()
-
- try:
- self.getPage("/ctrlc")
- except BadStatusLine:
- pass
- else:
- print(self.body)
- self.fail("AssertionError: BadStatusLine not raised")
-
- engine.block()
- self.assertEqual(db_connection.running, False)
- self.assertEqual(len(db_connection.threads), 0)
-
- def test_3_Deadlocks(self):
- cherrypy.config.update({'response.timeout': 0.2})
-
- engine.start()
- cherrypy.server.start()
- try:
- self.assertNotEqual(engine.timeout_monitor.thread, None)
-
- # Request a "normal" page.
- self.assertEqual(engine.timeout_monitor.servings, [])
- self.getPage("/")
- self.assertBody("Hello World")
- # request.close is called async.
- while engine.timeout_monitor.servings:
- sys.stdout.write(".")
- time.sleep(0.01)
-
- # Request a page that explicitly checks itself for deadlock.
- # The deadlock_timeout should be 2 secs.
- self.getPage("/block_explicit")
- self.assertBody("broken!")
-
- # Request a page that implicitly breaks deadlock.
- # If we deadlock, we want to touch as little code as possible,
- # so we won't even call handle_error, just bail ASAP.
- self.getPage("/block_implicit")
- self.assertStatus(500)
- self.assertInBody("raise cherrypy.TimeoutError()")
- finally:
- engine.exit()
-
- def test_4_Autoreload(self):
- # Start the demo script in a new process
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
- p.write_conf(
- extra='test_case_name: "test_4_Autoreload"')
- p.start(imports='cherrypy.test._test_states_demo')
- try:
- self.getPage("/start")
- start = float(self.body)
-
- # Give the autoreloader time to cache the file time.
- time.sleep(2)
-
- # Touch the file
- os.utime(os.path.join(thisdir, "_test_states_demo.py"), None)
-
- # Give the autoreloader time to re-exec the process
- time.sleep(2)
- host = cherrypy.server.socket_host
- port = cherrypy.server.socket_port
- cherrypy._cpserver.wait_for_occupied_port(host, port)
-
- self.getPage("/start")
- self.assert_(float(self.body) > start)
- finally:
- # Shut down the spawned process
- self.getPage("/exit")
- p.join()
-
- def test_5_Start_Error(self):
- # If a process errors during start, it should stop the engine
- # and exit with a non-zero exit code.
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
- wait=True)
- p.write_conf(
- extra="""starterror: True
-test_case_name: "test_5_Start_Error"
-"""
- )
- p.start(imports='cherrypy.test._test_states_demo')
- if p.exit_code == 0:
- self.fail("Process failed to return nonzero exit code.")
-
-
-class PluginTests(helper.CPWebCase):
- def test_daemonize(self):
- if os.name not in ['posix']:
- return self.skip("skipped (not on posix) ")
- self.HOST = '127.0.0.1'
- self.PORT = 8081
- # Spawn the process and wait, when this returns, the original process
- # is finished. If it daemonized properly, we should still be able
- # to access pages.
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
- wait=True, daemonize=True,
- socket_host='127.0.0.1',
- socket_port=8081)
- p.write_conf(
- extra='test_case_name: "test_daemonize"')
- p.start(imports='cherrypy.test._test_states_demo')
- try:
- # Just get the pid of the daemonization process.
- self.getPage("/pid")
- self.assertStatus(200)
- page_pid = int(self.body)
- self.assertEqual(page_pid, p.get_pid())
- finally:
- # Shut down the spawned process
- self.getPage("/exit")
- p.join()
-
- # Wait until here to test the exit code because we want to ensure
- # that we wait for the daemon to finish running before we fail.
- if p.exit_code != 0:
- self.fail("Daemonized parent process failed to exit cleanly.")
-
-
-class SignalHandlingTests(helper.CPWebCase):
- def test_SIGHUP_tty(self):
- # When not daemonized, SIGHUP should shut down the server.
- try:
- from signal import SIGHUP
- except ImportError:
- return self.skip("skipped (no SIGHUP) ")
-
- # Spawn the process.
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
- p.write_conf(
- extra='test_case_name: "test_SIGHUP_tty"')
- p.start(imports='cherrypy.test._test_states_demo')
- # Send a SIGHUP
- os.kill(p.get_pid(), SIGHUP)
- # This might hang if things aren't working right, but meh.
- p.join()
-
- def test_SIGHUP_daemonized(self):
- # When daemonized, SIGHUP should restart the server.
- try:
- from signal import SIGHUP
- except ImportError:
- return self.skip("skipped (no SIGHUP) ")
-
- if os.name not in ['posix']:
- return self.skip("skipped (not on posix) ")
-
- # Spawn the process and wait, when this returns, the original process
- # is finished. If it daemonized properly, we should still be able
- # to access pages.
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
- wait=True, daemonize=True)
- p.write_conf(
- extra='test_case_name: "test_SIGHUP_daemonized"')
- p.start(imports='cherrypy.test._test_states_demo')
-
- pid = p.get_pid()
- try:
- # Send a SIGHUP
- os.kill(pid, SIGHUP)
- # Give the server some time to restart
- time.sleep(2)
- self.getPage("/pid")
- self.assertStatus(200)
- new_pid = int(self.body)
- self.assertNotEqual(new_pid, pid)
- finally:
- # Shut down the spawned process
- self.getPage("/exit")
- p.join()
-
- def test_SIGTERM(self):
- # SIGTERM should shut down the server whether daemonized or not.
- try:
- from signal import SIGTERM
- except ImportError:
- return self.skip("skipped (no SIGTERM) ")
-
- try:
- from os import kill
- except ImportError:
- return self.skip("skipped (no os.kill) ")
-
- # Spawn a normal, undaemonized process.
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
- p.write_conf(
- extra='test_case_name: "test_SIGTERM"')
- p.start(imports='cherrypy.test._test_states_demo')
- # Send a SIGTERM
- os.kill(p.get_pid(), SIGTERM)
- # This might hang if things aren't working right, but meh.
- p.join()
-
- if os.name in ['posix']:
- # Spawn a daemonized process and test again.
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'),
- wait=True, daemonize=True)
- p.write_conf(
- extra='test_case_name: "test_SIGTERM_2"')
- p.start(imports='cherrypy.test._test_states_demo')
- # Send a SIGTERM
- os.kill(p.get_pid(), SIGTERM)
- # This might hang if things aren't working right, but meh.
- p.join()
-
- def test_signal_handler_unsubscribe(self):
- try:
- from signal import SIGTERM
- except ImportError:
- return self.skip("skipped (no SIGTERM) ")
-
- try:
- from os import kill
- except ImportError:
- return self.skip("skipped (no os.kill) ")
-
- # Spawn a normal, undaemonized process.
- p = helper.CPProcess(ssl=(self.scheme.lower()=='https'))
- p.write_conf(
- extra="""unsubsig: True
-test_case_name: "test_signal_handler_unsubscribe"
-""")
- p.start(imports='cherrypy.test._test_states_demo')
- # Send a SIGTERM
- os.kill(p.get_pid(), SIGTERM)
- # This might hang if things aren't working right, but meh.
- p.join()
-
- # Assert the old handler ran.
- target_line = open(p.error_log, 'rb').readlines()[-10]
- if not ntob("I am an old SIGTERM handler.") in target_line:
- self.fail("Old SIGTERM handler did not run.\n%r" % target_line)
-
diff --git a/cherrypy/test/test_static.py b/cherrypy/test/test_static.py
deleted file mode 100755
index 871420b..0000000
--- a/cherrypy/test/test_static.py
+++ /dev/null
@@ -1,300 +0,0 @@
-from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, ntob
-from cherrypy._cpcompat import BytesIO
-
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-has_space_filepath = os.path.join(curdir, 'static', 'has space.html')
-bigfile_filepath = os.path.join(curdir, "static", "bigfile.log")
-BIGFILE_SIZE = 1024 * 1024
-import threading
-
-import cherrypy
-from cherrypy.lib import static
-from cherrypy.test import helper
-
-
-class StaticTest(helper.CPWebCase):
-
- def setup_server():
- if not os.path.exists(has_space_filepath):
- open(has_space_filepath, 'wb').write(ntob('Hello, world\r\n'))
- if not os.path.exists(bigfile_filepath):
- open(bigfile_filepath, 'wb').write(ntob("x" * BIGFILE_SIZE))
-
- class Root:
-
- def bigfile(self):
- from cherrypy.lib import static
- self.f = static.serve_file(bigfile_filepath)
- return self.f
- bigfile.exposed = True
- bigfile._cp_config = {'response.stream': True}
-
- def tell(self):
- if self.f.input.closed:
- return ''
- return repr(self.f.input.tell()).rstrip('L')
- tell.exposed = True
-
- def fileobj(self):
- f = open(os.path.join(curdir, 'style.css'), 'rb')
- return static.serve_fileobj(f, content_type='text/css')
- fileobj.exposed = True
-
- def bytesio(self):
- f = BytesIO(ntob('Fee\nfie\nfo\nfum'))
- return static.serve_fileobj(f, content_type='text/plain')
- bytesio.exposed = True
-
- class Static:
-
- def index(self):
- return 'You want the Baron? You can have the Baron!'
- index.exposed = True
-
- def dynamic(self):
- return "This is a DYNAMIC page"
- dynamic.exposed = True
-
-
- root = Root()
- root.static = Static()
-
- rootconf = {
- '/static': {
- 'tools.staticdir.on': True,
- 'tools.staticdir.dir': 'static',
- 'tools.staticdir.root': curdir,
- },
- '/style.css': {
- 'tools.staticfile.on': True,
- 'tools.staticfile.filename': os.path.join(curdir, 'style.css'),
- },
- '/docroot': {
- 'tools.staticdir.on': True,
- 'tools.staticdir.root': curdir,
- 'tools.staticdir.dir': 'static',
- 'tools.staticdir.index': 'index.html',
- },
- '/error': {
- 'tools.staticdir.on': True,
- 'request.show_tracebacks': True,
- },
- }
- rootApp = cherrypy.Application(root)
- rootApp.merge(rootconf)
-
- test_app_conf = {
- '/test': {
- 'tools.staticdir.index': 'index.html',
- 'tools.staticdir.on': True,
- 'tools.staticdir.root': curdir,
- 'tools.staticdir.dir': 'static',
- },
- }
- testApp = cherrypy.Application(Static())
- testApp.merge(test_app_conf)
-
- vhost = cherrypy._cpwsgi.VirtualHost(rootApp, {'virt.net': testApp})
- cherrypy.tree.graft(vhost)
- setup_server = staticmethod(setup_server)
-
-
- def teardown_server():
- for f in (has_space_filepath, bigfile_filepath):
- if os.path.exists(f):
- try:
- os.unlink(f)
- except:
- pass
- teardown_server = staticmethod(teardown_server)
-
-
- def testStatic(self):
- self.getPage("/static/index.html")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html')
- self.assertBody('Hello, world\r\n')
-
- # Using a staticdir.root value in a subdir...
- self.getPage("/docroot/index.html")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html')
- self.assertBody('Hello, world\r\n')
-
- # Check a filename with spaces in it
- self.getPage("/static/has%20space.html")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html')
- self.assertBody('Hello, world\r\n')
-
- self.getPage("/style.css")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/css')
- # Note: The body should be exactly 'Dummy stylesheet\n', but
- # unfortunately some tools such as WinZip sometimes turn \n
- # into \r\n on Windows when extracting the CherryPy tarball so
- # we just check the content
- self.assertMatchesBody('^Dummy stylesheet')
-
- def test_fallthrough(self):
- # Test that NotFound will then try dynamic handlers (see [878]).
- self.getPage("/static/dynamic")
- self.assertBody("This is a DYNAMIC page")
-
- # Check a directory via fall-through to dynamic handler.
- self.getPage("/static/")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html;charset=utf-8')
- self.assertBody('You want the Baron? You can have the Baron!')
-
- def test_index(self):
- # Check a directory via "staticdir.index".
- self.getPage("/docroot/")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/html')
- self.assertBody('Hello, world\r\n')
- # The same page should be returned even if redirected.
- self.getPage("/docroot")
- self.assertStatus(301)
- self.assertHeader('Location', '%s/docroot/' % self.base())
- self.assertMatchesBody("This resource .* <a href='%s/docroot/'>"
- "%s/docroot/</a>." % (self.base(), self.base()))
-
- def test_config_errors(self):
- # Check that we get an error if no .file or .dir
- self.getPage("/error/thing.html")
- self.assertErrorPage(500)
- self.assertMatchesBody(ntob("TypeError: staticdir\(\) takes at least 2 "
- "(positional )?arguments \(0 given\)"))
-
- def test_security(self):
- # Test up-level security
- self.getPage("/static/../../test/style.css")
- self.assertStatus((400, 403))
-
- def test_modif(self):
- # Test modified-since on a reasonably-large file
- self.getPage("/static/dirback.jpg")
- self.assertStatus("200 OK")
- lastmod = ""
- for k, v in self.headers:
- if k == 'Last-Modified':
- lastmod = v
- ims = ("If-Modified-Since", lastmod)
- self.getPage("/static/dirback.jpg", headers=[ims])
- self.assertStatus(304)
- self.assertNoHeader("Content-Type")
- self.assertNoHeader("Content-Length")
- self.assertNoHeader("Content-Disposition")
- self.assertBody("")
-
- def test_755_vhost(self):
- self.getPage("/test/", [('Host', 'virt.net')])
- self.assertStatus(200)
- self.getPage("/test", [('Host', 'virt.net')])
- self.assertStatus(301)
- self.assertHeader('Location', self.scheme + '://virt.net/test/')
-
- def test_serve_fileobj(self):
- self.getPage("/fileobj")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/css;charset=utf-8')
- self.assertMatchesBody('^Dummy stylesheet')
-
- def test_serve_bytesio(self):
- self.getPage("/bytesio")
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/plain;charset=utf-8')
- self.assertHeader('Content-Length', 14)
- self.assertMatchesBody('Fee\nfie\nfo\nfum')
-
- def test_file_stream(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- # Make an initial request
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/bigfile", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 200)
-
- body = ntob('')
- remaining = BIGFILE_SIZE
- while remaining > 0:
- data = response.fp.read(65536)
- if not data:
- break
- body += data
- remaining -= len(data)
-
- if self.scheme == "https":
- newconn = HTTPSConnection
- else:
- newconn = HTTPConnection
- s, h, b = helper.webtest.openURL(
- ntob("/tell"), headers=[], host=self.HOST, port=self.PORT,
- http_conn=newconn)
- if not b:
- # The file was closed on the server.
- tell_position = BIGFILE_SIZE
- else:
- tell_position = int(b)
-
- expected = len(body)
- if tell_position >= BIGFILE_SIZE:
- # We can't exactly control how much content the server asks for.
- # Fudge it by only checking the first half of the reads.
- if expected < (BIGFILE_SIZE / 2):
- self.fail(
- "The file should have advanced to position %r, but has "
- "already advanced to the end of the file. It may not be "
- "streamed as intended, or at the wrong chunk size (64k)" %
- expected)
- elif tell_position < expected:
- self.fail(
- "The file should have advanced to position %r, but has "
- "only advanced to position %r. It may not be streamed "
- "as intended, or at the wrong chunk size (65536)" %
- (expected, tell_position))
-
- if body != ntob("x" * BIGFILE_SIZE):
- self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
- (BIGFILE_SIZE, body[:50], len(body)))
- conn.close()
-
- def test_file_stream_deadlock(self):
- if cherrypy.server.protocol_version != "HTTP/1.1":
- return self.skip()
-
- self.PROTOCOL = "HTTP/1.1"
-
- # Make an initial request but abort early.
- self.persistent = True
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/bigfile", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- response = conn.response_class(conn.sock, method="GET")
- response.begin()
- self.assertEqual(response.status, 200)
- body = response.fp.read(65536)
- if body != ntob("x" * len(body)):
- self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
- (65536, body[:50], len(body)))
- response.close()
- conn.close()
-
- # Make a second request, which should fetch the whole file.
- self.persistent = False
- self.getPage("/bigfile")
- if self.body != ntob("x" * BIGFILE_SIZE):
- self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." %
- (BIGFILE_SIZE, self.body[:50], len(body)))
-
diff --git a/cherrypy/test/test_tools.py b/cherrypy/test/test_tools.py
deleted file mode 100755
index bc8579f..0000000
--- a/cherrypy/test/test_tools.py
+++ /dev/null
@@ -1,393 +0,0 @@
-"""Test the various means of instantiating and invoking tools."""
-
-import gzip
-import sys
-from cherrypy._cpcompat import BytesIO, copyitems, itervalues, IncompleteRead, ntob, ntou, xrange
-import time
-timeout = 0.2
-import types
-
-import cherrypy
-from cherrypy import tools
-
-
-europoundUnicode = ntou('\x80\xa3')
-
-
-# Client-side code #
-
-from cherrypy.test import helper
-
-
-class ToolTests(helper.CPWebCase):
- def setup_server():
-
- # Put check_access in a custom toolbox with its own namespace
- myauthtools = cherrypy._cptools.Toolbox("myauth")
-
- def check_access(default=False):
- if not getattr(cherrypy.request, "userid", default):
- raise cherrypy.HTTPError(401)
- myauthtools.check_access = cherrypy.Tool('before_request_body', check_access)
-
- def numerify():
- def number_it(body):
- for chunk in body:
- for k, v in cherrypy.request.numerify_map:
- chunk = chunk.replace(k, v)
- yield chunk
- cherrypy.response.body = number_it(cherrypy.response.body)
-
- class NumTool(cherrypy.Tool):
- def _setup(self):
- def makemap():
- m = self._merged_args().get("map", {})
- cherrypy.request.numerify_map = copyitems(m)
- cherrypy.request.hooks.attach('on_start_resource', makemap)
-
- def critical():
- cherrypy.request.error_response = cherrypy.HTTPError(502).set_response
- critical.failsafe = True
-
- cherrypy.request.hooks.attach('on_start_resource', critical)
- cherrypy.request.hooks.attach(self._point, self.callable)
-
- tools.numerify = NumTool('before_finalize', numerify)
-
- # It's not mandatory to inherit from cherrypy.Tool.
- class NadsatTool:
-
- def __init__(self):
- self.ended = {}
- self._name = "nadsat"
-
- def nadsat(self):
- def nadsat_it_up(body):
- for chunk in body:
- chunk = chunk.replace(ntob("good"), ntob("horrorshow"))
- chunk = chunk.replace(ntob("piece"), ntob("lomtick"))
- yield chunk
- cherrypy.response.body = nadsat_it_up(cherrypy.response.body)
- nadsat.priority = 0
-
- def cleanup(self):
- # This runs after the request has been completely written out.
- cherrypy.response.body = [ntob("razdrez")]
- id = cherrypy.request.params.get("id")
- if id:
- self.ended[id] = True
- cleanup.failsafe = True
-
- def _setup(self):
- cherrypy.request.hooks.attach('before_finalize', self.nadsat)
- cherrypy.request.hooks.attach('on_end_request', self.cleanup)
- tools.nadsat = NadsatTool()
-
- def pipe_body():
- cherrypy.request.process_request_body = False
- clen = int(cherrypy.request.headers['Content-Length'])
- cherrypy.request.body = cherrypy.request.rfile.read(clen)
-
- # Assert that we can use a callable object instead of a function.
- class Rotator(object):
- def __call__(self, scale):
- r = cherrypy.response
- r.collapse_body()
- r.body = [chr((ord(x) + scale) % 256) for x in r.body[0]]
- cherrypy.tools.rotator = cherrypy.Tool('before_finalize', Rotator())
-
- def stream_handler(next_handler, *args, **kwargs):
- cherrypy.response.output = o = BytesIO()
- try:
- response = next_handler(*args, **kwargs)
- # Ignore the response and return our accumulated output instead.
- return o.getvalue()
- finally:
- o.close()
- cherrypy.tools.streamer = cherrypy._cptools.HandlerWrapperTool(stream_handler)
-
- class Root:
- def index(self):
- return "Howdy earth!"
- index.exposed = True
-
- def tarfile(self):
- cherrypy.response.output.write(ntob('I am '))
- cherrypy.response.output.write(ntob('a tarfile'))
- tarfile.exposed = True
- tarfile._cp_config = {'tools.streamer.on': True}
-
- def euro(self):
- hooks = list(cherrypy.request.hooks['before_finalize'])
- hooks.sort()
- cbnames = [x.callback.__name__ for x in hooks]
- assert cbnames == ['gzip'], cbnames
- priorities = [x.priority for x in hooks]
- assert priorities == [80], priorities
- yield ntou("Hello,")
- yield ntou("world")
- yield europoundUnicode
- euro.exposed = True
-
- # Bare hooks
- def pipe(self):
- return cherrypy.request.body
- pipe.exposed = True
- pipe._cp_config = {'hooks.before_request_body': pipe_body}
-
- # Multiple decorators; include kwargs just for fun.
- # Note that rotator must run before gzip.
- def decorated_euro(self, *vpath):
- yield ntou("Hello,")
- yield ntou("world")
- yield europoundUnicode
- decorated_euro.exposed = True
- decorated_euro = tools.gzip(compress_level=6)(decorated_euro)
- decorated_euro = tools.rotator(scale=3)(decorated_euro)
-
- root = Root()
-
-
- class TestType(type):
- """Metaclass which automatically exposes all functions in each subclass,
- and adds an instance of the subclass as an attribute of root.
- """
- def __init__(cls, name, bases, dct):
- type.__init__(cls, name, bases, dct)
- for value in itervalues(dct):
- if isinstance(value, types.FunctionType):
- value.exposed = True
- setattr(root, name.lower(), cls())
- class Test(object):
- __metaclass__ = TestType
-
-
- # METHOD ONE:
- # Declare Tools in _cp_config
- class Demo(Test):
-
- _cp_config = {"tools.nadsat.on": True}
-
- def index(self, id=None):
- return "A good piece of cherry pie"
-
- def ended(self, id):
- return repr(tools.nadsat.ended[id])
-
- def err(self, id=None):
- raise ValueError()
-
- def errinstream(self, id=None):
- yield "nonconfidential"
- raise ValueError()
- yield "confidential"
-
- # METHOD TWO: decorator using Tool()
- # We support Python 2.3, but the @-deco syntax would look like this:
- # @tools.check_access()
- def restricted(self):
- return "Welcome!"
- restricted = myauthtools.check_access()(restricted)
- userid = restricted
-
- def err_in_onstart(self):
- return "success!"
-
- def stream(self, id=None):
- for x in xrange(100000000):
- yield str(x)
- stream._cp_config = {'response.stream': True}
-
-
- conf = {
- # METHOD THREE:
- # Declare Tools in detached config
- '/demo': {
- 'tools.numerify.on': True,
- 'tools.numerify.map': {ntob("pie"): ntob("3.14159")},
- },
- '/demo/restricted': {
- 'request.show_tracebacks': False,
- },
- '/demo/userid': {
- 'request.show_tracebacks': False,
- 'myauth.check_access.default': True,
- },
- '/demo/errinstream': {
- 'response.stream': True,
- },
- '/demo/err_in_onstart': {
- # Because this isn't a dict, on_start_resource will error.
- 'tools.numerify.map': "pie->3.14159"
- },
- # Combined tools
- '/euro': {
- 'tools.gzip.on': True,
- 'tools.encode.on': True,
- },
- # Priority specified in config
- '/decorated_euro/subpath': {
- 'tools.gzip.priority': 10,
- },
- # Handler wrappers
- '/tarfile': {'tools.streamer.on': True}
- }
- app = cherrypy.tree.mount(root, config=conf)
- app.request_class.namespaces['myauth'] = myauthtools
-
- if sys.version_info >= (2, 5):
- from cherrypy.test import _test_decorators
- root.tooldecs = _test_decorators.ToolExamples()
- setup_server = staticmethod(setup_server)
-
- def testHookErrors(self):
- self.getPage("/demo/?id=1")
- # If body is "razdrez", then on_end_request is being called too early.
- self.assertBody("A horrorshow lomtick of cherry 3.14159")
- # If this fails, then on_end_request isn't being called at all.
- time.sleep(0.1)
- self.getPage("/demo/ended/1")
- self.assertBody("True")
-
- valerr = '\n raise ValueError()\nValueError'
- self.getPage("/demo/err?id=3")
- # If body is "razdrez", then on_end_request is being called too early.
- self.assertErrorPage(502, pattern=valerr)
- # If this fails, then on_end_request isn't being called at all.
- time.sleep(0.1)
- self.getPage("/demo/ended/3")
- self.assertBody("True")
-
- # If body is "razdrez", then on_end_request is being called too early.
- if (cherrypy.server.protocol_version == "HTTP/1.0" or
- getattr(cherrypy.server, "using_apache", False)):
- self.getPage("/demo/errinstream?id=5")
- # Because this error is raised after the response body has
- # started, the status should not change to an error status.
- self.assertStatus("200 OK")
- self.assertBody("nonconfidential")
- else:
- # Because this error is raised after the response body has
- # started, and because it's chunked output, an error is raised by
- # the HTTP client when it encounters incomplete output.
- self.assertRaises((ValueError, IncompleteRead), self.getPage,
- "/demo/errinstream?id=5")
- # If this fails, then on_end_request isn't being called at all.
- time.sleep(0.1)
- self.getPage("/demo/ended/5")
- self.assertBody("True")
-
- # Test the "__call__" technique (compile-time decorator).
- self.getPage("/demo/restricted")
- self.assertErrorPage(401)
-
- # Test compile-time decorator with kwargs from config.
- self.getPage("/demo/userid")
- self.assertBody("Welcome!")
-
- def testEndRequestOnDrop(self):
- old_timeout = None
- try:
- httpserver = cherrypy.server.httpserver
- old_timeout = httpserver.timeout
- except (AttributeError, IndexError):
- return self.skip()
-
- try:
- httpserver.timeout = timeout
-
- # Test that on_end_request is called even if the client drops.
- self.persistent = True
- try:
- conn = self.HTTP_CONN
- conn.putrequest("GET", "/demo/stream?id=9", skip_host=True)
- conn.putheader("Host", self.HOST)
- conn.endheaders()
- # Skip the rest of the request and close the conn. This will
- # cause the server's active socket to error, which *should*
- # result in the request being aborted, and request.close being
- # called all the way up the stack (including WSGI middleware),
- # eventually calling our on_end_request hook.
- finally:
- self.persistent = False
- time.sleep(timeout * 2)
- # Test that the on_end_request hook was called.
- self.getPage("/demo/ended/9")
- self.assertBody("True")
- finally:
- if old_timeout is not None:
- httpserver.timeout = old_timeout
-
- def testGuaranteedHooks(self):
- # The 'critical' on_start_resource hook is 'failsafe' (guaranteed
- # to run even if there are failures in other on_start methods).
- # This is NOT true of the other hooks.
- # Here, we have set up a failure in NumerifyTool.numerify_map,
- # but our 'critical' hook should run and set the error to 502.
- self.getPage("/demo/err_in_onstart")
- self.assertErrorPage(502)
- self.assertInBody("AttributeError: 'str' object has no attribute 'items'")
-
- def testCombinedTools(self):
- expectedResult = (ntou("Hello,world") + europoundUnicode).encode('utf-8')
- zbuf = BytesIO()
- zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=9)
- zfile.write(expectedResult)
- zfile.close()
-
- self.getPage("/euro", headers=[("Accept-Encoding", "gzip"),
- ("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7")])
- self.assertInBody(zbuf.getvalue()[:3])
-
- zbuf = BytesIO()
- zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=6)
- zfile.write(expectedResult)
- zfile.close()
-
- self.getPage("/decorated_euro", headers=[("Accept-Encoding", "gzip")])
- self.assertInBody(zbuf.getvalue()[:3])
-
- # This returns a different value because gzip's priority was
- # lowered in conf, allowing the rotator to run after gzip.
- # Of course, we don't want breakage in production apps,
- # but it proves the priority was changed.
- self.getPage("/decorated_euro/subpath",
- headers=[("Accept-Encoding", "gzip")])
- self.assertInBody(''.join([chr((ord(x) + 3) % 256) for x in zbuf.getvalue()]))
-
- def testBareHooks(self):
- content = "bit of a pain in me gulliver"
- self.getPage("/pipe",
- headers=[("Content-Length", str(len(content))),
- ("Content-Type", "text/plain")],
- method="POST", body=content)
- self.assertBody(content)
-
- def testHandlerWrapperTool(self):
- self.getPage("/tarfile")
- self.assertBody("I am a tarfile")
-
- def testToolWithConfig(self):
- if not sys.version_info >= (2, 5):
- return self.skip("skipped (Python 2.5+ only)")
-
- self.getPage('/tooldecs/blah')
- self.assertHeader('Content-Type', 'application/data')
-
- def testWarnToolOn(self):
- # get
- try:
- numon = cherrypy.tools.numerify.on
- except AttributeError:
- pass
- else:
- raise AssertionError("Tool.on did not error as it should have.")
-
- # set
- try:
- cherrypy.tools.numerify.on = True
- except AttributeError:
- pass
- else:
- raise AssertionError("Tool.on did not error as it should have.")
-
diff --git a/cherrypy/test/test_tutorials.py b/cherrypy/test/test_tutorials.py
deleted file mode 100755
index aab2786..0000000
--- a/cherrypy/test/test_tutorials.py
+++ /dev/null
@@ -1,201 +0,0 @@
-import sys
-
-import cherrypy
-from cherrypy.test import helper
-
-
-class TutorialTest(helper.CPWebCase):
-
- def setup_server(cls):
-
- conf = cherrypy.config.copy()
-
- def load_tut_module(name):
- """Import or reload tutorial module as needed."""
- cherrypy.config.reset()
- cherrypy.config.update(conf)
-
- target = "cherrypy.tutorial." + name
- if target in sys.modules:
- module = reload(sys.modules[target])
- else:
- module = __import__(target, globals(), locals(), [''])
- # The above import will probably mount a new app at "".
- app = cherrypy.tree.apps[""]
-
- app.root.load_tut_module = load_tut_module
- app.root.sessions = sessions
- app.root.traceback_setting = traceback_setting
-
- cls.supervisor.sync_apps()
- load_tut_module.exposed = True
-
- def sessions():
- cherrypy.config.update({"tools.sessions.on": True})
- sessions.exposed = True
-
- def traceback_setting():
- return repr(cherrypy.request.show_tracebacks)
- traceback_setting.exposed = True
-
- class Dummy:
- pass
- root = Dummy()
- root.load_tut_module = load_tut_module
- cherrypy.tree.mount(root)
- setup_server = classmethod(setup_server)
-
-
- def test01HelloWorld(self):
- self.getPage("/load_tut_module/tut01_helloworld")
- self.getPage("/")
- self.assertBody('Hello world!')
-
- def test02ExposeMethods(self):
- self.getPage("/load_tut_module/tut02_expose_methods")
- self.getPage("/showMessage")
- self.assertBody('Hello world!')
-
- def test03GetAndPost(self):
- self.getPage("/load_tut_module/tut03_get_and_post")
-
- # Try different GET queries
- self.getPage("/greetUser?name=Bob")
- self.assertBody("Hey Bob, what's up?")
-
- self.getPage("/greetUser")
- self.assertBody('Please enter your name <a href="./">here</a>.')
-
- self.getPage("/greetUser?name=")
- self.assertBody('No, really, enter your name <a href="./">here</a>.')
-
- # Try the same with POST
- self.getPage("/greetUser", method="POST", body="name=Bob")
- self.assertBody("Hey Bob, what's up?")
-
- self.getPage("/greetUser", method="POST", body="name=")
- self.assertBody('No, really, enter your name <a href="./">here</a>.')
-
- def test04ComplexSite(self):
- self.getPage("/load_tut_module/tut04_complex_site")
- msg = '''
- <p>Here are some extra useful links:</p>
-
- <ul>
- <li><a href="http://del.icio.us">del.icio.us</a></li>
- <li><a href="http://www.mornography.de">Hendrik's weblog</a></li>
- </ul>
-
- <p>[<a href="../">Return to links page</a>]</p>'''
- self.getPage("/links/extra/")
- self.assertBody(msg)
-
- def test05DerivedObjects(self):
- self.getPage("/load_tut_module/tut05_derived_objects")
- msg = '''
- <html>
- <head>
- <title>Another Page</title>
- <head>
- <body>
- <h2>Another Page</h2>
-
- <p>
- And this is the amazing second page!
- </p>
-
- </body>
- </html>
- '''
- self.getPage("/another/")
- self.assertBody(msg)
-
- def test06DefaultMethod(self):
- self.getPage("/load_tut_module/tut06_default_method")
- self.getPage('/hendrik')
- self.assertBody('Hendrik Mans, CherryPy co-developer & crazy German '
- '(<a href="./">back</a>)')
-
- def test07Sessions(self):
- self.getPage("/load_tut_module/tut07_sessions")
- self.getPage("/sessions")
-
- self.getPage('/')
- self.assertBody("\n During your current session, you've viewed this"
- "\n page 1 times! Your life is a patio of fun!"
- "\n ")
-
- self.getPage('/', self.cookies)
- self.assertBody("\n During your current session, you've viewed this"
- "\n page 2 times! Your life is a patio of fun!"
- "\n ")
-
- def test08GeneratorsAndYield(self):
- self.getPage("/load_tut_module/tut08_generators_and_yield")
- self.getPage('/')
- self.assertBody('<html><body><h2>Generators rule!</h2>'
- '<h3>List of users:</h3>'
- 'Remi<br/>Carlos<br/>Hendrik<br/>Lorenzo Lamas<br/>'
- '</body></html>')
-
- def test09Files(self):
- self.getPage("/load_tut_module/tut09_files")
-
- # Test upload
- filesize = 5
- h = [("Content-type", "multipart/form-data; boundary=x"),
- ("Content-Length", str(105 + filesize))]
- b = '--x\n' + \
- 'Content-Disposition: form-data; name="myFile"; filename="hello.txt"\r\n' + \
- 'Content-Type: text/plain\r\n' + \
- '\r\n' + \
- 'a' * filesize + '\n' + \
- '--x--\n'
- self.getPage('/upload', h, "POST", b)
- self.assertBody('''<html>
- <body>
- myFile length: %d<br />
- myFile filename: hello.txt<br />
- myFile mime-type: text/plain
- </body>
- </html>''' % filesize)
-
- # Test download
- self.getPage('/download')
- self.assertStatus("200 OK")
- self.assertHeader("Content-Type", "application/x-download")
- self.assertHeader("Content-Disposition",
- # Make sure the filename is quoted.
- 'attachment; filename="pdf_file.pdf"')
- self.assertEqual(len(self.body), 85698)
-
- def test10HTTPErrors(self):
- self.getPage("/load_tut_module/tut10_http_errors")
-
- self.getPage("/")
- self.assertInBody("""<a href="toggleTracebacks">""")
- self.assertInBody("""<a href="/doesNotExist">""")
- self.assertInBody("""<a href="/error?code=403">""")
- self.assertInBody("""<a href="/error?code=500">""")
- self.assertInBody("""<a href="/messageArg">""")
-
- self.getPage("/traceback_setting")
- setting = self.body
- self.getPage("/toggleTracebacks")
- self.assertStatus((302, 303))
- self.getPage("/traceback_setting")
- self.assertBody(str(not eval(setting)))
-
- self.getPage("/error?code=500")
- self.assertStatus(500)
- self.assertInBody("The server encountered an unexpected condition "
- "which prevented it from fulfilling the request.")
-
- self.getPage("/error?code=403")
- self.assertStatus(403)
- self.assertInBody("<h2>You can't do that!</h2>")
-
- self.getPage("/messageArg")
- self.assertStatus(500)
- self.assertInBody("If you construct an HTTPError with a 'message'")
-
diff --git a/cherrypy/test/test_virtualhost.py b/cherrypy/test/test_virtualhost.py
deleted file mode 100755
index d6eed0e..0000000
--- a/cherrypy/test/test_virtualhost.py
+++ /dev/null
@@ -1,107 +0,0 @@
-import os
-curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
-import cherrypy
-from cherrypy.test import helper
-
-
-class VirtualHostTest(helper.CPWebCase):
-
- def setup_server():
- class Root:
- def index(self):
- return "Hello, world"
- index.exposed = True
-
- def dom4(self):
- return "Under construction"
- dom4.exposed = True
-
- def method(self, value):
- return "You sent %s" % repr(value)
- method.exposed = True
-
- class VHost:
- def __init__(self, sitename):
- self.sitename = sitename
-
- def index(self):
- return "Welcome to %s" % self.sitename
- index.exposed = True
-
- def vmethod(self, value):
- return "You sent %s" % repr(value)
- vmethod.exposed = True
-
- def url(self):
- return cherrypy.url("nextpage")
- url.exposed = True
-
- # Test static as a handler (section must NOT include vhost prefix)
- static = cherrypy.tools.staticdir.handler(section='/static', dir=curdir)
-
- root = Root()
- root.mydom2 = VHost("Domain 2")
- root.mydom3 = VHost("Domain 3")
- hostmap = {'www.mydom2.com': '/mydom2',
- 'www.mydom3.com': '/mydom3',
- 'www.mydom4.com': '/dom4',
- }
- cherrypy.tree.mount(root, config={
- '/': {'request.dispatch': cherrypy.dispatch.VirtualHost(**hostmap)},
- # Test static in config (section must include vhost prefix)
- '/mydom2/static2': {'tools.staticdir.on': True,
- 'tools.staticdir.root': curdir,
- 'tools.staticdir.dir': 'static',
- 'tools.staticdir.index': 'index.html',
- },
- })
- setup_server = staticmethod(setup_server)
-
- def testVirtualHost(self):
- self.getPage("/", [('Host', 'www.mydom1.com')])
- self.assertBody('Hello, world')
- self.getPage("/mydom2/", [('Host', 'www.mydom1.com')])
- self.assertBody('Welcome to Domain 2')
-
- self.getPage("/", [('Host', 'www.mydom2.com')])
- self.assertBody('Welcome to Domain 2')
- self.getPage("/", [('Host', 'www.mydom3.com')])
- self.assertBody('Welcome to Domain 3')
- self.getPage("/", [('Host', 'www.mydom4.com')])
- self.assertBody('Under construction')
-
- # Test GET, POST, and positional params
- self.getPage("/method?value=root")
- self.assertBody("You sent u'root'")
- self.getPage("/vmethod?value=dom2+GET", [('Host', 'www.mydom2.com')])
- self.assertBody("You sent u'dom2 GET'")
- self.getPage("/vmethod", [('Host', 'www.mydom3.com')], method="POST",
- body="value=dom3+POST")
- self.assertBody("You sent u'dom3 POST'")
- self.getPage("/vmethod/pos", [('Host', 'www.mydom3.com')])
- self.assertBody("You sent 'pos'")
-
- # Test that cherrypy.url uses the browser url, not the virtual url
- self.getPage("/url", [('Host', 'www.mydom2.com')])
- self.assertBody("%s://www.mydom2.com/nextpage" % self.scheme)
-
- def test_VHost_plus_Static(self):
- # Test static as a handler
- self.getPage("/static/style.css", [('Host', 'www.mydom2.com')])
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'text/css;charset=utf-8')
-
- # Test static in config
- self.getPage("/static2/dirback.jpg", [('Host', 'www.mydom2.com')])
- self.assertStatus('200 OK')
- self.assertHeader('Content-Type', 'image/jpeg')
-
- # Test static config with "index" arg
- self.getPage("/static2/", [('Host', 'www.mydom2.com')])
- self.assertStatus('200 OK')
- self.assertBody('Hello, world\r\n')
- # Since tools.trailing_slash is on by default, this should redirect
- self.getPage("/static2", [('Host', 'www.mydom2.com')])
- self.assertStatus(301)
-
diff --git a/cherrypy/test/test_wsgi_ns.py b/cherrypy/test/test_wsgi_ns.py
deleted file mode 100755
index d57013c..0000000
--- a/cherrypy/test/test_wsgi_ns.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-
-class WSGI_Namespace_Test(helper.CPWebCase):
-
- def setup_server():
-
- class WSGIResponse(object):
-
- def __init__(self, appresults):
- self.appresults = appresults
- self.iter = iter(appresults)
-
- def __iter__(self):
- return self
-
- def next(self):
- return self.iter.next()
-
- def close(self):
- if hasattr(self.appresults, "close"):
- self.appresults.close()
-
-
- class ChangeCase(object):
-
- def __init__(self, app, to=None):
- self.app = app
- self.to = to
-
- def __call__(self, environ, start_response):
- res = self.app(environ, start_response)
- class CaseResults(WSGIResponse):
- def next(this):
- return getattr(this.iter.next(), self.to)()
- return CaseResults(res)
-
- class Replacer(object):
-
- def __init__(self, app, map={}):
- self.app = app
- self.map = map
-
- def __call__(self, environ, start_response):
- res = self.app(environ, start_response)
- class ReplaceResults(WSGIResponse):
- def next(this):
- line = this.iter.next()
- for k, v in self.map.iteritems():
- line = line.replace(k, v)
- return line
- return ReplaceResults(res)
-
- class Root(object):
-
- def index(self):
- return "HellO WoRlD!"
- index.exposed = True
-
-
- root_conf = {'wsgi.pipeline': [('replace', Replacer)],
- 'wsgi.replace.map': {'L': 'X', 'l': 'r'},
- }
-
- app = cherrypy.Application(Root())
- app.wsgiapp.pipeline.append(('changecase', ChangeCase))
- app.wsgiapp.config['changecase'] = {'to': 'upper'}
- cherrypy.tree.mount(app, config={'/': root_conf})
- setup_server = staticmethod(setup_server)
-
-
- def test_pipeline(self):
- if not cherrypy.server.httpserver:
- return self.skip()
-
- self.getPage("/")
- # If body is "HEXXO WORXD!", the middleware was applied out of order.
- self.assertBody("HERRO WORRD!")
-
diff --git a/cherrypy/test/test_wsgi_vhost.py b/cherrypy/test/test_wsgi_vhost.py
deleted file mode 100755
index abb1a91..0000000
--- a/cherrypy/test/test_wsgi_vhost.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import cherrypy
-from cherrypy.test import helper
-
-
-class WSGI_VirtualHost_Test(helper.CPWebCase):
-
- def setup_server():
-
- class ClassOfRoot(object):
-
- def __init__(self, name):
- self.name = name
-
- def index(self):
- return "Welcome to the %s website!" % self.name
- index.exposed = True
-
-
- default = cherrypy.Application(None)
-
- domains = {}
- for year in range(1997, 2008):
- app = cherrypy.Application(ClassOfRoot('Class of %s' % year))
- domains['www.classof%s.example' % year] = app
-
- cherrypy.tree.graft(cherrypy._cpwsgi.VirtualHost(default, domains))
- setup_server = staticmethod(setup_server)
-
- def test_welcome(self):
- if not cherrypy.server.using_wsgi:
- return self.skip("skipped (not using WSGI)... ")
-
- for year in range(1997, 2008):
- self.getPage("/", headers=[('Host', 'www.classof%s.example' % year)])
- self.assertBody("Welcome to the Class of %s website!" % year)
-
diff --git a/cherrypy/test/test_wsgiapps.py b/cherrypy/test/test_wsgiapps.py
deleted file mode 100755
index fa5420c..0000000
--- a/cherrypy/test/test_wsgiapps.py
+++ /dev/null
@@ -1,111 +0,0 @@
-from cherrypy.test import helper
-
-
-class WSGIGraftTests(helper.CPWebCase):
-
- def setup_server():
- import os
- curdir = os.path.join(os.getcwd(), os.path.dirname(__file__))
-
- import cherrypy
-
- def test_app(environ, start_response):
- status = '200 OK'
- response_headers = [('Content-type', 'text/plain')]
- start_response(status, response_headers)
- output = ['Hello, world!\n',
- 'This is a wsgi app running within CherryPy!\n\n']
- keys = list(environ.keys())
- keys.sort()
- for k in keys:
- output.append('%s: %s\n' % (k,environ[k]))
- return output
-
- def test_empty_string_app(environ, start_response):
- status = '200 OK'
- response_headers = [('Content-type', 'text/plain')]
- start_response(status, response_headers)
- return ['Hello', '', ' ', '', 'world']
-
-
- class WSGIResponse(object):
-
- def __init__(self, appresults):
- self.appresults = appresults
- self.iter = iter(appresults)
-
- def __iter__(self):
- return self
-
- def next(self):
- return self.iter.next()
-
- def close(self):
- if hasattr(self.appresults, "close"):
- self.appresults.close()
-
-
- class ReversingMiddleware(object):
-
- def __init__(self, app):
- self.app = app
-
- def __call__(self, environ, start_response):
- results = app(environ, start_response)
- class Reverser(WSGIResponse):
- def next(this):
- line = list(this.iter.next())
- line.reverse()
- return "".join(line)
- return Reverser(results)
-
- class Root:
- def index(self):
- return "I'm a regular CherryPy page handler!"
- index.exposed = True
-
-
- cherrypy.tree.mount(Root())
-
- cherrypy.tree.graft(test_app, '/hosted/app1')
- cherrypy.tree.graft(test_empty_string_app, '/hosted/app3')
-
- # Set script_name explicitly to None to signal CP that it should
- # be pulled from the WSGI environ each time.
- app = cherrypy.Application(Root(), script_name=None)
- cherrypy.tree.graft(ReversingMiddleware(app), '/hosted/app2')
- setup_server = staticmethod(setup_server)
-
- wsgi_output = '''Hello, world!
-This is a wsgi app running within CherryPy!'''
-
- def test_01_standard_app(self):
- self.getPage("/")
- self.assertBody("I'm a regular CherryPy page handler!")
-
- def test_04_pure_wsgi(self):
- import cherrypy
- if not cherrypy.server.using_wsgi:
- return self.skip("skipped (not using WSGI)... ")
- self.getPage("/hosted/app1")
- self.assertHeader("Content-Type", "text/plain")
- self.assertInBody(self.wsgi_output)
-
- def test_05_wrapped_cp_app(self):
- import cherrypy
- if not cherrypy.server.using_wsgi:
- return self.skip("skipped (not using WSGI)... ")
- self.getPage("/hosted/app2/")
- body = list("I'm a regular CherryPy page handler!")
- body.reverse()
- body = "".join(body)
- self.assertInBody(body)
-
- def test_06_empty_string_app(self):
- import cherrypy
- if not cherrypy.server.using_wsgi:
- return self.skip("skipped (not using WSGI)... ")
- self.getPage("/hosted/app3")
- self.assertHeader("Content-Type", "text/plain")
- self.assertInBody('Hello world')
-
diff --git a/cherrypy/test/test_xmlrpc.py b/cherrypy/test/test_xmlrpc.py
deleted file mode 100755
index c4bf61e..0000000
--- a/cherrypy/test/test_xmlrpc.py
+++ /dev/null
@@ -1,172 +0,0 @@
-import sys
-from xmlrpclib import DateTime, Fault, ServerProxy, SafeTransport
-
-class HTTPSTransport(SafeTransport):
- """Subclass of SafeTransport to fix sock.recv errors (by using file)."""
-
- def request(self, host, handler, request_body, verbose=0):
- # issue XML-RPC request
- h = self.make_connection(host)
- if verbose:
- h.set_debuglevel(1)
-
- self.send_request(h, handler, request_body)
- self.send_host(h, host)
- self.send_user_agent(h)
- self.send_content(h, request_body)
-
- errcode, errmsg, headers = h.getreply()
- if errcode != 200:
- raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg,
- headers)
-
- self.verbose = verbose
-
- # Here's where we differ from the superclass. It says:
- # try:
- # sock = h._conn.sock
- # except AttributeError:
- # sock = None
- # return self._parse_response(h.getfile(), sock)
-
- return self.parse_response(h.getfile())
-
-import cherrypy
-
-
-def setup_server():
- from cherrypy import _cptools
-
- class Root:
- def index(self):
- return "I'm a standard index!"
- index.exposed = True
-
-
- class XmlRpc(_cptools.XMLRPCController):
-
- def foo(self):
- return "Hello world!"
- foo.exposed = True
-
- def return_single_item_list(self):
- return [42]
- return_single_item_list.exposed = True
-
- def return_string(self):
- return "here is a string"
- return_string.exposed = True
-
- def return_tuple(self):
- return ('here', 'is', 1, 'tuple')
- return_tuple.exposed = True
-
- def return_dict(self):
- return dict(a=1, b=2, c=3)
- return_dict.exposed = True
-
- def return_composite(self):
- return dict(a=1,z=26), 'hi', ['welcome', 'friend']
- return_composite.exposed = True
-
- def return_int(self):
- return 42
- return_int.exposed = True
-
- def return_float(self):
- return 3.14
- return_float.exposed = True
-
- def return_datetime(self):
- return DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1))
- return_datetime.exposed = True
-
- def return_boolean(self):
- return True
- return_boolean.exposed = True
-
- def test_argument_passing(self, num):
- return num * 2
- test_argument_passing.exposed = True
-
- def test_returning_Fault(self):
- return Fault(1, "custom Fault response")
- test_returning_Fault.exposed = True
-
- root = Root()
- root.xmlrpc = XmlRpc()
- cherrypy.tree.mount(root, config={'/': {
- 'request.dispatch': cherrypy.dispatch.XMLRPCDispatcher(),
- 'tools.xmlrpc.allow_none': 0,
- }})
-
-
-from cherrypy.test import helper
-
-class XmlRpcTest(helper.CPWebCase):
- setup_server = staticmethod(setup_server)
- def testXmlRpc(self):
-
- scheme = "http"
- try:
- scheme = self.harness.scheme
- except AttributeError:
- pass
-
- if scheme == "https":
- url = 'https://%s:%s/xmlrpc/' % (self.interface(), self.PORT)
- proxy = ServerProxy(url, transport=HTTPSTransport())
- else:
- url = 'http://%s:%s/xmlrpc/' % (self.interface(), self.PORT)
- proxy = ServerProxy(url)
-
- # begin the tests ...
- self.getPage("/xmlrpc/foo")
- self.assertBody("Hello world!")
-
- self.assertEqual(proxy.return_single_item_list(), [42])
- self.assertNotEqual(proxy.return_single_item_list(), 'one bazillion')
- self.assertEqual(proxy.return_string(), "here is a string")
- self.assertEqual(proxy.return_tuple(), list(('here', 'is', 1, 'tuple')))
- self.assertEqual(proxy.return_dict(), {'a': 1, 'c': 3, 'b': 2})
- self.assertEqual(proxy.return_composite(),
- [{'a': 1, 'z': 26}, 'hi', ['welcome', 'friend']])
- self.assertEqual(proxy.return_int(), 42)
- self.assertEqual(proxy.return_float(), 3.14)
- self.assertEqual(proxy.return_datetime(),
- DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1)))
- self.assertEqual(proxy.return_boolean(), True)
- self.assertEqual(proxy.test_argument_passing(22), 22 * 2)
-
- # Test an error in the page handler (should raise an xmlrpclib.Fault)
- try:
- proxy.test_argument_passing({})
- except Exception:
- x = sys.exc_info()[1]
- self.assertEqual(x.__class__, Fault)
- self.assertEqual(x.faultString, ("unsupported operand type(s) "
- "for *: 'dict' and 'int'"))
- else:
- self.fail("Expected xmlrpclib.Fault")
-
- # http://www.cherrypy.org/ticket/533
- # if a method is not found, an xmlrpclib.Fault should be raised
- try:
- proxy.non_method()
- except Exception:
- x = sys.exc_info()[1]
- self.assertEqual(x.__class__, Fault)
- self.assertEqual(x.faultString, 'method "non_method" is not supported')
- else:
- self.fail("Expected xmlrpclib.Fault")
-
- # Test returning a Fault from the page handler.
- try:
- proxy.test_returning_Fault()
- except Exception:
- x = sys.exc_info()[1]
- self.assertEqual(x.__class__, Fault)
- self.assertEqual(x.faultString, ("custom Fault response"))
- else:
- self.fail("Expected xmlrpclib.Fault")
-
diff --git a/cherrypy/test/webtest.py b/cherrypy/test/webtest.py
deleted file mode 100755
index 969eab0..0000000
--- a/cherrypy/test/webtest.py
+++ /dev/null
@@ -1,535 +0,0 @@
-"""Extensions to unittest for web frameworks.
-
-Use the WebCase.getPage method to request a page from your HTTP server.
-
-Framework Integration
-=====================
-
-If you have control over your server process, you can handle errors
-in the server-side of the HTTP conversation a bit better. You must run
-both the client (your WebCase tests) and the server in the same process
-(but in separate threads, obviously).
-
-When an error occurs in the framework, call server_error. It will print
-the traceback to stdout, and keep any assertions you have from running
-(the assumption is that, if the server errors, the page output will not
-be of further significance to your tests).
-"""
-
-import os
-import pprint
-import re
-import socket
-import sys
-import time
-import traceback
-import types
-
-from unittest import *
-from unittest import _TextTestResult
-
-from cherrypy._cpcompat import basestring, HTTPConnection, HTTPSConnection, unicodestr
-
-
-
-def interface(host):
- """Return an IP address for a client connection given the server host.
-
- If the server is listening on '0.0.0.0' (INADDR_ANY)
- or '::' (IN6ADDR_ANY), this will return the proper localhost."""
- if host == '0.0.0.0':
- # INADDR_ANY, which should respond on localhost.
- return "127.0.0.1"
- if host == '::':
- # IN6ADDR_ANY, which should respond on localhost.
- return "::1"
- return host
-
-
-class TerseTestResult(_TextTestResult):
-
- def printErrors(self):
- # Overridden to avoid unnecessary empty line
- if self.errors or self.failures:
- if self.dots or self.showAll:
- self.stream.writeln()
- self.printErrorList('ERROR', self.errors)
- self.printErrorList('FAIL', self.failures)
-
-
-class TerseTestRunner(TextTestRunner):
- """A test runner class that displays results in textual form."""
-
- def _makeResult(self):
- return TerseTestResult(self.stream, self.descriptions, self.verbosity)
-
- def run(self, test):
- "Run the given test case or test suite."
- # Overridden to remove unnecessary empty lines and separators
- result = self._makeResult()
- test(result)
- result.printErrors()
- if not result.wasSuccessful():
- self.stream.write("FAILED (")
- failed, errored = list(map(len, (result.failures, result.errors)))
- if failed:
- self.stream.write("failures=%d" % failed)
- if errored:
- if failed: self.stream.write(", ")
- self.stream.write("errors=%d" % errored)
- self.stream.writeln(")")
- return result
-
-
-class ReloadingTestLoader(TestLoader):
-
- def loadTestsFromName(self, name, module=None):
- """Return a suite of all tests cases given a string specifier.
-
- The name may resolve either to a module, a test case class, a
- test method within a test case class, or a callable object which
- returns a TestCase or TestSuite instance.
-
- The method optionally resolves the names relative to a given module.
- """
- parts = name.split('.')
- unused_parts = []
- if module is None:
- if not parts:
- raise ValueError("incomplete test name: %s" % name)
- else:
- parts_copy = parts[:]
- while parts_copy:
- target = ".".join(parts_copy)
- if target in sys.modules:
- module = reload(sys.modules[target])
- parts = unused_parts
- break
- else:
- try:
- module = __import__(target)
- parts = unused_parts
- break
- except ImportError:
- unused_parts.insert(0,parts_copy[-1])
- del parts_copy[-1]
- if not parts_copy:
- raise
- parts = parts[1:]
- obj = module
- for part in parts:
- obj = getattr(obj, part)
-
- if type(obj) == types.ModuleType:
- return self.loadTestsFromModule(obj)
- elif (isinstance(obj, (type, types.ClassType)) and
- issubclass(obj, TestCase)):
- return self.loadTestsFromTestCase(obj)
- elif type(obj) == types.UnboundMethodType:
- return obj.im_class(obj.__name__)
- elif hasattr(obj, '__call__'):
- test = obj()
- if not isinstance(test, TestCase) and \
- not isinstance(test, TestSuite):
- raise ValueError("calling %s returned %s, "
- "not a test" % (obj,test))
- return test
- else:
- raise ValueError("do not know how to make test from: %s" % obj)
-
-
-try:
- # Jython support
- if sys.platform[:4] == 'java':
- def getchar():
- # Hopefully this is enough
- return sys.stdin.read(1)
- else:
- # On Windows, msvcrt.getch reads a single char without output.
- import msvcrt
- def getchar():
- return msvcrt.getch()
-except ImportError:
- # Unix getchr
- import tty, termios
- def getchar():
- fd = sys.stdin.fileno()
- old_settings = termios.tcgetattr(fd)
- try:
- tty.setraw(sys.stdin.fileno())
- ch = sys.stdin.read(1)
- finally:
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
- return ch
-
-
-class WebCase(TestCase):
- HOST = "127.0.0.1"
- PORT = 8000
- HTTP_CONN = HTTPConnection
- PROTOCOL = "HTTP/1.1"
-
- scheme = "http"
- url = None
-
- status = None
- headers = None
- body = None
-
- encoding = 'utf-8'
-
- time = None
-
- def get_conn(self, auto_open=False):
- """Return a connection to our HTTP server."""
- if self.scheme == "https":
- cls = HTTPSConnection
- else:
- cls = HTTPConnection
- conn = cls(self.interface(), self.PORT)
- # Automatically re-connect?
- conn.auto_open = auto_open
- conn.connect()
- return conn
-
- def set_persistent(self, on=True, auto_open=False):
- """Make our HTTP_CONN persistent (or not).
-
- If the 'on' argument is True (the default), then self.HTTP_CONN
- will be set to an instance of HTTPConnection (or HTTPS
- if self.scheme is "https"). This will then persist across requests.
-
- We only allow for a single open connection, so if you call this
- and we currently have an open connection, it will be closed.
- """
- try:
- self.HTTP_CONN.close()
- except (TypeError, AttributeError):
- pass
-
- if on:
- self.HTTP_CONN = self.get_conn(auto_open=auto_open)
- else:
- if self.scheme == "https":
- self.HTTP_CONN = HTTPSConnection
- else:
- self.HTTP_CONN = HTTPConnection
-
- def _get_persistent(self):
- return hasattr(self.HTTP_CONN, "__class__")
- def _set_persistent(self, on):
- self.set_persistent(on)
- persistent = property(_get_persistent, _set_persistent)
-
- def interface(self):
- """Return an IP address for a client connection.
-
- If the server is listening on '0.0.0.0' (INADDR_ANY)
- or '::' (IN6ADDR_ANY), this will return the proper localhost."""
- return interface(self.HOST)
-
- def getPage(self, url, headers=None, method="GET", body=None, protocol=None):
- """Open the url with debugging support. Return status, headers, body."""
- ServerError.on = False
-
- if isinstance(url, unicodestr):
- url = url.encode('utf-8')
- if isinstance(body, unicodestr):
- body = body.encode('utf-8')
-
- self.url = url
- self.time = None
- start = time.time()
- result = openURL(url, headers, method, body, self.HOST, self.PORT,
- self.HTTP_CONN, protocol or self.PROTOCOL)
- self.time = time.time() - start
- self.status, self.headers, self.body = result
-
- # Build a list of request cookies from the previous response cookies.
- self.cookies = [('Cookie', v) for k, v in self.headers
- if k.lower() == 'set-cookie']
-
- if ServerError.on:
- raise ServerError()
- return result
-
- interactive = True
- console_height = 30
-
- def _handlewebError(self, msg):
- print("")
- print(" ERROR: %s" % msg)
-
- if not self.interactive:
- raise self.failureException(msg)
-
- p = " Show: [B]ody [H]eaders [S]tatus [U]RL; [I]gnore, [R]aise, or sys.e[X]it >> "
- sys.stdout.write(p)
- sys.stdout.flush()
- while True:
- i = getchar().upper()
- if i not in "BHSUIRX":
- continue
- print(i.upper()) # Also prints new line
- if i == "B":
- for x, line in enumerate(self.body.splitlines()):
- if (x + 1) % self.console_height == 0:
- # The \r and comma should make the next line overwrite
- sys.stdout.write("<-- More -->\r")
- m = getchar().lower()
- # Erase our "More" prompt
- sys.stdout.write(" \r")
- if m == "q":
- break
- print(line)
- elif i == "H":
- pprint.pprint(self.headers)
- elif i == "S":
- print(self.status)
- elif i == "U":
- print(self.url)
- elif i == "I":
- # return without raising the normal exception
- return
- elif i == "R":
- raise self.failureException(msg)
- elif i == "X":
- self.exit()
- sys.stdout.write(p)
- sys.stdout.flush()
-
- def exit(self):
- sys.exit()
-
- def assertStatus(self, status, msg=None):
- """Fail if self.status != status."""
- if isinstance(status, basestring):
- if not self.status == status:
- if msg is None:
- msg = 'Status (%r) != %r' % (self.status, status)
- self._handlewebError(msg)
- elif isinstance(status, int):
- code = int(self.status[:3])
- if code != status:
- if msg is None:
- msg = 'Status (%r) != %r' % (self.status, status)
- self._handlewebError(msg)
- else:
- # status is a tuple or list.
- match = False
- for s in status:
- if isinstance(s, basestring):
- if self.status == s:
- match = True
- break
- elif int(self.status[:3]) == s:
- match = True
- break
- if not match:
- if msg is None:
- msg = 'Status (%r) not in %r' % (self.status, status)
- self._handlewebError(msg)
-
- def assertHeader(self, key, value=None, msg=None):
- """Fail if (key, [value]) not in self.headers."""
- lowkey = key.lower()
- for k, v in self.headers:
- if k.lower() == lowkey:
- if value is None or str(value) == v:
- return v
-
- if msg is None:
- if value is None:
- msg = '%r not in headers' % key
- else:
- msg = '%r:%r not in headers' % (key, value)
- self._handlewebError(msg)
-
- def assertHeaderItemValue(self, key, value, msg=None):
- """Fail if the header does not contain the specified value"""
- actual_value = self.assertHeader(key, msg=msg)
- header_values = map(str.strip, actual_value.split(','))
- if value in header_values:
- return value
-
- if msg is None:
- msg = "%r not in %r" % (value, header_values)
- self._handlewebError(msg)
-
- def assertNoHeader(self, key, msg=None):
- """Fail if key in self.headers."""
- lowkey = key.lower()
- matches = [k for k, v in self.headers if k.lower() == lowkey]
- if matches:
- if msg is None:
- msg = '%r in headers' % key
- self._handlewebError(msg)
-
- def assertBody(self, value, msg=None):
- """Fail if value != self.body."""
- if value != self.body:
- if msg is None:
- msg = 'expected body:\n%r\n\nactual body:\n%r' % (value, self.body)
- self._handlewebError(msg)
-
- def assertInBody(self, value, msg=None):
- """Fail if value not in self.body."""
- if value not in self.body:
- if msg is None:
- msg = '%r not in body: %s' % (value, self.body)
- self._handlewebError(msg)
-
- def assertNotInBody(self, value, msg=None):
- """Fail if value in self.body."""
- if value in self.body:
- if msg is None:
- msg = '%r found in body' % value
- self._handlewebError(msg)
-
- def assertMatchesBody(self, pattern, msg=None, flags=0):
- """Fail if value (a regex pattern) is not in self.body."""
- if re.search(pattern, self.body, flags) is None:
- if msg is None:
- msg = 'No match for %r in body' % pattern
- self._handlewebError(msg)
-
-
-methods_with_bodies = ("POST", "PUT")
-
-def cleanHeaders(headers, method, body, host, port):
- """Return request headers, with required headers added (if missing)."""
- if headers is None:
- headers = []
-
- # Add the required Host request header if not present.
- # [This specifies the host:port of the server, not the client.]
- found = False
- for k, v in headers:
- if k.lower() == 'host':
- found = True
- break
- if not found:
- if port == 80:
- headers.append(("Host", host))
- else:
- headers.append(("Host", "%s:%s" % (host, port)))
-
- if method in methods_with_bodies:
- # Stick in default type and length headers if not present
- found = False
- for k, v in headers:
- if k.lower() == 'content-type':
- found = True
- break
- if not found:
- headers.append(("Content-Type", "application/x-www-form-urlencoded"))
- headers.append(("Content-Length", str(len(body or ""))))
-
- return headers
-
-
-def shb(response):
- """Return status, headers, body the way we like from a response."""
- h = []
- key, value = None, None
- for line in response.msg.headers:
- if line:
- if line[0] in " \t":
- value += line.strip()
- else:
- if key and value:
- h.append((key, value))
- key, value = line.split(":", 1)
- key = key.strip()
- value = value.strip()
- if key and value:
- h.append((key, value))
-
- return "%s %s" % (response.status, response.reason), h, response.read()
-
-
-def openURL(url, headers=None, method="GET", body=None,
- host="127.0.0.1", port=8000, http_conn=HTTPConnection,
- protocol="HTTP/1.1"):
- """Open the given HTTP resource and return status, headers, and body."""
-
- headers = cleanHeaders(headers, method, body, host, port)
-
- # Trying 10 times is simply in case of socket errors.
- # Normal case--it should run once.
- for trial in range(10):
- try:
- # Allow http_conn to be a class or an instance
- if hasattr(http_conn, "host"):
- conn = http_conn
- else:
- conn = http_conn(interface(host), port)
-
- conn._http_vsn_str = protocol
- conn._http_vsn = int("".join([x for x in protocol if x.isdigit()]))
-
- # skip_accept_encoding argument added in python version 2.4
- if sys.version_info < (2, 4):
- def putheader(self, header, value):
- if header == 'Accept-Encoding' and value == 'identity':
- return
- self.__class__.putheader(self, header, value)
- import new
- conn.putheader = new.instancemethod(putheader, conn, conn.__class__)
- conn.putrequest(method.upper(), url, skip_host=True)
- else:
- conn.putrequest(method.upper(), url, skip_host=True,
- skip_accept_encoding=True)
-
- for key, value in headers:
- conn.putheader(key, value)
- conn.endheaders()
-
- if body is not None:
- conn.send(body)
-
- # Handle response
- response = conn.getresponse()
-
- s, h, b = shb(response)
-
- if not hasattr(http_conn, "host"):
- # We made our own conn instance. Close it.
- conn.close()
-
- return s, h, b
- except socket.error:
- time.sleep(0.5)
- raise
-
-
-# Add any exceptions which your web framework handles
-# normally (that you don't want server_error to trap).
-ignored_exceptions = []
-
-# You'll want set this to True when you can't guarantee
-# that each response will immediately follow each request;
-# for example, when handling requests via multiple threads.
-ignore_all = False
-
-class ServerError(Exception):
- on = False
-
-
-def server_error(exc=None):
- """Server debug hook. Return True if exception handled, False if ignored.
-
- You probably want to wrap this, so you can still handle an error using
- your framework when it's ignored.
- """
- if exc is None:
- exc = sys.exc_info()
-
- if ignore_all or exc[0] in ignored_exceptions:
- return False
- else:
- ServerError.on = True
- print("")
- print("".join(traceback.format_exception(*exc)))
- return True
-