tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

msgutil.py (7692B)


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