bpo-35934: Add socket.create_server() utility function (GH-11784) · python/cpython@eb7e29f

@@ -60,8 +60,8 @@

6060

EAGAIN = getattr(errno, 'EAGAIN', 11)

6161

EWOULDBLOCK = getattr(errno, 'EWOULDBLOCK', 11)

626263-

__all__ = ["fromfd", "getfqdn", "create_connection",

64-

"AddressFamily", "SocketKind"]

63+

__all__ = ["fromfd", "getfqdn", "create_connection", "create_server",

64+

"has_dualstack_ipv6", "AddressFamily", "SocketKind"]

6565

__all__.extend(os._get_exports_list(_socket))

66666767

# 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,

728728

else:

729729

raise error("getaddrinfo returns an empty list")

730730731+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+731814

def getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):

732815

"""Resolve host and port into list of address info entries.

733816