|
7 | 7 | import time
|
8 | 8 | import collections
|
9 | 9 | import os
|
| 10 | +import socket |
| 11 | +import itertools |
10 | 12 | import atexit
|
11 | 13 | import string
|
12 | 14 |
|
@@ -243,3 +245,109 @@ def get_windows_drives():
|
243 | 245 | drives.append(letter)
|
244 | 246 | bitmask >>= 1
|
245 | 247 | return drives
|
| 248 | + |
| 249 | +if sys.version_info[:3] == (3, 5, 1): |
| 250 | + # See https://github.com/m-labs/artiq/issues/253 |
| 251 | + @asyncio.coroutines.coroutine |
| 252 | + def create_server(self, protocol_factory, host=None, port=None, |
| 253 | + *, |
| 254 | + family=socket.AF_UNSPEC, |
| 255 | + flags=socket.AI_PASSIVE, |
| 256 | + sock=None, |
| 257 | + backlog=100, |
| 258 | + ssl=None, |
| 259 | + reuse_address=None, |
| 260 | + reuse_port=None): |
| 261 | + """Create a TCP server. |
| 262 | + The host parameter can be a string, in that case the TCP server is bound |
| 263 | + to host and port. |
| 264 | + The host parameter can also be a sequence of strings and in that case |
| 265 | + the TCP server is bound to all hosts of the sequence. If a host |
| 266 | + appears multiple times (possibly indirectly e.g. when hostnames |
| 267 | + resolve to the same IP address), the server is only bound once to that |
| 268 | + host. |
| 269 | + Return a Server object which can be used to stop the service. |
| 270 | + This method is a coroutine. |
| 271 | + """ |
| 272 | + if isinstance(ssl, bool): |
| 273 | + raise TypeError('ssl argument must be an SSLContext or None') |
| 274 | + if host is not None or port is not None: |
| 275 | + if sock is not None: |
| 276 | + raise ValueError( |
| 277 | + 'host/port and sock can not be specified at the same time') |
| 278 | + |
| 279 | + AF_INET6 = getattr(socket, 'AF_INET6', 0) |
| 280 | + if reuse_address is None: |
| 281 | + reuse_address = os.name == 'posix' and sys.platform != 'cygwin' |
| 282 | + sockets = [] |
| 283 | + if host == '': |
| 284 | + hosts = [None] |
| 285 | + elif (isinstance(host, str) or |
| 286 | + not isinstance(host, collections.Iterable)): |
| 287 | + hosts = [host] |
| 288 | + else: |
| 289 | + hosts = host |
| 290 | + |
| 291 | + fs = [self._create_server_getaddrinfo(host, port, family=family, |
| 292 | + flags=flags) |
| 293 | + for host in hosts] |
| 294 | + infos = yield from asyncio.tasks.gather(*fs, loop=self) |
| 295 | + infos = set(itertools.chain.from_iterable(infos)) |
| 296 | + |
| 297 | + completed = False |
| 298 | + try: |
| 299 | + for res in infos: |
| 300 | + af, socktype, proto, canonname, sa = res |
| 301 | + try: |
| 302 | + sock = socket.socket(af, socktype, proto) |
| 303 | + except socket.error: |
| 304 | + # Assume it's a bad family/type/protocol combination. |
| 305 | + if self._debug: |
| 306 | + asyncio.log.logger.warning('create_server() failed to create ' |
| 307 | + 'socket.socket(%r, %r, %r)', |
| 308 | + af, socktype, proto, exc_info=True) |
| 309 | + continue |
| 310 | + sockets.append(sock) |
| 311 | + if reuse_address: |
| 312 | + sock.setsockopt( |
| 313 | + socket.SOL_SOCKET, socket.SO_REUSEADDR, True) |
| 314 | + if reuse_port: |
| 315 | + if not hasattr(socket, 'SO_REUSEPORT'): |
| 316 | + raise ValueError( |
| 317 | + 'reuse_port not supported by socket module') |
| 318 | + else: |
| 319 | + sock.setsockopt( |
| 320 | + socket.SOL_SOCKET, socket.SO_REUSEPORT, True) |
| 321 | + # Disable IPv4/IPv6 dual stack support (enabled by |
| 322 | + # default on Linux) which makes a single socket |
| 323 | + # listen on both address families. |
| 324 | + if af == AF_INET6 and hasattr(socket, 'IPPROTO_IPV6'): |
| 325 | + sock.setsockopt(socket.IPPROTO_IPV6, |
| 326 | + socket.IPV6_V6ONLY, |
| 327 | + True) |
| 328 | + try: |
| 329 | + sock.bind(sa) |
| 330 | + except OSError as err: |
| 331 | + raise OSError(err.errno, 'error while attempting ' |
| 332 | + 'to bind on address %r: %s' |
| 333 | + % (sa, err.strerror.lower())) |
| 334 | + completed = True |
| 335 | + finally: |
| 336 | + if not completed: |
| 337 | + for sock in sockets: |
| 338 | + sock.close() |
| 339 | + else: |
| 340 | + if sock is None: |
| 341 | + raise ValueError('Neither host/port nor sock were specified') |
| 342 | + sockets = [sock] |
| 343 | + |
| 344 | + server = asyncio.base_events.Server(self, sockets) |
| 345 | + for sock in sockets: |
| 346 | + sock.listen(backlog) |
| 347 | + sock.setblocking(False) |
| 348 | + self._start_serving(protocol_factory, sock, ssl, server) |
| 349 | + if self._debug: |
| 350 | + asyncio.log.logger.info("%r is serving", server) |
| 351 | + return server |
| 352 | + |
| 353 | + asyncio.base_events.BaseEventLoop.create_server = create_server |
0 commit comments