Skip to content

Commit bde6ce2

Browse files
d-w-moorealanking
authored andcommitted
[#623] hard-limit large connection timeouts
Also, make sure that hard-limiting and error checks to happen at session creation time.
1 parent 051be1f commit bde6ce2

3 files changed

Lines changed: 34 additions & 8 deletions

File tree

irods/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ def client_logging(flag=True,handler=None):
4141
CHALLENGE_LEN = 64
4242
MAX_SQL_ROWS = 256
4343
DEFAULT_CONNECTION_TIMEOUT = 120
44+
# https://stackoverflow.com/questions/45704243/value-of-c-pytime-t-in-python
45+
MAXIMUM_CONNECTION_TIMEOUT = 9223372036
4446

4547
AUTH_SCHEME_KEY = 'a_scheme'
4648
AUTH_USER_KEY = 'a_user'

irods/session.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@
2525
from irods.exception import (NetworkException, NotImplementedInIRODSServer)
2626
from irods.password_obfuscation import decode
2727
from irods import NATIVE_AUTH_SCHEME, PAM_AUTH_SCHEMES
28-
from . import DEFAULT_CONNECTION_TIMEOUT
28+
from . import (DEFAULT_CONNECTION_TIMEOUT, MAXIMUM_CONNECTION_TIMEOUT)
2929

3030
_fds = None
3131
_fds_lock = threading.Lock()
3232
_sessions = None
3333
_sessions_lock = threading.Lock()
3434

35-
3635
def _cleanup_remaining_sessions():
3736
for fd in list(_fds.keys()):
3837
if not fd.closed:
@@ -59,6 +58,7 @@ def _weakly_reference(ses):
5958

6059
class NonAnonymousLoginWithoutPassword(RuntimeError): pass
6160

61+
6262
class iRODSSession(object):
6363

6464
def library_features(self):
@@ -141,7 +141,8 @@ def __init__(self, configure = True, auto_cleanup = True, **kwargs):
141141
self._env_file = ''
142142
self._auth_file = ''
143143
self.do_configure = (kwargs if configure else {})
144-
self._cached_connection_timeout = kwargs.pop('connection_timeout', DEFAULT_CONNECTION_TIMEOUT)
144+
self._cached_connection_timeout = None
145+
self.connection_timeout = kwargs.pop('connection_timeout', DEFAULT_CONNECTION_TIMEOUT)
145146
self.__configured = None
146147
if configure:
147148
self.__configured = self.configure(**kwargs)
@@ -366,16 +367,22 @@ def connection_timeout(self, seconds):
366367
exc = ValueError("Setting an iRODS connection_timeout to 0 seconds would make it non-blocking.")
367368
raise exc
368369
elif isinstance(seconds, Number):
369-
if seconds < 0 or str(seconds) == 'nan' or str(abs(seconds)) == 'inf':
370-
exc = ValueError("The iRODS connection_timeout may not be assigned a negative or otherwise rogue value (eg: NaN, Inf).")
370+
# Note: We can handle infinities because -Inf < 0 and Inf > MAXIMUM_CONNECTION_TIMEOUT.
371+
if seconds < 0 or str(seconds) == 'nan':
372+
exc = ValueError("The iRODS connection_timeout may not be assigned a negative, out-of-bounds, or otherwise rogue value (eg: NaN, -Inf).")
371373
raise exc
374+
elif seconds > MAXIMUM_CONNECTION_TIMEOUT:
375+
logging.getLogger(__name__).warning('Hard limiting connection timeout of %g to the maximum allowable value of %g',
376+
seconds, MAXIMUM_CONNECTION_TIMEOUT)
377+
seconds = MAXIMUM_CONNECTION_TIMEOUT
372378
elif seconds is None:
373379
pass
374380
else:
375381
exc = ValueError("The iRODS connection_timeout must be assigned a positive int, positive float, or None.")
376382
raise exc
377383
self._cached_connection_timeout = seconds
378-
self.pool.connection_timeout = seconds
384+
if self.pool:
385+
self.pool.connection_timeout = seconds
379386

380387
@staticmethod
381388
def get_irods_password_file():

irods/test/connection_test.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import tempfile
66
import unittest
77
from irods.exception import NetworkException
8+
from irods import MAXIMUM_CONNECTION_TIMEOUT
89
import irods.test.helpers as helpers
910
from irods.test.helpers import (server_side_sleep, temporarily_assign_attribute as temp_setter)
1011

@@ -159,8 +160,24 @@ def test_assigning_session_connection_timeout_to_invalid_values__issue_569(self)
159160
with self.assertRaises(ValueError):
160161
sess.connection_timeout = value
161162

162-
# Test previously set value is unaffected
163-
self.assertEqual(sess.connection_timeout, DESIRED_TIMEOUT)
163+
def test_assigning_session_connection_timeout_to_large_values__issue_623(self):
164+
# Test use of a too-large timeout in iRODSSession constructor as well as on assignment to the
165+
# iRODSSession property 'connection_timeout'. In both cases, error checking and hard-limiting
166+
# should be immediate.
167+
sess = helpers.make_session(connection_timeout = MAXIMUM_CONNECTION_TIMEOUT + 1)
168+
# The session attribute '_cached_connection_timeout' is where the session timeout value is kept
169+
# safe for whenever a Pool sub-object is initialized (or re-initialized).
170+
self.assertEqual(sess._cached_connection_timeout, MAXIMUM_CONNECTION_TIMEOUT)
171+
172+
# Make (and check) a change of the connection_timeout value so that second of the surrounding
173+
# equality assertions does not accidentally succeed due to the value remaining untouched.
174+
sess.connection_timeout = 1
175+
self.assertEqual(sess._cached_connection_timeout, 1)
176+
177+
sess.connection_timeout = MAXIMUM_CONNECTION_TIMEOUT + 1
178+
self.assertEqual(sess._cached_connection_timeout, MAXIMUM_CONNECTION_TIMEOUT)
179+
180+
self.assertEqual(sess.pool.connection_timeout, MAXIMUM_CONNECTION_TIMEOUT)
164181

165182
def test_assigning_session_connection_timeout__issue_569(self):
166183
sess = helpers.make_session()

0 commit comments

Comments
 (0)