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