def test_mixing_simple_messages_with_bulk_messages(): def read(bufsize): return "+SIMPLE\r\n*1\r\n$4\r\nBULK\r\n+SIMPLE\r\n*1\r\n$4\r\nBULK\r\n" p = Parser(read) assert list(p.get_instructions()) == [['SIMPLE'], ['BULK'], ['SIMPLE'], ['BULK']]
def test_missing_crlf_after_string(): responses = ["*1\r\n$4\r\nPING"] def read(bufsize): return responses.pop(0) p = Parser(read) with pytest.raises(StopIteration): next(p.get_instructions()) responses.append("\r\n") assert next(p.get_instructions()) == ['PING']
def test_bulk_string_inside_array(): def read(n): return "\ *5\r\n\ $4\r\n\ EVAL\r\n\ $69\r\n\ redis.call('set', KEYS[1], KEYS[2])\n\ return redis.call('get', KEYS[1])\r\n\ $1\r\n\ 2\r\n\ $7\r\n\ testkey\r\n\ $9\r\n\ testvalue\r\n" p = Parser(read) assert list(p.get_instructions()) == [[ 'EVAL', '''redis.call('set', KEYS[1], KEYS[2])\nreturn redis.call('get', KEYS[1])''', '2', 'testkey', 'testvalue' ]]
class CommandHandler(asyncore.dispatcher): def __init__(self, *args, **kwargs): asyncore.dispatcher.__init__(self, *args, **kwargs) self._parser = Parser(self.recv) # contains client message buffer self.keyspace = Keyspace() self.out_buffer = bytearray( ) # asyncore.py uses `str` instead of `bytearray` def handle_read(self): try: for cmd in self._parser.get_instructions(): logger.debug('{} data = {}'.format(self.addr, repr(cmd))) execute_cmd(self.keyspace, self.debug_send, *cmd) except socket.error as exc: # try again later if no data is available if exc.errno == errno.EAGAIN: return else: raise def debug_send(self, *args): logger.debug("out={}".format(repr(args))) return self.buffered_send(*args) def handle_close(self): logger.debug("closing {}".format(self.addr)) self.close() # buffer logic based on `asyncore.dispatcher_with_send` def handle_write(self): self.initiate_send() def initiate_send(self): num_sent = self.send(self.out_buffer) self.out_buffer = self.out_buffer[num_sent:] def buffered_send(self, data): self.out_buffer.extend(data) self.initiate_send() def writable(self): return (not self.connected) or len(self.out_buffer)
def test_parser_should_work_with_chunks_sent_separately(): responses = ["*1"] def read(bufsize): return responses.pop(0) p = Parser(read) with pytest.raises(StopIteration): next(p.get_instructions()) responses.append("\r\n$4\r") with pytest.raises(StopIteration): next(p.get_instructions()) responses.append("\nPIN") with pytest.raises(StopIteration): next(p.get_instructions()) responses.append("G\r\n") assert next(p.get_instructions()) == ['PING']
def __init__(self, *args, **kwargs): asyncore.dispatcher.__init__(self, *args, **kwargs) self._parser = Parser(self.recv) # contains client message buffer self.keyspace = Keyspace() self.out_buffer = bytearray( ) # asyncore.py uses `str` instead of `bytearray`
def test_parser_should_ignore_half_sent_commands(line): def read(bufsize): return line p = Parser(read) assert list(p.get_instructions()) == []
def test_parse_simple_string(): def read(n): return "+PING\r\n" p = Parser(read) assert list(p.get_instructions()) == [['PING']]
def test_multiple_arrays(): def read(n): return "*1\r\n$4\r\nPING\r\n*1\r\n$4\r\nPING\r\n" p = Parser(read) assert list(p.get_instructions()) == [['PING'], ['PING']]