]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
websocket: Make ping() argument optional
authorBen Darnell <ben@bendarnell.com>
Sun, 11 Mar 2018 20:13:51 +0000 (16:13 -0400)
committerBen Darnell <ben@bendarnell.com>
Sun, 11 Mar 2018 20:13:51 +0000 (16:13 -0400)
Also accept both bytes and str, and add a client-side ping() method.

Fixes #2295

tornado/test/websocket_test.py
tornado/websocket.py

index 38b3211d7a46611425178628c44a1f4f15d0a8de..2e14342c197939572d8ad4b5aeffe6b753bab5a3 100644 (file)
@@ -621,6 +621,34 @@ class ClientPeriodicPingTest(WebSocketBaseTestCase):
         # TODO: test that the connection gets closed if ping responses stop.
 
 
+class ManualPingTest(WebSocketBaseTestCase):
+    def get_app(self):
+        class PingHandler(TestWebSocketHandler):
+            def on_ping(self, data):
+                self.write_message(data, binary=isinstance(data, bytes))
+
+        self.close_future = Future()
+        return Application([
+            ('/', PingHandler, dict(close_future=self.close_future)),
+        ])
+
+    @gen_test
+    def test_manual_ping(self):
+        ws = yield self.ws_connect('/')
+
+        self.assertRaises(ValueError, ws.ping, 'a' * 126)
+
+        ws.ping('hello')
+        resp = yield ws.read_message()
+        # on_ping always sees bytes.
+        self.assertEqual(resp, b'hello')
+
+        ws.ping(b'binary hello')
+        resp = yield ws.read_message()
+        self.assertEqual(resp, b'binary hello')
+        yield self.close(ws)
+
+
 class MaxMessageSizeTest(WebSocketBaseTestCase):
     def get_app(self):
         self.close_future = Future()
index df60885e2ed6006e29516ac0eff28a48d38ae2f9..629e9417e0c3a317da149e7493bdbf21d1f5cb28 100644 (file)
@@ -312,8 +312,23 @@ class WebSocketHandler(tornado.web.RequestHandler):
         """
         raise NotImplementedError
 
-    def ping(self, data):
-        """Send ping frame to the remote end."""
+    def ping(self, data=b''):
+        """Send ping frame to the remote end.
+
+        The data argument allows a small amount of data (up to 125
+        bytes) to be sent as a part of the ping message. Note that not
+        all websocket implementations expose this data to
+        applications.
+
+        Consider using the ``websocket_ping_interval`` application
+        setting instead of sending pings manually.
+
+        .. versionchanged:: 5.1
+
+           The data argument is now optional.
+
+        """
+        data = utf8(data)
         if self.ws_connection is None:
             raise WebSocketClosedError()
         self.ws_connection.write_ping(data)
@@ -756,12 +771,19 @@ class WebSocketProtocol13(WebSocketProtocol):
             **self._get_compressor_options(other_side, agreed_parameters, compression_options))
 
     def _write_frame(self, fin, opcode, data, flags=0):
+        data_len = len(data)
+        if opcode & 0x8:
+            # All control frames MUST have a payload length of 125
+            # bytes or less and MUST NOT be fragmented.
+            if not fin:
+                raise ValueError("control frames may not be fragmented")
+            if data_len > 125:
+                raise ValueError("control frame payloads may not exceed 125 bytes")
         if fin:
             finbit = self.FIN
         else:
             finbit = 0
         frame = struct.pack("B", finbit | opcode | flags)
-        data_len = len(data)
         if self.mask_outgoing:
             mask_bit = 0x80
         else:
@@ -1205,6 +1227,25 @@ class WebSocketClientConnection(simple_httpclient._HTTPConnection):
         else:
             self.read_queue.append(message)
 
+    def ping(self, data=b''):
+        """Send ping frame to the remote end.
+
+        The data argument allows a small amount of data (up to 125
+        bytes) to be sent as a part of the ping message. Note that not
+        all websocket implementations expose this data to
+        applications.
+
+        Consider using the ``ping_interval`` argument to
+        `websocket_connect` instead of sending pings manually.
+
+        .. versionadded:: 5.1
+
+        """
+        data = utf8(data)
+        if self.protocol is None:
+            raise WebSocketClosedError()
+        self.protocol.write_ping(data)
+
     def on_pong(self, data):
         pass