![](https://seccdn.libravatar.org/avatar/e2145bc5cf53dda95c308a3c75e8fef3.jpg?s=120&d=mm&r=g)
Hello community,
here is the log from the commit of package python-CherryPy for openSUSE:Factory checked in at 2015-02-27 11:07:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-CherryPy (Old)
and /work/SRC/openSUSE:Factory/.python-CherryPy.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-CherryPy"
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-CherryPy/python-CherryPy.changes 2014-05-21 16:22:11.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-CherryPy.new/python-CherryPy.changes 2015-02-27 11:07:39.000000000 +0100
@@ -1,0 +2,26 @@
+Sun Feb 22 17:22:27 UTC 2015 - benoit.monin@gmx.fr
+
+- update to version 3.6.0:
+ * Fixed HTTP range headers for negative length larger than
+ content size
+ * Disabled universal wheel generation as wsgiserver has Python
+ duality
+ * Pull Request #42: Correct TypeError in ``check_auth`` when
+ encrypt is used
+ * Pull Request #59: Correct signature of HandlerWrapperTool
+ * Pull Request #60: Fix error in SessionAuth where login_screen
+ was incorrectly used
+ * Issue #1077: Support keyword-only arguments in dispatchers
+ (Python 3)
+ * Issue #1019: Allow logging host name in the access log
+ * Pull Request #50: Fixed race condition in session cleanup
+- changes from version 3.5.0:
+ * Issue #1301: When the incoming queue is full, now reject
+ additional connections. This functionality was added to
+ CherryPy 3.0, but unintentionally lost in 3.1
+- changes from version 3.4.0:
+ * Miscellaneous quality improvements
+- remove CFLAGS export
+- use update-alternatives for cherryd binary
+
+-------------------------------------------------------------------
Old:
----
CherryPy-3.3.0.tar.gz
New:
----
CherryPy-3.6.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-CherryPy.spec ++++++
--- /var/tmp/diff_new_pack.Rv7ast/_old 2015-02-27 11:07:40.000000000 +0100
+++ /var/tmp/diff_new_pack.Rv7ast/_new 2015-02-27 11:07:40.000000000 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-CherryPy
#
-# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
Name: python-CherryPy
-Version: 3.3.0
+Version: 3.6.0
Release: 0
Url: http://www.cherrypy.org
Summary: Object-Oriented HTTP framework
@@ -26,6 +26,8 @@
Source: https://pypi.python.org/packages/source/C/CherryPy/CherryPy-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRequires: python-devel
+Requires(post): update-alternatives
+Requires(postun): update-alternatives
%if 0%{?suse_version} && 0%{?suse_version} <= 1110
%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
%else
@@ -51,7 +53,6 @@
%setup -q -n CherryPy-%{version}
%build
-export CFLAGS="%{optflags}"
python setup.py build
%install
@@ -59,10 +60,27 @@
find . -name sessiondemo.py -type f -exec chmod 0755 {} \; # Fix non-executable bit rpmlint warning
python setup.py install --prefix=%{_prefix} --root=%{buildroot}
+# update-alternatives
+mv %{buildroot}%{_bindir}/cherryd %{buildroot}%{_bindir}/cherryd-%{py_ver}
+mkdir -p %{buildroot}%{_sysconfdir}/alternatives
+touch %{buildroot}%{_sysconfdir}/alternatives/cherryd
+ln -sf %{_sysconfdir}/alternatives/cherryd %{buildroot}%{_bindir}/cherryd
+
+%post
+update-alternatives \
+ --install %{_bindir}/cherryd cherryd %{_bindir}/cherryd-%{py_ver} 20
+
+%postun
+if [ $1 -eq 0 ] ; then
+ update-alternatives --remove cherryd %{_bindir}/cherryd-%{py_ver}
+fi
+
%files
%defattr(-,root,root,-)
%doc README.txt
+%ghost %{_sysconfdir}/alternatives/cherryd
%{_bindir}/cherryd
+%{_bindir}/cherryd-%{py_ver}
%{python_sitelib}/cherrypy/
%{python_sitelib}/CherryPy-%{version}-py%{py_ver}.egg-info
++++++ CherryPy-3.3.0.tar.gz -> CherryPy-3.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/CherryPy.egg-info/PKG-INFO new/CherryPy-3.6.0/CherryPy.egg-info/PKG-INFO
--- old/CherryPy-3.3.0/CherryPy.egg-info/PKG-INFO 2014-04-16 23:53:55.000000000 +0200
+++ new/CherryPy-3.6.0/CherryPy.egg-info/PKG-INFO 2014-09-14 06:05:58.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: CherryPy
-Version: 3.3.0
+Version: 3.6.0
Summary: Object-Oriented HTTP framework
Home-page: http://www.cherrypy.org
Author: CherryPy Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/CherryPy.egg-info/SOURCES.txt new/CherryPy-3.6.0/CherryPy.egg-info/SOURCES.txt
--- old/CherryPy-3.3.0/CherryPy.egg-info/SOURCES.txt 2014-04-16 23:53:56.000000000 +0200
+++ new/CherryPy-3.6.0/CherryPy.egg-info/SOURCES.txt 2014-09-14 06:06:03.000000000 +0200
@@ -87,6 +87,7 @@
cherrypy/test/test_http.py
cherrypy/test/test_httpauth.py
cherrypy/test/test_httplib.py
+cherrypy/test/test_iterator.py
cherrypy/test/test_json.py
cherrypy/test/test_logging.py
cherrypy/test/test_mime.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/PKG-INFO new/CherryPy-3.6.0/PKG-INFO
--- old/CherryPy-3.3.0/PKG-INFO 2014-04-16 23:53:56.000000000 +0200
+++ new/CherryPy-3.6.0/PKG-INFO 2014-09-14 06:06:03.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: CherryPy
-Version: 3.3.0
+Version: 3.6.0
Summary: Object-Oriented HTTP framework
Home-page: http://www.cherrypy.org
Author: CherryPy Team
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/__init__.py new/CherryPy-3.6.0/cherrypy/__init__.py
--- old/CherryPy-3.3.0/cherrypy/__init__.py 2014-04-16 23:53:44.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/__init__.py 2014-09-14 06:05:54.000000000 +0200
@@ -56,7 +56,7 @@
These API's are described in the `CherryPy specification https://bitbucket.org/cherrypy/cherrypy/wiki/CherryPySpec`_.
"""
-__version__ = "3.3.0"
+__version__ = "3.6.0"
from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode
from cherrypy._cpcompat import basestring, unicodestr, set
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/_cpdispatch.py new/CherryPy-3.6.0/cherrypy/_cpdispatch.py
--- old/CherryPy-3.3.0/cherrypy/_cpdispatch.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/_cpdispatch.py 2014-09-14 05:00:33.000000000 +0200
@@ -93,11 +93,11 @@
show_mismatched_params = getattr(
cherrypy.serving.request, 'show_mismatched_params', False)
try:
- (args, varargs, varkw, defaults) = inspect.getargspec(callable)
+ (args, varargs, varkw, defaults) = getargspec(callable)
except TypeError:
if isinstance(callable, object) and hasattr(callable, '__call__'):
(args, varargs, varkw,
- defaults) = inspect.getargspec(callable.__call__)
+ defaults) = getargspec(callable.__call__)
else:
# If it wasn't one of our own types, re-raise
# the original error
@@ -205,6 +205,12 @@
import inspect
except ImportError:
test_callable_spec = lambda callable, args, kwargs: None
+else:
+ getargspec = inspect.getargspec
+ # Python 3 requires using getfullargspec if keyword-only arguments are present
+ if hasattr(inspect, 'getfullargspec'):
+ def getargspec(callable):
+ return inspect.getfullargspec(callable)[:4]
class LateParamPageHandler(PageHandler):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/_cplogging.py new/CherryPy-3.6.0/cherrypy/_cplogging.py
--- old/CherryPy-3.3.0/cherrypy/_cplogging.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/_cplogging.py 2014-09-14 04:55:36.000000000 +0200
@@ -56,6 +56,13 @@
isn't--it receives messages from a variety of sources (including full error
tracebacks, if enabled).
+If you are logging the access log and error log to the same source, then there
+is a possibility that a specially crafted error message may replicate an access
+log message as described in CWE-117. In this case it is the application
+developer's responsibility to manually escape data before using CherryPy's log()
+functionality, or they may create an application that is vulnerable to CWE-117.
+This would be achieved by using a custom handler escape any special characters,
+and attached as described below.
Custom Handlers
===============
@@ -249,6 +256,7 @@
'b': dict.get(outheaders, 'Content-Length', '') or "-",
'f': dict.get(inheaders, 'Referer', ''),
'a': dict.get(inheaders, 'User-Agent', ''),
+ 'o': dict.get(inheaders, 'Host', '-'),
}
if py3k:
for k, v in atoms.items():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/_cpserver.py new/CherryPy-3.6.0/cherrypy/_cpserver.py
--- old/CherryPy-3.3.0/cherrypy/_cpserver.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/_cpserver.py 2014-09-11 02:52:02.000000000 +0200
@@ -61,6 +61,14 @@
socket_timeout = 10
"""The timeout in seconds for accepted connections (default 10)."""
+
+ accepted_queue_size = -1
+ """The maximum number of requests which will be queued up before
+ the server refuses to accept it (default -1, meaning no limit)."""
+
+ accepted_queue_timeout = 10
+ """The timeout in seconds for attempting to add a request to the
+ queue when the queue is full (default 10)."""
shutdown_timeout = 5
"""The time to wait for HTTP worker threads to clean up."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/_cptools.py new/CherryPy-3.6.0/cherrypy/_cptools.py
--- old/CherryPy-3.3.0/cherrypy/_cptools.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/_cptools.py 2014-09-11 14:59:15.000000000 +0200
@@ -224,7 +224,7 @@
self._name = name
self._priority = priority
- def callable(self, debug=False):
+ def callable(self, *args, **kwargs):
innerfunc = cherrypy.serving.request.handler
def wrap(*args, **kwargs):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/_cpwsgi.py new/CherryPy-3.6.0/cherrypy/_cpwsgi.py
--- old/CherryPy-3.3.0/cherrypy/_cpwsgi.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/_cpwsgi.py 2014-09-11 02:52:02.000000000 +0200
@@ -13,7 +13,7 @@
from cherrypy._cpcompat import BytesIO, bytestr, ntob, ntou, py3k, unicodestr
from cherrypy import _cperror
from cherrypy.lib import httputil
-
+from cherrypy.lib import is_closable_iterator
def downgrade_wsgi_ux_to_1x(environ):
"""Return a new environ dict for WSGI 1.x from the given WSGI u.x environ.
@@ -278,8 +278,20 @@
def close(self):
"""Close and de-reference the current request and response. (Core)"""
+ streaming = _cherrypy.serving.response.stream
self.cpapp.release_serving()
+ # We avoid the expense of examining the iterator to see if it's
+ # closable unless we are streaming the response, as that's the
+ # only situation where we are going to have an iterator which
+ # may not have been exhausted yet.
+ if streaming and is_closable_iterator(self.iter_response):
+ iter_close = self.iter_response.close
+ try:
+ iter_close()
+ except Exception:
+ _cherrypy.log(traceback=True, severity=40)
+
def run(self):
"""Create a Request object using environ."""
env = self.environ.get
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/_cpwsgi_server.py new/CherryPy-3.6.0/cherrypy/_cpwsgi_server.py
--- old/CherryPy-3.3.0/cherrypy/_cpwsgi_server.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/_cpwsgi_server.py 2014-09-11 02:52:02.000000000 +0200
@@ -39,6 +39,8 @@
request_queue_size=self.server_adapter.socket_queue_size,
timeout=self.server_adapter.socket_timeout,
shutdown_timeout=self.server_adapter.shutdown_timeout,
+ accepted_queue_size=self.server_adapter.accepted_queue_size,
+ accepted_queue_timeout=self.server_adapter.accepted_queue_timeout,
)
self.protocol = self.server_adapter.protocol_version
self.nodelay = self.server_adapter.nodelay
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/lib/__init__.py new/CherryPy-3.6.0/cherrypy/lib/__init__.py
--- old/CherryPy-3.3.0/cherrypy/lib/__init__.py 2014-04-16 23:51:37.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/lib/__init__.py 2014-09-11 02:52:02.000000000 +0200
@@ -16,6 +16,29 @@
# Types which implement the protocol must return themselves when
# invoking 'iter' upon them.
return iter(obj) is obj
+
+def is_closable_iterator(obj):
+
+ # Not an iterator.
+ if not is_iterator(obj):
+ return False
+
+ # A generator - the easiest thing to deal with.
+ import inspect
+ if inspect.isgenerator(obj):
+ return True
+
+ # A custom iterator. Look for a close method...
+ if not (hasattr(obj, 'close') and callable(obj.close)):
+ return False
+
+ # ... which doesn't require any arguments.
+ try:
+ inspect.getcallargs(obj.close)
+ except TypeError:
+ return False
+ else:
+ return True
class file_generator(object):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/lib/cptools.py new/CherryPy-3.6.0/cherrypy/lib/cptools.py
--- old/CherryPy-3.3.0/cherrypy/lib/cptools.py 2014-04-16 23:51:37.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/lib/cptools.py 2014-09-14 04:38:02.000000000 +0200
@@ -354,56 +354,51 @@
username = sess.get(self.session_key)
if not username:
sess[self.session_key] = username = self.anonymous()
- if self.debug:
- cherrypy.log(
- 'No session[username], trying anonymous', 'TOOLS.SESSAUTH')
+ self._debug_message('No session[username], trying anonymous')
if not username:
url = cherrypy.url(qs=request.query_string)
- if self.debug:
- cherrypy.log('No username, routing to login_screen with '
- 'from_page %r' % url, 'TOOLS.SESSAUTH')
+ self._debug_message(
+ 'No username, routing to login_screen with from_page %(url)r',
+ locals(),
+ )
response.body = self.login_screen(url)
if "Content-Length" in response.headers:
# Delete Content-Length header so finalize() recalcs it.
del response.headers["Content-Length"]
return True
- if self.debug:
- cherrypy.log('Setting request.login to %r' %
- username, 'TOOLS.SESSAUTH')
+ self._debug_message('Setting request.login to %(username)r', locals())
request.login = username
self.on_check(username)
+ def _debug_message(self, template, context={}):
+ if not self.debug:
+ return
+ cherrypy.log(template % context, 'TOOLS.SESSAUTH')
+
def run(self):
request = cherrypy.serving.request
response = cherrypy.serving.response
path = request.path_info
if path.endswith('login_screen'):
- if self.debug:
- cherrypy.log('routing %r to login_screen' %
- path, 'TOOLS.SESSAUTH')
- return self.login_screen(**request.params)
+ self._debug_message('routing %(path)r to login_screen', locals())
+ response.body = self.login_screen()
+ return True
elif path.endswith('do_login'):
if request.method != 'POST':
response.headers['Allow'] = "POST"
- if self.debug:
- cherrypy.log('do_login requires POST', 'TOOLS.SESSAUTH')
+ self._debug_message('do_login requires POST')
raise cherrypy.HTTPError(405)
- if self.debug:
- cherrypy.log('routing %r to do_login' % path, 'TOOLS.SESSAUTH')
+ self._debug_message('routing %(path)r to do_login', locals())
return self.do_login(**request.params)
elif path.endswith('do_logout'):
if request.method != 'POST':
response.headers['Allow'] = "POST"
raise cherrypy.HTTPError(405)
- if self.debug:
- cherrypy.log('routing %r to do_logout' %
- path, 'TOOLS.SESSAUTH')
+ self._debug_message('routing %(path)r to do_logout', locals())
return self.do_logout(**request.params)
else:
- if self.debug:
- cherrypy.log('No special path, running do_check',
- 'TOOLS.SESSAUTH')
+ self._debug_message('No special path, running do_check')
return self.do_check()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/lib/encoding.py new/CherryPy-3.6.0/cherrypy/lib/encoding.py
--- old/CherryPy-3.3.0/cherrypy/lib/encoding.py 2014-04-16 23:51:37.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/lib/encoding.py 2014-09-11 02:52:02.000000000 +0200
@@ -4,6 +4,7 @@
import cherrypy
from cherrypy._cpcompat import basestring, BytesIO, ntob, set, unicodestr
from cherrypy.lib import file_generator
+from cherrypy.lib import is_closable_iterator
from cherrypy.lib import set_vary_header
@@ -32,23 +33,27 @@
if not isinstance(default_encoding, list):
default_encoding = [default_encoding]
body.attempt_charsets = body.attempt_charsets + default_encoding
-
+
class UTF8StreamEncoder:
def __init__(self, iterator):
self._iterator = iterator
-
+
def __iter__(self):
return self
-
+
def next(self):
return self.__next__()
-
+
def __next__(self):
res = next(self._iterator)
if isinstance(res, unicodestr):
res = res.encode('utf-8')
return res
-
+
+ def close(self):
+ if is_closable_iterator(self._iterator):
+ self._iterator.close()
+
def __getattr__(self, attr):
if attr.startswith('__'):
raise AttributeError(self, attr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/lib/httpauth.py new/CherryPy-3.6.0/cherrypy/lib/httpauth.py
--- old/CherryPy-3.3.0/cherrypy/lib/httpauth.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/lib/httpauth.py 2014-09-11 03:11:00.000000000 +0200
@@ -334,10 +334,14 @@
**kwargs):
# Note that the Basic response doesn't provide the realm value so we cannot
# test it
+ pass_through = lambda password, username=None: password
+ encrypt = encrypt or pass_through
try:
- return encrypt(auth_map["password"], auth_map["username"]) == password
+ candidate = encrypt(auth_map["password"], auth_map["username"])
except TypeError:
- return encrypt(auth_map["password"]) == password
+ # if encrypt only takes one parameter, it's the password
+ candidate = encrypt(auth_map["password"])
+ return candidate == password
AUTH_RESPONSES = {
"basic": _checkBasicResponse,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/lib/httputil.py new/CherryPy-3.6.0/cherrypy/lib/httputil.py
--- old/CherryPy-3.3.0/cherrypy/lib/httputil.py 2014-04-16 23:51:37.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/lib/httputil.py 2014-09-11 02:52:02.000000000 +0200
@@ -103,7 +103,14 @@
# See rfc quote above.
return None
# Negative subscript (last N bytes)
- result.append((content_length - int(stop), content_length))
+ #
+ # RFC 2616 Section 14.35.1:
+ # If the entity is shorter than the specified suffix-length,
+ # the entire entity-body is used.
+ if int(stop) > content_length:
+ result.append((0, content_length))
+ else:
+ result.append((content_length - int(stop), content_length))
return result
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/lib/sessions.py new/CherryPy-3.6.0/cherrypy/lib/sessions.py
--- old/CherryPy-3.3.0/cherrypy/lib/sessions.py 2014-04-16 23:51:37.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/lib/sessions.py 2014-09-14 05:50:14.000000000 +0200
@@ -393,22 +393,26 @@
def clean_up(self):
"""Clean up expired sessions."""
+
now = self.now()
- for id, (data, expiration_time) in copyitems(self.cache):
+ for _id, (data, expiration_time) in copyitems(self.cache):
if expiration_time <= now:
try:
- del self.cache[id]
+ del self.cache[_id]
except KeyError:
pass
try:
- del self.locks[id]
+ if self.locks[_id].acquire(blocking=False):
+ lock = self.locks.pop(_id)
+ lock.release()
except KeyError:
pass
# added to remove obsolete lock objects
- for id in list(self.locks):
- if id not in self.cache:
- self.locks.pop(id, None)
+ for _id in list(self.locks):
+ if _id not in self.cache and self.locks[_id].acquire(blocking=False):
+ lock = self.locks.pop(_id)
+ lock.release()
def _exists(self):
return self.id in self.cache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/lib/static.py new/CherryPy-3.6.0/cherrypy/lib/static.py
--- old/CherryPy-3.3.0/cherrypy/lib/static.py 2014-04-12 15:33:59.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/lib/static.py 2014-09-14 04:55:37.000000000 +0200
@@ -1,24 +1,24 @@
+import os
+import re
+import stat
+import mimetypes
+
try:
from io import UnsupportedOperation
except ImportError:
UnsupportedOperation = object()
-import logging
-import mimetypes
+
+import cherrypy
+from cherrypy._cpcompat import ntob, unquote
+from cherrypy.lib import cptools, httputil, file_generator_limited
+
+
mimetypes.init()
mimetypes.types_map['.dwg'] = 'image/x-dwg'
mimetypes.types_map['.ico'] = 'image/x-icon'
mimetypes.types_map['.bz2'] = 'application/x-bzip2'
mimetypes.types_map['.gz'] = 'application/x-gzip'
-import os
-import re
-import stat
-import time
-
-import cherrypy
-from cherrypy._cpcompat import ntob, unquote
-from cherrypy.lib import cptools, httputil, file_generator_limited
-
def serve_file(path, content_type=None, disposition=None, name=None,
debug=False):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_config.py new/CherryPy-3.6.0/cherrypy/test/test_config.py
--- old/CherryPy-3.3.0/cherrypy/test/test_config.py 2014-04-16 23:52:08.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/test/test_config.py 2014-09-14 06:00:08.000000000 +0200
@@ -206,7 +206,7 @@
if not compat.py3k:
self.getPage("/repr?key=thing3")
- self.assertBody(repr(u'test'))
+ self.assertBody(repr(unicode('test')))
self.getPage("/repr?key=complex")
self.assertBody("(3+2j)")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_conn.py new/CherryPy-3.6.0/cherrypy/test/test_conn.py
--- old/CherryPy-3.3.0/cherrypy/test/test_conn.py 2014-04-16 23:52:08.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/test/test_conn.py 2014-09-14 06:00:08.000000000 +0200
@@ -1,5 +1,6 @@
"""Tests for TCP connection handling, including proper and timely close."""
+import httplib
import socket
import sys
import time
@@ -8,7 +9,7 @@
import cherrypy
from cherrypy._cpcompat import HTTPConnection, HTTPSConnection, NotConnected
-from cherrypy._cpcompat import BadStatusLine, ntob, urlopen, unicodestr
+from cherrypy._cpcompat import BadStatusLine, ntob, tonative, urlopen, unicodestr
from cherrypy.test import webtest
from cherrypy import _cperror
@@ -133,10 +134,22 @@
self.assertRaises(NotConnected, self.getPage, "/")
def test_Streaming_no_len(self):
- self._streaming(set_cl=False)
+ try:
+ self._streaming(set_cl=False)
+ finally:
+ try:
+ self.HTTP_CONN.close()
+ except (TypeError, AttributeError):
+ pass
def test_Streaming_with_len(self):
- self._streaming(set_cl=True)
+ try:
+ self._streaming(set_cl=True)
+ finally:
+ try:
+ self.HTTP_CONN.close()
+ except (TypeError, AttributeError):
+ pass
def _streaming(self, set_cl):
if cherrypy.server.protocol_version == "HTTP/1.1":
@@ -448,49 +461,53 @@
# 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()
+ try:
+ 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)
+ finally:
+ 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")
+ try:
+ 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
+ # ...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)
+ # ...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()
+ # ...get the final response
+ response.begin()
+ self.status, self.headers, self.body = webtest.shb(response)
+ self.assertStatus(200)
+ self.assertBody("thanks for '%s'" % body)
+ finally:
+ conn.close()
class ConnectionTests(helper.CPWebCase):
@@ -717,6 +734,92 @@
remote_data_conn.close()
+def setup_upload_server():
+
+ class Root:
+ def upload(self):
+ if not cherrypy.request.method == 'POST':
+ raise AssertionError("'POST' != request.method %r" %
+ cherrypy.request.method)
+ return "thanks for '%s'" % tonative(cherrypy.request.body.read())
+ upload.exposed = True
+
+ cherrypy.tree.mount(Root())
+ cherrypy.config.update({
+ 'server.max_request_body_size': 1001,
+ 'server.socket_timeout': 10,
+ 'server.accepted_queue_size': 5,
+ 'server.accepted_queue_timeout': 0.1,
+ })
+
+import errno
+socket_reset_errors = []
+# Not all of these names will be defined for every platform.
+for _ in ("ECONNRESET", "WSAECONNRESET"):
+ if _ in dir(errno):
+ socket_reset_errors.append(getattr(errno, _))
+
+class LimitedRequestQueueTests(helper.CPWebCase):
+ setup_server = staticmethod(setup_upload_server)
+
+ def test_queue_full(self):
+ conns = []
+ overflow_conn = None
+
+ try:
+ # Make 15 initial requests and leave them open, which should use
+ # all of wsgiserver's WorkerThreads and fill its Queue.
+ import time
+ for i in range(15):
+ conn = self.HTTP_CONN(self.HOST, self.PORT)
+ 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()
+ conns.append(conn)
+
+ # Now try a 16th conn, which should be closed by the server immediately.
+ overflow_conn = self.HTTP_CONN(self.HOST, self.PORT)
+ # Manually connect since httplib won't let us set a timeout
+ for res in socket.getaddrinfo(self.HOST, self.PORT, 0,
+ socket.SOCK_STREAM):
+ af, socktype, proto, canonname, sa = res
+ overflow_conn.sock = socket.socket(af, socktype, proto)
+ overflow_conn.sock.settimeout(5)
+ overflow_conn.sock.connect(sa)
+ break
+
+ overflow_conn.putrequest("GET", "/", skip_host=True)
+ overflow_conn.putheader("Host", self.HOST)
+ overflow_conn.endheaders()
+ response = overflow_conn.response_class(overflow_conn.sock, method="GET")
+ try:
+ response.begin()
+ except socket.error as exc:
+ if exc.args[0] in socket_reset_errors:
+ pass # Expected.
+ else:
+ raise AssertionError("Overflow conn did not get RST. "
+ "Got %s instead" % repr(exc.args))
+ except httplib.BadStatusLine:
+ # This is a special case in OS X. Linux and Windows will
+ # RST correctly.
+ assert sys.platform == 'darwin'
+ else:
+ raise AssertionError("Overflow conn did not get RST ")
+ finally:
+ for conn in conns:
+ conn.send(ntob("done"))
+ response = conn.response_class(conn.sock, method="POST")
+ response.begin()
+ self.body = response.read()
+ self.assertBody("thanks for 'done'")
+ self.assertEqual(response.status, 200)
+ conn.close()
+ if overflow_conn:
+ overflow_conn.close()
+
class BadRequestTests(helper.CPWebCase):
setup_server = staticmethod(setup_server)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_core.py new/CherryPy-3.6.0/cherrypy/test/test_core.py
--- old/CherryPy-3.3.0/cherrypy/test/test_core.py 2014-04-16 23:52:08.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/test/test_core.py 2014-09-14 06:00:08.000000000 +0200
@@ -496,6 +496,12 @@
self.getPage("/ranges/get_ranges?bytes=2-4,-1")
self.assertBody("[(2, 5), (7, 8)]")
+ # Test a suffix-byte-range longer than the content
+ # length. Note that in this test, the content length
+ # is 8 bytes.
+ self.getPage("/ranges/get_ranges?bytes=-100")
+ self.assertBody("[(0, 8)]")
+
# Get a partial file.
if cherrypy.server.protocol_version == "HTTP/1.1":
self.getPage("/ranges/slice_file", [('Range', 'bytes=2-5')])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_iterator.py new/CherryPy-3.6.0/cherrypy/test/test_iterator.py
--- old/CherryPy-3.3.0/cherrypy/test/test_iterator.py 1970-01-01 01:00:00.000000000 +0100
+++ new/CherryPy-3.6.0/cherrypy/test/test_iterator.py 2014-09-14 06:00:08.000000000 +0200
@@ -0,0 +1,181 @@
+import cherrypy
+from cherrypy._cpcompat import unicodestr
+
+class IteratorBase(object):
+
+ created = 0
+ datachunk = u'butternut squash' * 256
+
+ @classmethod
+ def incr(cls):
+ cls.created += 1
+
+ @classmethod
+ def decr(cls):
+ cls.created -= 1
+
+class OurGenerator(IteratorBase):
+
+ def __iter__(self):
+ self.incr()
+ try:
+ for i in range(1024):
+ yield self.datachunk
+ finally:
+ self.decr()
+
+class OurIterator(IteratorBase):
+
+ started = False
+ closed_off = False
+ count = 0
+
+ def increment(self):
+ self.incr()
+
+ def decrement(self):
+ if not self.closed_off:
+ self.closed_off = True
+ self.decr()
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if not self.started:
+ self.started = True
+ self.increment()
+ self.count += 1
+ if self.count > 1024:
+ raise StopIteration
+ return self.datachunk
+
+ next = __next__
+
+ def __del__(self):
+ self.decrement()
+
+class OurClosableIterator(OurIterator):
+
+ def close(self):
+ self.decrement()
+
+class OurNotClosableIterator(OurIterator):
+
+ # We can't close something which requires an additional argument.
+ def close(self, somearg):
+ self.decrement()
+
+class OurUnclosableIterator(OurIterator):
+ close = 'close' # not callable!
+
+from cherrypy.test import helper
+class IteratorTest(helper.CPWebCase):
+
+ @staticmethod
+ def setup_server():
+
+ class Root(object):
+
+ @cherrypy.expose
+ def count(self, clsname):
+ cherrypy.response.headers['Content-Type'] = 'text/plain'
+ return unicodestr(globals()[clsname].created)
+
+ @cherrypy.expose
+ def getall(self, clsname):
+ cherrypy.response.headers['Content-Type'] = 'text/plain'
+ return globals()[clsname]()
+
+ @cherrypy.expose
+ def stream(self, clsname):
+ return self.getall(clsname)
+ stream._cp_config = {'response.stream': True}
+
+ cherrypy.tree.mount(Root())
+
+ def test_iterator(self):
+ if cherrypy.server.protocol_version != "HTTP/1.1":
+ return self.skip()
+
+ self.PROTOCOL = "HTTP/1.1"
+
+ # Check the counts of all the classes, they should be zero.
+ closables = ['OurClosableIterator', 'OurGenerator']
+ unclosables = ['OurUnclosableIterator', 'OurNotClosableIterator']
+ all_classes = closables + unclosables
+
+ import random
+ random.shuffle(all_classes)
+
+ for clsname in all_classes:
+ self.getPage("/count/" + clsname)
+ self.assertStatus(200)
+ self.assertBody('0')
+
+ # We should also be able to read the entire content body
+ # successfully, though we don't need to, we just want to
+ # check the header.
+ for clsname in all_classes:
+ itr_conn = self.get_conn()
+ itr_conn.putrequest("GET", "/getall/" + clsname)
+ itr_conn.endheaders()
+ response = itr_conn.getresponse()
+ self.assertEqual(response.status, 200)
+ headers = response.getheaders()
+ for header_name, header_value in headers:
+ if header_name.lower() == 'content-length':
+ assert header_value == unicodestr(1024 * 16 * 256), header_value
+ break
+ else:
+ raise AssertionError('No Content-Length header found')
+
+ # As the response should be fully consumed by CherryPy
+ # before sending back, the count should still be at zero
+ # by the time the response has been sent.
+ self.getPage("/count/" + clsname)
+ self.assertStatus(200)
+ self.assertBody('0')
+
+ # Now we do the same check with streaming - some classes will
+ # be automatically closed, while others cannot.
+ stream_counts = {}
+ for clsname in all_classes:
+ itr_conn = self.get_conn()
+ itr_conn.putrequest("GET", "/stream/" + clsname)
+ itr_conn.endheaders()
+ response = itr_conn.getresponse()
+ self.assertEqual(response.status, 200)
+ response.fp.read(65536)
+
+ # Let's check the count - this should always be one.
+ self.getPage("/count/" + clsname)
+ self.assertBody('1')
+
+ # Now if we close the connection, the count should go back
+ # to zero.
+ itr_conn.close()
+ self.getPage("/count/" + clsname)
+
+ # If this is a response which should be easily closed, then
+ # we will test to see if the value has gone back down to
+ # zero.
+ if clsname in closables:
+
+ # Sometimes we try to get the answer too quickly - we
+ # will wait for 100 ms before asking again if we didn't
+ # get the answer we wanted.
+ if self.body != '0':
+ import time
+ time.sleep(0.1)
+ self.getPage("/count/" + clsname)
+
+ stream_counts[clsname] = int(self.body)
+
+ # Check that we closed off the classes which should provide
+ # easy mechanisms for doing so.
+ for clsname in closables:
+ assert stream_counts[clsname] == 0, (
+ 'did not close off stream response correctly, expected '
+ 'count of zero for %s: %s' % (clsname, stream_counts)
+ )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_logging.py new/CherryPy-3.6.0/cherrypy/test/test_logging.py
--- old/CherryPy-3.3.0/cherrypy/test/test_logging.py 2014-04-16 23:52:08.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/test/test_logging.py 2014-09-14 06:00:08.000000000 +0200
@@ -105,6 +105,26 @@
self.assertLog(-1, '] "GET %s/as_yield HTTP/1.1" 200 - "" ""'
% self.prefix())
+ def testCustomLogFormat(self):
+ '''Test a customized access_log_format string,
+ which is a feature of _cplogging.LogManager.access() '''
+
+ original_logformat = cherrypy._cplogging.LogManager.access_log_format
+ cherrypy._cplogging.LogManager.access_log_format = \
+ '{h} {l} {u} {t} "{r}" {s} {b} "{f}" "{a}" {o}' \
+ if py3k else \
+ '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(o)s'
+
+ self.markLog()
+ self.getPage("/as_string", headers=[('Referer', 'REFERER'),
+ ('User-Agent', 'USERAGENT'),
+ ('Host', 'HOST')])
+ self.assertLog(-1, '%s - - [' % self.interface())
+ self.assertLog(-1, '] "GET /as_string HTTP/1.1" '
+ '200 7 "REFERER" "USERAGENT" HOST')
+
+ cherrypy._cplogging.LogManager.access_log_format = original_logformat
+
def testEscapedOutput(self):
# Test unicode in access log pieces.
self.markLog()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_objectmapping.py new/CherryPy-3.6.0/cherrypy/test/test_objectmapping.py
--- old/CherryPy-3.3.0/cherrypy/test/test_objectmapping.py 2014-04-16 23:52:08.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/test/test_objectmapping.py 2014-09-14 06:00:08.000000000 +0200
@@ -1,3 +1,4 @@
+import sys
import cherrypy
from cherrypy._cpcompat import ntou
from cherrypy._cptree import Application
@@ -414,3 +415,17 @@
a = Application(Root(), script_name=None)
# However, this does not apply to tree.mount
self.assertRaises(TypeError, cherrypy.tree.mount, a, None)
+
+ def testKeywords(self):
+ if sys.version_info < (3,):
+ return self.skip("skipped (Python 3 only)")
+ exec("""class Root(object):
+ @cherrypy.expose
+ def hello(self, *, name='world'):
+ return 'Hello %s!' % name
+cherrypy.tree.mount(Application(Root(), '/keywords'))""")
+
+ self.getPage('/keywords/hello')
+ self.assertStatus(200)
+ self.getPage('/keywords/hello/extra')
+ self.assertStatus(404)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_session.py new/CherryPy-3.6.0/cherrypy/test/test_session.py
--- old/CherryPy-3.3.0/cherrypy/test/test_session.py 2014-04-16 23:52:08.000000000 +0200
+++ new/CherryPy-3.6.0/cherrypy/test/test_session.py 2014-09-14 06:00:08.000000000 +0200
@@ -1,6 +1,5 @@
import os
localDir = os.path.dirname(__file__)
-import sys
import threading
import time
@@ -358,10 +357,24 @@
else:
self.fail("Unknown session id in cache: %r", cache)
+ def test_8_Ram_Cleanup(self):
+ def lock():
+ s1 = sessions.RamSession()
+ s1.acquire_lock()
+ time.sleep(1)
+ s1.release_lock()
+
+ t = threading.Thread(target=lock)
+ t.start()
+ s2 = sessions.RamSession()
+ s2.clean_up()
+ self.assertEqual(len(sessions.RamSession.locks), 1, 'Clean up should not remove active lock')
+ t.join()
+
import socket
try:
- import memcache
+ import memcache # NOQA
host, port = '127.0.0.1', 11211
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
@@ -436,7 +449,7 @@
## sys.stdout.write("%d " % index)
if not self.body.isdigit():
self.fail(self.body)
- data_dict[index] = v = int(self.body)
+ data_dict[index] = int(self.body)
# Start