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 <request_count> concurrent requests from # each of <client_thread_count> clients diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_static.py new/CherryPy-3.6.0/cherrypy/test/test_static.py --- old/CherryPy-3.3.0/cherrypy/test/test_static.py 2014-04-16 23:52:08.000000000 +0200 +++ new/CherryPy-3.6.0/cherrypy/test/test_static.py 2014-09-14 06:00:08.000000000 +0200 @@ -7,7 +7,11 @@ 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 + +# The file size needs to be big enough such that half the size of it +# won't be socket-buffered (or server-buffered) all in one go. See +# test_file_stream. +BIGFILE_SIZE = 1024 * 1024 * 4 import cherrypy from cherrypy.lib import static @@ -19,7 +23,8 @@ 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): + if not os.path.exists(bigfile_filepath) or \ + os.path.getsize(bigfile_filepath) != BIGFILE_SIZE: open(bigfile_filepath, 'wb').write(ntob("x" * BIGFILE_SIZE)) class Root: @@ -256,23 +261,38 @@ else: tell_position = int(b) - expected = len(body) + read_so_far = len(body) + + # It is difficult for us to force the server to only read + # the bytes that we ask for - there are going to be buffers + # inbetween. + # + # CherryPy will attempt to write as much data as it can to + # the socket, and we don't have a way to determine what that + # size will be. So we make the following assumption - by + # the time we have read in the entire file on the server, + # we will have at least received half of it. If this is not + # the case, then this is an indicator that either: + # - machines that are running this test are using buffer + # sizes greater than half of BIGFILE_SIZE; or + # - streaming is broken. + # + # At the time of writing, we seem to have encountered + # buffer sizes bigger than 512K, so we've increased + # BIGFILE_SIZE to 4MB. 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): + if read_so_far < (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: + "chunk size (64k)" % read_so_far) + elif tell_position < read_so_far: 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)) + "as intended, or at the wrong chunk size (64k)" % + (read_so_far, tell_position)) if body != ntob("x" * BIGFILE_SIZE): self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." % @@ -307,7 +327,7 @@ if self.body != ntob("x" * BIGFILE_SIZE): self.fail("Body != 'x' * %d. Got %r instead (%d bytes)." % (BIGFILE_SIZE, self.body[:50], len(body))) - + def test_error_page_with_serve_file(self): self.getPage("/404test/yunyeen") self.assertStatus(404) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/test/test_tools.py new/CherryPy-3.6.0/cherrypy/test/test_tools.py --- old/CherryPy-3.3.0/cherrypy/test/test_tools.py 2014-04-16 23:52:08.000000000 +0200 +++ new/CherryPy-3.6.0/cherrypy/test/test_tools.py 2014-09-14 06:00:08.000000000 +0200 @@ -108,6 +108,7 @@ cherrypy.tools.rotator = cherrypy.Tool('before_finalize', Rotator()) def stream_handler(next_handler, *args, **kwargs): + assert cherrypy.request.config.get('tools.streamer.arg') == 'arg value' cherrypy.response.output = o = BytesIO() try: response = next_handler(*args, **kwargs) @@ -126,10 +127,11 @@ index.exposed = True def tarfile(self): + assert cherrypy.request.config.get('tools.streamer.arg') == 'arg value' cherrypy.response.output.write(ntob('I am ')) cherrypy.response.output.write(ntob('a tarfile')) tarfile.exposed = True - tarfile._cp_config = {'tools.streamer.on': True} + tarfile._cp_config = {'tools.streamer.on': True, 'tools.streamer.arg': 'arg value'} def euro(self): hooks = list(cherrypy.request.hooks['before_finalize']) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/wsgiserver/wsgiserver2.py new/CherryPy-3.6.0/cherrypy/wsgiserver/wsgiserver2.py --- old/CherryPy-3.3.0/cherrypy/wsgiserver/wsgiserver2.py 2014-04-16 23:53:44.000000000 +0200 +++ new/CherryPy-3.6.0/cherrypy/wsgiserver/wsgiserver2.py 2014-09-14 06:05:54.000000000 +0200 @@ -1545,12 +1545,14 @@ and stop(timeout) attributes. """ - def __init__(self, server, min=10, max=-1): + def __init__(self, server, min=10, max=-1, + accepted_queue_size=-1, accepted_queue_timeout=10): self.server = server self.min = min self.max = max self._threads = [] - self._queue = queue.Queue() + self._queue = queue.Queue(maxsize=accepted_queue_size) + self._queue_put_timeout = accepted_queue_timeout self.get = self._queue.get def start(self): @@ -1570,7 +1572,7 @@ idle = property(_get_idle, doc=_get_idle.__doc__) def put(self, obj): - self._queue.put(obj) + self._queue.put(obj, block=True, timeout=self._queue_put_timeout) if obj is _SHUTDOWNREQUEST: return @@ -1755,7 +1757,7 @@ timeout = 10 """The timeout in seconds for accepted connections (default 10).""" - version = "CherryPy/3.3.0" + version = "CherryPy/3.6.0" """A version string for the HTTPServer.""" software = None @@ -2072,7 +2074,12 @@ conn.ssl_env = ssl_env - self.requests.put(conn) + try: + self.requests.put(conn) + except queue.Full: + # Just drop the conn. TODO: write 503 back? + conn.close() + return except socket.timeout: # The only reason for the timeout in start() is so we can # notice keyboard interrupts on Win32, which don't interrupt @@ -2215,8 +2222,11 @@ """The version of WSGI to produce.""" def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, - max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5): - self.requests = ThreadPool(self, min=numthreads or 1, max=max) + max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5, + accepted_queue_size=-1, accepted_queue_timeout=10): + self.requests = ThreadPool(self, min=numthreads or 1, max=max, + accepted_queue_size=accepted_queue_size, + accepted_queue_timeout=accepted_queue_timeout) self.wsgi_app = wsgi_app self.gateway = wsgi_gateways[self.wsgi_version] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/cherrypy/wsgiserver/wsgiserver3.py new/CherryPy-3.6.0/cherrypy/wsgiserver/wsgiserver3.py --- old/CherryPy-3.3.0/cherrypy/wsgiserver/wsgiserver3.py 2014-04-16 23:53:44.000000000 +0200 +++ new/CherryPy-3.6.0/cherrypy/wsgiserver/wsgiserver3.py 2014-09-14 06:05:54.000000000 +0200 @@ -1261,12 +1261,14 @@ and stop(timeout) attributes. """ - def __init__(self, server, min=10, max=-1): + def __init__(self, server, min=10, max=-1, + accepted_queue_size=-1, accepted_queue_timeout=10): self.server = server self.min = min self.max = max self._threads = [] - self._queue = queue.Queue() + self._queue = queue.Queue(maxsize=accepted_queue_size) + self._queue_put_timeout = accepted_queue_timeout self.get = self._queue.get def start(self): @@ -1286,7 +1288,7 @@ idle = property(_get_idle, doc=_get_idle.__doc__) def put(self, obj): - self._queue.put(obj) + self._queue.put(obj, block=True, timeout=self._queue_put_timeout) if obj is _SHUTDOWNREQUEST: return @@ -1466,7 +1468,7 @@ timeout = 10 """The timeout in seconds for accepted connections (default 10).""" - version = "CherryPy/3.3.0" + version = "CherryPy/3.6.0" """A version string for the HTTPServer.""" software = None @@ -1764,7 +1766,12 @@ conn.ssl_env = ssl_env - self.requests.put(conn) + try: + self.requests.put(conn) + except queue.Full: + # Just drop the conn. TODO: write 503 back? + conn.close() + return except socket.timeout: # The only reason for the timeout in start() is so we can # notice keyboard interrupts on Win32, which don't interrupt @@ -1906,8 +1913,11 @@ """The version of WSGI to produce.""" def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, - max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5): - self.requests = ThreadPool(self, min=numthreads or 1, max=max) + max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5, + accepted_queue_size=-1, accepted_queue_timeout=10): + self.requests = ThreadPool(self, min=numthreads or 1, max=max, + accepted_queue_size=accepted_queue_size, + accepted_queue_timeout=accepted_queue_timeout) self.wsgi_app = wsgi_app self.gateway = wsgi_gateways[self.wsgi_version] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/setup.cfg new/CherryPy-3.6.0/setup.cfg --- old/CherryPy-3.3.0/setup.cfg 2014-04-16 23:53:56.000000000 +0200 +++ new/CherryPy-3.6.0/setup.cfg 2014-09-14 06:06:03.000000000 +0200 @@ -7,11 +7,8 @@ verbosity = 2 nocapture = True -[wheel] -universal = 1 - [egg_info] -tag_build = tag_svn_revision = 0 tag_date = 0 +tag_build = diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/CherryPy-3.3.0/setup.py new/CherryPy-3.6.0/setup.py --- old/CherryPy-3.3.0/setup.py 2014-04-16 23:53:44.000000000 +0200 +++ new/CherryPy-3.6.0/setup.py 2014-09-14 06:05:54.000000000 +0200 @@ -36,7 +36,7 @@ # arguments for the setup command ############################################################################### name = "CherryPy" -version = "3.3.0" +version = "3.6.0" desc = "Object-Oriented HTTP framework" long_desc = "CherryPy is a pythonic, object-oriented HTTP framework" classifiers = [ @@ -126,6 +126,21 @@ if 'bdist_wininst' in sys.argv or '--format=wininst' in sys.argv: data_files = [(r'\PURELIB\%s' % path, files) for path, files in data_files] +setup_params = dict( + name=name, + version=version, + description=desc, + long_description=long_desc, + classifiers=classifiers, + author=author, + author_email=author_email, + url=url, + license=cp_license, + packages=packages, + data_files=data_files, + scripts=scripts, + cmdclass=cmd_class, +) def main(): if sys.version < required_python_version: @@ -137,21 +152,7 @@ for scheme in list(INSTALL_SCHEMES.values()): scheme['data'] = scheme['purelib'] - setup( - name=name, - version=version, - description=desc, - long_description=long_desc, - classifiers=classifiers, - author=author, - author_email=author_email, - url=url, - license=cp_license, - packages=packages, - data_files=data_files, - scripts=scripts, - cmdclass=cmd_class, - ) + setup(**setup_params) if __name__ == "__main__": -- To unsubscribe, e-mail: opensuse-commit+unsubscribe@opensuse.org For additional commands, e-mail: opensuse-commit+help@opensuse.org