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 # https://github.com/cherrypy/cherrypy/issues/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', [ntou('111'), ntou('333')]), ('foo', ntou('bar'))]))
def __call__(self, environ, start_response): # If you're calling this, then you're probably setting SCRIPT_NAME # to '' (some WSGI servers always set SCRIPT_NAME to ''). # Try to look up the app using the full path. env1x = environ if environ.get(ntou('wsgi.version')) == (ntou('u'), 0): env1x = _cpwsgi.downgrade_wsgi_ux_to_1x(environ) path = httputil.urljoin(env1x.get('SCRIPT_NAME', ''), env1x.get('PATH_INFO', '')) sn = self.script_name(path or "/") if sn is None: start_response('404 Not Found', []) return [] app = self.apps[sn] # Correct the SCRIPT_NAME and PATH_INFO environ entries. environ = environ.copy() if environ.get(u'wsgi.version') == (u'u', 0): # Python 2/WSGI u.0: all strings MUST be of type unicode enc = environ[u'wsgi.url_encoding'] environ[u'SCRIPT_NAME'] = sn.decode(enc) environ[u'PATH_INFO'] = path[len(sn.rstrip("/")):].decode(enc) else: # Python 2/WSGI 1.x: all strings MUST be of type str environ['SCRIPT_NAME'] = sn environ['PATH_INFO'] = path[len(sn.rstrip("/")):] return app(environ, start_response)
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 # https://bitbucket.org/cherrypy/cherrypy/issue/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', [ntou('111'), ntou('333')]), ('foo', ntou('bar'))]))
def json_processor(entity): """Read application/json data into request.json.""" if not entity.headers.get(ntou('Content-Length'), ntou('')): raise cherrypy.HTTPError(411) body = entity.fp.read() with cherrypy.HTTPError.handle(ValueError, 400, 'Invalid JSON document'): cherrypy.serving.request.json = json.decode(body.decode('utf-8'))
def euro(self): hooks = list(cherrypy.request.hooks['before_finalize']) hooks.sort() cbnames = [ x.callback.__name__ for x in hooks ] priorities = [ x.priority for x in hooks ] yield ntou('Hello,') yield ntou('world') yield europoundUnicode
def euro(self): hooks = list(cherrypy.request.hooks['before_finalize']) hooks.sort() cbnames = [x.callback.__name__ for x in hooks] priorities = [x.priority for x in hooks] yield ntou('Hello,') yield ntou('world') yield europoundUnicode
def json_processor(entity): if not entity.headers.get(ntou('Content-Length'), ntou('')): raise cherrypy.HTTPError(411) body = entity.fp.read() try: cherrypy.serving.request.json = json_decode(body.decode('utf-8')) except ValueError: raise cherrypy.HTTPError(400, 'Invalid JSON document')
def json_processor(entity): """Read application/json data into request.json.""" if not entity.headers.get(ntou('Content-Length'), ntou('')): raise cherrypy.HTTPError(411) body = entity.fp.read() with cherrypy.HTTPError.handle(ValueError, 400, 'Invalid JSON document'): cherrypy.serving.request.json = json_decode(body.decode('utf-8'))
def unicode_file(): filename = ntou("Слава Україні.html", 'utf-8') filepath = os.path.join(curdir, "static", filename) with io.open(filepath, 'w', encoding='utf-8') as strm: strm.write(ntou("Героям Слава!", 'utf-8')) try: yield finally: os.remove(filepath)
def unicode_file(): filename = ntou('Слава Україні.html', 'utf-8') filepath = os.path.join(curdir, 'static', filename) with io.open(filepath, 'w', encoding='utf-8') as strm: strm.write(ntou('Героям Слава!', 'utf-8')) try: yield finally: os.remove(filepath)
def test_unicode(self): url = ntou("/static/Слава Україні.html", 'utf-8') # quote function requires str url = tonative(url, 'utf-8') url = urllib.parse.quote(url) self.getPage(url) expected = ntou("Героям Слава!", 'utf-8') self.assertInBody(expected)
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
def json_processor(entity): """Read application/json data into request.json.""" if not entity.headers.get(ntou("Content-Length"), ntou("")): raise cherrypy.HTTPError(411) body = entity.fp.read() try: cherrypy.serving.request.json = json_decode(body.decode('utf-8')) except ValueError: raise cherrypy.HTTPError(400, 'Invalid JSON document')
def test_unicode(self): ensure_unicode_filesystem() with self.unicode_file(): url = ntou('/static/Слава Україні.html', 'utf-8') # quote function requires str url = tonative(url, 'utf-8') url = urllib.parse.quote(url) self.getPage(url) expected = ntou('Героям Слава!', 'utf-8') self.assertInBody(expected)
def test_unicode(self): ensure_unicode_filesystem() self.unicode_file() url = ntou('/static/Слава Україні.html', 'utf-8') # quote function requires str url = tonative(url, 'utf-8') url = urllib.parse.quote(url) self.getPage(url) expected = ntou('Героям Слава!', 'utf-8') self.assertInBody(expected)
def json_in(content_type = [ntou('application/json'), ntou('text/javascript')], force = True, debug = False, processor = json_processor): request = cherrypy.serving.request if isinstance(content_type, basestring): content_type = [content_type] if force: if debug: cherrypy.log('Removing body processors %s' % repr(request.body.processors.keys()), 'TOOLS.JSON_IN') request.body.processors.clear() request.body.default_proc = cherrypy.HTTPError(415, 'Expected an entity of content type %s' % ', '.join(content_type)) for ct in content_type: if debug: cherrypy.log('Adding body processor for %s' % ct, 'TOOLS.JSON_IN') request.body.processors[ct] = processor
def downgrade_wsgi_ux_to_1x(environ): """Return a new environ dict for WSGI 1.x from the given WSGI u.x environ.""" env1x = {} url_encoding = environ[ntou('wsgi.url_encoding')] for k, v in list(environ.items()): if k in [ntou('PATH_INFO'), ntou('SCRIPT_NAME'), ntou('QUERY_STRING')]: v = v.encode(url_encoding) elif isinstance(v, unicodestr): v = v.encode('ISO-8859-1') env1x[k.encode('ISO-8859-1')] = v return env1x
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">\n<html>\n<head>\n <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">\n</head>\n<body bgcolor="#ffffff" text="#000000">\n\nThis is the <strong>HTML</strong> version\n</body>\n</html>\n' ) 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 model_processor(entity): """Read application/json data into request.model.""" if not entity.headers.get(ntou('Content-Length'), ntou('')): # pragma: no cover raise cherrypy.HTTPError(411) body = entity.fp.read() with cherrypy.HTTPError.handle(ValueError, 400, 'Invalid JSON document'): model_json = json.decode(body.decode('utf-8')) try: model = cls(model_json) model.validate() except DataError as e: raise PayloadValidationError(e) cherrypy.serving.request.model = model
def testParams(self): self.getPage("/params/?thing=a") self.assertBody(repr(ntou("a"))) self.getPage("/params/?thing=a&thing=b&thing=c") self.assertBody(repr([ntou('a'), ntou('b'), ntou('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¬athing=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¬athing=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("args: %s kwargs: %s" % (('\xd4 \xe3', 'cheese'), { 'Gruy\xe8re': ntou('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("args: %s kwargs: %s" % (('code', ), { 'url': ntou('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: %s kwargs: %s" % (('dictlike', ), { 'a[1]': ntou('1'), 'b[bar]': ntou('baz'), 'b': ntou('foo'), 'a[2]': ntou('2') }))
def _old_process_multipart(entity): """The behavior of 3.2 and lower. Deprecated and will be changed in 3.3.""" process_multipart(entity) params = entity.params for part in entity.parts: if part.name is None: key = ntou('parts') else: key = part.name if part.filename is None: # It's a regular field value = part.fullvalue() else: # It's a file upload. Retain the whole part so consumer code # has access to its .file and .filename attributes. value = part if key in params: if not isinstance(params[key], list): params[key] = [params[key]] params[key].append(value) else: params[key] = value
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(repr(['another', ntou('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 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(bytes([(x + 3) % 256 for x in zbuf.getvalue()]))
def testCombinedTools(self): expectedResult = (ntou('Hello,world') + europoundUnicode).encode('utf-8') zbuf = io.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 = io.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')]) if six.PY3: self.assertInBody(bytes([(x + 3) % 256 for x in zbuf.getvalue()])) else: self.assertInBody(''.join( [chr((ord(x) + 3) % 256) for x in zbuf.getvalue()]))
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(repr(['another', ntou('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 testCombinedTools(self): expectedResult = (ntou('Hello,world') + europoundUnicode).encode('utf-8') zbuf = io.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 = io.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(bytes([(x + 3) % 256 for x in zbuf.getvalue()]))
def json_in(content_type=[ntou('application/json'), ntou('text/javascript')], force=True, debug=False, processor=json_processor): """Add a processor to parse JSON request entities: The default processor places the parsed data into request.json. Incoming request entities which match the given content_type(s) will be deserialized from JSON to the Python equivalent, and the result stored at cherrypy.request.json. The 'content_type' argument may be a Content-Type string or a list of allowable Content-Type strings. If the 'force' argument is True (the default), then entities of other content types will not be allowed; "415 Unsupported Media Type" is raised instead. Supply your own processor to use a custom decoder, or to handle the parsed data differently. The processor can be configured via tools.json_in.processor or via the decorator method. Note that the deserializer requires the client send a Content-Length request header, or it will raise "411 Length Required". If for any other reason the request entity cannot be deserialized from JSON, it will raise "400 Bad Request: Invalid JSON document". You must be using Python 2.6 or greater, or have the 'simplejson' package importable; otherwise, ValueError is raised during processing. """ request = cherrypy.serving.request if isinstance(content_type, text_or_bytes): content_type = [content_type] if force: if debug: cherrypy.log( 'Removing body processors %s' % repr(request.body.processors.keys()), 'TOOLS.JSON_IN') request.body.processors.clear() request.body.default_proc = cherrypy.HTTPError( 415, 'Expected an entity of content type %s' % ', '.join(content_type)) for ct in content_type: if debug: cherrypy.log('Adding body processor for %s' % ct, 'TOOLS.JSON_IN') request.body.processors[ct] = processor
def _check_unicode_filesystem(tmpdir): filename = tmpdir / ntou('☃', 'utf-8') tmpl = 'File system encoding ({encoding}) cannot support unicode filenames' msg = tmpl.format(encoding=sys.getfilesystemencoding()) try: io.open(str(filename), 'w').close() except UnicodeEncodeError: pytest.skip(msg)
def unicode_filesystem(tmpdir): filename = tmpdir / ntou('☃', 'utf-8') tmpl = 'File system encoding ({encoding}) cannot support unicode filenames' msg = tmpl.format(encoding=sys.getfilesystemencoding()) try: io.open(str(filename), 'w').close() except UnicodeEncodeError: pytest.skip(msg)
def test_encoded_headers(self): # First, make sure the innards work like expected. self.assertEqual(httputil.decode_TEXT(ntou('=?utf-8?q?f=C3=BCr?=')), ntou('f\xfcr')) if cherrypy.server.protocol_version == 'HTTP/1.1': # Test RFC-2047-encoded request and response header values u = ntou('\u212bngstr\xf6m', 'escape') c = ntou('=E2=84=ABngstr=C3=B6m') self.getPage('/headers/ifmatch', [('If-Match', ntou('=?utf-8?q?%s?=') % c)]) # The body should be utf-8 encoded. self.assertBody(ntob('\xe2\x84\xabngstr\xc3\xb6m')) # But the Etag header should be RFC-2047 encoded (binary) self.assertHeader('ETag', ntou('=?utf-8?b?4oSrbmdzdHLDtm0=?=')) # Test a *LONG* RFC-2047-encoded request and response header value self.getPage('/headers/ifmatch', [('If-Match', ntou('=?utf-8?q?%s?=') % (c * 10))]) self.assertBody(ntob('\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_encoded_headers(self): # First, make sure the innards work like expected. self.assertEqual(httputil.decode_TEXT(ntou("=?utf-8?q?f=C3=BCr?=")), ntou("f\xfcr")) if cherrypy.server.protocol_version == "HTTP/1.1": # Test RFC-2047-encoded request and response header values u = ntou('\u212bngstr\xf6m', 'escape') c = ntou("=E2=84=ABngstr=C3=B6m") self.getPage("/headers/ifmatch", [('If-Match', ntou('=?utf-8?q?%s?=') % c)]) # The body should be utf-8 encoded. self.assertBody(ntob("\xe2\x84\xabngstr\xc3\xb6m")) # But the Etag header should be RFC-2047 encoded (binary) self.assertHeader("ETag", ntou('=?utf-8?b?4oSrbmdzdHLDtm0=?=')) # Test a *LONG* RFC-2047-encoded request and response header value self.getPage("/headers/ifmatch", [('If-Match', ntou('=?utf-8?q?%s?=') % (c * 10))]) self.assertBody(ntob("\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 __init__(self, environ, start_response, cpapp): if not py3k: if environ.get(ntou('wsgi.version')) == (ntou('u'), 0): environ = downgrade_wsgi_ux_to_1x(environ) self.environ = environ self.cpapp = cpapp try: self.run() except: self.close() raise r = _cherrypy.serving.response self.iter_response = iter(r.body) try: self.write = start_response(r.output_status, r.header_list) except: self.close() raise
def testParams(self): self.getPage("/params/?thing=a") self.assertBody(repr(ntou("a"))) self.getPage("/params/?thing=a&thing=b&thing=c") self.assertBody(repr([ntou('a'), ntou('b'), ntou('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¬athing=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¬athing=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("args: %s kwargs: %s" % (('\xd4 \xe3', 'cheese'), [('Gruy\xe8re', ntou('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("args: %s kwargs: %s" % (('code',), [('url', ntou('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: %s kwargs: %s" % (('dictlike',), [('a[1]', ntou('1')), ('a[2]', ntou('2')), ('b', ntou('foo')), ('b[bar]', ntou('baz'))]))
def __call__(self, environ, start_response): env1x = environ if environ.get(ntou('wsgi.version')) == (ntou('u'), 0): env1x = _cpwsgi.downgrade_wsgi_ux_to_1x(environ) path = httputil.urljoin(env1x.get('SCRIPT_NAME', ''), env1x.get('PATH_INFO', '')) sn = self.script_name(path or '/') if sn is None: start_response('404 Not Found', []) return [] app = self.apps[sn] environ = environ.copy() if environ.get(u'wsgi.version') == (u'u', 0): enc = environ[u'wsgi.url_encoding'] environ[u'SCRIPT_NAME'] = sn.decode(enc) environ[u'PATH_INFO'] = path[len(sn.rstrip('/')):].decode(enc) else: environ['SCRIPT_NAME'] = sn environ['PATH_INFO'] = path[len(sn.rstrip('/')):] return app(environ, start_response)
def __init__(self, environ, start_response, cpapp): self.cpapp = cpapp try: if six.PY2: if environ.get(ntou('wsgi.version')) == (ntou('u'), 0): environ = downgrade_wsgi_ux_to_1x(environ) self.environ = environ self.run() r = _cherrypy.serving.response outstatus = r.output_status if not isinstance(outstatus, bytes): raise TypeError('response.output_status is not a byte string.') outheaders = [] for k, v in r.header_list: if not isinstance(k, bytes): tmpl = 'response.header_list key %r is not a byte string.' raise TypeError(tmpl % k) if not isinstance(v, bytes): tmpl = ( 'response.header_list value %r is not a byte string.' ) raise TypeError(tmpl % v) outheaders.append((k, v)) if six.PY3: # According to PEP 3333, when using Python 3, the response # status and headers must be bytes masquerading as unicode; # that is, they must be of type "str" but are restricted to # code points in the "latin-1" set. outstatus = outstatus.decode('ISO-8859-1') outheaders = [ (k.decode('ISO-8859-1'), v.decode('ISO-8859-1')) for k, v in outheaders ] self.iter_response = iter(r.body) self.write = start_response(outstatus, outheaders) except BaseException: self.close() raise
def json_in(content_type=[ntou('application/json'), ntou('text/javascript')], force=True, debug=False, processor = json_processor): """Add a processor to parse JSON request entities: The default processor places the parsed data into request.json. Incoming request entities which match the given content_type(s) will be deserialized from JSON to the Python equivalent, and the result stored at cherrypy.request.json. The 'content_type' argument may be a Content-Type string or a list of allowable Content-Type strings. If the 'force' argument is True (the default), then entities of other content types will not be allowed; "415 Unsupported Media Type" is raised instead. Supply your own processor to use a custom decoder, or to handle the parsed data differently. The processor can be configured via tools.json_in.processor or via the decorator method. Note that the deserializer requires the client send a Content-Length request header, or it will raise "411 Length Required". If for any other reason the request entity cannot be deserialized from JSON, it will raise "400 Bad Request: Invalid JSON document". You must be using Python 2.6 or greater, or have the 'simplejson' package importable; otherwise, ValueError is raised during processing. """ request = cherrypy.serving.request if isinstance(content_type, basestring): content_type = [content_type] if force: if debug: cherrypy.log('Removing body processors %s' % repr(request.body.processors.keys()), 'TOOLS.JSON_IN') request.body.processors.clear() request.body.default_proc = cherrypy.HTTPError( 415, 'Expected an entity of content type %s' % ', '.join(content_type)) for ct in content_type: if debug: cherrypy.log('Adding body processor for %s' % ct, 'TOOLS.JSON_IN') request.body.processors[ct] = processor
def decompress_json(entity): """Try decompressing json before parsing, incase compressed content was sent to the server""" if not entity.headers.get(ntou("Content-Length"), ntou("")): raise cherrypy.HTTPError(411) body = entity.fp.read() # decompress if gzip content type if entity.headers.get(ntou("Content-Type")) == ntou("application/gzip"): try: body = zlib.decompress(body) except: raise cherrypy.HTTPError(500, 'Invalid gzip data') try: cherrypy.serving.request.json = json_decode(body.decode('utf-8')) except ValueError: raise cherrypy.HTTPError(400, 'Invalid JSON document')
def json_processor(entity): """ Read application/json data into the request arguments. This is am almost identical copy of the CherryPy JSON tool's json_processor() with the only difference that this version merges any json data decoded from the body, into the request arguments instead of the request.json object. This makes JSON and normal form input data completely indistinguishable fvrom each other as far as the request handlers go. """ if not entity.headers.get(ntou("Content-Length"), ntou("")): raise cherrypy.HTTPError(411) body = entity.fp.read() try: cherrypy.serving.request.params.update(json_decode(body.decode('utf-8'))) except ValueError: raise cherrypy.HTTPError(400, 'Invalid JSON document')
def test_redirect_with_unicode(self): """ A redirect to a URL with Unicode should return a Location header containing that Unicode URL. """ # test disabled due to #1440 return self.getPage('/redirect/url_with_unicode') self.assertStatus(303) loc = self.assertHeader('Location') assert ntou('тест', encoding='utf-8') in loc
def test_redirect_with_unicode(self): """ A redirect to a URL with Unicode should return a Location header containing that Unicode URL. """ # test disabled due to #1440 return self.getPage("/redirect/url_with_unicode") self.assertStatus(303) loc = self.assertHeader('Location') assert ntou('тест', encoding='utf-8') in loc
def json_in(content_type=[ntou('application/json'), ntou('text/javascript')], force=True, debug=False, processor=json_processor): request = cherrypy.serving.request if isinstance(content_type, basestring): content_type = [content_type] if force: if debug: cherrypy.log( 'Removing body processors %s' % repr(request.body.processors.keys()), 'TOOLS.JSON_IN') request.body.processors.clear() request.body.default_proc = cherrypy.HTTPError( 415, 'Expected an entity of content type %s' % ', '.join(content_type)) for ct in content_type: if debug: cherrypy.log('Adding body processor for %s' % ct, 'TOOLS.JSON_IN') request.body.processors[ct] = processor
def zlib_processor( entity): # pragma: no cover, not used in the testing environment... """Read application/zlib data and put content into entity.params for later use. :param entity: cherrypy entity :type entity: cherrypy._cpreqbody.Entity :return: None """ if not entity.headers.get(ntou("Content-Length"), ntou("")): raise cherrypy.HTTPError(411) body = entity.fp.read() try: body = zlib.decompress(body) except zlib.error: raise cherrypy.HTTPError(400, 'Invalid zlib data') try: raw_params = json.loads(body) except ValueError: raise cherrypy.HTTPError(400, 'Invalid JSON document in zlib data') try: params = {} for key, value in list(raw_params.items()): params[key] = unserialize(value.encode("utf8")) except TypeError: raise cherrypy.HTTPError(400, 'Invalid serialized data in JSON document') except AlignakClassLookupException as exp: cherrypy.HTTPError(400, 'Cannot un-serialize data received: %s' % exp) # Now that all values have been successfully parsed and decoded, # apply them to the entity.params dict. for key, value in list(params.items()): if key in entity.params: if not isinstance(entity.params[key], list): entity.params[key] = [entity.params[key]] entity.params[key].append(value) else: entity.params[key] = value
def zlib_processor(entity): # pragma: no cover, not used in the testing environment... """Read application/zlib data and put content into entity.params for later use. :param entity: cherrypy entity :type entity: cherrypy._cpreqbody.Entity :return: None """ if not entity.headers.get(ntou("Content-Length"), ntou("")): raise cherrypy.HTTPError(411) body = entity.fp.read() try: body = zlib.decompress(body) except zlib.error: raise cherrypy.HTTPError(400, 'Invalid zlib data') try: raw_params = json.loads(body) except ValueError: raise cherrypy.HTTPError(400, 'Invalid JSON document in zlib data') try: params = {} for key, value in list(raw_params.items()): params[key] = unserialize(value.encode("utf8")) except TypeError: raise cherrypy.HTTPError(400, 'Invalid serialized data in JSON document') except AlignakClassLookupException as exp: cherrypy.HTTPError(400, 'Cannot un-serialize data received: %s' % exp) # Now that all values have been successfully parsed and decoded, # apply them to the entity.params dict. for key, value in list(params.items()): if key in entity.params: if not isinstance(entity.params[key], list): entity.params[key] = [entity.params[key]] entity.params[key].append(value) else: entity.params[key] = value
def zlib_processor(entity): """Read application/zlib data and put content into entity.params for later use. :param entity: cherrypy entity :type entity: cherrypy._cpreqbody.Entity :return: None """ if not entity.headers.get(ntou("Content-Length"), ntou("")): raise cherrypy.HTTPError(411) body = entity.fp.read() try: body = zlib.decompress(body) except zlib.error: raise cherrypy.HTTPError(400, 'Invalid zlib data') try: raw_params = json.loads(body) except ValueError: raise cherrypy.HTTPError(400, 'Invalid JSON document in zlib data') try: params = {} for key, value in raw_params.iteritems(): params[key] = cPickle.loads(value.encode("utf8")) except TypeError: raise cherrypy.HTTPError(400, 'Invalid Pickle data in JSON document') # Now that all values have been successfully parsed and decoded, # apply them to the entity.params dict. for key, value in params.items(): if key in entity.params: if not isinstance(entity.params[key], list): entity.params[key] = [entity.params[key]] entity.params[key].append(value) else: entity.params[key] = value
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]) self.getPage('/decorated_euro/subpath', headers=[('Accept-Encoding', 'gzip')]) self.assertInBody(''.join([ chr((ord(x) + 3) % 256) for x in zbuf.getvalue() ]))