forked from mirrors/gecko-dev
		
	 217bbab384
			
		
	
	
		217bbab384
		
	
	
	
	
		
			
			Update testing/mochitest/pywebsocket with the latest version available: pywebsocket3 is python 3 compatible. This keeps the basic structure of the old pywebsocket, but changes the directory name to pywebsocket3 to reflect the project renaming. Differential Revision: https://phabricator.services.mozilla.com/D84455
		
			
				
	
	
		
			214 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # 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.
 | |
| """
 | |
| 
 | |
| from __future__ import absolute_import
 | |
| import six.moves.queue
 | |
| import threading
 | |
| 
 | |
| # Export Exception symbols from msgutil for backward compatibility
 | |
| from mod_pywebsocket._stream_exceptions import ConnectionTerminatedException
 | |
| from mod_pywebsocket._stream_exceptions import InvalidFrameException
 | |
| from mod_pywebsocket._stream_exceptions import BadOperationException
 | |
| from mod_pywebsocket._stream_exceptions 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 = six.moves.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 six.moves.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 = six.moves.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
 |