Send ping packets to keep connection alive

Fix #126

Idle WebSocket connections tend to be closed after some period of time.
This commit enables the Butterfly server to send ping packets
periodically in order to keep the connection alive.

A new option `keepalive_interval` is also introduced for users to
specify the interval to send `ping` packets. By default it is 30
seconds.
This commit is contained in:
Peter Cai
2017-02-10 15:11:51 +08:00
parent fdeba5a5d4
commit a36579bb12
2 changed files with 22 additions and 1 deletions

View File

@@ -40,6 +40,8 @@ tornado.options.define("unminified", default=False,
tornado.options.define("host", default='localhost', help="Server host")
tornado.options.define("port", default=57575, type=int, help="Server port")
tornado.options.define("keepalive_interval", default=30, type=int,
help="Interval between ping packets sent from server to client (in seconds)")
tornado.options.define("one_shot", default=False,
help="Run a one-shot instance. Quit at term close")
tornado.options.define("shell", help="Shell to execute at login")

View File

@@ -17,7 +17,9 @@
import os
import struct
import sys
import time
import tornado.options
import tornado.process
import tornado.escape
@@ -133,10 +135,15 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
# Session history
history = {}
# Keepalive timer
keepalive_timer = None
def open(self, user, path, session):
opts = tornado.options.options
self.session = session
self.closed = False
self.secure_user = None
self.keepalive_timer = tornado.ioloop.PeriodicCallback(self.send_ping, opts.keepalive_interval * 1000)
# Prevent cross domain
if self.request.headers['Origin'] not in (
@@ -155,7 +162,6 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
self.set_nodelay(True)
socket = utils.Socket(self.ws_connection.stream.socket)
opts = tornado.options.options
if not opts.unsecure:
user = utils.parse_cert(
@@ -206,6 +212,8 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
else:
self._terminal = terminal
self.keepalive_timer.start()
@property
def user_sessions(self):
"""Return the dict session of socket lists"""
@@ -278,6 +286,8 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
def on_close(self):
if self.closed:
return
if self.keepalive_timer != None:
self.keepalive_timer.stop()
self.closed = True
self.log.info('Websocket closed %r' % self)
TermWebSocket.sockets.remove(self)
@@ -297,6 +307,15 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
for user, sessions in TermWebSocket.terminals.items()])):
sys.exit(0)
def send_ping(self):
t = int(time.time())
frame = struct.pack('<I', t) # A ping frame based on time
self.log.info("Sending ping frame %s" % t)
try:
self.ping(frame)
except tornado.websocket.WebSocketClosedError:
self.keepalive_timer.stop()
@url(r'/sessions/list.json')
class SessionsList(Route):