ipc: Remove support for connecting to legacy servers

This commit is contained in:
Florian Bruhin 2017-07-09 12:45:26 +02:00
parent cfb169b5f0
commit 6a2163d36f
2 changed files with 14 additions and 125 deletions

View File

@ -20,7 +20,6 @@
"""Utilities for IPC with existing instances.""" """Utilities for IPC with existing instances."""
import os import os
import sys
import time import time
import json import json
import getpass import getpass
@ -41,8 +40,8 @@ ATIME_INTERVAL = 60 * 60 * 3 * 1000 # 3 hours
PROTOCOL_VERSION = 1 PROTOCOL_VERSION = 1
def _get_socketname_legacy(basedir): def _get_socketname_windows(basedir):
"""Legacy implementation of _get_socketname.""" """Get a socketname to use for Windows."""
parts = ['qutebrowser', getpass.getuser()] parts = ['qutebrowser', getpass.getuser()]
if basedir is not None: if basedir is not None:
md5 = hashlib.md5(basedir.encode('utf-8')).hexdigest() md5 = hashlib.md5(basedir.encode('utf-8')).hexdigest()
@ -50,10 +49,10 @@ def _get_socketname_legacy(basedir):
return '-'.join(parts) return '-'.join(parts)
def _get_socketname(basedir, legacy=False): def _get_socketname(basedir):
"""Get a socketname to use.""" """Get a socketname to use."""
if legacy or os.name == 'nt': if os.name == 'nt':
return _get_socketname_legacy(basedir) return _get_socketname_windows(basedir)
parts_to_hash = [getpass.getuser()] parts_to_hash = [getpass.getuser()]
if basedir is not None: if basedir is not None:
@ -415,41 +414,7 @@ class IPCServer(QObject):
self._remove_server() self._remove_server()
def _has_legacy_server(name): def send_to_running_instance(socketname, command, target_arg, *, socket=None):
"""Check if there is a legacy server.
Args:
name: The name to try to connect to.
Return:
True if there is a server with the given name, False otherwise.
"""
socket = QLocalSocket()
log.ipc.debug("Trying to connect to {}".format(name))
socket.connectToServer(name)
err = socket.error()
if err != QLocalSocket.UnknownSocketError:
log.ipc.debug("Socket error: {} ({})".format(
socket.errorString(), err))
mac_fail = (sys.platform == 'darwin' and
socket.errorString() == 'QLocalSocket::connectToServer: '
'Unknown error 38')
if err not in [QLocalSocket.ServerNotFoundError,
QLocalSocket.ConnectionRefusedError] and not mac_fail:
return True
socket.disconnectFromServer()
if socket.state() != QLocalSocket.UnconnectedState:
socket.waitForDisconnected(CONNECT_TIMEOUT)
return False
def send_to_running_instance(socketname, command, target_arg, *,
legacy_name=None, socket=None):
"""Try to send a commandline to a running instance. """Try to send a commandline to a running instance.
Blocks for CONNECT_TIMEOUT ms. Blocks for CONNECT_TIMEOUT ms.
@ -459,7 +424,6 @@ def send_to_running_instance(socketname, command, target_arg, *,
command: The command to send to the running instance. command: The command to send to the running instance.
target_arg: --target command line argument target_arg: --target command line argument
socket: The socket to read data from, or None. socket: The socket to read data from, or None.
legacy_name: The legacy name to first try to connect to.
Return: Return:
True if connecting was successful, False if no connection was made. True if connecting was successful, False if no connection was made.
@ -467,13 +431,8 @@ def send_to_running_instance(socketname, command, target_arg, *,
if socket is None: if socket is None:
socket = QLocalSocket() socket = QLocalSocket()
if legacy_name is not None and _has_legacy_server(legacy_name): log.ipc.debug("Connecting to {}".format(socketname))
name_to_use = legacy_name socket.connectToServer(socketname)
else:
name_to_use = socketname
log.ipc.debug("Connecting to {}".format(name_to_use))
socket.connectToServer(name_to_use)
connected = socket.waitForConnected(CONNECT_TIMEOUT) connected = socket.waitForConnected(CONNECT_TIMEOUT)
if connected: if connected:
@ -527,12 +486,10 @@ def send_or_listen(args):
None if an instance was running and received our request. None if an instance was running and received our request.
""" """
socketname = _get_socketname(args.basedir) socketname = _get_socketname(args.basedir)
legacy_socketname = _get_socketname(args.basedir, legacy=True)
try: try:
try: try:
sent = send_to_running_instance(socketname, args.command, sent = send_to_running_instance(socketname, args.command,
args.target, args.target)
legacy_name=legacy_socketname)
if sent: if sent:
return None return None
log.init.debug("Starting IPC server...") log.init.debug("Starting IPC server...")
@ -545,8 +502,7 @@ def send_or_listen(args):
log.init.debug("Got AddressInUseError, trying again.") log.init.debug("Got AddressInUseError, trying again.")
time.sleep(0.5) time.sleep(0.5)
sent = send_to_running_instance(socketname, args.command, sent = send_to_running_instance(socketname, args.command,
args.target, args.target)
legacy_name=legacy_socketname)
if sent: if sent:
return None return None
else: else:

View File

@ -182,11 +182,6 @@ def md5(inp):
class TestSocketName: class TestSocketName:
LEGACY_TESTS = [
(None, 'qutebrowser-testusername'),
('/x', 'qutebrowser-testusername-{}'.format(md5('/x'))),
]
POSIX_TESTS = [ POSIX_TESTS = [
(None, 'ipc-{}'.format(md5('testusername'))), (None, 'ipc-{}'.format(md5('testusername'))),
('/x', 'ipc-{}'.format(md5('testusername-/x'))), ('/x', 'ipc-{}'.format(md5('testusername-/x'))),
@ -196,12 +191,10 @@ class TestSocketName:
def patch_user(self, monkeypatch): def patch_user(self, monkeypatch):
monkeypatch.setattr(ipc.getpass, 'getuser', lambda: 'testusername') monkeypatch.setattr(ipc.getpass, 'getuser', lambda: 'testusername')
@pytest.mark.parametrize('basedir, expected', LEGACY_TESTS) @pytest.mark.parametrize('basedir, expected', [
def test_legacy(self, basedir, expected): (None, 'qutebrowser-testusername'),
socketname = ipc._get_socketname(basedir, legacy=True) ('/x', 'qutebrowser-testusername-{}'.format(md5('/x'))),
assert socketname == expected ])
@pytest.mark.parametrize('basedir, expected', LEGACY_TESTS)
@pytest.mark.windows @pytest.mark.windows
def test_windows(self, basedir, expected): def test_windows(self, basedir, expected):
socketname = ipc._get_socketname(basedir) socketname = ipc._get_socketname(basedir)
@ -629,14 +622,6 @@ class TestSendOrListen:
setattr(m, attr, getattr(QLocalSocket, attr)) setattr(m, attr, getattr(QLocalSocket, attr))
return m return m
@pytest.fixture
def legacy_server(self, args):
legacy_name = ipc._get_socketname(args.basedir, legacy=True)
legacy_server = ipc.IPCServer(legacy_name)
legacy_server.listen()
yield legacy_server
legacy_server.shutdown()
@pytest.mark.linux(reason="Flaky on Windows and macOS") @pytest.mark.linux(reason="Flaky on Windows and macOS")
def test_normal_connection(self, caplog, qtbot, args): def test_normal_connection(self, caplog, qtbot, args):
ret_server = ipc.send_or_listen(args) ret_server = ipc.send_or_listen(args)
@ -651,54 +636,6 @@ class TestSendOrListen:
assert ret_client is None assert ret_client is None
@pytest.mark.posix(reason="Unneeded on Windows")
def test_legacy_name(self, caplog, qtbot, args, legacy_server):
with qtbot.waitSignal(legacy_server.got_args):
ret = ipc.send_or_listen(args)
assert ret is None
msgs = [e.message for e in caplog.records]
assert "Connecting to {}".format(legacy_server._socketname) in msgs
@pytest.mark.posix(reason="Unneeded on Windows")
def test_stale_legacy_server(self, caplog, qtbot, args, legacy_server,
ipc_server, py_proc):
legacy_name = ipc._get_socketname(args.basedir, legacy=True)
logging.debug('== Setting up the legacy server ==')
cmdline = py_proc("""
import sys
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtNetwork import QLocalServer
app = QCoreApplication([])
QLocalServer.removeServer(sys.argv[1])
server = QLocalServer()
ok = server.listen(sys.argv[1])
assert ok
print(server.fullServerName())
""")
name = subprocess.check_output(
[cmdline[0]] + cmdline[1] + [legacy_name])
name = name.decode('utf-8').rstrip('\n')
# Closing the server should not remove the FIFO yet
assert os.path.exists(name)
## Setting up the new server
logging.debug('== Setting up new server ==')
ret_server = ipc.send_or_listen(args)
assert isinstance(ret_server, ipc.IPCServer)
logging.debug('== Connecting ==')
with qtbot.waitSignal(ret_server.got_args):
ret_client = ipc.send_or_listen(args)
assert ret_client is None
@pytest.mark.posix(reason="Unneeded on Windows") @pytest.mark.posix(reason="Unneeded on Windows")
def test_correct_socket_name(self, args): def test_correct_socket_name(self, args):
server = ipc.send_or_listen(args) server = ipc.send_or_listen(args)
@ -723,9 +660,7 @@ class TestSendOrListen:
qlocalsocket_mock().waitForConnected.side_effect = [False, True] qlocalsocket_mock().waitForConnected.side_effect = [False, True]
qlocalsocket_mock().error.side_effect = [ qlocalsocket_mock().error.side_effect = [
QLocalSocket.ServerNotFoundError, # legacy name
QLocalSocket.ServerNotFoundError, QLocalSocket.ServerNotFoundError,
QLocalSocket.ServerNotFoundError, # legacy name
QLocalSocket.UnknownSocketError, QLocalSocket.UnknownSocketError,
QLocalSocket.UnknownSocketError, # error() gets called twice QLocalSocket.UnknownSocketError, # error() gets called twice
] ]
@ -761,10 +696,8 @@ class TestSendOrListen:
# If it fails, that's the "not sent" case above. # If it fails, that's the "not sent" case above.
qlocalsocket_mock().waitForConnected.side_effect = [False, has_error] qlocalsocket_mock().waitForConnected.side_effect = [False, has_error]
qlocalsocket_mock().error.side_effect = [ qlocalsocket_mock().error.side_effect = [
QLocalSocket.ServerNotFoundError, # legacy name
QLocalSocket.ServerNotFoundError, QLocalSocket.ServerNotFoundError,
QLocalSocket.ServerNotFoundError, QLocalSocket.ServerNotFoundError,
QLocalSocket.ServerNotFoundError, # legacy name
QLocalSocket.ConnectionRefusedError, QLocalSocket.ConnectionRefusedError,
QLocalSocket.ConnectionRefusedError, # error() gets called twice QLocalSocket.ConnectionRefusedError, # error() gets called twice
] ]