testing/mochitest/pywebsocket/mod_pywebsocket/msgutil.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 # Copyright 2011, Google Inc.
michael@0 2 # All rights reserved.
michael@0 3 #
michael@0 4 # Redistribution and use in source and binary forms, with or without
michael@0 5 # modification, are permitted provided that the following conditions are
michael@0 6 # met:
michael@0 7 #
michael@0 8 # * Redistributions of source code must retain the above copyright
michael@0 9 # notice, this list of conditions and the following disclaimer.
michael@0 10 # * Redistributions in binary form must reproduce the above
michael@0 11 # copyright notice, this list of conditions and the following disclaimer
michael@0 12 # in the documentation and/or other materials provided with the
michael@0 13 # distribution.
michael@0 14 # * Neither the name of Google Inc. nor the names of its
michael@0 15 # contributors may be used to endorse or promote products derived from
michael@0 16 # this software without specific prior written permission.
michael@0 17 #
michael@0 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 29
michael@0 30
michael@0 31 """Message related utilities.
michael@0 32
michael@0 33 Note: request.connection.write/read are used in this module, even though
michael@0 34 mod_python document says that they should be used only in connection
michael@0 35 handlers. Unfortunately, we have no other options. For example,
michael@0 36 request.write/read are not suitable because they don't allow direct raw
michael@0 37 bytes writing/reading.
michael@0 38 """
michael@0 39
michael@0 40
michael@0 41 import Queue
michael@0 42 import threading
michael@0 43
michael@0 44
michael@0 45 # Export Exception symbols from msgutil for backward compatibility
michael@0 46 from mod_pywebsocket._stream_base import ConnectionTerminatedException
michael@0 47 from mod_pywebsocket._stream_base import InvalidFrameException
michael@0 48 from mod_pywebsocket._stream_base import BadOperationException
michael@0 49 from mod_pywebsocket._stream_base import UnsupportedFrameException
michael@0 50
michael@0 51
michael@0 52 # An API for handler to send/receive WebSocket messages.
michael@0 53 def close_connection(request):
michael@0 54 """Close connection.
michael@0 55
michael@0 56 Args:
michael@0 57 request: mod_python request.
michael@0 58 """
michael@0 59 request.ws_stream.close_connection()
michael@0 60
michael@0 61
michael@0 62 def send_message(request, message, end=True, binary=False):
michael@0 63 """Send message.
michael@0 64
michael@0 65 Args:
michael@0 66 request: mod_python request.
michael@0 67 message: unicode text or str binary to send.
michael@0 68 end: False to send message as a fragment. All messages until the
michael@0 69 first call with end=True (inclusive) will be delivered to the
michael@0 70 client in separate frames but as one WebSocket message.
michael@0 71 binary: send message as binary frame.
michael@0 72 Raises:
michael@0 73 BadOperationException: when server already terminated.
michael@0 74 """
michael@0 75 request.ws_stream.send_message(message, end, binary)
michael@0 76
michael@0 77
michael@0 78 def receive_message(request):
michael@0 79 """Receive a WebSocket frame and return its payload as a text in
michael@0 80 unicode or a binary in str.
michael@0 81
michael@0 82 Args:
michael@0 83 request: mod_python request.
michael@0 84 Raises:
michael@0 85 InvalidFrameException: when client send invalid frame.
michael@0 86 UnsupportedFrameException: when client send unsupported frame e.g. some
michael@0 87 of reserved bit is set but no extension can
michael@0 88 recognize it.
michael@0 89 InvalidUTF8Exception: when client send a text frame containing any
michael@0 90 invalid UTF-8 string.
michael@0 91 ConnectionTerminatedException: when the connection is closed
michael@0 92 unexpectedly.
michael@0 93 BadOperationException: when client already terminated.
michael@0 94 """
michael@0 95 return request.ws_stream.receive_message()
michael@0 96
michael@0 97
michael@0 98 def send_ping(request, body=''):
michael@0 99 request.ws_stream.send_ping(body)
michael@0 100
michael@0 101
michael@0 102 class MessageReceiver(threading.Thread):
michael@0 103 """This class receives messages from the client.
michael@0 104
michael@0 105 This class provides three ways to receive messages: blocking,
michael@0 106 non-blocking, and via callback. Callback has the highest precedence.
michael@0 107
michael@0 108 Note: This class should not be used with the standalone server for wss
michael@0 109 because pyOpenSSL used by the server raises a fatal error if the socket
michael@0 110 is accessed from multiple threads.
michael@0 111 """
michael@0 112
michael@0 113 def __init__(self, request, onmessage=None):
michael@0 114 """Construct an instance.
michael@0 115
michael@0 116 Args:
michael@0 117 request: mod_python request.
michael@0 118 onmessage: a function to be called when a message is received.
michael@0 119 May be None. If not None, the function is called on
michael@0 120 another thread. In that case, MessageReceiver.receive
michael@0 121 and MessageReceiver.receive_nowait are useless
michael@0 122 because they will never return any messages.
michael@0 123 """
michael@0 124
michael@0 125 threading.Thread.__init__(self)
michael@0 126 self._request = request
michael@0 127 self._queue = Queue.Queue()
michael@0 128 self._onmessage = onmessage
michael@0 129 self._stop_requested = False
michael@0 130 self.setDaemon(True)
michael@0 131 self.start()
michael@0 132
michael@0 133 def run(self):
michael@0 134 try:
michael@0 135 while not self._stop_requested:
michael@0 136 message = receive_message(self._request)
michael@0 137 if self._onmessage:
michael@0 138 self._onmessage(message)
michael@0 139 else:
michael@0 140 self._queue.put(message)
michael@0 141 finally:
michael@0 142 close_connection(self._request)
michael@0 143
michael@0 144 def receive(self):
michael@0 145 """ Receive a message from the channel, blocking.
michael@0 146
michael@0 147 Returns:
michael@0 148 message as a unicode string.
michael@0 149 """
michael@0 150 return self._queue.get()
michael@0 151
michael@0 152 def receive_nowait(self):
michael@0 153 """ Receive a message from the channel, non-blocking.
michael@0 154
michael@0 155 Returns:
michael@0 156 message as a unicode string if available. None otherwise.
michael@0 157 """
michael@0 158 try:
michael@0 159 message = self._queue.get_nowait()
michael@0 160 except Queue.Empty:
michael@0 161 message = None
michael@0 162 return message
michael@0 163
michael@0 164 def stop(self):
michael@0 165 """Request to stop this instance.
michael@0 166
michael@0 167 The instance will be stopped after receiving the next message.
michael@0 168 This method may not be very useful, but there is no clean way
michael@0 169 in Python to forcefully stop a running thread.
michael@0 170 """
michael@0 171 self._stop_requested = True
michael@0 172
michael@0 173
michael@0 174 class MessageSender(threading.Thread):
michael@0 175 """This class sends messages to the client.
michael@0 176
michael@0 177 This class provides both synchronous and asynchronous ways to send
michael@0 178 messages.
michael@0 179
michael@0 180 Note: This class should not be used with the standalone server for wss
michael@0 181 because pyOpenSSL used by the server raises a fatal error if the socket
michael@0 182 is accessed from multiple threads.
michael@0 183 """
michael@0 184
michael@0 185 def __init__(self, request):
michael@0 186 """Construct an instance.
michael@0 187
michael@0 188 Args:
michael@0 189 request: mod_python request.
michael@0 190 """
michael@0 191 threading.Thread.__init__(self)
michael@0 192 self._request = request
michael@0 193 self._queue = Queue.Queue()
michael@0 194 self.setDaemon(True)
michael@0 195 self.start()
michael@0 196
michael@0 197 def run(self):
michael@0 198 while True:
michael@0 199 message, condition = self._queue.get()
michael@0 200 condition.acquire()
michael@0 201 send_message(self._request, message)
michael@0 202 condition.notify()
michael@0 203 condition.release()
michael@0 204
michael@0 205 def send(self, message):
michael@0 206 """Send a message, blocking."""
michael@0 207
michael@0 208 condition = threading.Condition()
michael@0 209 condition.acquire()
michael@0 210 self._queue.put((message, condition))
michael@0 211 condition.wait()
michael@0 212
michael@0 213 def send_nowait(self, message):
michael@0 214 """Send a message, non-blocking."""
michael@0 215
michael@0 216 self._queue.put((message, threading.Condition()))
michael@0 217
michael@0 218
michael@0 219 # vi:sts=4 sw=4 et

mercurial