Save old socket for IPC

At least on Windows with Qt 5.8, we get readyRead notifications *after*
disconnect...
This commit is contained in:
Florian Bruhin 2017-03-28 21:06:20 +02:00
parent 57223b78f3
commit a55d1b1ee8
2 changed files with 27 additions and 29 deletions

View File

@ -27,7 +27,7 @@ import getpass
import binascii import binascii
import hashlib import hashlib
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt, QTimer from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, Qt
from PyQt5.QtNetwork import QLocalSocket, QLocalServer, QAbstractSocket from PyQt5.QtNetwork import QLocalSocket, QLocalServer, QAbstractSocket
import qutebrowser import qutebrowser
@ -182,6 +182,7 @@ class IPCServer(QObject):
self._server.newConnection.connect(self.handle_connection) self._server.newConnection.connect(self.handle_connection)
self._socket = None self._socket = None
self._old_socket = None
self._socketopts_ok = os.name == 'nt' self._socketopts_ok = os.name == 'nt'
if self._socketopts_ok: # pragma: no cover if self._socketopts_ok: # pragma: no cover
# If we use setSocketOptions on Unix with Qt < 5.4, we get a # If we use setSocketOptions on Unix with Qt < 5.4, we get a
@ -278,15 +279,8 @@ class IPCServer(QObject):
log.ipc.debug("Client disconnected from socket 0x{:x}.".format( log.ipc.debug("Client disconnected from socket 0x{:x}.".format(
id(self._socket))) id(self._socket)))
self._timer.stop() self._timer.stop()
if self._socket is None: self._old_socket = self._socket
log.ipc.debug("In on_disconnected with None socket!") self._socket = None
else:
# For some reason Qt can still get delayed canReadNotifications
# internally, so if we call deleteLater() right away and then call
# QApplication::processEvents() somewhere in the code, we can get a
# segfault.
QTimer.singleShot(500, self._socket.deleteLater)
self._socket = None
# Maybe another connection is waiting. # Maybe another connection is waiting.
self.handle_connection() self.handle_connection()
@ -349,17 +343,23 @@ class IPCServer(QObject):
@pyqtSlot() @pyqtSlot()
def on_ready_read(self): def on_ready_read(self):
"""Read json data from the client.""" """Read json data from the client."""
if self._socket is None: if self._socket is None: # pragma: no cover
# This happens when doing a connection while another one is already # This happens when doing a connection while another one is already
# active for some reason. # active for some reason.
log.ipc.warning("In on_ready_read with None socket!") if self._old_socket is None:
return log.ipc.warning("In on_ready_read with None socket and "
"old_socket!")
return
log.ipc.debug("In on_ready_read with None socket!")
socket = self._old_socket
else:
socket = self._socket
self._timer.stop() self._timer.stop()
while self._socket is not None and self._socket.canReadLine(): while socket is not None and socket.canReadLine():
data = bytes(self._socket.readLine()) data = bytes(socket.readLine())
self.got_raw.emit(data) self.got_raw.emit(data)
log.ipc.debug("Read from socket 0x{:x}: {!r}".format( log.ipc.debug("Read from socket 0x{:x}: {!r}".format(
id(self._socket), data)) id(socket), data))
self._handle_data(data) self._handle_data(data)
self._timer.start() self._timer.start()

View File

@ -587,22 +587,20 @@ def test_timeout(qtbot, caplog, qlocalsocket, ipc_server):
assert caplog.records[-1].message.startswith("IPC connection timed out") assert caplog.records[-1].message.startswith("IPC connection timed out")
@pytest.mark.parametrize('method, args, is_warning', [ def test_ipcserver_socket_none_readyread(ipc_server, caplog):
pytest.mark.posix(('on_error', [0], False)),
('on_disconnected', [], False),
('on_ready_read', [], True),
])
def test_ipcserver_socket_none(ipc_server, caplog, method, args, is_warning):
func = getattr(ipc_server, method)
assert ipc_server._socket is None assert ipc_server._socket is None
assert ipc_server._old_socket is None
with caplog.at_level(logging.WARNING):
ipc_server.on_ready_read()
msg = "In on_ready_read with None socket and old_socket!"
assert msg in [r.message for r in caplog.records]
if is_warning:
with caplog.at_level(logging.WARNING):
func(*args)
else:
func(*args)
msg = "In {} with None socket!".format(method) @pytest.mark.posix
def test_ipcserver_socket_none_error(ipc_server, caplog):
assert ipc_server._socket is None
ipc_server.on_error(0)
msg = "In on_error with None socket!"
assert msg in [r.message for r in caplog.records] assert msg in [r.message for r in caplog.records]