From f88dc8495f7ec5dc7f1b01c43f0237141af6b98f Mon Sep 17 00:00:00 2001 From: Stuart McLaren Date: Fri, 5 Sep 2014 12:48:04 +0000 Subject: [PATCH] Add client_socket_timeout option 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 client_socket_timeout option (default=0). Conflicts: cinder/wsgi.py etc/cinder/cinder.conf.sample Note: This patch is not 1:1 cherry-pick, I have changed the default value of client_socket_timeout to 0. Change-Id: If492810a2f10fa5954f8c8bb708b14be0b77fb90 Closes-bug: #1361360 Closes-bug: #1371022 (cherry picked from commit 08bfa77aeccb8ca589e3fb5cf9771879818f59de) --- cinder/tests/test_wsgi.py | 37 +++++++++++++++++++++++++++++++++++ cinder/wsgi.py | 9 ++++++++- etc/cinder/cinder.conf.sample | 6 ++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/cinder/tests/test_wsgi.py b/cinder/tests/test_wsgi.py index d86d313967a..1d021c9ca9f 100644 --- a/cinder/tests/test_wsgi.py +++ b/cinder/tests/test_wsgi.py @@ -17,7 +17,10 @@ """Unit tests for `cinder.wsgi`.""" import os.path +import re +import socket import tempfile +import time import urllib2 import mock @@ -151,7 +154,41 @@ class TestWSGIServer(test.TestCase): response = open_no_proxy('http://127.0.0.1:%d/' % server.port) self.assertEqual(greetings, response.read()) + server.stop() + def test_client_socket_timeout(self): + CONF.set_default("client_socket_timeout", 0.1) + greetings = 'Hello, World!!!' + + def hello_world(env, start_response): + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [greetings] + + server = cinder.wsgi.Server("test_app", hello_world, + host="127.0.0.1", port=0) + server.start() + + s = socket.socket() + s.connect(("127.0.0.1", server.port)) + + fd = s.makefile('rw') + fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') + fd.flush() + + buf = fd.read() + self.assertTrue(re.search(greetings, buf)) + + s2 = socket.socket() + s2.connect(("127.0.0.1", server.port)) + time.sleep(0.2) + + fd = s2.makefile('rw') + fd.write(b'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') + fd.flush() + + buf = fd.read() + # connection is closed so we get nothing from the server + self.assertFalse(buf) server.stop() def test_app_using_ssl(self): diff --git a/cinder/wsgi.py b/cinder/wsgi.py index 56ebfd6abb9..de0936a49cd 100644 --- a/cinder/wsgi.py +++ b/cinder/wsgi.py @@ -84,6 +84,11 @@ eventlet_opts = [ help='If False, closes the client socket connection ' 'explicitly. Setting it to True to maintain backward ' 'compatibility. Recommended setting is set it to False.'), + 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 @@ -112,6 +117,7 @@ class Server(object): """ # Allow operators to customize http requests max header line size. eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line + self.client_socket_timeout = CONF.client_socket_timeout or None self.name = name self.app = app self._host = host or "0.0.0.0" @@ -235,7 +241,8 @@ class Server(object): 'protocol': self._protocol, 'custom_pool': self._pool, 'log': self._wsgi_logger, - 'keepalive': CONF.wsgi_keep_alive + 'keepalive': CONF.wsgi_keep_alive, + 'socket_timeout': self.client_socket_timeout } self._server = eventlet.spawn(**wsgi_kwargs) diff --git a/etc/cinder/cinder.conf.sample b/etc/cinder/cinder.conf.sample index 2b1982014bc..47e30847ff9 100644 --- a/etc/cinder/cinder.conf.sample +++ b/etc/cinder/cinder.conf.sample @@ -311,6 +311,12 @@ # Recommended setting is set it to False. (boolean value) #wsgi_keep_alive=true +# 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. (integer +# value) +#client_socket_timeout=0 + # Sets the value of TCP_KEEPALIVE (True/False) for each server # socket. (boolean value) #tcp_keepalive=true