Bump cheroot from 8.5.2 to 8.6.0 (#1603)

* Bump cheroot from 8.5.2 to 8.6.0

Bumps [cheroot](https://github.com/cherrypy/cheroot) from 8.5.2 to 8.6.0.
- [Release notes](https://github.com/cherrypy/cheroot/releases)
- [Changelog](https://github.com/cherrypy/cheroot/blob/master/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cheroot/compare/v8.5.2...v8.6.0)

---
updated-dependencies:
- dependency-name: cheroot
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update cheroot==8.6.0

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2022-01-04 13:20:50 -08:00 committed by GitHub
commit 3689834051
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 708 additions and 113 deletions

View file

@ -35,4 +35,16 @@ def pytest_load_initial_conftests(early_config, parser, args):
'<socket.socket fd=-1, family=AddressFamily.AF_INET6, '
'type=SocketKind.SOCK_STREAM, proto=.:'
'pytest.PytestUnraisableExceptionWarning:_pytest.unraisableexception',
'ignore:Exception ignored in. '
'<socket.socket fd=-1, family=AF_INET, '
'type=SocketKind.SOCK_STREAM, proto=.:'
'pytest.PytestUnraisableExceptionWarning:_pytest.unraisableexception',
'ignore:Exception ignored in. '
'<socket.socket fd=-1, family=AF_INET6, '
'type=SocketKind.SOCK_STREAM, proto=.:'
'pytest.PytestUnraisableExceptionWarning:_pytest.unraisableexception',
'ignore:Exception ignored in. '
'<ssl.SSLSocket fd=-1, family=AddressFamily.AF_UNIX, '
'type=SocketKind.SOCK_STREAM, proto=.:'
'pytest.PytestUnraisableExceptionWarning:_pytest.unraisableexception',
))

View file

@ -5,7 +5,7 @@ itself, useless for end-users' app testing.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
__metaclass__ = type # pylint: disable=invalid-name
import threading
import time
@ -13,19 +13,21 @@ import time
import pytest
from ..server import Gateway, HTTPServer
from ..testing import ( # noqa: F401
from ..testing import ( # noqa: F401 # pylint: disable=unused-import
native_server, wsgi_server,
)
from ..testing import get_server_client
@pytest.fixture
# pylint: disable=redefined-outer-name
def wsgi_server_client(wsgi_server): # noqa: F811
"""Create a test client out of given WSGI server."""
return get_server_client(wsgi_server)
@pytest.fixture
# pylint: disable=redefined-outer-name
def native_server_client(native_server): # noqa: F811
"""Create a test client out of given HTTP server."""
return get_server_client(native_server)
@ -43,7 +45,7 @@ def http_server():
yield httpserver
srv_creator = iter(start_srv())
next(srv_creator)
next(srv_creator) # pylint: disable=stop-iteration-return
yield srv_creator
try:
while True:

View file

@ -87,6 +87,7 @@ def wsgi_app(monkeypatch):
('main', 'main'),
),
)
# pylint: disable=invalid-name
def test_Aplication_resolve(app_name, app_method, wsgi_app):
"""Check the wsgi application name conversion."""
if app_name is None:

View file

@ -17,10 +17,13 @@ import pytest
from jaraco.text import trim, unwrap
from cheroot.test import helper, webtest
from cheroot._compat import IS_CI, IS_PYPY, IS_WINDOWS
from cheroot._compat import IS_CI, IS_MACOS, IS_PYPY, IS_WINDOWS
import cheroot.server
IS_SLOW_ENV = IS_MACOS or IS_WINDOWS
timeout = 1
pov = 'pPeErRsSiIsStTeEnNcCeE oOfF vViIsSiIoOnN'
@ -169,6 +172,7 @@ def testing_server(raw_testing_server, monkeypatch):
# Teardown verification, in case that the server logged an
# error that wasn't notified to the client or we just made a mistake.
# pylint: disable=possibly-unused-variable
for c_msg, c_level, c_traceback in raw_testing_server.error_log.calls:
if c_level <= logging.WARNING:
continue
@ -651,9 +655,13 @@ def test_broken_connection_during_tcp_fin(
mocker.mock_module.Mock(side_effect=exc_instance),
)
_close_kernel_socket.fin_spy = mocker.spy(self.socket, 'shutdown')
_close_kernel_socket.exception_leaked = True
old_close_kernel_socket(self)
_close_kernel_socket.exception_leaked = False
try:
old_close_kernel_socket(self)
except simulated_exception:
_close_kernel_socket.exception_leaked = True
else:
_close_kernel_socket.exception_leaked = False
monkeypatch.setattr(
test_client.server_instance.ConnectionClass,
@ -668,7 +676,8 @@ def test_broken_connection_during_tcp_fin(
conn.send(('Host: %s' % conn.host).encode('ascii'))
conn.close()
for _ in range(10): # Let the server attempt TCP shutdown
# Let the server attempt TCP shutdown:
for _ in range(10 * (2 if IS_SLOW_ENV else 1)):
time.sleep(0.1)
if hasattr(_close_kernel_socket, 'exception_leaked'):
break
@ -867,7 +876,7 @@ def test_100_Continue(test_client):
conn.endheaders()
conn.send(b"d'oh")
response = conn.response_class(conn.sock, method='POST')
version, status, reason = response._read_status()
_version, status, _reason = response._read_status()
assert status != 100
conn.close()
@ -900,7 +909,7 @@ def test_100_Continue(test_client):
# ...get the final response
response.begin()
status_line, actual_headers, actual_resp_body = webtest.shb(response)
status_line, _actual_headers, actual_resp_body = webtest.shb(response)
actual_status = int(status_line[:3])
assert actual_status == 200
expected_resp_body = ("thanks for '%s'" % body).encode()
@ -933,7 +942,7 @@ def test_readall_or_close(test_client, max_request_body_size):
response = conn.response_class(conn.sock, method='POST')
# ...assert and then skip the 100 response
version, status, reason = response._read_status()
_version, status, _reason = response._read_status()
assert status == 100
skip = True
while skip:
@ -944,7 +953,7 @@ def test_readall_or_close(test_client, max_request_body_size):
# ...get the final response
response.begin()
status_line, actual_headers, actual_resp_body = webtest.shb(response)
status_line, _actual_headers, actual_resp_body = webtest.shb(response)
actual_status = int(status_line[:3])
assert actual_status == 500
@ -1049,7 +1058,7 @@ def test_Chunked_Encoding(test_client):
conn.endheaders()
conn.send(body)
response = conn.getresponse()
status_line, actual_headers, actual_resp_body = webtest.shb(response)
status_line, _actual_headers, actual_resp_body = webtest.shb(response)
actual_status = int(status_line[:3])
assert actual_status == 200
assert status_line[4:] == 'OK'
@ -1089,7 +1098,7 @@ def test_Content_Length_in(test_client):
conn.putheader('Content-Length', '9999')
conn.endheaders()
response = conn.getresponse()
status_line, actual_headers, actual_resp_body = webtest.shb(response)
status_line, _actual_headers, actual_resp_body = webtest.shb(response)
actual_status = int(status_line[:3])
assert actual_status == 413
expected_resp_body = (
@ -1102,7 +1111,7 @@ def test_Content_Length_in(test_client):
def test_Content_Length_not_int(test_client):
"""Test that malicious Content-Length header returns 400."""
status_line, actual_headers, actual_resp_body = test_client.post(
status_line, _actual_headers, actual_resp_body = test_client.post(
'/upload',
headers=[
('Content-Type', 'text/plain'),
@ -1142,7 +1151,7 @@ def test_Content_Length_out(
conn.endheaders()
response = conn.getresponse()
status_line, actual_headers, actual_resp_body = webtest.shb(response)
status_line, _actual_headers, actual_resp_body = webtest.shb(response)
actual_status = int(status_line[:3])
assert actual_status == expected_resp_status
@ -1281,7 +1290,7 @@ def test_invalid_selected_connection(test_client, monkeypatch):
# request a page with connection keep-alive to make sure
# we'll have a connection to be modified.
resp_status, resp_headers, resp_body = test_client.request(
resp_status, _resp_headers, _resp_body = test_client.request(
'/page1', headers=[('Connection', 'Keep-Alive')],
)

View file

@ -43,6 +43,7 @@ class HelloController(helper.Controller):
def asterisk(req, resp):
"""Render request method value."""
# pylint: disable=possibly-unused-variable
method = req.environ.get('REQUEST_METHOD', 'NO METHOD FOUND')
tmpl = 'Got asterisk URI path with {method} method'
return tmpl.format(**locals())

View file

@ -4,7 +4,7 @@ import pytest
from cheroot import errors
from .._compat import IS_LINUX, IS_MACOS, IS_WINDOWS
from .._compat import IS_LINUX, IS_MACOS, IS_WINDOWS # noqa: WPS130
@pytest.mark.parametrize(

View file

@ -5,12 +5,10 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from contextlib import closing
import os
import socket
import tempfile
import threading
import time
import uuid
import pytest
@ -18,6 +16,7 @@ import requests
import requests_unixsocket
import six
from pypytools.gc.custom import DefaultGc
from six.moves import queue, urllib
from .._compat import bton, ntob
@ -30,6 +29,9 @@ from ..testing import (
)
IS_SLOW_ENV = IS_MACOS or IS_WINDOWS
unix_only_sock_test = pytest.mark.skipif(
not hasattr(socket, 'AF_UNIX'),
reason='UNIX domain sockets are only available under UNIX-based OS',
@ -181,7 +183,9 @@ def test_serving_is_false_and_stop_returns_after_ctrlc():
serve_thread.start()
# The thread should exit right away due to the interrupt.
serve_thread.join(httpserver.expiration_interval * 2)
serve_thread.join(
httpserver.expiration_interval * (4 if IS_SLOW_ENV else 2),
)
assert not serve_thread.is_alive()
assert not httpserver._connections._serving
@ -263,6 +267,7 @@ def test_peercreds_unix_sock(peercreds_enabled_server):
if isinstance(bind_addr, six.binary_type):
bind_addr = bind_addr.decode()
# pylint: disable=possibly-unused-variable
quoted = urllib.parse.quote(bind_addr, safe='')
unix_base_uri = 'http+unix://{quoted}'.format(**locals())
@ -295,6 +300,7 @@ def test_peercreds_unix_sock_with_lookup(peercreds_enabled_server):
if isinstance(bind_addr, six.binary_type):
bind_addr = bind_addr.decode()
# pylint: disable=possibly-unused-variable
quoted = urllib.parse.quote(bind_addr, safe='')
unix_base_uri = 'http+unix://{quoted}'.format(**locals())
@ -325,7 +331,7 @@ def test_peercreds_unix_sock_with_lookup(peercreds_enabled_server):
indirect=('resource_limit',),
)
@pytest.mark.usefixtures('many_open_sockets')
def test_high_number_of_file_descriptors(resource_limit):
def test_high_number_of_file_descriptors(native_server_client, resource_limit):
"""Test the server does not crash with a high file-descriptor value.
This test shouldn't cause a server crash when trying to access
@ -337,26 +343,24 @@ def test_high_number_of_file_descriptors(resource_limit):
# We want to force the server to use a file-descriptor with
# a number above resource_limit
# Create our server
httpserver = HTTPServer(
bind_addr=(ANY_INTERFACE_IPV4, EPHEMERAL_PORT), gateway=Gateway,
)
# Patch the method that processes
_old_process_conn = native_server_client.server_instance.process_conn
try:
# This will trigger a crash if select() is used in the implementation
with httpserver._run_in_thread():
# allow server to run long enough to invoke select()
time.sleep(1.0)
except: # noqa: E722
raise # only needed for `else` to work
else:
# We use closing here for py2-compat
with closing(socket.socket()) as sock:
# Check new sockets created are still above our target number
assert sock.fileno() >= resource_limit
finally:
# Stop our server
httpserver.stop()
def native_process_conn(conn):
native_process_conn.filenos.add(conn.socket.fileno())
return _old_process_conn(conn)
native_process_conn.filenos = set()
native_server_client.server_instance.process_conn = native_process_conn
# Trigger a crash if select() is used in the implementation
native_server_client.connect('/')
# Ensure that at least one connection got accepted, otherwise the
# follow-up check wouldn't make sense
assert len(native_process_conn.filenos) > 0
# Check at least one of the sockets created are above the target number
assert any(fn >= resource_limit for fn in native_process_conn.filenos)
if not IS_WINDOWS:
@ -365,6 +369,13 @@ if not IS_WINDOWS:
)
@pytest.fixture
def _garbage_bin():
"""Disable garbage collection when this fixture is in use."""
with DefaultGc().nogc():
yield
@pytest.fixture
def resource_limit(request):
"""Set the resource limit two times bigger then requested."""
@ -392,25 +403,26 @@ def resource_limit(request):
@pytest.fixture
def many_open_sockets(resource_limit):
def many_open_sockets(request, resource_limit):
"""Allocate a lot of file descriptors by opening dummy sockets."""
# NOTE: `@pytest.mark.usefixtures` doesn't work on fixtures which
# NOTE: forces us to invoke this one dynamically to avoid having an
# NOTE: unused argument.
request.getfixturevalue('_garbage_bin')
# Hoard a lot of file descriptors by opening and storing a lot of sockets
test_sockets = []
# Open a lot of file descriptors, so the next one the server
# opens is a high number
try:
for i in range(resource_limit):
for _ in range(resource_limit):
sock = socket.socket()
test_sockets.append(sock)
# NOTE: We used to interrupt the loop early but this doesn't seem
# NOTE: to work well in envs with indeterministic runtimes like
# NOTE: PyPy. It looks like sometimes it frees some file
# NOTE: descriptors in between running this fixture and the actual
# NOTE: test code so the early break has been removed to try
# NOTE: address that. The approach may need to be rethought if the
# NOTE: issue reoccurs. Another approach may be disabling the GC.
# If we reach a high enough number, we don't need to open more
if sock.fileno() >= resource_limit:
break
# Check we opened enough descriptors to reach a high number
the_highest_fileno = max(sock.fileno() for sock in test_sockets)
the_highest_fileno = test_sockets[-1].fileno()
assert the_highest_fileno >= resource_limit
yield the_highest_fileno
finally:

View file

@ -22,7 +22,7 @@ import six
import trustme
from .._compat import bton, ntob, ntou
from .._compat import IS_ABOVE_OPENSSL10, IS_PYPY
from .._compat import IS_ABOVE_OPENSSL10, IS_CI, IS_PYPY
from .._compat import IS_LINUX, IS_MACOS, IS_WINDOWS
from ..server import HTTPServer, get_ssl_adapter_class
from ..testing import (
@ -52,6 +52,7 @@ IS_PYOPENSSL_SSL_VERSION_1_0 = (
PY27 = sys.version_info[:2] == (2, 7)
PY34 = sys.version_info[:2] == (3, 4)
PY3 = not six.PY2
PY310_PLUS = sys.version_info[:2] >= (3, 10)
_stdlib_to_openssl_verify = {
@ -149,8 +150,8 @@ def tls_ca_certificate_pem_path(ca):
@pytest.fixture
def tls_certificate(ca):
"""Provide a leaf certificate via fixture."""
interface, host, port = _get_conn_data(ANY_INTERFACE_IPV4)
return ca.issue_server_cert(ntou(interface))
interface, _host, _port = _get_conn_data(ANY_INTERFACE_IPV4)
return ca.issue_cert(ntou(interface))
@pytest.fixture
@ -270,6 +271,11 @@ def test_ssl_adapters(
ssl.CERT_REQUIRED, # server should validate if client cert CA is OK
),
)
@pytest.mark.xfail(
IS_PYPY and IS_CI,
reason='Fails under PyPy in CI for unknown reason',
strict=False,
)
def test_tls_client_auth( # noqa: C901 # FIXME
# FIXME: remove twisted logic, separate tests
mocker,
@ -294,8 +300,7 @@ def test_tls_client_auth( # noqa: C901 # FIXME
'idna.core.ulabel',
return_value=ntob(tls_client_identity),
):
client_cert = client_cert_root_ca.issue_server_cert(
# FIXME: change to issue_cert once new trustme is out
client_cert = client_cert_root_ca.issue_cert(
ntou(tls_client_identity),
)
del client_cert_root_ca
@ -419,6 +424,10 @@ def test_tls_client_auth( # noqa: C901 # FIXME
'ConnectionResetError(10054, '
"'An existing connection was forcibly closed "
"by the remote host', None, 10054, None))",
"('Connection aborted.', "
'error(10054, '
"'An existing connection was forcibly closed "
"by the remote host'))",
) if IS_WINDOWS else (
"('Connection aborted.', "
'OSError("(104, \'ECONNRESET\')"))',
@ -437,13 +446,35 @@ def test_tls_client_auth( # noqa: C901 # FIXME
"('Connection aborted.', "
"BrokenPipeError(32, 'Broken pipe'))",
)
if PY310_PLUS:
# FIXME: Figure out what's happening and correct the problem
expected_substrings += (
'SSLError(SSLEOFError(8, '
"'EOF occurred in violation of protocol (_ssl.c:",
)
if IS_GITHUB_ACTIONS_WORKFLOW and IS_WINDOWS and PY310_PLUS:
expected_substrings += (
"('Connection aborted.', "
'RemoteDisconnected('
"'Remote end closed connection without response'))",
)
assert any(e in err_text for e in expected_substrings)
@pytest.mark.parametrize( # noqa: C901 # FIXME
'adapter_type',
(
'builtin',
pytest.param(
'builtin',
marks=pytest.mark.xfail(
IS_GITHUB_ACTIONS_WORKFLOW and IS_MACOS and PY310_PLUS,
reason='Unclosed TLS resource warnings happen on macOS '
'under Python 3.10',
strict=False,
),
),
'pyopenssl',
),
)
@ -602,18 +633,19 @@ def test_https_over_http_error(http_server, ip_addr):
assert expected_substring in ssl_err.value.args[-1]
http_over_https_error_builtin_marks = []
if IS_WINDOWS and six.PY2:
http_over_https_error_builtin_marks.append(
pytest.mark.flaky(reruns=5, reruns_delay=2),
)
@pytest.mark.parametrize(
'adapter_type',
(
pytest.param(
'builtin',
marks=pytest.mark.xfail(
IS_WINDOWS and six.PY2,
raises=requests.exceptions.ConnectionError,
reason='Stdlib `ssl` module behaves weirdly '
'on Windows under Python 2',
strict=False,
),
marks=http_over_https_error_builtin_marks,
),
'pyopenssl',
),
@ -654,7 +686,7 @@ def test_http_over_https_error(
interface, _host, port = _get_conn_data(ip_addr)
tlshttpserver = tls_http_server((interface, port), tls_adapter)
interface, host, port = _get_conn_data(
interface, _host, port = _get_conn_data(
tlshttpserver.bind_addr,
)

View file

@ -1,6 +1,7 @@
"""Test wsgi."""
from concurrent.futures.thread import ThreadPoolExecutor
from traceback import print_tb
import pytest
import portend
@ -20,7 +21,7 @@ def simple_wsgi_server():
"""Fucking simple wsgi server fixture (duh)."""
port = portend.find_available_local_port()
def app(environ, start_response):
def app(_environ, start_response):
status = '200 OK'
response_headers = [('Content-type', 'text/plain')]
start_response(status, response_headers)
@ -29,7 +30,9 @@ def simple_wsgi_server():
host = '::'
addr = host, port
server = wsgi.Server(addr, app, timeout=600 if IS_SLOW_ENV else 20)
# pylint: disable=possibly-unused-variable
url = 'http://localhost:{port}/'.format(**locals())
# pylint: disable=possibly-unused-variable
with server._run_in_thread() as thread:
yield locals()
@ -46,6 +49,7 @@ def test_connection_keepalive(simple_wsgi_server):
with ExceptionTrap(requests.exceptions.ConnectionError) as trap:
resp = session.get('info')
resp.raise_for_status()
print_tb(trap.tb)
return bool(trap)
with ThreadPoolExecutor(max_workers=10 if IS_SLOW_ENV else 50) as pool:
@ -56,3 +60,24 @@ def test_connection_keepalive(simple_wsgi_server):
failures = sum(task.result() for task in tasks)
assert not failures
def test_gateway_start_response_called_twice(monkeypatch):
"""Verify that repeat calls of ``Gateway.start_response()`` fail."""
monkeypatch.setattr(wsgi.Gateway, 'get_environ', lambda self: {})
wsgi_gateway = wsgi.Gateway(None)
wsgi_gateway.started_response = True
err_msg = '^WSGI start_response called a second time with no exc_info.$'
with pytest.raises(RuntimeError, match=err_msg):
wsgi_gateway.start_response('200', (), None)
def test_gateway_write_needs_start_response_called_before(monkeypatch):
"""Check that calling ``Gateway.write()`` needs started response."""
monkeypatch.setattr(wsgi.Gateway, 'get_environ', lambda self: {})
wsgi_gateway = wsgi.Gateway(None)
err_msg = '^WSGI write called before start_response.$'
with pytest.raises(RuntimeError, match=err_msg):
wsgi_gateway.write(None) # The actual arg value is unimportant

View file

@ -26,7 +26,7 @@ import time
import traceback
import os
import json
import unittest
import unittest # pylint: disable=deprecated-module,preferred-module
import warnings
import functools
@ -434,7 +434,7 @@ def cleanHeaders(headers, method, body, host, port):
# Add the required Host request header if not present.
# [This specifies the host:port of the server, not the client.]
found = False
for k, v in headers:
for k, _v in headers:
if k.lower() == 'host':
found = True
break
@ -498,9 +498,9 @@ def openURL(*args, **kwargs):
opener = functools.partial(_open_url_once, *args, **kwargs)
def on_exception():
type_, exc = sys.exc_info()[:2]
exc = sys.exc_info()[1]
if isinstance(exc, raise_subcls):
raise
raise exc
time.sleep(0.5)
# Try up to 10 times
@ -559,6 +559,10 @@ def strip_netloc(url):
Useful for wrapping an absolute-URI for which only the
path is expected (such as in calls to :py:meth:`WebCase.getPage`).
.. testsetup::
from cheroot.test.webtest import strip_netloc
>>> strip_netloc('https://google.com/foo/bar?bing#baz')
'/foo/bar?bing'
@ -569,7 +573,7 @@ def strip_netloc(url):
'/foo/bar?bing'
"""
parsed = urllib_parse.urlparse(url)
scheme, netloc, path, params, query, fragment = parsed
_scheme, _netloc, path, params, query, _fragment = parsed
stripped = '', '', path, params, query, ''
return urllib_parse.urlunparse(stripped)