diff --git a/dbus_next/message_bus.py b/dbus_next/message_bus.py index 2a25f6b..7d9ca27 100644 --- a/dbus_next/message_bus.py +++ b/dbus_next/message_bus.py @@ -55,9 +55,6 @@ def __init__(self, # buffer messages until connect self._buffered_messages = [] self._serial = 0 - self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM | socket.SOCK_NONBLOCK) - self._stream = self._sock.makefile('rwb') - self._fd = self._sock.fileno() self._user_message_handlers = [] # the key is the name and the value is the unique name of the owner. # This cache is kept up to date by the NameOwnerChanged signal and is @@ -503,23 +500,48 @@ def _setup_socket(self): for transport, options in self._bus_address: filename = None + ip_addr = '' + ip_port = 0 if transport == 'unix': + self._sock = socket.socket(socket.AF_UNIX, + socket.SOCK_STREAM | socket.SOCK_NONBLOCK) + self._stream = self._sock.makefile('rwb') + self._fd = self._sock.fileno() + if 'path' in options: filename = options['path'] elif 'abstract' in options: filename = f'\0{options["abstract"]}' else: raise InvalidAddressError('got unix transport with unknown path specifier') + + try: + self._sock.connect(filename) + break + except Exception as e: + err = e + + elif transport == 'tcp': + self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._stream = self._sock.makefile('rwb') + self._fd = self._sock.fileno() + + if 'host' in options: + ip_addr = options['host'] + if 'port' in options: + ip_port = int(options['port']) + + try: + self._sock.connect((ip_addr, ip_port)) + self._sock.setblocking(False) + break + except Exception as e: + err = e + else: raise InvalidAddressError(f'got unknown address transport: {transport}') - try: - self._sock.connect(filename) - break - except Exception as e: - err = e - if err: raise err diff --git a/examples/aio-tcp-notification.py b/examples/aio-tcp-notification.py new file mode 100755 index 0000000..357c6ff --- /dev/null +++ b/examples/aio-tcp-notification.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +# In order for this to work a local tcp connection to the DBus a port +# must be opened to forward to the dbus socket file. The easiest way +# to achieve this is using "socat": +# socat TCP-LISTEN:55556,reuseaddr,fork,range=127.0.0.1/32 UNIX-CONNECT:$(echo $DBUS_SESSION_BUS_ADDRESS | sed 's/unix:path=//g') +# For actual DBus transport over network the authentication might +# be a further problem. More information here: +# https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms + +import sys +import os +sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/..')) + +from dbus_next.aio import MessageBus + +import asyncio + +loop = asyncio.get_event_loop() + + +async def main(): + bus = await MessageBus(bus_address="tcp:host=127.0.0.1,port=55556").connect() + introspection = await bus.introspect('org.freedesktop.Notifications', + '/org/freedesktop/Notifications') + obj = bus.get_proxy_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications', + introspection) + notification = obj.get_interface('org.freedesktop.Notifications') + await notification.call_notify("test.py", 0, "", "DBus Test", "Test notification", [""], dict(), + 5000) + + +loop.run_until_complete(main()) diff --git a/test/test_address_parser.py b/test/test_address_parser.py index 3991024..5d63c3f 100644 --- a/test/test_address_parser.py +++ b/test/test_address_parser.py @@ -20,6 +20,10 @@ def test_valid_addresses(): })], 'unix:escaped=hello%20world': [('unix', { 'escaped': 'hello world' + })], + 'tcp:host=127.0.0.1,port=55556': [('tcp', { + 'host': '127.0.0.1', + 'port': '55556' })] } diff --git a/test/test_aio_low_level.py b/test/test_aio_low_level.py index 1042a1a..f855e26 100644 --- a/test/test_aio_low_level.py +++ b/test/test_aio_low_level.py @@ -1,6 +1,7 @@ from dbus_next.aio import MessageBus from dbus_next import Message, MessageType, MessageFlag +import asyncio import pytest @@ -136,3 +137,14 @@ def message_handler(signal): assert signal.member == 'SomeSignal' assert signal.signature == 's' assert signal.body == ['a signal'] + + +@pytest.mark.asyncio +async def test_connection_via_tcp(): + server = await asyncio.start_server(None, '127.0.0.1', 55556) + bus = MessageBus(bus_address="tcp:host=127.0.0.1,port=55556") + assert bus._sock.getpeername()[0] == '127.0.0.1' + assert bus._sock.getsockname()[0] == '127.0.0.1' + assert bus._sock.gettimeout() == 0 + assert bus._stream.closed is False + server.close()