12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2011, Google Inc. 22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# All rights reserved. 32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Redistribution and use in source and binary forms, with or without 52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# modification, are permitted provided that the following conditions are 62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# met: 72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# * Redistributions of source code must retain the above copyright 92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# notice, this list of conditions and the following disclaimer. 102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# * Redistributions in binary form must reproduce the above 112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# copyright notice, this list of conditions and the following disclaimer 122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# in the documentation and/or other materials provided with the 132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distribution. 142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# * Neither the name of Google Inc. nor the names of its 152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# contributors may be used to endorse or promote products derived from 162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# this software without specific prior written permission. 172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""Message related utilities. 322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 332da489cd246702bee5938545b18a6f710ed214bcJamie GennisNote: request.connection.write/read are used in this module, even though 342da489cd246702bee5938545b18a6f710ed214bcJamie Gennismod_python document says that they should be used only in connection 352da489cd246702bee5938545b18a6f710ed214bcJamie Gennishandlers. Unfortunately, we have no other options. For example, 362da489cd246702bee5938545b18a6f710ed214bcJamie Gennisrequest.write/read are not suitable because they don't allow direct raw 372da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbytes writing/reading. 382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis""" 392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 412da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport Queue 422da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport threading 432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Export Exception symbols from msgutil for backward compatibility 462da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import ConnectionTerminatedException 472da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import InvalidFrameException 482da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import BadOperationException 492da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import UnsupportedFrameException 502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# An API for handler to send/receive WebSocket messages. 532da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef close_connection(request): 542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Close connection. 552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request: mod_python request. 582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request.ws_stream.close_connection() 602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 622da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef send_message(request, message, end=True, binary=False): 632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Send message. 642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request: mod_python request. 672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis message: unicode text or str binary to send. 682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end: False to send message as a fragment. All messages until the 692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis first call with end=True (inclusive) will be delivered to the 702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis client in separate frames but as one WebSocket message. 712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis binary: send message as binary frame. 722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Raises: 732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis BadOperationException: when server already terminated. 742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request.ws_stream.send_message(message, end, binary) 762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 782da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef receive_message(request): 792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Receive a WebSocket frame and return its payload as a text in 802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis unicode or a binary in str. 812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request: mod_python request. 842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Raises: 852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis InvalidFrameException: when client send invalid frame. 862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis UnsupportedFrameException: when client send unsupported frame e.g. some 872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis of reserved bit is set but no extension can 882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis recognize it. 892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis InvalidUTF8Exception: when client send a text frame containing any 902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis invalid UTF-8 string. 912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ConnectionTerminatedException: when the connection is closed 922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis unexpectedly. 932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis BadOperationException: when client already terminated. 942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return request.ws_stream.receive_message() 962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 982da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef send_ping(request, body=''): 992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request.ws_stream.send_ping(body) 1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass MessageReceiver(threading.Thread): 1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """This class receives messages from the client. 1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis This class provides three ways to receive messages: blocking, 1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis non-blocking, and via callback. Callback has the highest precedence. 1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Note: This class should not be used with the standalone server for wss 1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis because pyOpenSSL used by the server raises a fatal error if the socket 1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is accessed from multiple threads. 1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __init__(self, request, onmessage=None): 1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Construct an instance. 1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request: mod_python request. 1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis onmessage: a function to be called when a message is received. 1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis May be None. If not None, the function is called on 1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis another thread. In that case, MessageReceiver.receive 1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and MessageReceiver.receive_nowait are useless 1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis because they will never return any messages. 1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis threading.Thread.__init__(self) 1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._request = request 1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._queue = Queue.Queue() 1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._onmessage = onmessage 1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._stop_requested = False 1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.setDaemon(True) 1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.start() 1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def run(self): 1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try: 1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while not self._stop_requested: 1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis message = receive_message(self._request) 1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if self._onmessage: 1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._onmessage(message) 1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._queue.put(message) 1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis finally: 1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis close_connection(self._request) 1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def receive(self): 1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ Receive a message from the channel, blocking. 1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Returns: 1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis message as a unicode string. 1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return self._queue.get() 1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def receive_nowait(self): 1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ Receive a message from the channel, non-blocking. 1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Returns: 1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis message as a unicode string if available. None otherwise. 1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try: 1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis message = self._queue.get_nowait() 1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis except Queue.Empty: 1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis message = None 1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return message 1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def stop(self): 1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Request to stop this instance. 1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis The instance will be stopped after receiving the next message. 1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis This method may not be very useful, but there is no clean way 1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis in Python to forcefully stop a running thread. 1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._stop_requested = True 1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass MessageSender(threading.Thread): 1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """This class sends messages to the client. 1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis This class provides both synchronous and asynchronous ways to send 1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis messages. 1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Note: This class should not be used with the standalone server for wss 1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis because pyOpenSSL used by the server raises a fatal error if the socket 1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is accessed from multiple threads. 1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __init__(self, request): 1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Construct an instance. 1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis request: mod_python request. 1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis threading.Thread.__init__(self) 1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._request = request 1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._queue = Queue.Queue() 1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.setDaemon(True) 1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.start() 1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def run(self): 1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while True: 1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis message, condition = self._queue.get() 2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis condition.acquire() 2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis send_message(self._request, message) 2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis condition.notify() 2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis condition.release() 2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def send(self, message): 2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Send a message, blocking.""" 2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis condition = threading.Condition() 2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis condition.acquire() 2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._queue.put((message, condition)) 2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis condition.wait() 2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def send_nowait(self, message): 2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Send a message, non-blocking.""" 2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._queue.put((message, threading.Condition())) 2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# vi:sts=4 sw=4 et 220