feat: Option to use unix sockets for xml-rpc

This commit is contained in:
2026-08-06 09:59:53 +02:00
parent 0f04b210d0
commit c4531a33bf
2 changed files with 66 additions and 18 deletions
+55 -18
View File
@@ -15,7 +15,7 @@ from queue import Queue
from re import search
from threading import Lock
from uuid import uuid4
from xmlrpc.client import Binary, ServerProxy
from xmlrpc.client import Binary, ServerProxy, Transport
from PyQt5 import QtCore
from paramiko.ssh_exception import AuthenticationException
@@ -32,6 +32,21 @@ homedir = environ.get("HOME", "") or environ.get("APPDATA", "")
"""Home dir of user."""
class UnixStreamTransport(Transport):
"""Transport for xmlrpc to use unix domain sockets."""
def __init__(self, socket_path):
super().__init__()
self._socket_path = socket_path
def make_connection(self, host):
import http.client
conn = http.client.HTTPConnection("localhost")
conn.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
conn.sock.connect(self._socket_path)
return conn
class ConnectionFail(IntEnum):
NO_XML_RPC = 1
SSH_CONNECT = 2
@@ -91,6 +106,11 @@ class RevPiSettings:
if load_index is not None:
self.load_from_index(load_index)
@property
def is_unix_socket(self) -> bool:
"""Check if connection is a unix domain socket."""
return self.address.startswith("/") or self.address.startswith("./")
def load_from_index(self, settings_index: int) -> None:
"""Load settings from 'connections' index."""
self._settings.beginReadArray("connections")
@@ -129,8 +149,11 @@ class RevPiSettings:
pass
# These values must exists
if not (self.name and self.address and self.port):
raise ValueError("Could not geht all required values from saved settings")
if not (self.name and self.address):
raise ValueError("Could not get all required values from saved settings")
if not self.is_unix_socket and not self.port:
raise ValueError("Port is required for IP connections")
self._settings.endArray()
@@ -379,10 +402,10 @@ class ConnectionManager(QtCore.QThread):
)
return False
sp = ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
sp = create_server_proxy(revpi_settings, ssh_tunnel_port)
else:
sp = ServerProxy("http://{0}:{1}".format(revpi_settings.address, revpi_settings.port))
sp = create_server_proxy(revpi_settings)
# Load values and test connection to Revolution Pi
try:
@@ -435,10 +458,7 @@ class ConnectionManager(QtCore.QThread):
with self._lck_cli:
self.ssh_tunnel_server = ssh_tunnel_server
self._cli = sp
self._cli_connect.put_nowait((
"127.0.0.1" if revpi_settings.ssh_use_tunnel else revpi_settings.address,
ssh_tunnel_port if revpi_settings.ssh_use_tunnel else revpi_settings.port
))
self._cli_connect.put_nowait((revpi_settings, ssh_tunnel_port))
self.connection_established.emit()
@@ -552,8 +572,8 @@ class ConnectionManager(QtCore.QThread):
self.status_changed.emit(self.tr("Not connected"), "lightblue")
elif not self._cli_connect.empty():
# Get new connection information to create object in this thread
item = self._cli_connect.get()
sp = ServerProxy("http://{0}:{1}".format(*item))
revpi_settings, ssh_tunnel_port = self._cli_connect.get()
sp = create_server_proxy(revpi_settings, ssh_tunnel_port)
self._cli_connect.task_done()
if sp:
@@ -582,7 +602,7 @@ class ConnectionManager(QtCore.QThread):
self.settings.ssh_user,
self.ssh_pass
)
sp = ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
sp = create_server_proxy(self.settings, ssh_tunnel_port)
with self._lck_cli:
self.ssh_tunnel_server = ssh_tunnel_server
self._cli = sp
@@ -669,12 +689,8 @@ class ConnectionManager(QtCore.QThread):
Use connection_recovered signal to figure out new parameters.
"""
if not self.settings.ssh_use_tunnel and self.settings.address and self.settings.port:
return ServerProxy("http://{0}:{1}".format(self.settings.address, self.settings.port))
if self.settings.ssh_use_tunnel and self.ssh_tunnel_server and self.ssh_tunnel_server.connected:
return ServerProxy("http://127.0.0.1:{0}".format(self.ssh_tunnel_server.local_tunnel_port))
return None
ssh_tunnel_port = self.ssh_tunnel_server.local_tunnel_port if self.ssh_tunnel_server else None
return create_server_proxy(self.settings, ssh_tunnel_port)
@property
def connected(self) -> bool:
@@ -699,6 +715,27 @@ cm = ConnectionManager()
"""Clobal connection manager instance."""
def create_server_proxy(revpi_settings: RevPiSettings, ssh_tunnel_port: int = None) -> ServerProxy:
"""
Create a ServerProxy instance based on the given settings.
:param revpi_settings: Revolution Pi saved connection settings
:param ssh_tunnel_port: Use this port if an SSH tunnel is already established
:return: ServerProxy instance
"""
if revpi_settings.is_unix_socket:
return ServerProxy("http://localhost", transport=UnixStreamTransport(revpi_settings.address))
if ssh_tunnel_port:
return ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
if revpi_settings.ssh_use_tunnel:
# This case is usually handled by passing ssh_tunnel_port after connecting the tunnel
return ServerProxy("http://127.0.0.1:{0}".format(revpi_settings.port))
return ServerProxy("http://{0}:{1}".format(revpi_settings.address, revpi_settings.port))
def all_revpi_settings() -> [RevPiSettings]:
"""Get all revpi settings objects."""
# Get length of array and close it, the RevPiSettings-class need it
+11
View File
@@ -206,6 +206,16 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
self.sbx_ssh_port.setEnabled(con_item)
self.txt_ssh_user.setEnabled(con_item)
if con_item:
address = self.txt_address.text()
is_unix = address.startswith("/") or address.startswith("./")
if is_unix:
self.sbx_port.setEnabled(False)
self.cbx_ssh_use_tunnel.setChecked(False)
self.cbx_ssh_use_tunnel.setEnabled(False)
self.sbx_ssh_port.setEnabled(False)
self.txt_ssh_user.setEnabled(False)
def _get_folder_item(self, name: str):
"""Find the folder entry by name."""
for i in range(self.tre_connections.topLevelItemCount()):
@@ -369,6 +379,7 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
settings = self.__current_item.data(0, WidgetData.revpi_settings) # type: RevPiSettings
settings.address = text
self.changes = True
self._edit_state()
@QtCore.pyqtSlot(int)
def on_sbx_port_valueChanged(self, value: int):