Configure TCP Keep-Alive for certain Sessions

If the user creates a keystoneclient.session.Session without passing a
custom session, we will enable TCP Keep-Alive for the requests session
used by keystoneclient's Session.

novaclient and other clients can experience hung TCP connections. Most
clients use keystoneclient's session and will need this merged here
before they can make use of it in their projects.

Change-Id: Ib70a8b3270d2492596b9fb8981b8584b85567a9c
Closes-bug: 1323862
This commit is contained in:
Ian Cordasco
2015-01-15 18:21:17 -06:00
parent acb56e38ec
commit fe9692ea6b
2 changed files with 28 additions and 0 deletions

View File

@@ -15,6 +15,7 @@ import functools
import hashlib import hashlib
import logging import logging
import os import os
import socket
import time import time
from oslo.config import cfg from oslo.config import cfg
@@ -123,6 +124,9 @@ class Session(object):
redirect=_DEFAULT_REDIRECT_LIMIT): redirect=_DEFAULT_REDIRECT_LIMIT):
if not session: if not session:
session = requests.Session() session = requests.Session()
# Use TCPKeepAliveAdapter to fix bug 1323862
for scheme in session.adapters.keys():
session.mount(scheme, TCPKeepAliveAdapter())
self.auth = auth self.auth = auth
self.session = session self.session = session
@@ -778,3 +782,14 @@ class Session(object):
kwargs['timeout'] = args.timeout kwargs['timeout'] = args.timeout
return cls._make(**kwargs) return cls._make(**kwargs)
class TCPKeepAliveAdapter(requests.adapters.HTTPAdapter):
"""The custom adapter used to set TCP Keep-Alive on all connections."""
def init_poolmanager(self, *args, **kwargs):
if requests.__version__ >= '2.4.1':
kwargs.setdefault('socket_options', [
(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),
(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
])
super(TCPKeepAliveAdapter, self).init_poolmanager(*args, **kwargs)

View File

@@ -205,6 +205,19 @@ class SessionTests(utils.TestCase):
self.assertThat(self.requests.request_history, self.assertThat(self.requests.request_history,
matchers.HasLength(retries + 1)) matchers.HasLength(retries + 1))
def test_uses_tcp_keepalive_by_default(self):
session = client_session.Session()
requests_session = session.session
self.assertIsInstance(requests_session.adapters['http://'],
client_session.TCPKeepAliveAdapter)
self.assertIsInstance(requests_session.adapters['https://'],
client_session.TCPKeepAliveAdapter)
def test_does_not_set_tcp_keepalive_on_custom_sessions(self):
mock_session = mock.Mock()
client_session.Session(session=mock_session)
self.assertFalse(mock_session.mount.called)
class RedirectTests(utils.TestCase): class RedirectTests(utils.TestCase):