Skip to content

Commit

Permalink
Fix HTTP response hanging if system time set back. Closes #802
Browse files Browse the repository at this point in the history
  • Loading branch information
mnaberez committed Aug 1, 2016
1 parent e1b592d commit 2736ea7
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
- Zope ``trackrefs``, a debugging tool that was included in the ``tests``
directory but hadn't been used for years, has been removed.

- Fixed an issue where `supervisord` could hang when responding to HTTP
requests (including `supervisorctl` commands) if the system time was set
back after `supervisord` was started.

3.3.0 (2016-05-14)
------------------

Expand Down
12 changes: 5 additions & 7 deletions supervisor/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,17 +334,16 @@ class deferring_http_channel(http_server.http_channel):
# order to spew tail -f output faster (speculative)
ac_out_buffer_size = 4096

delay = False
writable_check = time.time()
delay = 0 # seconds
last_writable_check = time.time()

def writable(self, t=time.time):
now = t()
if self.delay:
# we called a deferred producer via this channel (see refill_buffer)
last_writable_check = self.writable_check
elapsed = now - last_writable_check
if elapsed > self.delay:
self.writable_check = now
elapsed = now - self.last_writable_check
if (elapsed > self.delay) or (elapsed < 0):
self.last_writable_check = now
return True
else:
return False
Expand Down Expand Up @@ -901,4 +900,3 @@ def __init__(self, dict, handler, realm='default'):
auth_handler.__init__(self, dict, handler, realm)
# override the authorizer with one that knows about SHA hashes too
self.authorizer = encrypted_dictionary_authorizer(dict)

53 changes: 52 additions & 1 deletion supervisor/tests/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ def test_more_noproducer(self):
producer = self._makeOne(None, None)
self.assertEqual(producer.more(), '')

class Test_deferring_http_request(unittest.TestCase):
class DeferringHttpRequestTests(unittest.TestCase):
def _getTargetClass(self):
from supervisor.http import deferring_http_request
return deferring_http_request
Expand Down Expand Up @@ -428,6 +428,57 @@ def test_done_http_09(self):
inst.done()
self.assertTrue(channel.closed)

class DeferringHttpChannelTests(unittest.TestCase):
def _getTargetClass(self):
from supervisor.http import deferring_http_channel
return deferring_http_channel

def _makeOne(self):
return self._getTargetClass()(
server=None,
conn=None,
addr=None
)

def test_defaults_delay_and_last_writable_check_time(self):
channel = self._makeOne()
self.assertEqual(channel.delay, 0)
self.assertTrue(channel.last_writable_check > 0)

def test_writable_with_delay_is_False_if_elapsed_lt_delay(self):
channel = self._makeOne()
channel.delay = 2
channel.last_writable_check = _NOW
later = _NOW + 1
self.assertFalse(channel.writable(t=lambda: later))
self.assertEqual(channel.last_writable_check, _NOW)

def test_writable_with_delay_is_False_if_elapsed_eq_delay(self):
channel = self._makeOne()
channel.delay = 2
channel.last_writable_check = _NOW
later = _NOW + channel.delay
self.assertFalse(channel.writable(t=lambda: later))
self.assertEqual(channel.last_writable_check, _NOW)

def test_writable_with_delay_is_True_if_elapsed_gt_delay(self):
channel = self._makeOne()
channel.delay = 2
channel.last_writable_check = _NOW
later = _NOW + channel.delay + 0.1
self.assertTrue(channel.writable(t=lambda: later))
self.assertEqual(channel.last_writable_check, later)

def test_writable_with_delay_is_True_if_system_time_goes_backwards(self):
channel = self._makeOne()
channel.delay = 2
channel.last_writable_check = _NOW + 3600 # last check was in the future
later = _NOW
self.assertTrue(channel.writable(t=lambda: later))
self.assertEqual(channel.last_writable_check, later)

_NOW = 1470085990

class EncryptedDictionaryAuthorizedTests(unittest.TestCase):
def _getTargetClass(self):
from supervisor.http import encrypted_dictionary_authorizer
Expand Down

0 comments on commit 2736ea7

Please sign in to comment.