@@ -700,75 +700,109 @@ def _create_connection_transport(self, sock, protocol_factory, ssl,
700700 @coroutine
701701 def create_datagram_endpoint (self , protocol_factory ,
702702 local_addr = None , remote_addr = None , * ,
703- family = 0 , proto = 0 , flags = 0 ):
703+ family = 0 , proto = 0 , flags = 0 ,
704+ reuse_address = None , reuse_port = None ,
705+ allow_broadcast = None , sock = None ):
704706 """Create datagram connection."""
705- if not (local_addr or remote_addr ):
706- if family == 0 :
707- raise ValueError ('unexpected address family' )
708- addr_pairs_info = (((family , proto ), (None , None )),)
709- else :
710- # join address by (family, protocol)
711- addr_infos = collections .OrderedDict ()
712- for idx , addr in ((0 , local_addr ), (1 , remote_addr )):
713- if addr is not None :
714- assert isinstance (addr , tuple ) and len (addr ) == 2 , (
715- '2-tuple is expected' )
716-
717- infos = yield from self .getaddrinfo (
718- * addr , family = family , type = socket .SOCK_DGRAM ,
719- proto = proto , flags = flags )
720- if not infos :
721- raise OSError ('getaddrinfo() returned empty list' )
722-
723- for fam , _ , pro , _ , address in infos :
724- key = (fam , pro )
725- if key not in addr_infos :
726- addr_infos [key ] = [None , None ]
727- addr_infos [key ][idx ] = address
728-
729- # each addr has to have info for each (family, proto) pair
730- addr_pairs_info = [
731- (key , addr_pair ) for key , addr_pair in addr_infos .items ()
732- if not ((local_addr and addr_pair [0 ] is None ) or
733- (remote_addr and addr_pair [1 ] is None ))]
734-
735- if not addr_pairs_info :
736- raise ValueError ('can not get address information' )
737-
738- exceptions = []
739-
740- for ((family , proto ),
741- (local_address , remote_address )) in addr_pairs_info :
742- sock = None
707+ if sock is not None :
708+ if (local_addr or remote_addr or
709+ family or proto or flags or
710+ reuse_address or reuse_port or allow_broadcast ):
711+ # show the problematic kwargs in exception msg
712+ opts = dict (local_addr = local_addr , remote_addr = remote_addr ,
713+ family = family , proto = proto , flags = flags ,
714+ reuse_address = reuse_address , reuse_port = reuse_port ,
715+ allow_broadcast = allow_broadcast )
716+ problems = ', ' .join (
717+ '{}={}' .format (k , v ) for k , v in opts .items () if v )
718+ raise ValueError (
719+ 'socket modifier keyword arguments can not be used '
720+ 'when sock is specified. ({})' .format (problems ))
721+ sock .setblocking (False )
743722 r_addr = None
744- try :
745- sock = socket .socket (
746- family = family , type = socket .SOCK_DGRAM , proto = proto )
747- sock .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
748- sock .setblocking (False )
749-
750- if local_addr :
751- sock .bind (local_address )
752- if remote_addr :
753- yield from self .sock_connect (sock , remote_address )
754- r_addr = remote_address
755- except OSError as exc :
756- if sock is not None :
757- sock .close ()
758- exceptions .append (exc )
759- except :
760- if sock is not None :
761- sock .close ()
762- raise
763- else :
764- break
765723 else :
766- raise exceptions [0 ]
724+ if not (local_addr or remote_addr ):
725+ if family == 0 :
726+ raise ValueError ('unexpected address family' )
727+ addr_pairs_info = (((family , proto ), (None , None )),)
728+ else :
729+ # join address by (family, protocol)
730+ addr_infos = collections .OrderedDict ()
731+ for idx , addr in ((0 , local_addr ), (1 , remote_addr )):
732+ if addr is not None :
733+ assert isinstance (addr , tuple ) and len (addr ) == 2 , (
734+ '2-tuple is expected' )
735+
736+ infos = yield from self .getaddrinfo (
737+ * addr , family = family , type = socket .SOCK_DGRAM ,
738+ proto = proto , flags = flags )
739+ if not infos :
740+ raise OSError ('getaddrinfo() returned empty list' )
741+
742+ for fam , _ , pro , _ , address in infos :
743+ key = (fam , pro )
744+ if key not in addr_infos :
745+ addr_infos [key ] = [None , None ]
746+ addr_infos [key ][idx ] = address
747+
748+ # each addr has to have info for each (family, proto) pair
749+ addr_pairs_info = [
750+ (key , addr_pair ) for key , addr_pair in addr_infos .items ()
751+ if not ((local_addr and addr_pair [0 ] is None ) or
752+ (remote_addr and addr_pair [1 ] is None ))]
753+
754+ if not addr_pairs_info :
755+ raise ValueError ('can not get address information' )
756+
757+ exceptions = []
758+
759+ if reuse_address is None :
760+ reuse_address = os .name == 'posix' and sys .platform != 'cygwin'
761+
762+ for ((family , proto ),
763+ (local_address , remote_address )) in addr_pairs_info :
764+ sock = None
765+ r_addr = None
766+ try :
767+ sock = socket .socket (
768+ family = family , type = socket .SOCK_DGRAM , proto = proto )
769+ if reuse_address :
770+ sock .setsockopt (
771+ socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
772+ if reuse_port :
773+ if not hasattr (socket , 'SO_REUSEPORT' ):
774+ raise ValueError (
775+ 'reuse_port not supported by socket module' )
776+ else :
777+ sock .setsockopt (
778+ socket .SOL_SOCKET , socket .SO_REUSEPORT , 1 )
779+ if allow_broadcast :
780+ sock .setsockopt (
781+ socket .SOL_SOCKET , socket .SO_BROADCAST , 1 )
782+ sock .setblocking (False )
783+
784+ if local_addr :
785+ sock .bind (local_address )
786+ if remote_addr :
787+ yield from self .sock_connect (sock , remote_address )
788+ r_addr = remote_address
789+ except OSError as exc :
790+ if sock is not None :
791+ sock .close ()
792+ exceptions .append (exc )
793+ except :
794+ if sock is not None :
795+ sock .close ()
796+ raise
797+ else :
798+ break
799+ else :
800+ raise exceptions [0 ]
767801
768802 protocol = protocol_factory ()
769803 waiter = futures .Future (loop = self )
770- transport = self ._make_datagram_transport (sock , protocol , r_addr ,
771- waiter )
804+ transport = self ._make_datagram_transport (
805+ sock , protocol , r_addr , waiter )
772806 if self ._debug :
773807 if local_addr :
774808 logger .info ("Datagram endpoint local_addr=%r remote_addr=%r "
@@ -804,7 +838,8 @@ def create_server(self, protocol_factory, host=None, port=None,
804838 sock = None ,
805839 backlog = 100 ,
806840 ssl = None ,
807- reuse_address = None ):
841+ reuse_address = None ,
842+ reuse_port = None ):
808843 """Create a TCP server.
809844
810845 The host parameter can be a string, in that case the TCP server is bound
@@ -857,8 +892,15 @@ def create_server(self, protocol_factory, host=None, port=None,
857892 continue
858893 sockets .append (sock )
859894 if reuse_address :
860- sock .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR ,
861- True )
895+ sock .setsockopt (
896+ socket .SOL_SOCKET , socket .SO_REUSEADDR , True )
897+ if reuse_port :
898+ if not hasattr (socket , 'SO_REUSEPORT' ):
899+ raise ValueError (
900+ 'reuse_port not supported by socket module' )
901+ else :
902+ sock .setsockopt (
903+ socket .SOL_SOCKET , socket .SO_REUSEPORT , True )
862904 # Disable IPv4/IPv6 dual stack support (enabled by
863905 # default on Linux) which makes a single socket
864906 # listen on both address families.
0 commit comments