fix: handle asynchronous QMP events before greeting
This commit is contained in:
@@ -23,6 +23,12 @@ class QMPClient:
|
||||
self.socket.settimeout(timeout)
|
||||
self.socket.connect(str(path))
|
||||
greeting = self._receive(deadline)
|
||||
# Real QEMU can queue a SPICE event across monitor reconnects.
|
||||
# Consume only protocol events, retaining the original deadline.
|
||||
while 'event' in greeting:
|
||||
if not self._async_event(greeting):
|
||||
raise QMPError('Invalid QMP event before greeting')
|
||||
greeting = self._receive(deadline)
|
||||
if not isinstance(greeting.get('QMP'), dict):
|
||||
raise QMPError('Invalid QMP greeting')
|
||||
self._command('qmp_capabilities', None, deadline)
|
||||
@@ -32,6 +38,22 @@ class QMPClient:
|
||||
raise
|
||||
raise QMPError(f'QMP connection failed: {exc}') from exc
|
||||
|
||||
@staticmethod
|
||||
def _async_event(message: dict) -> bool:
|
||||
if not {'event', 'timestamp'} <= message.keys() <= {'event', 'timestamp', 'data'}:
|
||||
return False
|
||||
if not isinstance(message['event'], str) or not message['event']:
|
||||
return False
|
||||
if 'data' in message and not isinstance(message['data'], dict):
|
||||
return False
|
||||
stamp = message['timestamp']
|
||||
if (not isinstance(stamp, dict) or set(stamp) != {'seconds', 'microseconds'}
|
||||
or any(type(value) is not int for value in stamp.values())):
|
||||
return False
|
||||
seconds, micros = stamp['seconds'], stamp['microseconds']
|
||||
# QMP specifies (-1, -1) when the server cannot retrieve host time.
|
||||
return (seconds == -1 and micros == -1) or (seconds >= 0 and 0 <= micros < 1000000)
|
||||
|
||||
def _receive(self, deadline: float) -> dict:
|
||||
while True:
|
||||
remaining = deadline - time.monotonic()
|
||||
|
||||
Reference in New Issue
Block a user