| # Copyright 2011, Google Inc. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of Google Inc. nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| |
| """Message related utilities. |
| |
| Note: request.connection.write/read are used in this module, even though |
| mod_python document says that they should be used only in connection |
| handlers. Unfortunately, we have no other options. For example, |
| request.write/read are not suitable because they don't allow direct raw |
| bytes writing/reading. |
| """ |
| |
| |
| import Queue |
| import threading |
| |
| |
| # Export Exception symbols from msgutil for backward compatibility |
| from mod_pywebsocket._stream_base import ConnectionTerminatedException |
| from mod_pywebsocket._stream_base import InvalidFrameException |
| from mod_pywebsocket._stream_base import BadOperationException |
| from mod_pywebsocket._stream_base import UnsupportedFrameException |
| |
| |
| # An API for handler to send/receive WebSocket messages. |
| def close_connection(request): |
| """Close connection. |
| |
| Args: |
| request: mod_python request. |
| """ |
| request.ws_stream.close_connection() |
| |
| |
| def send_message(request, payload_data, end=True, binary=False): |
| """Send a message (or part of a message). |
| |
| Args: |
| request: mod_python request. |
| payload_data: unicode text or str binary to send. |
| end: True to terminate a message. |
| False to send payload_data as part of a message that is to be |
| terminated by next or later send_message call with end=True. |
| binary: send payload_data as binary frame(s). |
| Raises: |
| BadOperationException: when server already terminated. |
| """ |
| request.ws_stream.send_message(payload_data, end, binary) |
| |
| |
| def receive_message(request): |
| """Receive a WebSocket frame and return its payload as a text in |
| unicode or a binary in str. |
| |
| Args: |
| request: mod_python request. |
| Raises: |
| InvalidFrameException: when client send invalid frame. |
| UnsupportedFrameException: when client send unsupported frame e.g. some |
| of reserved bit is set but no extension can |
| recognize it. |
| InvalidUTF8Exception: when client send a text frame containing any |
| invalid UTF-8 string. |
| ConnectionTerminatedException: when the connection is closed |
| unexpectedly. |
| BadOperationException: when client already terminated. |
| """ |
| return request.ws_stream.receive_message() |
| |
| |
| def send_ping(request, body=''): |
| request.ws_stream.send_ping(body) |
| |
| |
| class MessageReceiver(threading.Thread): |
| """This class receives messages from the client. |
| |
| This class provides three ways to receive messages: blocking, |
| non-blocking, and via callback. Callback has the highest precedence. |
| |
| Note: This class should not be used with the standalone server for wss |
| because pyOpenSSL used by the server raises a fatal error if the socket |
| is accessed from multiple threads. |
| """ |
| |
| def __init__(self, request, onmessage=None): |
| """Construct an instance. |
| |
| Args: |
| request: mod_python request. |
| onmessage: a function to be called when a message is received. |
| May be None. If not None, the function is called on |
| another thread. In that case, MessageReceiver.receive |
| and MessageReceiver.receive_nowait are useless |
| because they will never return any messages. |
| """ |
| |
| threading.Thread.__init__(self) |
| self._request = request |
| self._queue = Queue.Queue() |
| self._onmessage = onmessage |
| self._stop_requested = False |
| self.setDaemon(True) |
| self.start() |
| |
| def run(self): |
| try: |
| while not self._stop_requested: |
| message = receive_message(self._request) |
| if self._onmessage: |
| self._onmessage(message) |
| else: |
| self._queue.put(message) |
| finally: |
| close_connection(self._request) |
| |
| def receive(self): |
| """ Receive a message from the channel, blocking. |
| |
| Returns: |
| message as a unicode string. |
| """ |
| return self._queue.get() |
| |
| def receive_nowait(self): |
| """ Receive a message from the channel, non-blocking. |
| |
| Returns: |
| message as a unicode string if available. None otherwise. |
| """ |
| try: |
| message = self._queue.get_nowait() |
| except Queue.Empty: |
| message = None |
| return message |
| |
| def stop(self): |
| """Request to stop this instance. |
| |
| The instance will be stopped after receiving the next message. |
| This method may not be very useful, but there is no clean way |
| in Python to forcefully stop a running thread. |
| """ |
| self._stop_requested = True |
| |
| |
| class MessageSender(threading.Thread): |
| """This class sends messages to the client. |
| |
| This class provides both synchronous and asynchronous ways to send |
| messages. |
| |
| Note: This class should not be used with the standalone server for wss |
| because pyOpenSSL used by the server raises a fatal error if the socket |
| is accessed from multiple threads. |
| """ |
| |
| def __init__(self, request): |
| """Construct an instance. |
| |
| Args: |
| request: mod_python request. |
| """ |
| threading.Thread.__init__(self) |
| self._request = request |
| self._queue = Queue.Queue() |
| self.setDaemon(True) |
| self.start() |
| |
| def run(self): |
| while True: |
| message, condition = self._queue.get() |
| condition.acquire() |
| send_message(self._request, message) |
| condition.notify() |
| condition.release() |
| |
| def send(self, message): |
| """Send a message, blocking.""" |
| |
| condition = threading.Condition() |
| condition.acquire() |
| self._queue.put((message, condition)) |
| condition.wait() |
| |
| def send_nowait(self, message): |
| """Send a message, non-blocking.""" |
| |
| self._queue.put((message, threading.Condition())) |
| |
| |
| # vi:sts=4 sw=4 et |