def _parser(self): """ Generator to parse bytes into a frame. Yields until enough bytes have been read or an error is met. """ buf = '' bytes = '' # yield until we get the first header's byte while not bytes or len(bytes) < 1: bytes = (yield 1) first_byte = ord(bytes[0]) self.fin = (first_byte >> 7) & 1 self.rsv1 = (first_byte >> 6) & 1 self.rsv2 = (first_byte >> 5) & 1 self.rsv3 = (first_byte >> 4) & 1 self.opcode = first_byte & 0xf # frame-fin = %x0 ; more frames of this message follow # / %x1 ; final frame of this message if self.fin not in [0, 1]: raise ProtocolException() # frame-rsv1 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise # frame-rsv2 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise # frame-rsv3 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise if self.rsv1 or self.rsv2 or self.rsv3: raise ProtocolException() # control frames cannot be fragmented if self.opcode > 0x7 and self.fin == 0: raise ProtocolException() # do we already have enough bytes to continue? if bytes and len(bytes) > 1: buf = bytes[1:] bytes = buf else: bytes = '' # Yield until we get the second header's byte while not bytes or len(bytes) < 1: bytes = (yield 1) second_byte = ord(bytes[0]) mask = (second_byte >> 7) & 1 self.payload_length = second_byte & 0x7f # All control frames MUST have a payload length of 125 bytes or less if self.opcode > 0x7 and self.payload_length > 125: raise FrameTooLargeException() if bytes and len(bytes) > 1: buf = bytes[1:] bytes = buf else: buf = '' bytes = '' # The spec doesn't disallow putting a value in 0x0-0xFFFF into the # 8-octet extended payload length field (or 0x0-0xFD in 2-octet field). # So, we don't check the range of extended_payload_length. if self.payload_length == 127: if len(buf) < 8: nxt_buf_size = 8 - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or '') while len(bytes) < 8: b = (yield 8 - len(bytes)) if isinstance(b, basestring): bytes = bytes + b if len(bytes) > 8: buf = bytes[8:] else: bytes = buf[:8] buf = buf[8:] extended_payload_length = bytes self.payload_length = struct.unpack('!Q', extended_payload_length)[0] if self.payload_length > 0x7FFFFFFFFFFFFFFF: raise FrameTooLargeException() elif self.payload_length == 126: if len(buf) < 2: nxt_buf_size = 2 - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or '') while len(bytes) < 2: b = (yield 2 - len(bytes)) if isinstance(b, basestring): bytes = bytes + b if len(bytes) > 2: buf = bytes[2:] else: bytes = buf[:2] buf = buf[2:] extended_payload_length = bytes self.payload_length = struct.unpack('!H', extended_payload_length)[0] if mask: if len(buf) < 4: nxt_buf_size = 4 - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or '') while not bytes or len(bytes) < 4: b = (yield 4 - len(bytes)) if isinstance(b, basestring): bytes = bytes + b if len(bytes) > 4: buf = bytes[4:] else: bytes = buf[:4] buf = buf[4:] self.masking_key = bytes if len(buf) < self.payload_length: nxt_buf_size = self.payload_length - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or '') while len(bytes) < self.payload_length: l = self.payload_length - len(bytes) b = (yield l) if isinstance(b, basestring): bytes = bytes + b else: bytes = buf[:self.payload_length] self.body = bytes yield
def _parsing(self): """ Generator to parse bytes into a frame. Yields until enough bytes have been read or an error is met. """ buf = enc('') bytes = enc('') # yield until we get the first header's byte while not bytes: bytes = (yield 1) first_byte = ord(bytes[0]) self.fin = (first_byte >> 7) & 1 self.rsv1 = (first_byte >> 6) & 1 self.rsv2 = (first_byte >> 5) & 1 self.rsv3 = (first_byte >> 4) & 1 self.opcode = first_byte & 0xf # frame-fin = %x0 ; more frames of this message follow # / %x1 ; final frame of this message if self.fin not in [0, 1]: raise ProtocolException() # frame-rsv1 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise # frame-rsv2 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise # frame-rsv3 = %x0 ; 1 bit, MUST be 0 unless negotiated otherwise if self.rsv1 or self.rsv2 or self.rsv3: raise ProtocolException() # control frames between 3 and 7 as well as above 0xA are currently reserved if 2 < self.opcode < 8 or self.opcode > 0xA: raise ProtocolException() # control frames cannot be fragmented if self.opcode > 0x7 and self.fin == 0: raise ProtocolException() # do we already have enough bytes to continue? bytes = bytes[1:] if bytes and len(bytes) > 1 else enc('') # Yield until we get the second header's byte while not bytes: bytes = (yield 1) second_byte = ord(bytes[0]) mask = (second_byte >> 7) & 1 self.payload_length = second_byte & 0x7f # All control frames MUST have a payload length of 125 bytes or less if self.opcode > 0x7 and self.payload_length > 125: raise FrameTooLargeException() if bytes and len(bytes) > 1: buf = bytes[1:] bytes = buf else: buf = enc('') bytes = enc('') if self.payload_length == 127: if len(buf) < 8: nxt_buf_size = 8 - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or enc('')) while len(bytes) < 8: b = (yield 8 - len(bytes)) if b is not None: bytes = bytes + b if len(bytes) > 8: buf = bytes[8:] else: bytes = buf[:8] buf = buf[8:] extended_payload_length = bytes self.payload_length = unpack('!Q', extended_payload_length)[0] if self.payload_length > 0x7FFFFFFFFFFFFFFF: raise FrameTooLargeException() elif self.payload_length == 126: if len(buf) < 2: nxt_buf_size = 2 - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or enc('')) while len(bytes) < 2: b = (yield 2 - len(bytes)) if b is not None: bytes = bytes + b if len(bytes) > 2: buf = bytes[2:] else: bytes = buf[:2] buf = buf[2:] extended_payload_length = bytes self.payload_length = unpack('!H', extended_payload_length)[0] if mask: if len(buf) < 4: nxt_buf_size = 4 - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or enc('')) while not bytes or len(bytes) < 4: b = (yield 4 - len(bytes)) if b is not None: bytes = bytes + b if len(bytes) > 4: buf = bytes[4:] else: bytes = buf[:4] buf = buf[4:] self.masking_key = bytes if len(buf) < self.payload_length: nxt_buf_size = self.payload_length - len(buf) bytes = (yield nxt_buf_size) bytes = buf + (bytes or enc('')) while len(bytes) < self.payload_length: l = self.payload_length - len(bytes) b = (yield l) if b is not None: bytes = bytes + b else: if self.payload_length == len(buf): bytes = buf else: bytes = buf[:self.payload_length] self.body = bytes yield
def receiver(self): """ Parser that keeps trying to interpret bytes it is fed with as incoming frames part of a message. Control message are single frames only while data messages, like text and binary, may be fragmented accross frames. The way it works is by instanciating a framing.Frame object, then running its parser generator which yields how much bytes it requires to performs its task. The stream parser yields this value to its caller and feeds the frame parser. When the frame parser raises StopIteration, the stream parser tries to make sense of the parsed frame. It dispatches the frame's bytes to the most appropriate message type based on the frame's opcode. Overall this makes the stream parser totally agonstic to the data provider. """ running = True while running: frame = Frame() while True: try: bytes = (yield frame.parser.next()) if bytes is None: raise InvalidBytesError() frame.parser.send(bytes) except StopIteration: bytes = frame.body or '' if frame.masking_key and bytes: bytes = frame.unmask(bytes) if frame.opcode == OPCODE_TEXT: if self.message and not self.message.completed: # We got a text frame before we completed the previous one raise ProtocolException() try: m = TextMessage(bytes.decode("utf-8", "replace")) m.completed = (frame.fin == 1) self.message = m except UnicodeDecodeError: self.errors.append(CloseControlMessage(code=1007)) break elif frame.opcode == OPCODE_BINARY: m = BinaryMessage(bytes) m.completed = (frame.fin == 1) self.message = m elif frame.opcode == OPCODE_CONTINUATION: m = self.message if m is None: raise ProtocolException() m.completed = (frame.fin == 1) if m.opcode == OPCODE_TEXT: try: m.extend(bytes.decode("utf-8", "replace")) except UnicodeDecodeError: self.errors.append( CloseControlMessage(code=1007)) break else: m.extend(bytes) elif frame.opcode == OPCODE_CLOSE: self.closing = CloseControlMessage( reason=bytes.decode("utf-8", "replace")) elif frame.opcode == OPCODE_PING: self.pings.append(PingControlMessage(bytes)) elif frame.opcode == OPCODE_PONG: self.pongs.append(PongControlMessage(bytes)) else: self.errors.append(CloseControlMessage(code=1003)) # When the frame's payload is empty, we must yield # once more so that the caller is properly aligned if not bytes: yield 0 break except ProtocolException: self.errors.append(CloseControlMessage(code=1002)) except FrameTooLargeException: self.errors.append(CloseControlMessage(code=1004)) except StreamClosed: running = False break frame.parser.close()