def receive(self): if self._closed: raise WebSocketError("Connection is already closed") try: return uwsgi.websocket_recv_nb() except IOError, e: self.close() raise WebSocketError(e)
def send_frame(self, message, opcode): """ Send a frame over the websocket with message as its payload """ if self._closed: raise WebSocketError("Connection is already closed") if opcode == self.OPCODE_TEXT: message = self._encode_bytes(message) elif opcode == self.OPCODE_BINARY: message = str(message) header = Header.encode_header(True, opcode, '', len(message), 0) try: self.stream.write(header + message) except socket_error: raise WebSocketError("Socket is dead")
def read_frame(self): """ Block until a full frame has been read from the socket. This is an internal method as calling this will not cleanup correctly if an exception is called. Use `receive` instead. :return: The header and payload as a tuple. """ header = Header.decode_header(self.stream) if header.flags: raise WebSocketError if not header.length: return header, '' try: payload = self.stream.read(header.length) except socket_error: payload = '' except Exception: # TODO log out this exception payload = '' if len(payload) != header.length: raise WebSocketError('Unexpected EOF reading frame payload') if header.mask: payload = header.unmask_payload(payload) return header, payload
def get_file_descriptor(self): """Return the file descriptor for the given websocket""" try: return uwsgi.connection_fd() except IOError, e: self.close() raise WebSocketError(e)
def read_message(self): """ Return the next text or binary message from the socket. This is an internal method as calling this will not cleanup correctly if an exception is called. Use `receive` instead. """ opcode = None message = "" while True: header, payload = self.read_frame() f_opcode = header.opcode if f_opcode in (self.OPCODE_TEXT, self.OPCODE_BINARY): # a new frame if opcode: raise WebSocketError( "The opcode in non-fin frame is expected to be zero, got {0!r}" .format(f_opcode)) # Start reading a new message, reset the validator self.utf8validator.reset() self.utf8validate_last = (True, True, 0, 0) opcode = f_opcode elif f_opcode == self.OPCODE_CONTINUATION: if not opcode: raise WebSocketError("Unexpected frame with opcode=0") elif f_opcode == self.OPCODE_PING: self.handle_ping(header, payload) continue elif f_opcode == self.OPCODE_PONG: self.handle_pong(header, payload) continue elif f_opcode == self.OPCODE_CLOSE: self.handle_close(header, payload) return else: raise WebSocketError( "Unexpected opcode={0!r}".format(f_opcode)) if opcode == self.OPCODE_TEXT: self.validate_utf8(payload) message += payload if header.fin: break if opcode == self.OPCODE_TEXT: self.validate_utf8(message) return message else: return bytearray(message)
def decode_header(cls, stream): """ Decode a WebSocket header. :param stream: A file like object that can be 'read' from. :returns: A `Header` instance. """ read = stream.read data = read(2) if len(data) != 2: raise WebSocketError("Unexpected EOF while decoding header") first_byte, second_byte = struct.unpack('!BB', data) header = cls(fin=first_byte & cls.FIN_MASK == cls.FIN_MASK, opcode=first_byte & cls.OPCODE_MASK, flags=first_byte & cls.HEADER_FLAG_MASK, length=second_byte & cls.LENGTH_MASK) has_mask = second_byte & cls.MASK_MASK == cls.MASK_MASK if header.opcode > 0x07: if not header.fin: raise WebSocketError( 'Received fragmented control frame: {0!r}'.format(data)) # Control frames MUST have a payload length of 125 bytes or less if header.length > 125: raise FrameTooLargeException( 'Control frame cannot be larger than 125 bytes: {0!r}'. format(data)) if header.length == 126: # 16 bit length data = read(2) if len(data) != 2: raise WebSocketError('Unexpected EOF while decoding header') header.length = struct.unpack('!H', data)[0] elif header.length == 127: # 64 bit length data = read(8) if len(data) != 8: raise WebSocketError('Unexpected EOF while decoding header') header.length = struct.unpack('!Q', data)[0] if has_mask: mask = read(4) if len(mask) != 4: raise WebSocketError('Unexpected EOF while decoding header') header.mask = mask return header
def send(self, message, binary=False): """ Send a frame over the websocket with message as its payload """ if binary is None: binary = not isinstance(message, (str, unicode)) opcode = self.OPCODE_BINARY if binary else self.OPCODE_TEXT try: self.send_frame(message, opcode) except WebSocketError: raise WebSocketError("Socket is dead")
def handle_close(self, header, payload): """ Called when a close frame has been decoded from the stream. :param header: The decoded `Header`. :param payload: The bytestring payload associated with the close frame. """ if not payload: self.close(1000, None) return if len(payload) < 2: raise WebSocketError('Invalid close frame: {0} {1}'.format( header, payload)) code = struct.unpack('!H', str(payload[:2]))[0] payload = payload[2:] if payload: validator = Utf8Validator() val = validator.validate(payload) if not val[0]: raise UnicodeError if not self._is_valid_close_code(code): raise WebSocketError('Invalid close code {0}'.format(code)) self.close(code, payload)
def receive(self): """ Read and return a message from the stream. If `None` is returned, then the socket is considered closed/errored. """ if self._closed: raise WebSocketError("Connection is already closed") try: return self.read_message() except UnicodeError: logger.info('websocket.receive: UnicodeError') self.close(1007) except WebSocketError: logger.info('websocket.receive: WebSocketError') self.close(1002) except Exception, e: logger.info('websocket.receive: Unknown error %s', e) raise e
def send(self, message, binary=None): try: uwsgi.websocket_send(message) except IOError, e: self.close() raise WebSocketError(e)