From d664c11e2285560e56aaaec646a578fa64b84d7e Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 7 Jun 2011 14:09:15 +0200 Subject: [PATCH 1/5] Pull network related functions out of mopidy.frontends.mpd.server --- mopidy/frontends/mpd/server.py | 33 +++------------------------ mopidy/utils/network.py | 36 ++++++++++++++++++++++++++++++ tests/frontends/mpd/server_test.py | 23 ------------------- tests/utils/network_test.py | 19 ++++++++++++++++ 4 files changed, 58 insertions(+), 53 deletions(-) create mode 100644 mopidy/utils/network.py create mode 100644 tests/utils/network_test.py diff --git a/mopidy/frontends/mpd/server.py b/mopidy/frontends/mpd/server.py index 1be46ef4..87a1cd0a 100644 --- a/mopidy/frontends/mpd/server.py +++ b/mopidy/frontends/mpd/server.py @@ -1,28 +1,13 @@ import asyncore import logging -import re -import socket import sys from mopidy import settings +from mopidy.utils import network from .session import MpdSession logger = logging.getLogger('mopidy.frontends.mpd.server') -def _try_ipv6_socket(): - """Determine if system really supports IPv6""" - if not socket.has_ipv6: - return False - try: - socket.socket(socket.AF_INET6).close() - return True - except IOError, e: - logger.debug(u'Platform supports IPv6, but socket ' - 'creation failed, disabling: %s', e) - return False - -has_ipv6 = _try_ipv6_socket() - class MpdServer(asyncore.dispatcher): """ The MPD server. Creates a :class:`mopidy.frontends.mpd.session.MpdSession` @@ -35,15 +20,9 @@ class MpdServer(asyncore.dispatcher): def start(self): """Start MPD server.""" try: - if has_ipv6: - self.create_socket(socket.AF_INET6, socket.SOCK_STREAM) - # Explicitly configure socket to work for both IPv4 and IPv6 - self.socket.setsockopt( - socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) - else: - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket = network.create_socket() self.set_reuse_addr() - hostname = self._format_hostname(settings.MPD_SERVER_HOSTNAME) + hostname = network.format_hostname(settings.MPD_SERVER_HOSTNAME) port = settings.MPD_SERVER_PORT logger.debug(u'MPD server is binding to [%s]:%s', hostname, port) self.bind((hostname, port)) @@ -65,9 +44,3 @@ class MpdServer(asyncore.dispatcher): def handle_close(self): """Handle end of client connection.""" self.close() - - def _format_hostname(self, hostname): - if (has_ipv6 - and re.match('\d+.\d+.\d+.\d+', hostname) is not None): - hostname = '::ffff:%s' % hostname - return hostname diff --git a/mopidy/utils/network.py b/mopidy/utils/network.py new file mode 100644 index 00000000..1dedf7d7 --- /dev/null +++ b/mopidy/utils/network.py @@ -0,0 +1,36 @@ +import logging +import re +import socket + +logger = logging.getLogger('mopidy.utils.server') + +def _try_ipv6_socket(): + """Determine if system really supports IPv6""" + if not socket.has_ipv6: + return False + try: + socket.socket(socket.AF_INET6).close() + return True + except IOError, e: + logger.debug(u'Platform supports IPv6, but socket ' + 'creation failed, disabling: %s', e) + return False + +#: Boolean value that indicates if creating an IPv6 socket will succeed. +has_ipv6 = _try_ipv6_socket() + +def create_socket(): + """Create a TCP socket with or without IPv6 depending on system support""" + if has_ipv6: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + # Explicitly configure socket to work for both IPv4 and IPv6 + sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) + else: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + return sock + +def format_hostname(hostname): + """Format hostname for display.""" + if (has_ipv6 and re.match('\d+.\d+.\d+.\d+', hostname) is not None): + hostname = '::ffff:%s' % hostname + return hostname diff --git a/tests/frontends/mpd/server_test.py b/tests/frontends/mpd/server_test.py index 32e90450..76bf9e33 100644 --- a/tests/frontends/mpd/server_test.py +++ b/tests/frontends/mpd/server_test.py @@ -5,29 +5,6 @@ from mopidy.backends.dummy import DummyBackend from mopidy.frontends.mpd import server from mopidy.mixers.dummy import DummyMixer -class MpdServerTest(unittest.TestCase): - def setUp(self): - self.backend = DummyBackend.start().proxy() - self.mixer = DummyMixer.start().proxy() - self.server = server.MpdServer() - self.has_ipv6 = server.has_ipv6 - - def tearDown(self): - self.backend.stop().get() - self.mixer.stop().get() - server.has_ipv6 = self.has_ipv6 - - def test_format_hostname_prefixes_ipv4_addresses_when_ipv6_available(self): - server.has_ipv6 = True - self.assertEqual(self.server._format_hostname('0.0.0.0'), - '::ffff:0.0.0.0') - self.assertEqual(self.server._format_hostname('127.0.0.1'), - '::ffff:127.0.0.1') - - def test_format_hostname_does_nothing_when_only_ipv4_available(self): - server.has_ipv6 = False - self.assertEquals(self.server._format_hostname('0.0.0.0'), '0.0.0.0') - class MpdSessionTest(unittest.TestCase): def setUp(self): self.backend = DummyBackend.start().proxy() diff --git a/tests/utils/network_test.py b/tests/utils/network_test.py new file mode 100644 index 00000000..6217e910 --- /dev/null +++ b/tests/utils/network_test.py @@ -0,0 +1,19 @@ +import unittest + +from mopidy.utils import network + +class FormatHostnameTest(unittest.TestCase): + def setUp(self): + self.has_ipv6 = network.has_ipv6 + + def tearDown(self): + network.has_ipv6 = self.has_ipv6 + + def test_format_hostname_prefixes_ipv4_addresses_when_ipv6_available(self): + network.has_ipv6 = True + self.assertEqual(network.format_hostname('0.0.0.0'), '::ffff:0.0.0.0') + self.assertEqual(network.format_hostname('1.0.0.1'), '::ffff:1.0.0.1') + + def test_format_hostname_does_nothing_when_only_ipv4_available(self): + network.has_ipv6 = False + self.assertEquals(network.format_hostname('0.0.0.0'), '0.0.0.0') From ea9159a9babc17da7505ba334b0b71cd4c45a28c Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 7 Jun 2011 15:23:33 +0200 Subject: [PATCH 2/5] Add test for try_ipv6_socket --- tests/utils/network_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/utils/network_test.py b/tests/utils/network_test.py index 6217e910..5a753f6e 100644 --- a/tests/utils/network_test.py +++ b/tests/utils/network_test.py @@ -1,3 +1,4 @@ +import mock import unittest from mopidy.utils import network @@ -17,3 +18,20 @@ class FormatHostnameTest(unittest.TestCase): def test_format_hostname_does_nothing_when_only_ipv4_available(self): network.has_ipv6 = False self.assertEquals(network.format_hostname('0.0.0.0'), '0.0.0.0') + +class TryIPv6SocketTest(unittest.TestCase): + @mock.patch('socket.has_ipv6', False) + def test_system_that_claims_no_ipv6_support(self): + self.assertFalse(network._try_ipv6_socket()) + + @mock.patch('socket.has_ipv6', True) + @mock.patch('socket.socket') + def test_system_with_broken_ipv6(self, socket_mock): + socket_mock.side_effect = IOError() + self.assertFalse(network._try_ipv6_socket()) + + @mock.patch('socket.has_ipv6', True) + @mock.patch('socket.socket') + def test_with_working_ipv6(self, socket_mock): + socket_mock.return_value = mock.Mock() + self.assertTrue(network._try_ipv6_socket()) From 14a9a3fb6684f3eab628d59adacbff0a154716e7 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 7 Jun 2011 15:25:48 +0200 Subject: [PATCH 3/5] Use mocking for network.has_ipv6 monkey patching --- tests/utils/network_test.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/utils/network_test.py b/tests/utils/network_test.py index 5a753f6e..2f0cda66 100644 --- a/tests/utils/network_test.py +++ b/tests/utils/network_test.py @@ -4,17 +4,13 @@ import unittest from mopidy.utils import network class FormatHostnameTest(unittest.TestCase): - def setUp(self): - self.has_ipv6 = network.has_ipv6 - - def tearDown(self): - network.has_ipv6 = self.has_ipv6 - + @mock.patch('mopidy.utils.network.has_ipv6', True) def test_format_hostname_prefixes_ipv4_addresses_when_ipv6_available(self): network.has_ipv6 = True self.assertEqual(network.format_hostname('0.0.0.0'), '::ffff:0.0.0.0') self.assertEqual(network.format_hostname('1.0.0.1'), '::ffff:1.0.0.1') + @mock.patch('mopidy.utils.network.has_ipv6', False) def test_format_hostname_does_nothing_when_only_ipv4_available(self): network.has_ipv6 = False self.assertEquals(network.format_hostname('0.0.0.0'), '0.0.0.0') From cf3b6dcb2bdf4bcea2ce5b5f375c6f17b0601141 Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 7 Jun 2011 15:45:52 +0200 Subject: [PATCH 4/5] Add create socket test --- tests/utils/network_test.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/utils/network_test.py b/tests/utils/network_test.py index 2f0cda66..66229036 100644 --- a/tests/utils/network_test.py +++ b/tests/utils/network_test.py @@ -1,8 +1,11 @@ import mock +import socket import unittest from mopidy.utils import network +from tests import SkipTest + class FormatHostnameTest(unittest.TestCase): @mock.patch('mopidy.utils.network.has_ipv6', True) def test_format_hostname_prefixes_ipv4_addresses_when_ipv6_available(self): @@ -15,6 +18,7 @@ class FormatHostnameTest(unittest.TestCase): network.has_ipv6 = False self.assertEquals(network.format_hostname('0.0.0.0'), '0.0.0.0') + class TryIPv6SocketTest(unittest.TestCase): @mock.patch('socket.has_ipv6', False) def test_system_that_claims_no_ipv6_support(self): @@ -31,3 +35,23 @@ class TryIPv6SocketTest(unittest.TestCase): def test_with_working_ipv6(self, socket_mock): socket_mock.return_value = mock.Mock() self.assertTrue(network._try_ipv6_socket()) + + +class CreateSocketTest(unittest.TestCase): + @mock.patch('mopidy.utils.network.has_ipv6', False) + @mock.patch('socket.socket') + def test_ipv4_socket(self, socket_mock): + network.create_socket() + self.assertEqual(socket_mock.call_args[0], + (socket.AF_INET, socket.SOCK_STREAM)) + + @mock.patch('mopidy.utils.network.has_ipv6', True) + @mock.patch('socket.socket') + def test_ipv6_socket(self, socket_mock): + network.create_socket() + self.assertEqual(socket_mock.call_args[0], + (socket.AF_INET6, socket.SOCK_STREAM)) + + @SkipTest + def test_ipv6_only_is_set(self): + pass From 8b9fb90449a74db0fa719f34c3ac7e5fe013a99a Mon Sep 17 00:00:00 2001 From: Thomas Adamcik Date: Tue, 7 Jun 2011 16:11:34 +0200 Subject: [PATCH 5/5] Fix logging of mopidy server port --- mopidy/frontends/mpd/server.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mopidy/frontends/mpd/server.py b/mopidy/frontends/mpd/server.py index 68796c49..927e2a00 100644 --- a/mopidy/frontends/mpd/server.py +++ b/mopidy/frontends/mpd/server.py @@ -27,9 +27,7 @@ class MpdServer(asyncore.dispatcher): logger.debug(u'MPD server is binding to [%s]:%s', hostname, port) self.bind((hostname, port)) self.listen(1) - logger.info(u'MPD server running at [%s]:%s', - self._format_hostname(settings.MPD_SERVER_HOSTNAME), - settings.MPD_SERVER_PORT) + logger.info(u'MPD server running at [%s]:%s', hostname, port) except IOError, e: logger.error(u'MPD server startup failed: %s' % str(e).decode('utf-8'))