diff -r 1f94fec31265 Lib/asyncio/base_events.py --- a/Lib/asyncio/base_events.py Wed Aug 26 14:10:32 2015 -0400 +++ b/Lib/asyncio/base_events.py Thu Aug 27 18:55:05 2015 +0200 @@ -27,6 +27,7 @@ import traceback import sys import warnings +import itertools from . import compat from . import coroutines @@ -795,8 +796,13 @@ backlog=100, ssl=None, reuse_address=None): - """Create a TCP server bound to host and port. + """Create a TCP server. + host can be a string, in that case the TCP server is bound + to host and port. + + host can also be a sequence of strings and in that case the TCP server + is bound to all hosts of the sequence. Return a Server object which can be used to stop the service. This method is a coroutine. @@ -815,11 +821,18 @@ if host == '': host = None - infos = yield from self.getaddrinfo( - host, port, family=family, - type=socket.SOCK_STREAM, proto=0, flags=flags) - if not infos: - raise OSError('getaddrinfo() returned empty list') + if (isinstance(host, str) + or not isinstance(host, collections.Iterable)): + host = [host] + + fs = [tasks.ensure_future(self.getaddrinfo(h, port, family=family, + type=socket.SOCK_STREAM, proto=0, + flags=flags), loop=self) for h in host] + infos = yield from tasks.gather(*fs, loop=self) + for h, info in zip(host, infos): + if not info: + raise OSError('getaddrinfo({!r}) returned empty list'.format(h)) + infos = itertools.chain.from_iterable(infos) completed = False try: diff -r 1f94fec31265 Lib/asyncio/events.py --- a/Lib/asyncio/events.py Wed Aug 26 14:10:32 2015 -0400 +++ b/Lib/asyncio/events.py Thu Aug 27 18:55:05 2015 +0200 @@ -305,7 +305,8 @@ If host is an empty string or None all interfaces are assumed and a list of multiple sockets will be returned (most likely - one for IPv4 and another one for IPv6). + one for IPv4 and another one for IPv6). host can also be a + sequence (e.g. list) of hosts to bind to. family can be set to either AF_INET or AF_INET6 to force the socket to use IPv4 or IPv6. If not set it will be determined diff -r 1f94fec31265 Lib/test/test_asyncio/test_events.py --- a/Lib/test/test_asyncio/test_events.py Wed Aug 26 14:10:32 2015 -0400 +++ b/Lib/test/test_asyncio/test_events.py Thu Aug 27 18:55:05 2015 +0200 @@ -20,6 +20,8 @@ import unittest from unittest import mock import weakref +import test +import netifaces import asyncio @@ -53,6 +55,17 @@ return version < (10, 5) +def get_ip_addresses(family): + addresses = [] + for iface in netifaces.interfaces(): + addr = netifaces.ifaddresses(iface) + if family in addr: + ipv4_addresses = addr[family] + for addr in ipv4_addresses: + if 'addr' in addr: + addresses.append(addr['addr']) + return addresses + ONLYCERT = data_file('ssl_cert.pem') ONLYKEY = data_file('ssl_key.pem') SIGNED_CERTFILE = data_file('keycert3.pem') @@ -696,6 +709,33 @@ self.assertEqual(cm.exception.errno, errno.EADDRINUSE) self.assertIn(str(httpd.address), cm.exception.strerror) + def create_server_multiple_hosts(self, family): + hosts = get_ip_addresses(family) + proto = MyProto(self.loop) + f = self.loop.create_server(lambda: proto, hosts, 0) + server = self.loop.run_until_complete(f) + self.addCleanup(server.close) + payload = b'xxx' + nbytes_sent = 0 + for sock in server.sockets: + if family == netifaces.AF_INET: + host, port = sock.getsockname() + else: + host, port, _, _ = sock.getsockname() + + self.assertIn(host, hosts) + + @unittest.skipIf(not test.support.is_resource_enabled('network'), + 'No network') + def test_create_server_multiple_hosts_ipv4(self): + self.create_server_multiple_hosts(netifaces.AF_INET) + + @unittest.skipIf(not test.support.IPV6_ENABLED, 'No IPv6') + @unittest.skipIf(not test.support.is_resource_enabled('network'), + 'No network') + def test_create_server_multiple_hosts_ipv6(self): + self.create_server_multiple_hosts(netifaces.AF_INET6) + def test_create_server(self): proto = MyProto(self.loop) f = self.loop.create_server(lambda: proto, '0.0.0.0', 0)