Пример #1
0
    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
Пример #2
0
    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
Пример #3
0
    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()