Hello community,
here is the log from the commit of package python-gunicorn for openSUSE:Factory
checked in at 2020-04-04 12:19:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-gunicorn (Old)
and /work/SRC/openSUSE:Factory/.python-gunicorn.new.3248 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-gunicorn"
Sat Apr 4 12:19:47 2020 rev:17 rq:790071 version:19.10.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-gunicorn/python-gunicorn.changes
2019-07-30 13:04:08.590405200 +0200
+++
/work/SRC/openSUSE:Factory/.python-gunicorn.new.3248/python-gunicorn.changes
2020-04-04 12:19:51.451646457 +0200
@@ -1,0 +2,18 @@
+Tue Mar 31 09:59:42 UTC 2020 - Ondřej Súkup <[email protected]>
+
+- update to 19.10.0
+- last with py2 support
+ * unblock select loop during reload of a sync worker
+ * security fix: http desync attack
+ * handle `wsgi.input_terminated`
+ * added support for str and bytes in unix socket addresses
+ * fixed `max_requests` setting
+ * headers values are now encoded as LATN1, not ASCII
+ * fixed `InotifyReloadeder`: handle `module.__file__` is None
+ * fixed compatibility with tornado 6
+ * fixed root logging
+ * Prevent removalof unix sockets from `reuse_port`
+ * Clear tornado ioloop before os.fork
+ * Miscellaneous fixes and improvement for linting using Pylints
+
+-------------------------------------------------------------------
Old:
----
gunicorn-19.9.0.tar.gz
New:
----
gunicorn-19.10.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-gunicorn.spec ++++++
--- /var/tmp/diff_new_pack.Foqjmt/_old 2020-04-04 12:19:52.955647750 +0200
+++ /var/tmp/diff_new_pack.Foqjmt/_new 2020-04-04 12:19:52.959647753 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-gunicorn
#
-# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2020 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-gunicorn
-Version: 19.9.0
+Version: 19.10.0
Release: 0
Summary: WSGI HTTP Server for UNIX
License: MIT
@@ -73,7 +73,7 @@
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
-%python_exec setup.py test
+%pytest
%post
%python_install_alternative gunicorn
++++++ gunicorn-19.9.0.tar.gz -> gunicorn-19.10.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/PKG-INFO
new/gunicorn-19.10.0/PKG-INFO
--- old/gunicorn-19.9.0/PKG-INFO 2018-07-03 22:11:42.000000000 +0200
+++ new/gunicorn-19.10.0/PKG-INFO 2019-11-23 10:51:47.701091800 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: gunicorn
-Version: 19.9.0
+Version: 19.10.0
Summary: WSGI HTTP Server for UNIX
Home-page: http://gunicorn.org
Author: Benoit Chesneau
@@ -88,6 +88,7 @@
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Internet
Classifier: Topic :: Utilities
Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -96,7 +97,7 @@
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=2.6, !=3.0.*, !=3.1.*
-Provides-Extra: tornado
Provides-Extra: gevent
Provides-Extra: eventlet
+Provides-Extra: tornado
Provides-Extra: gthread
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/THANKS new/gunicorn-19.10.0/THANKS
--- old/gunicorn-19.9.0/THANKS 2018-06-29 20:06:23.000000000 +0200
+++ new/gunicorn-19.10.0/THANKS 2019-11-23 10:49:49.000000000 +0100
@@ -173,3 +173,4 @@
Xie Shi <[email protected]>
Yue Du <[email protected]>
zakdances <[email protected]>
+Emile Fugulin <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/docs/site/index.html
new/gunicorn-19.10.0/docs/site/index.html
--- old/gunicorn-19.9.0/docs/site/index.html 2018-07-03 22:08:39.000000000
+0200
+++ new/gunicorn-19.10.0/docs/site/index.html 2019-11-23 10:49:49.000000000
+0100
@@ -16,7 +16,7 @@
<div class="logo-div">
<div class="latest">
Latest version: <strong><a
- href="http://docs.gunicorn.org/en/stable">19.9.0</a></strong>
+ href="https://docs.gunicorn.org/en/stable/">19.9.0</a></strong>
</div>
<div class="logo"><img src="images/logo.jpg" ></div>
@@ -179,7 +179,7 @@
</div>
</div>
- <script
src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+ <script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="js/main.js"></script>
</body>
</html>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/docs/source/settings.rst
new/gunicorn-19.10.0/docs/source/settings.rst
--- old/gunicorn-19.9.0/docs/source/settings.rst 2018-07-03
22:08:22.000000000 +0200
+++ new/gunicorn-19.10.0/docs/source/settings.rst 2019-11-23
10:49:50.000000000 +0100
@@ -829,7 +829,7 @@
~~~~~
* ``--chdir``
-* ``/Users/randall/src/gunicorn/docs/source``
+* ``/usr/src/app``
Chdir to specified directory before apps loading.
@@ -1235,7 +1235,7 @@
The maximum number of requests a worker will process before restarting.
-Any value greater than zero will limit the number of requests a work
+Any value greater than zero will limit the number of requests a worker
will process before automatically restarting. This is a simple method
to help limit the damage of memory leaks.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/examples/echo.py
new/gunicorn-19.10.0/examples/echo.py
--- old/gunicorn-19.9.0/examples/echo.py 2018-04-30 20:14:25.000000000
+0200
+++ new/gunicorn-19.10.0/examples/echo.py 2019-11-23 10:49:50.000000000
+0100
@@ -24,8 +24,7 @@
response_headers = [
('Content-type', 'text/plain'),
('Content-Length', str(len(data))),
- ('X-Gunicorn-Version', __version__),
- ("Test", "test тест"),
+ ('X-Gunicorn-Version', __version__)
]
start_response(status, response_headers)
return iter([data])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/examples/log_app.py
new/gunicorn-19.10.0/examples/log_app.py
--- old/gunicorn-19.9.0/examples/log_app.py 2017-06-29 22:27:41.000000000
+0200
+++ new/gunicorn-19.10.0/examples/log_app.py 2019-11-19 22:58:55.000000000
+0100
@@ -13,4 +13,4 @@
log.info("Hello Info!")
log.warn("Hello Warn!")
log.error("Hello Error!")
- return ["Hello World!\n"]
+ return [b"Hello World!\n"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/examples/readline.py
new/gunicorn-19.10.0/examples/readline.py
--- old/gunicorn-19.9.0/examples/readline.py 2017-09-18 03:14:58.000000000
+0200
+++ new/gunicorn-19.10.0/examples/readline.py 1970-01-01 01:00:00.000000000
+0100
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -
-#
-# This file is part of gunicorn released under the MIT license.
-# See the NOTICE for more information.
-#
-# Simple example of readline, reading from a stream then echoing the response
-#
-# Usage:
-#
-# Launch a server with the app in a terminal
-#
-# $ gunicorn -w3 readline:app
-#
-# Then in another terminal launch the following command:
-#
-# $ curl -XPOST -d'test\r\ntest2\r\n' -H"Transfer-Encoding: Chunked"
http://localhost:8000
-
-
-
-from gunicorn import __version__
-
-
-def app(environ, start_response):
- """Simplest possible application object"""
- status = '200 OK'
-
- response_headers = [
- ('Content-type', 'text/plain'),
- ('Transfer-Encoding', "chunked"),
- ('X-Gunicorn-Version', __version__),
- #("Test", "test тест"),
- ]
- start_response(status, response_headers)
-
- body = environ['wsgi.input']
-
- lines = []
- while True:
- line = body.readline()
- if line == b"":
- break
- print(line)
- lines.append(line)
-
- return iter(lines)
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/examples/readline_app.py
new/gunicorn-19.10.0/examples/readline_app.py
--- old/gunicorn-19.9.0/examples/readline_app.py 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/examples/readline_app.py 2019-11-19
22:58:55.000000000 +0100
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -
+#
+# This file is part of gunicorn released under the MIT license.
+# See the NOTICE for more information.
+#
+# Simple example of readline, reading from a stream then echoing the response
+#
+# Usage:
+#
+# Launch a server with the app in a terminal
+#
+# $ gunicorn -w3 readline_app:app
+#
+# Then in another terminal launch the following command:
+#
+# $ curl -XPOST -d'test\r\ntest2\r\n' -H"Transfer-Encoding: Chunked"
http://localhost:8000
+
+
+
+from gunicorn import __version__
+
+
+def app(environ, start_response):
+ """Simplest possible application object"""
+ status = '200 OK'
+
+ response_headers = [
+ ('Content-type', 'text/plain'),
+ ('Transfer-Encoding', "chunked"),
+ ('X-Gunicorn-Version', __version__)
+ ]
+ start_response(status, response_headers)
+
+ body = environ['wsgi.input']
+
+ lines = []
+ while True:
+ line = body.readline()
+ if line == b"":
+ break
+ print(line)
+ lines.append(line)
+
+ return iter(lines)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/examples/test.py
new/gunicorn-19.10.0/examples/test.py
--- old/gunicorn-19.9.0/examples/test.py 2017-09-18 03:14:58.000000000
+0200
+++ new/gunicorn-19.10.0/examples/test.py 2019-11-22 14:10:19.000000000
+0100
@@ -21,7 +21,7 @@
('Content-type', 'text/plain'),
('Content-Length', str(len(data))),
('X-Gunicorn-Version', __version__),
- #("Test", "test тест"),
+ ('Foo', 'B\u00e5r'), # Foo: Bår
]
start_response(status, response_headers)
return iter([data])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/__init__.py
new/gunicorn-19.10.0/gunicorn/__init__.py
--- old/gunicorn-19.9.0/gunicorn/__init__.py 2018-07-03 22:08:44.000000000
+0200
+++ new/gunicorn-19.10.0/gunicorn/__init__.py 2019-11-23 10:50:53.000000000
+0100
@@ -3,6 +3,6 @@
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
-version_info = (19, 9, 0)
+version_info = (19, 10, 0)
__version__ = ".".join([str(v) for v in version_info])
SERVER_SOFTWARE = "gunicorn/%s" % __version__
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/app/wsgiapp.py
new/gunicorn-19.10.0/gunicorn/app/wsgiapp.py
--- old/gunicorn-19.9.0/gunicorn/app/wsgiapp.py 2018-06-29 20:06:20.000000000
+0200
+++ new/gunicorn-19.10.0/gunicorn/app/wsgiapp.py 2019-11-23
10:49:50.000000000 +0100
@@ -30,7 +30,7 @@
from .pasterapp import paste_config
return paste_config(self.cfg, self.cfgurl, self.relpath)
- if len(args) < 1:
+ if not args:
parser.error("No application module specified.")
self.cfg.set("default_proc_name", args[0])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/arbiter.py
new/gunicorn-19.10.0/gunicorn/arbiter.py
--- old/gunicorn-19.9.0/gunicorn/arbiter.py 2018-01-22 20:16:44.000000000
+0100
+++ new/gunicorn-19.10.0/gunicorn/arbiter.py 2019-11-23 10:49:50.000000000
+0100
@@ -377,8 +377,11 @@
:attr graceful: boolean, If True (the default) workers will be
killed gracefully (ie. trying to wait for the current connection)
"""
-
- unlink = self.reexec_pid == self.master_pid == 0 and not self.systemd
+ unlink = (
+ self.reexec_pid == self.master_pid == 0
+ and not self.systemd
+ and not self.cfg.reuse_port
+ )
sock.close_sockets(self.LISTENERS, unlink)
self.LISTENERS = []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/config.py
new/gunicorn-19.10.0/gunicorn/config.py
--- old/gunicorn-19.9.0/gunicorn/config.py 2018-05-26 20:25:25.000000000
+0200
+++ new/gunicorn-19.10.0/gunicorn/config.py 2019-11-23 10:49:50.000000000
+0100
@@ -611,7 +611,7 @@
A string referring to one of the following bundled classes:
* ``sync``
- * ``eventlet`` - Requires eventlet >= 0.9.7 (or install it via
+ * ``eventlet`` - Requires eventlet >= 0.24.1 (or install it via
``pip install gunicorn[eventlet]``)
* ``gevent`` - Requires gevent >= 0.13 (or install it via
``pip install gunicorn[gevent]``)
@@ -686,7 +686,7 @@
desc = """\
The maximum number of requests a worker will process before restarting.
- Any value greater than zero will limit the number of requests a work
+ Any value greater than zero will limit the number of requests a worker
will process before automatically restarting. This is a simple method
to help limit the damage of memory leaks.
@@ -1948,3 +1948,20 @@
.. versionadded:: 19.7
"""
+
+
+class StripHeaderSpaces(Setting):
+ name = "strip_header_spaces"
+ section = "Server Mechanics"
+ cli = ["--strip-header-spaces"]
+ validator = validate_bool
+ action = "store_true"
+ default = False
+ desc = """\
+ Strip spaces present between the header name and the the ``:``.
+
+ This is known to induce vulnerabilities and is not compliant with the
HTTP/1.1 standard.
+ See
https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn.
+
+ Use with care and only if necessary.
+ """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/glogging.py
new/gunicorn-19.10.0/gunicorn/glogging.py
--- old/gunicorn-19.9.0/gunicorn/glogging.py 2018-05-26 20:25:25.000000000
+0200
+++ new/gunicorn-19.10.0/gunicorn/glogging.py 2019-11-23 10:49:50.000000000
+0100
@@ -54,8 +54,8 @@
version=1,
disable_existing_loggers=False,
+ root={"level": "INFO", "handlers": ["console"]},
loggers={
- "root": {"level": "INFO", "handlers": ["console"]},
"gunicorn.error": {
"level": "INFO",
"handlers": ["error_console"],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/http/message.py
new/gunicorn-19.10.0/gunicorn/http/message.py
--- old/gunicorn-19.9.0/gunicorn/http/message.py 2018-04-30
20:34:58.000000000 +0200
+++ new/gunicorn-19.10.0/gunicorn/http/message.py 2019-11-23
10:49:50.000000000 +0100
@@ -15,7 +15,7 @@
LimitRequestLine, LimitRequestHeaders)
from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
from gunicorn.http.errors import InvalidSchemeHeaders
-from gunicorn.six import BytesIO, string_types
+from gunicorn.six import BytesIO
from gunicorn.util import split_request_uri
MAX_REQUEST_LINE = 8190
@@ -72,11 +72,11 @@
secure_scheme_headers = cfg.secure_scheme_headers
elif isinstance(self.unreader, SocketUnreader):
remote_addr = self.unreader.sock.getpeername()
- if isinstance(remote_addr, tuple):
+ if self.unreader.sock.family in (socket.AF_INET, socket.AF_INET6):
remote_host = remote_addr[0]
if remote_host in cfg.forwarded_allow_ips:
secure_scheme_headers = cfg.secure_scheme_headers
- elif isinstance(remote_addr, string_types):
+ elif self.unreader.sock.family == socket.AF_UNIX:
secure_scheme_headers = cfg.secure_scheme_headers
# Parse headers into key/value pairs paying attention
@@ -91,7 +91,10 @@
if curr.find(":") < 0:
raise InvalidHeader(curr.strip())
name, value = curr.split(":", 1)
- name = name.rstrip(" \t").upper()
+ if self.cfg.strip_header_spaces:
+ name = name.rstrip(" \t").upper()
+ else:
+ name = name.upper()
if HEADER_RE.search(name):
raise InvalidHeaderName(name)
@@ -129,9 +132,12 @@
content_length = None
for (name, value) in self.headers:
if name == "CONTENT-LENGTH":
+ if content_length is not None:
+ raise InvalidHeader("CONTENT-LENGTH", req=self)
content_length = value
elif name == "TRANSFER-ENCODING":
- chunked = value.lower() == "chunked"
+ if value.lower() == "chunked":
+ chunked = True
elif name == "SEC-WEBSOCKET-KEY1":
content_length = 8
@@ -243,7 +249,7 @@
if idx > limit > 0:
raise LimitRequestLine(idx, limit)
break
- elif len(data) - 2 > limit > 0:
+ if len(data) - 2 > limit > 0:
raise LimitRequestLine(len(data), limit)
self.get_data(unreader, buf)
data = buf.getvalue()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/http/wsgi.py
new/gunicorn-19.10.0/gunicorn/http/wsgi.py
--- old/gunicorn-19.9.0/gunicorn/http/wsgi.py 2018-05-26 20:25:25.000000000
+0200
+++ new/gunicorn-19.10.0/gunicorn/http/wsgi.py 2019-11-23 10:49:50.000000000
+0100
@@ -84,6 +84,7 @@
"wsgi.multiprocess": (cfg.workers > 1),
"wsgi.run_once": False,
"wsgi.file_wrapper": FileWrapper,
+ "wsgi.input_terminated": True,
"SERVER_SOFTWARE": SERVER_SOFTWARE,
}
@@ -141,6 +142,7 @@
continue
elif hdr_name == "CONTENT-LENGTH":
environ['CONTENT_LENGTH'] = hdr_value
+ environ['wsgi.input_terminated'] = False
continue
key = 'HTTP_' + hdr_name.replace('-', '_')
@@ -326,7 +328,7 @@
tosend.extend(["%s: %s\r\n" % (k, v) for k, v in self.headers])
header_str = "%s\r\n" % "".join(tosend)
- util.write(self.sock, util.to_bytestring(header_str, "ascii"))
+ util.write(self.sock, util.to_bytestring(header_str, "latin-1"))
self.headers_sent = True
def write(self, arg):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/reloader.py
new/gunicorn-19.10.0/gunicorn/reloader.py
--- old/gunicorn-19.9.0/gunicorn/reloader.py 2018-06-17 22:17:58.000000000
+0200
+++ new/gunicorn-19.10.0/gunicorn/reloader.py 2019-11-23 10:49:50.000000000
+0100
@@ -2,6 +2,7 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
+# pylint: disable=no-else-continue
import os
import os.path
@@ -96,7 +97,7 @@
fnames = [
os.path.dirname(COMPILED_EXT_RE.sub('py', module.__file__))
for module in tuple(sys.modules.values())
- if hasattr(module, '__file__')
+ if getattr(module, '__file__', None)
]
return set(fnames)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/sock.py
new/gunicorn-19.10.0/gunicorn/sock.py
--- old/gunicorn-19.9.0/gunicorn/sock.py 2018-01-22 20:16:44.000000000
+0100
+++ new/gunicorn-19.10.0/gunicorn/sock.py 2019-11-23 10:49:50.000000000
+0100
@@ -11,7 +11,7 @@
import time
from gunicorn import util
-from gunicorn.six import string_types
+from gunicorn.six import string_types, binary_type
class BaseSocket(object):
@@ -133,7 +133,7 @@
sock_type = TCP6Socket
else:
sock_type = TCPSocket
- elif isinstance(addr, string_types):
+ elif isinstance(addr, (string_types, binary_type)):
sock_type = UnixSocket
else:
raise TypeError("Unable to create socket from: %r" % addr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/util.py
new/gunicorn-19.10.0/gunicorn/util.py
--- old/gunicorn-19.9.0/gunicorn/util.py 2018-04-28 04:13:16.000000000
+0200
+++ new/gunicorn-19.10.0/gunicorn/util.py 2019-11-23 10:49:50.000000000
+0100
@@ -51,7 +51,7 @@
setproctitle("gunicorn: %s" % title)
except ImportError:
def _setproctitle(title):
- return
+ pass
try:
@@ -342,7 +342,7 @@
def import_app(module):
parts = module.split(":", 1)
if len(parts) == 1:
- module, obj = module, "application"
+ obj = "application"
else:
module, obj = parts[0], parts[1]
@@ -352,8 +352,7 @@
if module.endswith(".py") and os.path.exists(module):
msg = "Failed to find application, did you mean '%s:%s'?"
raise ImportError(msg % (module.rsplit(".", 1)[0], obj))
- else:
- raise
+ raise
mod = sys.modules[module]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/workers/base.py
new/gunicorn-19.10.0/gunicorn/workers/base.py
--- old/gunicorn-19.9.0/gunicorn/workers/base.py 2018-06-17
22:17:58.000000000 +0200
+++ new/gunicorn-19.10.0/gunicorn/workers/base.py 2019-11-23
10:49:50.000000000 +0100
@@ -18,7 +18,7 @@
from gunicorn.reloader import reloader_engines
from gunicorn.http.errors import (
InvalidHeader, InvalidHeaderName, InvalidRequestLine, InvalidRequestMethod,
- InvalidHTTPVersion, LimitRequestLine, LimitRequestHeaders,
+ InvalidHTTPVersion, LimitRequestLine, LimitRequestHeaders
)
from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
from gunicorn.http.errors import InvalidSchemeHeaders
@@ -51,8 +51,13 @@
self.reloader = None
self.nr = 0
- jitter = randint(0, cfg.max_requests_jitter)
- self.max_requests = cfg.max_requests + jitter or MAXSIZE
+
+ if cfg.max_requests > 0:
+ jitter = randint(0, cfg.max_requests_jitter)
+ self.max_requests = cfg.max_requests + jitter
+ else:
+ self.max_requests = MAXSIZE
+
self.alive = True
self.log = log
self.tmp = WorkerTmp(cfg)
@@ -117,6 +122,7 @@
def changed(fname):
self.log.info("Worker reloading: %s modified", fname)
self.alive = False
+ os.write(self.PIPE[1], b"1")
self.cfg.worker_int(self)
time.sleep(0.1)
sys.exit(0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/workers/geventlet.py
new/gunicorn-19.10.0/gunicorn/workers/geventlet.py
--- old/gunicorn-19.9.0/gunicorn/workers/geventlet.py 2018-06-29
20:06:23.000000000 +0200
+++ new/gunicorn-19.10.0/gunicorn/workers/geventlet.py 2019-11-23
10:49:50.000000000 +0100
@@ -10,12 +10,11 @@
try:
import eventlet
except ImportError:
- raise RuntimeError("You need eventlet installed to use this worker.")
+ raise RuntimeError("eventlet worker requires eventlet 0.24.1 or higher")
# validate the eventlet version
-if eventlet.version_info < (0, 9, 7):
- raise RuntimeError("You need eventlet >= 0.9.7")
-
+if eventlet.version_info < (0, 24, 1):
+ raise RuntimeError("eventlet worker requires eventlet 0.24.1 or higher")
from eventlet import hubs, greenthread
from eventlet.greenio import GreenSocket
@@ -95,12 +94,11 @@
def is_already_handled(self, respiter):
if respiter == EVENTLET_ALREADY_HANDLED:
raise StopIteration()
- else:
- return super(EventletWorker, self).is_already_handled(respiter)
+ return super(EventletWorker, self).is_already_handled(respiter)
def init_process(self):
- super(EventletWorker, self).init_process()
self.patch()
+ super(EventletWorker, self).init_process()
def handle_quit(self, sig, frame):
eventlet.spawn(super(EventletWorker, self).handle_quit, sig, frame)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/workers/gthread.py
new/gunicorn-19.10.0/gunicorn/workers/gthread.py
--- old/gunicorn-19.9.0/gunicorn/workers/gthread.py 2018-04-28
04:13:16.000000000 +0200
+++ new/gunicorn-19.10.0/gunicorn/workers/gthread.py 2019-11-23
10:49:50.000000000 +0100
@@ -9,6 +9,7 @@
# keepalive connections are put back in the loop waiting for an event.
# If no event happen after the keep alive timeout, the connectoin is
# closed.
+# pylint: disable=no-else-break
from collections import deque
from datetime import datetime
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn/workers/gtornado.py
new/gunicorn-19.10.0/gunicorn/workers/gtornado.py
--- old/gunicorn-19.9.0/gunicorn/workers/gtornado.py 2018-04-28
04:13:16.000000000 +0200
+++ new/gunicorn-19.10.0/gunicorn/workers/gtornado.py 2019-11-23
10:49:50.000000000 +0100
@@ -69,6 +69,14 @@
if not self.ioloop._callbacks:
self.ioloop.stop()
+ def init_process(self):
+ # IOLoop cannot survive a fork or be shared across processes
+ # in any way. When multiple processes are being used, each process
+ # should create its own IOLoop. We should clear current IOLoop
+ # if exists before os.fork.
+ IOLoop.clear_current()
+ super(TornadoWorker, self).init_process()
+
def run(self):
self.ioloop = IOLoop.instance()
self.alive = True
@@ -84,9 +92,11 @@
# instance of tornado.web.Application or is an
# instance of tornado.wsgi.WSGIApplication
app = self.wsgi
- if not isinstance(app, tornado.web.Application) or \
- isinstance(app, tornado.wsgi.WSGIApplication):
- app = WSGIContainer(app)
+
+ if tornado.version_info[0] < 6:
+ if not isinstance(app, tornado.web.Application) or \
+ isinstance(app, tornado.wsgi.WSGIApplication):
+ app = WSGIContainer(app)
# Monkey-patching HTTPConnection.finish to count the
# number of requests being handled by Tornado. This
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn.egg-info/PKG-INFO
new/gunicorn-19.10.0/gunicorn.egg-info/PKG-INFO
--- old/gunicorn-19.9.0/gunicorn.egg-info/PKG-INFO 2018-07-03
22:11:41.000000000 +0200
+++ new/gunicorn-19.10.0/gunicorn.egg-info/PKG-INFO 2019-11-23
10:51:47.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: gunicorn
-Version: 19.9.0
+Version: 19.10.0
Summary: WSGI HTTP Server for UNIX
Home-page: http://gunicorn.org
Author: Benoit Chesneau
@@ -88,6 +88,7 @@
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Internet
Classifier: Topic :: Utilities
Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -96,7 +97,7 @@
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=2.6, !=3.0.*, !=3.1.*
-Provides-Extra: tornado
Provides-Extra: gevent
Provides-Extra: eventlet
+Provides-Extra: tornado
Provides-Extra: gthread
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/gunicorn.egg-info/SOURCES.txt
new/gunicorn-19.10.0/gunicorn.egg-info/SOURCES.txt
--- old/gunicorn-19.9.0/gunicorn.egg-info/SOURCES.txt 2018-07-03
22:11:41.000000000 +0200
+++ new/gunicorn-19.10.0/gunicorn.egg-info/SOURCES.txt 2019-11-23
10:51:47.000000000 +0100
@@ -92,7 +92,7 @@
examples/multidomainapp.py
examples/nginx.conf
examples/read_django_settings.py
-examples/readline.py
+examples/readline_app.py
examples/sendfile.py
examples/server.crt
examples/server.key
@@ -232,6 +232,10 @@
tests/requests/invalid/018.py
tests/requests/invalid/019.http
tests/requests/invalid/019.py
+tests/requests/invalid/020.http
+tests/requests/invalid/020.py
+tests/requests/invalid/021.http
+tests/requests/invalid/021.py
tests/requests/invalid/pp_01.http
tests/requests/invalid/pp_01.py
tests/requests/invalid/pp_02.http
@@ -290,6 +294,12 @@
tests/requests/valid/026.py
tests/requests/valid/027.http
tests/requests/valid/027.py
+tests/requests/valid/028.http
+tests/requests/valid/028.py
+tests/requests/valid/029.http
+tests/requests/valid/029.py
+tests/requests/valid/030.http
+tests/requests/valid/030.py
tests/requests/valid/099.http
tests/requests/valid/099.py
tests/requests/valid/100.http
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/setup.py
new/gunicorn-19.10.0/setup.py
--- old/gunicorn-19.9.0/setup.py 2018-04-28 04:13:16.000000000 +0200
+++ new/gunicorn-19.10.0/setup.py 2019-11-23 10:49:50.000000000 +0100
@@ -29,6 +29,7 @@
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
'Topic :: Internet',
'Topic :: Utilities',
'Topic :: Software Development :: Libraries :: Python Modules',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/invalid/020.http
new/gunicorn-19.10.0/tests/requests/invalid/020.http
--- old/gunicorn-19.9.0/tests/requests/invalid/020.http 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/invalid/020.http 2019-11-20
21:51:41.000000000 +0100
@@ -0,0 +1,4 @@
+GET /stuff/here?foo=bar HTTP/1.1\r\n
+Content-Length : 3\r\n
+\r\n
+xyz
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/invalid/020.py
new/gunicorn-19.10.0/tests/requests/invalid/020.py
--- old/gunicorn-19.9.0/tests/requests/invalid/020.py 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/invalid/020.py 2019-11-20
21:51:41.000000000 +0100
@@ -0,0 +1,5 @@
+from gunicorn.config import Config
+from gunicorn.http.errors import InvalidHeaderName
+
+cfg = Config()
+request = InvalidHeaderName
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/invalid/021.http
new/gunicorn-19.10.0/tests/requests/invalid/021.http
--- old/gunicorn-19.9.0/tests/requests/invalid/021.http 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/invalid/021.http 2019-11-20
21:52:43.000000000 +0100
@@ -0,0 +1,5 @@
+GET /stuff/here?foo=bar HTTP/1.1\r\n
+Content-Length: 3\r\n
+Content-Length: 2\r\n
+\r\n
+xyz
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/invalid/021.py
new/gunicorn-19.10.0/tests/requests/invalid/021.py
--- old/gunicorn-19.9.0/tests/requests/invalid/021.py 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/invalid/021.py 2019-11-20
21:52:43.000000000 +0100
@@ -0,0 +1,5 @@
+from gunicorn.config import Config
+from gunicorn.http.errors import InvalidHeader
+
+cfg = Config()
+request = InvalidHeader
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/valid/028.http
new/gunicorn-19.10.0/tests/requests/valid/028.http
--- old/gunicorn-19.9.0/tests/requests/valid/028.http 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/valid/028.http 2019-11-20
21:51:41.000000000 +0100
@@ -0,0 +1,4 @@
+GET /stuff/here?foo=bar HTTP/1.1\r\n
+Content-Length : 3\r\n
+\r\n
+xyz
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/valid/028.py
new/gunicorn-19.10.0/tests/requests/valid/028.py
--- old/gunicorn-19.9.0/tests/requests/valid/028.py 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/valid/028.py 2019-11-20
21:51:41.000000000 +0100
@@ -0,0 +1,14 @@
+from gunicorn.config import Config
+
+cfg = Config()
+cfg.set("strip_header_spaces", True)
+
+request = {
+ "method": "GET",
+ "uri": uri("/stuff/here?foo=bar"),
+ "version": (1, 1),
+ "headers": [
+ ("CONTENT-LENGTH", "3"),
+ ],
+ "body": b"xyz"
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/valid/029.http
new/gunicorn-19.10.0/tests/requests/valid/029.http
--- old/gunicorn-19.9.0/tests/requests/valid/029.http 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/valid/029.http 2019-11-20
21:52:43.000000000 +0100
@@ -0,0 +1,7 @@
+GET /stuff/here?foo=bar HTTP/1.1\r\n
+Transfer-Encoding: chunked\r\n
+Transfer-Encoding: identity\r\n
+\r\n
+5\r\n
+hello\r\n
+000\r\n
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/valid/029.py
new/gunicorn-19.10.0/tests/requests/valid/029.py
--- old/gunicorn-19.9.0/tests/requests/valid/029.py 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/valid/029.py 2019-11-20
21:52:43.000000000 +0100
@@ -0,0 +1,14 @@
+from gunicorn.config import Config
+
+cfg = Config()
+
+request = {
+ "method": "GET",
+ "uri": uri("/stuff/here?foo=bar"),
+ "version": (1, 1),
+ "headers": [
+ ('TRANSFER-ENCODING', 'chunked'),
+ ('TRANSFER-ENCODING', 'identity')
+ ],
+ "body": b"hello"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/valid/030.http
new/gunicorn-19.10.0/tests/requests/valid/030.http
--- old/gunicorn-19.9.0/tests/requests/valid/030.http 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/valid/030.http 2019-11-20
21:52:43.000000000 +0100
@@ -0,0 +1,7 @@
+GET /stuff/here?foo=bar HTTP/1.1\r\n
+Transfer-Encoding: identity\r\n
+Transfer-Encoding: chunked\r\n
+\r\n
+5\r\n
+hello\r\n
+000\r\n
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/requests/valid/030.py
new/gunicorn-19.10.0/tests/requests/valid/030.py
--- old/gunicorn-19.9.0/tests/requests/valid/030.py 1970-01-01
01:00:00.000000000 +0100
+++ new/gunicorn-19.10.0/tests/requests/valid/030.py 2019-11-20
21:52:43.000000000 +0100
@@ -0,0 +1,14 @@
+from gunicorn.config import Config
+
+cfg = Config()
+
+request = {
+ "method": "GET",
+ "uri": uri("/stuff/here?foo=bar"),
+ "version": (1, 1),
+ "headers": [
+ ('TRANSFER-ENCODING', 'identity'),
+ ('TRANSFER-ENCODING', 'chunked')
+ ],
+ "body": b"hello"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/test_arbiter.py
new/gunicorn-19.10.0/tests/test_arbiter.py
--- old/gunicorn-19.9.0/tests/test_arbiter.py 2017-09-18 03:14:58.000000000
+0200
+++ new/gunicorn-19.10.0/tests/test_arbiter.py 2019-11-23 10:49:50.000000000
+0100
@@ -12,6 +12,7 @@
import gunicorn.app.base
import gunicorn.arbiter
+from gunicorn.config import ReusePort
class DummyApplication(gunicorn.app.base.BaseApplication):
@@ -63,6 +64,15 @@
arbiter.stop()
close_sockets.assert_called_with([], False)
+
[email protected]('gunicorn.sock.close_sockets')
+def test_arbiter_stop_does_not_unlink_when_using_reuse_port(close_sockets):
+ arbiter = gunicorn.arbiter.Arbiter(DummyApplication())
+ arbiter.cfg.settings['reuse_port'] = ReusePort()
+ arbiter.cfg.settings['reuse_port'].set(True)
+ arbiter.stop()
+ close_sockets.assert_called_with([], False)
+
@mock.patch('os.getpid')
@mock.patch('os.fork')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/test_http.py
new/gunicorn-19.10.0/tests/test_http.py
--- old/gunicorn-19.9.0/tests/test_http.py 2018-01-22 20:16:44.000000000
+0100
+++ new/gunicorn-19.10.0/tests/test_http.py 2019-11-23 10:49:50.000000000
+0100
@@ -81,8 +81,13 @@
mocked_request = mock.MagicMock()
response = Response(mocked_request, mocked_socket, None)
- # set umlaut header
+ # set umlaut header value - latin-1 is OK
response.headers.append(('foo', u'häder'))
+ response.send_headers()
+
+ # set a-breve header value - unicode, non-latin-1 fails
+ response = Response(mocked_request, mocked_socket, None)
+ response.headers.append(('apple', u'măr'))
with pytest.raises(UnicodeEncodeError):
response.send_headers()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/gunicorn-19.9.0/tests/test_sock.py
new/gunicorn-19.10.0/tests/test_sock.py
--- old/gunicorn-19.9.0/tests/test_sock.py 2017-09-18 03:14:58.000000000
+0200
+++ new/gunicorn-19.10.0/tests/test_sock.py 2019-11-23 10:49:50.000000000
+0100
@@ -11,6 +11,27 @@
from gunicorn import sock
[email protected]('os.stat')
+def test_create_sockets_unix_bytes(stat):
+ conf = mock.Mock(address=[b'127.0.0.1:8000'])
+ log = mock.Mock()
+ with mock.patch.object(sock.UnixSocket, '__init__', lambda *args: None):
+ listeners = sock.create_sockets(conf, log)
+ assert len(listeners) == 1
+ print(type(listeners[0]))
+ assert isinstance(listeners[0], sock.UnixSocket)
+
+
[email protected]('os.stat')
+def test_create_sockets_unix_strings(stat):
+ conf = mock.Mock(address=['127.0.0.1:8000'])
+ log = mock.Mock()
+ with mock.patch.object(sock.UnixSocket, '__init__', lambda *args: None):
+ listeners = sock.create_sockets(conf, log)
+ assert len(listeners) == 1
+ assert isinstance(listeners[0], sock.UnixSocket)
+
+
def test_socket_close():
listener1 = mock.Mock()
listener1.getsockname.return_value = ('127.0.0.1', '80')