Eventlet green threads not released back to pool

Presently, the wsgi server allows persist connections hence even after
the response is sent to the client, it doesn't close the client socket
connection.
Because of this problem, the green thread is not released back to the pool.

In order to close the client socket connection explicitly after the
response is sent and read successfully by the client, you simply have to
set keepalive to False when you create a wsgi server.

Add a parameter to take advantage of the new(ish) eventlet socket timeout
behaviour. Allows closing idle client connections after a period of
time, eg:

$ time nc localhost 8776
real    1m0.063s

Setting 'client_socket_timeout = 0' means do not timeout.

DocImpact:
Added wsgi_keep_alive option (default=True).
Added client_socket_timeout option (default=0).

Conflicts:
        nova/tests/unit/test_wsgi.py

Note: The required unit-tests are manually added to the below path,
as new path for unit-tests is not present in stable/icehouse release.
nova/tests/compute/test_host_api.py

This patch is not 1:1 cherry-pick, I have changed the default value
of client_socket_timeout to 0, as per the policy for changes to
stable branches.
(https://wiki.openstack.org/wiki/StableBranch#Appropriate_Fixes)

SecurityImpact

Closes-Bug: #1361360
Change-Id: I399b812f6d452226fd306c423de8dcea8520d2aa
(cherry picked from commit 04d7a724fd)
This commit is contained in:
abhishekkekane 2014-10-21 01:37:42 -07:00
parent 0a6d029deb
commit a657582c5c
2 changed files with 43 additions and 1 deletions

View File

@ -157,6 +157,36 @@ class TestWSGIServer(test.NoDBTestCase):
server.stop()
server.wait()
def test_client_socket_timeout(self):
self.flags(client_socket_timeout=5)
# mocking eventlet spawn method to check it is called with
# configured 'client_socket_timeout' value.
with mock.patch.object(eventlet,
'spawn') as mock_spawn:
server = nova.wsgi.Server("test_app", None,
host="127.0.0.1", port=0)
server.start()
_, kwargs = mock_spawn.call_args
self.assertEqual(CONF.client_socket_timeout,
kwargs['socket_timeout'])
server.stop()
def test_wsgi_keep_alive(self):
self.flags(wsgi_keep_alive=False)
# mocking eventlet spawn method to check it is called with
# configured 'wsgi_keep_alive' value.
with mock.patch.object(eventlet,
'spawn') as mock_spawn:
server = nova.wsgi.Server("test_app", None,
host="127.0.0.1", port=0)
server.start()
_, kwargs = mock_spawn.call_args
self.assertEqual(CONF.wsgi_keep_alive,
kwargs['keepalive'])
server.stop()
class TestWSGIServerWithSSL(test.NoDBTestCase):
"""WSGI server with SSL tests."""

View File

@ -69,6 +69,15 @@ wsgi_opts = [
"max_header_line may need to be increased when using "
"large tokens (typically those generated by the "
"Keystone v3 API with big service catalogs)."),
cfg.BoolOpt('wsgi_keep_alive',
default=True,
help="If False, closes the client socket connection "
"explicitly."),
cfg.IntOpt('client_socket_timeout', default=0,
help="Timeout for client connections' socket operations. "
"If an incoming connection is idle for this number of "
"seconds it will be closed. A value of '0' means "
"wait forever."),
]
CONF = cfg.CONF
CONF.register_opts(wsgi_opts)
@ -107,6 +116,7 @@ class Server(object):
self._wsgi_logger = logging.WritableLogger(self._logger)
self._use_ssl = use_ssl
self._max_url_len = max_url_len
self.client_socket_timeout = CONF.client_socket_timeout or None
if backlog < 1:
raise exception.InvalidInput(
@ -206,7 +216,9 @@ class Server(object):
'custom_pool': self._pool,
'log': self._wsgi_logger,
'log_format': CONF.wsgi_log_format,
'debug': False
'debug': False,
'keepalive': CONF.wsgi_keep_alive,
'socket_timeout': self.client_socket_timeout
}
if self._max_url_len: