12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2012, Google Inc.
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# All rights reserved.
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Redistribution and use in source and binary forms, with or without
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# modification, are permitted provided that the following conditions are
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# met:
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Redistributions of source code must retain the above copyright
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# notice, this list of conditions and the following disclaimer.
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Redistributions in binary form must reproduce the above
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# copyright notice, this list of conditions and the following disclaimer
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# in the documentation and/or other materials provided with the
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distribution.
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#     * Neither the name of Google Inc. nor the names of its
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# contributors may be used to endorse or promote products derived from
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# this software without specific prior written permission.
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""This file provides classes and helper functions for parsing/building frames
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennisof the WebSocket protocol (RFC 6455).
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
342da489cd246702bee5938545b18a6f710ed214bcJamie GennisSpecification:
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennishttp://tools.ietf.org/html/rfc6455
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom collections import deque
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport logging
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport os
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport struct
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport time
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import common
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket import util
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import BadOperationException
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import ConnectionTerminatedException
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import InvalidFrameException
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import InvalidUTF8Exception
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import StreamBase
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom mod_pywebsocket._stream_base import UnsupportedFrameException
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis_NOOP_MASKER = util.NoopMasker()
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass Frame(object):
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, fin=1, rsv1=0, rsv2=0, rsv3=0,
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                 opcode=None, payload=''):
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.fin = fin
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.rsv1 = rsv1
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.rsv2 = rsv2
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.rsv3 = rsv3
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.opcode = opcode
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.payload = payload
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Helper functions made public to be used for writing unittests for WebSocket
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# clients.
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_length_header(length, mask):
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Creates a length header.
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        length: Frame length. Must be less than 2^63.
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        mask: Mask bit. Must be boolean.
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Raises:
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ValueError: when bad data is given.
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if mask:
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        mask_bit = 1 << 7
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        mask_bit = 0
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if length < 0:
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raise ValueError('length must be non negative integer')
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif length <= 125:
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return chr(mask_bit | length)
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif length < (1 << 16):
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return chr(mask_bit | 126) + struct.pack('!H', length)
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif length < (1 << 63):
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return chr(mask_bit | 127) + struct.pack('!Q', length)
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raise ValueError('Payload is too big for one frame')
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_header(opcode, payload_length, fin, rsv1, rsv2, rsv3, mask):
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Creates a frame header.
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Raises:
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Exception: when bad data is given.
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if opcode < 0 or 0xf < opcode:
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raise ValueError('Opcode out of range')
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if payload_length < 0 or (1 << 63) <= payload_length:
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raise ValueError('payload_length out of range')
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (fin | rsv1 | rsv2 | rsv3) & ~1:
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raise ValueError('FIN bit and Reserved bit parameter must be 0 or 1')
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    header = ''
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    first_byte = ((fin << 7)
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  | (rsv1 << 6) | (rsv2 << 5) | (rsv3 << 4)
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                  | opcode)
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    header += chr(first_byte)
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    header += create_length_header(payload_length, mask)
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return header
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _build_frame(header, body, mask):
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not mask:
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return header + body
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    masking_nonce = os.urandom(4)
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    masker = util.RepeatedXorMasker(masking_nonce)
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return header + masking_nonce + masker.mask(body)
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _filter_and_format_frame_object(frame, mask, frame_filters):
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for frame_filter in frame_filters:
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame_filter.filter(frame)
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    header = create_header(
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame.opcode, len(frame.payload), frame.fin,
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame.rsv1, frame.rsv2, frame.rsv3, mask)
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return _build_frame(header, frame.payload, mask)
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_binary_frame(
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    message, opcode=common.OPCODE_BINARY, fin=1, mask=False, frame_filters=[]):
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Creates a simple binary frame with no extension, reserved bit."""
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    frame = Frame(fin=fin, opcode=opcode, payload=message)
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return _filter_and_format_frame_object(frame, mask, frame_filters)
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_text_frame(
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    message, opcode=common.OPCODE_TEXT, fin=1, mask=False, frame_filters=[]):
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Creates a simple text frame with no extension, reserved bit."""
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    encoded_message = message.encode('utf-8')
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return create_binary_frame(encoded_message, opcode, fin, mask,
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                               frame_filters)
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef parse_frame(receive_bytes, logger=None,
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                ws_version=common.VERSION_HYBI_LATEST,
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                unmask_receive=True):
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Parses a frame. Returns a tuple containing each header field and
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    payload.
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        receive_bytes: a function that reads frame data from a stream or
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            something similar. The function takes length of the bytes to be
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            read. The function must raise ConnectionTerminatedException if
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            there is not enough data to be read.
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger: a logging object.
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ws_version: the version of WebSocket protocol.
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unmask_receive: unmask received frames. When received unmasked
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            frame, raises InvalidFrameException.
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Raises:
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ConnectionTerminatedException: when receive_bytes raises it.
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        InvalidFrameException: when the frame contains invalid data.
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not logger:
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger = logging.getLogger()
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logger.log(common.LOGLEVEL_FINE, 'Receive the first 2 octets of a frame')
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    received = receive_bytes(2)
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    first_byte = ord(received[0])
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    fin = (first_byte >> 7) & 1
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    rsv1 = (first_byte >> 6) & 1
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    rsv2 = (first_byte >> 5) & 1
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    rsv3 = (first_byte >> 4) & 1
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    opcode = first_byte & 0xf
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    second_byte = ord(received[1])
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    mask = (second_byte >> 7) & 1
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    payload_length = second_byte & 0x7f
2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logger.log(common.LOGLEVEL_FINE,
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis               'FIN=%s, RSV1=%s, RSV2=%s, RSV3=%s, opcode=%s, '
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis               'Mask=%s, Payload_length=%s',
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis               fin, rsv1, rsv2, rsv3, opcode, mask, payload_length)
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (mask == 1) != unmask_receive:
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raise InvalidFrameException(
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Mask bit on the received frame did\'nt match masking '
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'configuration for received frames')
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # The HyBi and later specs disallow putting a value in 0x0-0xFFFF
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # into the 8-octet extended payload length field (or 0x0-0xFD in
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # 2-octet field).
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    valid_length_encoding = True
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    length_encoding_bytes = 1
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if payload_length == 127:
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(common.LOGLEVEL_FINE,
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                   'Receive 8-octet extended payload length')
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        extended_payload_length = receive_bytes(8)
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        payload_length = struct.unpack(
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            '!Q', extended_payload_length)[0]
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if payload_length > 0x7FFFFFFFFFFFFFFF:
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise InvalidFrameException(
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Extended payload length >= 2^63')
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if ws_version >= 13 and payload_length < 0x10000:
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            valid_length_encoding = False
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            length_encoding_bytes = 8
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(common.LOGLEVEL_FINE,
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                   'Decoded_payload_length=%s', payload_length)
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif payload_length == 126:
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(common.LOGLEVEL_FINE,
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                   'Receive 2-octet extended payload length')
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        extended_payload_length = receive_bytes(2)
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        payload_length = struct.unpack(
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            '!H', extended_payload_length)[0]
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if ws_version >= 13 and payload_length < 126:
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            valid_length_encoding = False
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            length_encoding_bytes = 2
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(common.LOGLEVEL_FINE,
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                   'Decoded_payload_length=%s', payload_length)
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not valid_length_encoding:
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.warning(
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Payload length is not encoded using the minimal number of '
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'bytes (%d is encoded using %d bytes)',
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            payload_length,
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            length_encoding_bytes)
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if mask == 1:
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(common.LOGLEVEL_FINE, 'Receive mask')
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        masking_nonce = receive_bytes(4)
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        masker = util.RepeatedXorMasker(masking_nonce)
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(common.LOGLEVEL_FINE, 'Mask=%r', masking_nonce)
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    else:
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        masker = _NOOP_MASKER
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logger.log(common.LOGLEVEL_FINE, 'Receive payload data')
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if logger.isEnabledFor(common.LOGLEVEL_FINE):
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        receive_start = time.time()
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    raw_payload_bytes = receive_bytes(payload_length)
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if logger.isEnabledFor(common.LOGLEVEL_FINE):
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            common.LOGLEVEL_FINE,
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Done receiving payload data at %s MB/s',
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            payload_length / (time.time() - receive_start) / 1000 / 1000)
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    logger.log(common.LOGLEVEL_FINE, 'Unmask payload data')
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if logger.isEnabledFor(common.LOGLEVEL_FINE):
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unmask_start = time.time()
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    bytes = masker.mask(raw_payload_bytes)
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if logger.isEnabledFor(common.LOGLEVEL_FINE):
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        logger.log(
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            common.LOGLEVEL_FINE,
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Done unmasking payload data at %s MB/s',
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            payload_length / (time.time() - unmask_start) / 1000 / 1000)
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return opcode, bytes, fin, rsv1, rsv2, rsv3
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass FragmentedFrameBuilder(object):
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """A stateful class to send a message as fragments."""
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, mask, frame_filters=[], encode_utf8=True):
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Constructs an instance."""
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._mask = mask
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._frame_filters = frame_filters
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # This is for skipping UTF-8 encoding when building text type frames
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # from compressed data.
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._encode_utf8 = encode_utf8
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._started = False
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Hold opcode of the first frame in messages to verify types of other
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # frames in the message are all the same.
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._opcode = common.OPCODE_TEXT
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def build(self, message, end, binary):
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if binary:
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            frame_type = common.OPCODE_BINARY
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            frame_type = common.OPCODE_TEXT
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._started:
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if self._opcode != frame_type:
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                raise ValueError('Message types are different in frames for '
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                 'the same message')
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            opcode = common.OPCODE_CONTINUATION
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            opcode = frame_type
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._opcode = frame_type
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if end:
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._started = False
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            fin = 1
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._started = True
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            fin = 0
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if binary or not self._encode_utf8:
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return create_binary_frame(
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                message, opcode, fin, self._mask, self._frame_filters)
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return create_text_frame(
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                message, opcode, fin, self._mask, self._frame_filters)
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _create_control_frame(opcode, body, mask, frame_filters):
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    frame = Frame(opcode=opcode, payload=body)
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for frame_filter in frame_filters:
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame_filter.filter(frame)
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if len(frame.payload) > 125:
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        raise BadOperationException(
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Payload data size of control frames must be 125 bytes or less')
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    header = create_header(
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame.opcode, len(frame.payload), frame.fin,
3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame.rsv1, frame.rsv2, frame.rsv3, mask)
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return _build_frame(header, frame.payload, mask)
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_ping_frame(body, mask=False, frame_filters=[]):
3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return _create_control_frame(common.OPCODE_PING, body, mask, frame_filters)
3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_pong_frame(body, mask=False, frame_filters=[]):
3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return _create_control_frame(common.OPCODE_PONG, body, mask, frame_filters)
3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_close_frame(body, mask=False, frame_filters=[]):
3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return _create_control_frame(
3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        common.OPCODE_CLOSE, body, mask, frame_filters)
3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef create_closing_handshake_body(code, reason):
3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    body = ''
3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if code is not None:
3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (code > common.STATUS_USER_PRIVATE_MAX or
3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            code < common.STATUS_NORMAL_CLOSURE):
3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise BadOperationException('Status code is out of range')
3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (code == common.STATUS_NO_STATUS_RECEIVED or
3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            code == common.STATUS_ABNORMAL_CLOSURE or
3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            code == common.STATUS_TLS_HANDSHAKE):
3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise BadOperationException('Status code is reserved pseudo '
3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'code')
3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        encoded_reason = reason.encode('utf-8')
3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        body = struct.pack('!H', code) + encoded_reason
3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return body
3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass StreamOptions(object):
3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Holds option values to configure Stream objects."""
3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self):
3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Constructs StreamOptions."""
3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Enables deflate-stream extension.
3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.deflate_stream = False
3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Filters applied to frames.
3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.outgoing_frame_filters = []
3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.incoming_frame_filters = []
3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Filters applied to messages. Control frames are not affected by them.
4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.outgoing_message_filters = []
4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.incoming_message_filters = []
4022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.encode_text_message_to_utf8 = True
4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.mask_send = False
4052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.unmask_receive = True
4062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # RFC6455 disallows fragmented control frames, but mux extension
4072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # relaxes the restriction.
4082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.allow_fragmented_control_frame = False
4092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4112da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass Stream(StreamBase):
4122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """A class for parsing/building frames of the WebSocket protocol
4132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    (RFC 6455).
4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def __init__(self, request, options):
4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Constructs an instance.
4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            request: mod_python request.
4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        StreamBase.__init__(self, request)
4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger = util.get_class_logger(self)
4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._options = options
4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._options.deflate_stream:
4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.debug('Setup filter for deflate-stream')
4312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._request = util.DeflateRequest(self._request)
4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._request.client_terminated = False
4342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._request.server_terminated = False
4352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Holds body of received fragments.
4372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._received_fragments = []
4382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Holds the opcode of the first fragment.
4392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._original_opcode = None
4402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._writer = FragmentedFrameBuilder(
4422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._options.mask_send, self._options.outgoing_frame_filters,
4432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._options.encode_text_message_to_utf8)
4442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._ping_queue = deque()
4462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _receive_frame(self):
4482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Receives a frame and return data in the frame as a tuple containing
4492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        each header field and payload separately.
4502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Raises:
4522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ConnectionTerminatedException: when read returns empty
4532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                string.
4542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            InvalidFrameException: when the frame contains invalid data.
4552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
4562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        def _receive_bytes(length):
4582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return self.receive_bytes(length)
4592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return parse_frame(receive_bytes=_receive_bytes,
4612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                           logger=self._logger,
4622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                           ws_version=self._request.ws_version,
4632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                           unmask_receive=self._options.unmask_receive)
4642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _receive_frame_as_frame_object(self):
4662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        opcode, bytes, fin, rsv1, rsv2, rsv3 = self._receive_frame()
4672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return Frame(fin=fin, rsv1=rsv1, rsv2=rsv2, rsv3=rsv3,
4692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                     opcode=opcode, payload=bytes)
4702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def send_message(self, message, end=True, binary=False):
4722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Send message.
4732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
4752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            message: text in unicode or binary in str to send.
4762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            binary: send message as binary frame.
4772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Raises:
4792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            BadOperationException: when called on a server-terminated
4802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                connection or called with inconsistent message type or
4812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                binary parameter.
4822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
4832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._request.server_terminated:
4852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise BadOperationException(
4862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Requested send_message after sending out a closing handshake')
4872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if binary and isinstance(message, unicode):
4892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise BadOperationException(
4902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Message for binary frame must be instance of str')
4912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for message_filter in self._options.outgoing_message_filters:
4932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            message = message_filter.filter(message, end, binary)
4942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        try:
4962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._write(self._writer.build(message, end, binary))
4972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        except ValueError, e:
4982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise BadOperationException(e)
4992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _get_message_from_frame(self, frame):
5012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Gets a message from frame. If the message is composed of fragmented
5022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frames and the frame is not the last fragmented frame, this method
5032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        returns None. The whole message will be returned when the last
5042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        fragmented frame is passed to this method.
5052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Raises:
5072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            InvalidFrameException: when the frame doesn't match defragmentation
5082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                context, or the frame contains invalid data.
5092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
5102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if frame.opcode == common.OPCODE_CONTINUATION:
5122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if not self._received_fragments:
5132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                if frame.fin:
5142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    raise InvalidFrameException(
5152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'Received a termination frame but fragmentation '
5162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'not started')
5172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                else:
5182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    raise InvalidFrameException(
5192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'Received an intermediate frame but '
5202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'fragmentation not started')
5212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if frame.fin:
5232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # End of fragmentation frame
5242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._received_fragments.append(frame.payload)
5252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                message = ''.join(self._received_fragments)
5262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._received_fragments = []
5272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return message
5282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            else:
5292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # Intermediate frame
5302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._received_fragments.append(frame.payload)
5312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return None
5322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
5332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if self._received_fragments:
5342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                if frame.fin:
5352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    raise InvalidFrameException(
5362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'Received an unfragmented frame without '
5372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'terminating existing fragmentation')
5382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                else:
5392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    raise InvalidFrameException(
5402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'New fragmentation started without terminating '
5412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'existing fragmentation')
5422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if frame.fin:
5442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # Unfragmented frame
5452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._original_opcode = frame.opcode
5472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return frame.payload
5482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            else:
5492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # Start of fragmentation frame
5502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                if (not self._options.allow_fragmented_control_frame and
5522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    common.is_control_opcode(frame.opcode)):
5532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    raise InvalidFrameException(
5542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'Control frames must not be fragmented')
5552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._original_opcode = frame.opcode
5572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._received_fragments.append(frame.payload)
5582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return None
5592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _process_close_message(self, message):
5612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Processes close message.
5622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
5642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            message: close message.
5652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Raises:
5672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            InvalidFrameException: when the message is invalid.
5682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
5692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._request.client_terminated = True
5712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Status code is optional. We can have status reason only if we
5732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # have status code. Status reason can be empty string. So,
5742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # allowed cases are
5752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # - no application data: no code no reason
5762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # - 2 octet of application data: has code but no reason
5772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # - 3 or more octet of application data: both code and reason
5782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if len(message) == 0:
5792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.debug('Received close frame (empty body)')
5802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._request.ws_close_code = (
5812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                common.STATUS_NO_STATUS_RECEIVED)
5822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elif len(message) == 1:
5832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise InvalidFrameException(
5842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'If a close frame has status code, the length of '
5852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'status code must be 2 octet')
5862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elif len(message) >= 2:
5872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._request.ws_close_code = struct.unpack(
5882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                '!H', message[0:2])[0]
5892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._request.ws_close_reason = message[2:].decode(
5902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'utf-8', 'replace')
5912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.debug(
5922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Received close frame (code=%d, reason=%r)',
5932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._request.ws_close_code,
5942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._request.ws_close_reason)
5952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Drain junk data after the close frame if necessary.
5972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._drain_received_data()
5982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._request.server_terminated:
6002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.debug(
6012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Received ack for server-initiated closing handshake')
6022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return
6032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger.debug(
6052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Received client-initiated closing handshake')
6062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        code = common.STATUS_NORMAL_CLOSURE
6082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        reason = ''
6092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if hasattr(self._request, '_dispatcher'):
6102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            dispatcher = self._request._dispatcher
6112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            code, reason = dispatcher.passive_closing_handshake(
6122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._request)
6132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if code is None and reason is not None and len(reason) > 0:
6142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.warning(
6152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'Handler specified reason despite code being None')
6162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                reason = ''
6172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if reason is None:
6182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                reason = ''
6192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._send_closing_handshake(code, reason)
6202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger.debug(
6212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Sent ack for client-initiated closing handshake '
6222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            '(code=%r, reason=%r)', code, reason)
6232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _process_ping_message(self, message):
6252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Processes ping message.
6262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
6282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            message: ping message.
6292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
6302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        try:
6322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            handler = self._request.on_ping_handler
6332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if handler:
6342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                handler(self._request, message)
6352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return
6362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        except AttributeError, e:
6372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            pass
6382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._send_pong(message)
6392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _process_pong_message(self, message):
6412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Processes pong message.
6422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
6442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            message: pong message.
6452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
6462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # TODO(tyoshino): Add ping timeout handling.
6482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        inflight_pings = deque()
6502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        while True:
6522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            try:
6532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                expected_body = self._ping_queue.popleft()
6542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                if expected_body == message:
6552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    # inflight_pings contains pings ignored by the
6562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    # other peer. Just forget them.
6572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    self._logger.debug(
6582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        'Ping %r is acked (%d pings were ignored)',
6592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                        expected_body, len(inflight_pings))
6602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    break
6612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                else:
6622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    inflight_pings.append(expected_body)
6632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            except IndexError, e:
6642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # The received pong was unsolicited pong. Keep the
6652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # ping queue as is.
6662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._ping_queue = inflight_pings
6672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._logger.debug('Received a unsolicited pong')
6682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                break
6692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        try:
6712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            handler = self._request.on_pong_handler
6722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if handler:
6732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                handler(self._request, message)
6742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        except AttributeError, e:
6752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            pass
6762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def receive_message(self):
6782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Receive a WebSocket frame and return its payload as a text in
6792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unicode or a binary in str.
6802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Returns:
6822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            payload data of the frame
6832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            - as unicode instance if received text frame
6842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            - as str instance if received binary frame
6852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            or None iff received closing handshake.
6862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Raises:
6872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            BadOperationException: when called on a client-terminated
6882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                connection.
6892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ConnectionTerminatedException: when read returns empty
6902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                string.
6912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            InvalidFrameException: when the frame contains invalid
6922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                data.
6932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            UnsupportedFrameException: when the received frame has
6942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                flags, opcode we cannot handle. You can ignore this
6952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                exception and continue receiving the next frame.
6962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
6972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._request.client_terminated:
6992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise BadOperationException(
7002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Requested receive_message after receiving a closing '
7012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'handshake')
7022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        while True:
7042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # mp_conn.read will block if no bytes are available.
7052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # Timeout is controlled by TimeOut directive of Apache.
7062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            frame = self._receive_frame_as_frame_object()
7082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # Check the constraint on the payload size for control frames
7102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # before extension processes the frame.
7112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # See also http://tools.ietf.org/html/rfc6455#section-5.5
7122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if (common.is_control_opcode(frame.opcode) and
7132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                len(frame.payload) > 125):
7142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                raise InvalidFrameException(
7152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'Payload data size of control frames must be 125 bytes or '
7162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'less')
7172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            for frame_filter in self._options.incoming_frame_filters:
7192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                frame_filter.filter(frame)
7202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if frame.rsv1 or frame.rsv2 or frame.rsv3:
7222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                raise UnsupportedFrameException(
7232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'Unsupported flag is set (rsv = %d%d%d)' %
7242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    (frame.rsv1, frame.rsv2, frame.rsv3))
7252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            message = self._get_message_from_frame(frame)
7272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if message is None:
7282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                continue
7292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            for message_filter in self._options.incoming_message_filters:
7312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                message = message_filter.filter(message)
7322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if self._original_opcode == common.OPCODE_TEXT:
7342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # The WebSocket protocol section 4.4 specifies that invalid
7352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # characters must be replaced with U+fffd REPLACEMENT
7362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                # CHARACTER.
7372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                try:
7382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    return message.decode('utf-8')
7392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                except UnicodeDecodeError, e:
7402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    raise InvalidUTF8Exception(e)
7412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            elif self._original_opcode == common.OPCODE_BINARY:
7422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return message
7432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            elif self._original_opcode == common.OPCODE_CLOSE:
7442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._process_close_message(message)
7452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                return None
7462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            elif self._original_opcode == common.OPCODE_PING:
7472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._process_ping_message(message)
7482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            elif self._original_opcode == common.OPCODE_PONG:
7492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                self._process_pong_message(message)
7502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            else:
7512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                raise UnsupportedFrameException(
7522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'Opcode %d is not supported' % self._original_opcode)
7532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _send_closing_handshake(self, code, reason):
7552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        body = create_closing_handshake_body(code, reason)
7562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame = create_close_frame(
7572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            body, mask=self._options.mask_send,
7582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            frame_filters=self._options.outgoing_frame_filters)
7592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._request.server_terminated = True
7612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._write(frame)
7632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def close_connection(self, code=common.STATUS_NORMAL_CLOSURE, reason=''):
7652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Closes a WebSocket connection.
7662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Args:
7682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            code: Status code for close frame. If code is None, a close
7692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                frame with empty body will be sent.
7702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            reason: string representing close reason.
7712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Raises:
7722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            BadOperationException: when reason is specified with code None
7732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            or reason is not an instance of both str and unicode.
7742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
7752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self._request.server_terminated:
7772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._logger.debug(
7782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Requested close_connection but server is already terminated')
7792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return
7802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if code is None:
7822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if reason is not None and len(reason) > 0:
7832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                raise BadOperationException(
7842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'close reason must not be specified if code is None')
7852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            reason = ''
7862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
7872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if not isinstance(reason, str) and not isinstance(reason, unicode):
7882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                raise BadOperationException(
7892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                    'close reason must be an instance of str or unicode')
7902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._send_closing_handshake(code, reason)
7922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._logger.debug(
7932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            'Sent server-initiated closing handshake (code=%r, reason=%r)',
7942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            code, reason)
7952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (code == common.STATUS_GOING_AWAY or
7972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            code == common.STATUS_PROTOCOL_ERROR):
7982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # It doesn't make sense to wait for a close frame if the reason is
7992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # protocol error or that the server is going away. For some of
8002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # other reasons, it might not make sense to wait for a close frame,
8012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            # but it's not clear, yet.
8022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return
8032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # TODO(ukai): 2. wait until the /client terminated/ flag has been set,
8052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # or until a server-defined timeout expires.
8062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        #
8072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # For now, we expect receiving closing handshake right after sending
8082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # out closing handshake.
8092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        message = self.receive_message()
8102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if message is not None:
8112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            raise ConnectionTerminatedException(
8122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                'Didn\'t receive valid ack for closing handshake')
8132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # TODO: 3. close the WebSocket connection.
8142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # note: mod_python Connection (mp_conn) doesn't have close method.
8152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def send_ping(self, body=''):
8172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame = create_ping_frame(
8182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            body,
8192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._options.mask_send,
8202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._options.outgoing_frame_filters)
8212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._write(frame)
8222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._ping_queue.append(body)
8242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _send_pong(self, body):
8262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame = create_pong_frame(
8272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            body,
8282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._options.mask_send,
8292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._options.outgoing_frame_filters)
8302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._write(frame)
8312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def get_last_received_opcode(self):
8332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Returns the opcode of the WebSocket message which the last received
8342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        frame belongs to. The return value is valid iff immediately after
8352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        receive_message call.
8362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
8372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return self._original_opcode
8392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    def _drain_received_data(self):
8412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """Drains unread data in the receive buffer to avoid sending out TCP
8422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        RST packet. This is because when deflate-stream is enabled, some
8432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        DEFLATE block for flushing data may follow a close frame. If any data
8442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        remains in the receive buffer of a socket when the socket is closed,
8452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        it sends out TCP RST packet to the other peer.
8462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Since mod_python's mp_conn object doesn't support non-blocking read,
8482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        we perform this only when pywebsocket is running in standalone mode.
8492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        """
8502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # If self._options.deflate_stream is true, self._request is
8522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # DeflateRequest, so we can get wrapped request object by
8532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # self._request._request.
8542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        #
8552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Only _StandaloneRequest has _drain_received_data method.
8562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (self._options.deflate_stream and
8572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ('_drain_received_data' in dir(self._request._request))):
8582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._request._request._drain_received_data()
8592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# vi:sts=4 sw=4 et
862