mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	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
 |