/opt/imh-python/lib/python3.9/site-packages/cherrypy/test
Edit: /opt/imh-python/lib/python3.9/site-packages/cherrypy/test/test_core.py (30408B)
# coding: utf-8
"""Basic tests for the CherryPy core: request handling."""
import os
import sys
import types
import cherrypy
from cherrypy._cpcompat import ntou
from cherrypy import _cptools, tools
from cherrypy.lib import httputil, static
from cherrypy.test._test_decorators import ExposeExamples
from cherrypy.test import helper
localDir = os.path.dirname(__file__)
favicon_path = os.path.join(os.getcwd(), localDir, '../favicon.ico')
# Client-side code #
class CoreRequestHandlingTest(helper.CPWebCase):
@staticmethod
def setup_server():
class Root:
@cherrypy.expose
def index(self):
return 'hello'
favicon_ico = tools.staticfile.handler(filename=favicon_path)
@cherrypy.expose
def defct(self, newct):
newct = 'text/%s' % newct
cherrypy.config.update({'tools.response_headers.on': True,
'tools.response_headers.headers':
[('Content-Type', newct)]})
@cherrypy.expose
def baseurl(self, path_info, relative=None):
return cherrypy.url(path_info, relative=bool(relative))
root = Root()
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 dct.values():
if isinstance(value, types.FunctionType):
value.exposed = True
setattr(root, name.lower(), cls())
Test = TestType('Test', (object, ), {})
@cherrypy.config(**{'tools.trailing_slash.on': False})
class URL(Test):
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)
def qs(self, qs):
return cherrypy.url(qs=qs)
def log_status():
Status.statuses.append(cherrypy.response.status)
cherrypy.tools.log_status = cherrypy.Tool(
'on_end_resource', log_status)
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'
statuses = []
@cherrypy.config(**{'tools.log_status.on': True})
def on_end_resource_stage(self):
return repr(self.statuses)
class Redirect(Test):
@cherrypy.config(**{
'tools.err_redirect.on': True,
'tools.err_redirect.url': '/errpage',
'tools.err_redirect.internal': False,
})
class Error:
@cherrypy.expose
def index(self):
raise NameError('redirect_test')
error = Error()
def index(self):
return 'child'
def custom(self, url, code):
raise cherrypy.HTTPRedirect(url, code)
@cherrypy.config(**{'tools.trailing_slash.extra': True})
def by_code(self, code):
raise cherrypy.HTTPRedirect('somewhere%20else', code)
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 url_with_quote(self):
raise cherrypy.HTTPRedirect("/some\"url/that'we/want")
def url_with_xss(self):
raise cherrypy.HTTPRedirect(
"/someurl/that'we/want")
def url_with_unicode(self):
raise cherrypy.HTTPRedirect(ntou('тест', 'utf-8'))
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('/')
@cherrypy.expose
@cherrypy.config(**{'hooks.before_error_response': redir_custom})
def choke(self):
return 3 / 0
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.'
@cherrypy.config(**{'hooks.before_request_body': redir_custom})
def early_ir(self, arg):
return 'whatever'
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 b'content'
@cherrypy.config(**{'tools.flatten.on': True})
def as_dblyield(self):
yield self.as_yield()
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):
list(map(self.single, names))
def append_headers(header_list, debug=False):
if debug:
cherrypy.log(
'Extending response headers with %s' % repr(header_list),
'TOOLS.APPEND_HEADERS')
cherrypy.serving.response.header_list.extend(header_list)
cherrypy.tools.append_headers = cherrypy.Tool(
'on_end_resource', append_headers)
class MultiHeader(Test):
def header_list(self):
pass
header_list = cherrypy.tools.append_headers(header_list=[
(b'WWW-Authenticate', b'Negotiate'),
(b'WWW-Authenticate', b'Basic realm="foo"'),
])(header_list)
def commas(self):
cherrypy.response.headers[
'WWW-Authenticate'] = 'Negotiate,Basic realm="foo"'
cherrypy.tree.mount(root)
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 test_on_end_resource_status(self):
self.getPage('/status/on_end_resource_stage')
self.assertBody('[]')
self.getPage('/status/on_end_resource_stage')
self.assertBody(repr(['200 OK']))
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.assertMatchesBody(
'
'
'%s/redirect/[?]id=3' % (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.assertMatchesBody("
%s/" %
(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.assertMatchesBody(
"
"
'%s/redirect/by_code[?]code=307'
% (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"
\2somewhere%20else")
self.assertStatus(300)
self.getPage('/redirect/by_code?code=301')
self.assertMatchesBody(
r"
\2somewhere%20else")
self.assertStatus(301)
self.getPage('/redirect/by_code?code=302')
self.assertMatchesBody(
r"
\2somewhere%20else")
self.assertStatus(302)
self.getPage('/redirect/by_code?code=303')
self.assertMatchesBody(
r"
\2somewhere%20else")
self.assertStatus(303)
self.getPage('/redirect/by_code?code=307')
self.assertMatchesBody(
r"
\2somewhere%20else")
self.assertStatus(307)
self.getPage('/redirect/by_code?code=308')
self.assertMatchesBody(
r"
\2somewhere%20else")
self.assertStatus(308)
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"
\2\/some\/url\#%s" % (
frag, frag))
loc = self.assertHeader('Location')
assert loc.endswith('#%s' % frag)
self.assertStatus(('302 Found', '303 See Other'))
# check injection protection
# See https://github.com/cherrypy/cherrypy/issues/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 assertValidXHTML():
from xml.etree import ElementTree
try:
ElementTree.fromstring(
'%s' % self.body,
)
except ElementTree.ParseError:
self._handlewebError(
'automatically generated redirect did not '
'generate well-formed html',
)
# check redirects to URLs generated valid HTML - we check this
# by seeing if it appears as valid XHTML.
self.getPage('/redirect/by_code?code=303')
self.assertStatus(303)
assertValidXHTML()
# do the same with a url containing quote characters.
self.getPage('/redirect/url_with_quote')
self.assertStatus(303)
assertValidXHTML()
def test_redirect_with_xss(self):
"""A redirect to a URL with HTML injected should result
in page contents escaped."""
self.getPage('/redirect/url_with_xss')
self.assertStatus(303)
assert b'