diff --git a/docs/ENV-NOTES b/docs/ENV-NOTES index 26048a6..58baf97 100644 --- a/docs/ENV-NOTES +++ b/docs/ENV-NOTES @@ -83,6 +83,9 @@ BANDWIDTH_LIMIT - per-session periodic bandwith usage limit in bytes. end of each period. Currently the period is hard-coded to be one hour. The default limit value is 2 million bytes. +SESSION_TIMEOUT - an integer number of seconds defaulting to 600. + Sessions with no activity for longer than this are + disconnected. If you want IRC connectivity to advertise your node: diff --git a/lib/jsonrpc.py b/lib/jsonrpc.py index 5688f84..7aa1764 100644 --- a/lib/jsonrpc.py +++ b/lib/jsonrpc.py @@ -79,6 +79,7 @@ class JSONRPC(asyncio.Protocol, LoggedClass): def __init__(self): super().__init__() self.start = time.time() + self.last_recv = self.start self.bandwidth_start = self.start self.bandwidth_interval = 3600 self.bandwidth_used = 0 @@ -155,6 +156,7 @@ class JSONRPC(asyncio.Protocol, LoggedClass): if npos == -1: self.parts.append(data) break + self.last_recv = time.time() self.recv_count += 1 tail, data = data[:npos], data[npos + 1:] parts, self.parts = self.parts, [] diff --git a/server/env.py b/server/env.py index 2b875a4..f13c56e 100644 --- a/server/env.py +++ b/server/env.py @@ -50,6 +50,7 @@ class Env(LoggedClass): self.max_subs = self.integer('MAX_SUBS', 250000) self.max_session_subs = self.integer('MAX_SESSION_SUBS', 50000) self.bandwidth_limit = self.integer('BANDWIDTH_LIMIT', 2000000) + self.session_timeout = self.integer('SESSION_TIMEOUT', 600) # IRC self.irc = self.default('IRC', False) self.irc_nick = self.default('IRC_NICK', None) diff --git a/server/protocol.py b/server/protocol.py index 20157a8..2ebc82d 100644 --- a/server/protocol.py +++ b/server/protocol.py @@ -228,8 +228,11 @@ class ServerManager(util.LoggedClass): self.next_log_sessions = 0 self.max_subs = env.max_subs self.subscription_count = 0 + self.next_stale_check = 0 self.futures = [] env.max_send = max(350000, env.max_send) + self.logger.info('session timeout: {:,d} seconds' + .format(env.session_timeout)) self.logger.info('session bandwidth limit {:,d} bytes' .format(env.bandwidth_limit)) self.logger.info('max response size {:,d} bytes'.format(env.max_send)) @@ -354,6 +357,7 @@ class ServerManager(util.LoggedClass): .format(len(self.sessions))) def add_session(self, session): + self.clear_stale_sessions() coro = session.serve_requests() future = asyncio.ensure_future(coro) self.sessions[session] = future @@ -373,6 +377,20 @@ class ServerManager(util.LoggedClass): future = self.sessions.pop(session) future.cancel() + def clear_stale_sessions(self): + '''Cut off sessions that haven't done anything for 10 minutes.''' + now = time.time() + if now > self.next_stale_check: + self.next_stale_check = now + 60 + cutoff = now - self.env.session_timeout + stale = [session for session in self.sessions + if session.last_recv < cutoff] + for session in stale: + self.close_session(session) + if stale: + self.logger.info('dropped {:,d} stale connections' + .format(len(stale))) + def new_subscription(self): if self.subscription_count >= self.max_subs: raise JSONRPC.RPCError('server subscription limit {:,d} reached'