|
60 | 60 | EAGAIN = getattr(errno, 'EAGAIN', 11) |
61 | 61 | EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11) |
62 | 62 |
|
63 | | -__all__ = ["fromfd", "getfqdn", "create_connection", |
64 | | - "AddressFamily", "SocketKind"] |
| 63 | +__all__ = ["fromfd", "getfqdn", "create_connection", "create_server", |
| 64 | + "has_dualstack_ipv6", "AddressFamily", "SocketKind"] |
65 | 65 | __all__.extend(os._get_exports_list(_socket)) |
66 | 66 |
|
67 | 67 | # Set up the socket.AF_* socket.SOCK_* constants as members of IntEnums for |
@@ -728,6 +728,89 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, |
728 | 728 | else: |
729 | 729 | raise error("getaddrinfo returns an empty list") |
730 | 730 |
|
| 731 | + |
| 732 | +def has_dualstack_ipv6(): |
| 733 | + """Return True if the platform supports creating a SOCK_STREAM socket |
| 734 | + which can handle both AF_INET and AF_INET6 (IPv4 / IPv6) connections. |
| 735 | + """ |
| 736 | + if not has_ipv6 \ |
| 737 | + or not hasattr(_socket, 'IPPROTO_IPV6') \ |
| 738 | + or not hasattr(_socket, 'IPV6_V6ONLY'): |
| 739 | + return False |
| 740 | + try: |
| 741 | + with socket(AF_INET6, SOCK_STREAM) as sock: |
| 742 | + sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0) |
| 743 | + return True |
| 744 | + except error: |
| 745 | + return False |
| 746 | + |
| 747 | + |
| 748 | +def create_server(address, *, family=AF_INET, backlog=0, reuse_port=False, |
| 749 | + dualstack_ipv6=False): |
| 750 | + """Convenience function which creates a SOCK_STREAM type socket |
| 751 | + bound to *address* (a 2-tuple (host, port)) and return the socket |
| 752 | + object. |
| 753 | +
|
| 754 | + *family* should be either AF_INET or AF_INET6. |
| 755 | + *backlog* is the queue size passed to socket.listen(). |
| 756 | + *reuse_port* dictates whether to use the SO_REUSEPORT socket option. |
| 757 | + *dualstack_ipv6*: if true and the platform supports it, it will |
| 758 | + create an AF_INET6 socket able to accept both IPv4 or IPv6 |
| 759 | + connections. When false it will explicitly disable this option on |
| 760 | + platforms that enable it by default (e.g. Linux). |
| 761 | +
|
| 762 | + >>> with create_server((None, 8000)) as server: |
| 763 | + ... while True: |
| 764 | + ... conn, addr = server.accept() |
| 765 | + ... # handle new connection |
| 766 | + """ |
| 767 | + if reuse_port and not hasattr(_socket, "SO_REUSEPORT"): |
| 768 | + raise ValueError("SO_REUSEPORT not supported on this platform") |
| 769 | + if dualstack_ipv6: |
| 770 | + if not has_dualstack_ipv6(): |
| 771 | + raise ValueError("dualstack_ipv6 not supported on this platform") |
| 772 | + if family != AF_INET6: |
| 773 | + raise ValueError("dualstack_ipv6 requires AF_INET6 family") |
| 774 | + sock = socket(family, SOCK_STREAM) |
| 775 | + try: |
| 776 | + # Note about Windows. We don't set SO_REUSEADDR because: |
| 777 | + # 1) It's unnecessary: bind() will succeed even in case of a |
| 778 | + # previous closed socket on the same address and still in |
| 779 | + # TIME_WAIT state. |
| 780 | + # 2) If set, another socket is free to bind() on the same |
| 781 | + # address, effectively preventing this one from accepting |
| 782 | + # connections. Also, it may set the process in a state where |
| 783 | + # it'll no longer respond to any signals or graceful kills. |
| 784 | + # See: msdn2.microsoft.com/en-us/library/ms740621(VS.85).aspx |
| 785 | + if os.name not in ('nt', 'cygwin') and \ |
| 786 | + hasattr(_socket, 'SO_REUSEADDR'): |
| 787 | + try: |
| 788 | + sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) |
| 789 | + except error: |
| 790 | + # Fail later on bind(), for platforms which may not |
| 791 | + # support this option. |
| 792 | + pass |
| 793 | + if reuse_port: |
| 794 | + sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1) |
| 795 | + if has_ipv6 and family == AF_INET6: |
| 796 | + if dualstack_ipv6: |
| 797 | + sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0) |
| 798 | + elif hasattr(_socket, "IPV6_V6ONLY") and \ |
| 799 | + hasattr(_socket, "IPPROTO_IPV6"): |
| 800 | + sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 1) |
| 801 | + try: |
| 802 | + sock.bind(address) |
| 803 | + except error as err: |
| 804 | + msg = '%s (while attempting to bind on address %r)' % \ |
| 805 | + (err.strerror, address) |
| 806 | + raise error(err.errno, msg) from None |
| 807 | + sock.listen(backlog) |
| 808 | + return sock |
| 809 | + except error: |
| 810 | + sock.close() |
| 811 | + raise |
| 812 | + |
| 813 | + |
731 | 814 | def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0): |
732 | 815 | """Resolve host and port into list of address info entries. |
733 | 816 |
|
|
0 commit comments