|
1 | 1 | import asyncio as __asyncio
|
2 | 2 | import errno
|
| 3 | +import os |
3 | 4 | import signal
|
4 | 5 | import socket
|
5 | 6 | import subprocess
|
|
12 | 13 | from asyncio.tasks import Task as _Task, ensure_future as _ensure_future, gather as _gather
|
13 | 14 | from concurrent.futures import ThreadPoolExecutor
|
14 | 15 | from contextvars import copy_context as _copy_context
|
| 16 | +from itertools import chain as _iterchain |
15 | 17 | from typing import Union
|
16 | 18 |
|
17 | 19 | from ._compat import _PY_311, _PYV
|
@@ -288,10 +290,10 @@ def set_default_executor(self, executor):
|
288 | 290 |
|
289 | 291 | #: network I/O methods
|
290 | 292 | async def getaddrinfo(self, host, port, *, family=0, type=0, proto=0, flags=0):
|
291 |
| - raise NotImplementedError |
| 293 | + return await self.run_in_executor(None, socket.getaddrinfo, host, port, family, type, proto, flags) |
292 | 294 |
|
293 | 295 | async def getnameinfo(self, sockaddr, flags=0):
|
294 |
| - raise NotImplementedError |
| 296 | + return await self.run_in_executor(None, socket.getnameinfo, sockaddr, flags) |
295 | 297 |
|
296 | 298 | async def create_connection(
|
297 | 299 | self,
|
@@ -331,7 +333,112 @@ async def create_server(
|
331 | 333 | ssl_shutdown_timeout=None,
|
332 | 334 | start_serving=True,
|
333 | 335 | ):
|
334 |
| - raise NotImplementedError |
| 336 | + if isinstance(ssl, bool): |
| 337 | + raise TypeError('ssl argument must be an SSLContext or None') |
| 338 | + |
| 339 | + if ssl_handshake_timeout is not None and ssl is None: |
| 340 | + raise ValueError('ssl_handshake_timeout is only meaningful with ssl') |
| 341 | + |
| 342 | + if ssl_shutdown_timeout is not None and ssl is None: |
| 343 | + raise ValueError('ssl_shutdown_timeout is only meaningful with ssl') |
| 344 | + |
| 345 | + # TODO |
| 346 | + # if sock is not None: |
| 347 | + # _check_ssl_socket(sock) |
| 348 | + |
| 349 | + if host is not None or port is not None: |
| 350 | + if sock is not None: |
| 351 | + raise ValueError('host/port and sock can not be specified at the same time') |
| 352 | + |
| 353 | + if reuse_address is None: |
| 354 | + reuse_address = os.name == 'posix' and sys.platform != 'cygwin' |
| 355 | + |
| 356 | + sockets = [] |
| 357 | + if host == '': |
| 358 | + hosts = [None] |
| 359 | + elif isinstance(host, str) or not isinstance(host, (tuple, list)): |
| 360 | + hosts = [host] |
| 361 | + else: |
| 362 | + hosts = host |
| 363 | + |
| 364 | + fs = [self._create_server_getaddrinfo(host, port, family=family, flags=flags) for host in hosts] |
| 365 | + infos = await _gather(*fs) |
| 366 | + infos = set(_iterchain.from_iterable(infos)) |
| 367 | + |
| 368 | + completed = False |
| 369 | + try: |
| 370 | + for res in infos: |
| 371 | + af, socktype, proto, canonname, sa = res |
| 372 | + try: |
| 373 | + sock = socket.socket(af, socktype, proto) |
| 374 | + except socket.error: |
| 375 | + # Assume it's a bad family/type/protocol combination. |
| 376 | + continue |
| 377 | + sockets.append(sock) |
| 378 | + if reuse_address: |
| 379 | + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) |
| 380 | + # TODO |
| 381 | + # if reuse_port: |
| 382 | + # _set_reuseport(sock) |
| 383 | + if keep_alive: |
| 384 | + sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True) |
| 385 | + # Disable IPv4/IPv6 dual stack support (enabled by |
| 386 | + # default on Linux) which makes a single socket |
| 387 | + # listen on both address families. |
| 388 | + if _HAS_IPv6 and af == socket.AF_INET6 and hasattr(socket, 'IPPROTO_IPV6'): |
| 389 | + sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, True) |
| 390 | + try: |
| 391 | + sock.bind(sa) |
| 392 | + except OSError as err: |
| 393 | + msg = 'error while attempting to bind on address %r: %s' % (sa, str(err).lower()) |
| 394 | + if err.errno == errno.EADDRNOTAVAIL: |
| 395 | + # Assume the family is not enabled (bpo-30945) |
| 396 | + sockets.pop() |
| 397 | + sock.close() |
| 398 | + continue |
| 399 | + raise OSError(err.errno, msg) from None |
| 400 | + |
| 401 | + if not sockets: |
| 402 | + raise OSError('could not bind on any address out of %r' % ([info[4] for info in infos],)) |
| 403 | + |
| 404 | + completed = True |
| 405 | + finally: |
| 406 | + if not completed: |
| 407 | + for sock in sockets: |
| 408 | + sock.close() |
| 409 | + else: |
| 410 | + if sock is None: |
| 411 | + raise ValueError('Neither host/port nor sock were specified') |
| 412 | + if sock.type != socket.SOCK_STREAM: |
| 413 | + raise ValueError(f'A Stream Socket was expected, got {sock!r}') |
| 414 | + sockets = [sock] |
| 415 | + |
| 416 | + for sock in sockets: |
| 417 | + sock.setblocking(False) |
| 418 | + |
| 419 | + # TODO |
| 420 | + # server = Server(self, sockets, protocol_factory, |
| 421 | + # ssl, backlog, ssl_handshake_timeout, |
| 422 | + # ssl_shutdown_timeout) |
| 423 | + server = self._tcp_server([sock.fileno() for sock in sockets], protocol_factory, backlog) |
| 424 | + |
| 425 | + # TODO |
| 426 | + # if start_serving: |
| 427 | + # server._start_serving() |
| 428 | + # # Skip one loop iteration so that all 'loop.add_reader' |
| 429 | + # # go through. |
| 430 | + # await tasks.sleep(0) |
| 431 | + |
| 432 | + return server |
| 433 | + |
| 434 | + async def _create_server_getaddrinfo(self, host, port, family, flags): |
| 435 | + infos = await self._ensure_resolved( |
| 436 | + (host, port), family=family, type=socket.SOCK_STREAM, flags=flags, loop=self |
| 437 | + ) |
| 438 | + if not infos: |
| 439 | + raise OSError(f'getaddrinfo({host!r}) returned empty list') |
| 440 | + |
| 441 | + return infos |
335 | 442 |
|
336 | 443 | async def sendfile(self, transport, file, offset=0, count=None, *, fallback=True):
|
337 | 444 | raise NotImplementedError
|
|
0 commit comments