1111import socket
1212import warnings
1313import signal
14+ import collections
1415
1516from . import base_events
1617from . import constants
2324from .log import logger
2425
2526
27+ def _set_socket_extra (transport , sock ):
28+ transport ._extra ['socket' ] = trsock .TransportSocket (sock )
29+
30+ try :
31+ transport ._extra ['sockname' ] = sock .getsockname ()
32+ except socket .error :
33+ if transport ._loop .get_debug ():
34+ logger .warning (
35+ "getsockname() failed on %r" , sock , exc_info = True )
36+
37+ if 'peername' not in transport ._extra :
38+ try :
39+ transport ._extra ['peername' ] = sock .getpeername ()
40+ except socket .error :
41+ # UDP sockets may not have a peer name
42+ transport ._extra ['peername' ] = None
43+
44+
2645class _ProactorBasePipeTransport (transports ._FlowControlMixin ,
2746 transports .BaseTransport ):
2847 """Base class for pipe and socket transports."""
@@ -430,6 +449,134 @@ def _pipe_closed(self, fut):
430449 self .close ()
431450
432451
452+ class _ProactorDatagramTransport (_ProactorBasePipeTransport ):
453+ max_size = 256 * 1024
454+ def __init__ (self , loop , sock , protocol , address = None ,
455+ waiter = None , extra = None ):
456+ self ._address = address
457+ self ._empty_waiter = None
458+ # We don't need to call _protocol.connection_made() since our base
459+ # constructor does it for us.
460+ super ().__init__ (loop , sock , protocol , waiter = waiter , extra = extra )
461+
462+ # The base constructor sets _buffer = None, so we set it here
463+ self ._buffer = collections .deque ()
464+ self ._loop .call_soon (self ._loop_reading )
465+
466+ def _set_extra (self , sock ):
467+ _set_socket_extra (self , sock )
468+
469+ def get_write_buffer_size (self ):
470+ return sum (len (data ) for data , _ in self ._buffer )
471+
472+ def abort (self ):
473+ self ._force_close (None )
474+
475+ def sendto (self , data , addr = None ):
476+ if not isinstance (data , (bytes , bytearray , memoryview )):
477+ raise TypeError ('data argument must be bytes-like object (%r)' ,
478+ type (data ))
479+
480+ if not data :
481+ return
482+
483+ if self ._address is not None and addr not in (None , self ._address ):
484+ raise ValueError (
485+ f'Invalid address: must be None or { self ._address } ' )
486+
487+ if self ._conn_lost and self ._address :
488+ if self ._conn_lost >= constants .LOG_THRESHOLD_FOR_CONNLOST_WRITES :
489+ logger .warning ('socket.sendto() raised exception.' )
490+ self ._conn_lost += 1
491+ return
492+
493+ # Ensure that what we buffer is immutable.
494+ self ._buffer .append ((bytes (data ), addr ))
495+
496+ if self ._write_fut is None :
497+ # No current write operations are active, kick one off
498+ self ._loop_writing ()
499+ # else: A write operation is already kicked off
500+
501+ self ._maybe_pause_protocol ()
502+
503+ def _loop_writing (self , fut = None ):
504+ try :
505+ if self ._conn_lost :
506+ return
507+
508+ assert fut is self ._write_fut
509+ self ._write_fut = None
510+ if fut :
511+ # We are in a _loop_writing() done callback, get the result
512+ fut .result ()
513+
514+ if not self ._buffer or (self ._conn_lost and self ._address ):
515+ # The connection has been closed
516+ if self ._closing :
517+ self ._loop .call_soon (self ._call_connection_lost , None )
518+ return
519+
520+ data , addr = self ._buffer .popleft ()
521+ if self ._address is not None :
522+ self ._write_fut = self ._loop ._proactor .send (self ._sock ,
523+ data )
524+ else :
525+ self ._write_fut = self ._loop ._proactor .sendto (self ._sock ,
526+ data ,
527+ addr = addr )
528+ except OSError as exc :
529+ self ._protocol .error_received (exc )
530+ except Exception as exc :
531+ self ._fatal_error (exc , 'Fatal write error on datagram transport' )
532+ else :
533+ self ._write_fut .add_done_callback (self ._loop_writing )
534+ self ._maybe_resume_protocol ()
535+
536+ def _loop_reading (self , fut = None ):
537+ data = None
538+ try :
539+ if self ._conn_lost :
540+ return
541+
542+ assert self ._read_fut is fut or (self ._read_fut is None and
543+ self ._closing )
544+
545+ self ._read_fut = None
546+ if fut is not None :
547+ res = fut .result ()
548+
549+ if self ._closing :
550+ # since close() has been called we ignore any read data
551+ data = None
552+ return
553+
554+ if self ._address is not None :
555+ data , addr = res , self ._address
556+ else :
557+ data , addr = res
558+
559+ if self ._conn_lost :
560+ return
561+ if self ._address is not None :
562+ self ._read_fut = self ._loop ._proactor .recv (self ._sock ,
563+ self .max_size )
564+ else :
565+ self ._read_fut = self ._loop ._proactor .recvfrom (self ._sock ,
566+ self .max_size )
567+ except OSError as exc :
568+ self ._protocol .error_received (exc )
569+ except exceptions .CancelledError :
570+ if not self ._closing :
571+ raise
572+ else :
573+ if self ._read_fut is not None :
574+ self ._read_fut .add_done_callback (self ._loop_reading )
575+ finally :
576+ if data :
577+ self ._protocol .datagram_received (data , addr )
578+
579+
433580class _ProactorDuplexPipeTransport (_ProactorReadPipeTransport ,
434581 _ProactorBaseWritePipeTransport ,
435582 transports .Transport ):
@@ -455,22 +602,7 @@ def __init__(self, loop, sock, protocol, waiter=None,
455602 base_events ._set_nodelay (sock )
456603
457604 def _set_extra (self , sock ):
458- self ._extra ['socket' ] = trsock .TransportSocket (sock )
459-
460- try :
461- self ._extra ['sockname' ] = sock .getsockname ()
462- except (socket .error , AttributeError ):
463- if self ._loop .get_debug ():
464- logger .warning (
465- "getsockname() failed on %r" , sock , exc_info = True )
466-
467- if 'peername' not in self ._extra :
468- try :
469- self ._extra ['peername' ] = sock .getpeername ()
470- except (socket .error , AttributeError ):
471- if self ._loop .get_debug ():
472- logger .warning ("getpeername() failed on %r" ,
473- sock , exc_info = True )
605+ _set_socket_extra (self , sock )
474606
475607 def can_write_eof (self ):
476608 return True
@@ -515,6 +647,11 @@ def _make_ssl_transport(
515647 extra = extra , server = server )
516648 return ssl_protocol ._app_transport
517649
650+ def _make_datagram_transport (self , sock , protocol ,
651+ address = None , waiter = None , extra = None ):
652+ return _ProactorDatagramTransport (self , sock , protocol , address ,
653+ waiter , extra )
654+
518655 def _make_duplex_pipe_transport (self , sock , protocol , waiter = None ,
519656 extra = None ):
520657 return _ProactorDuplexPipeTransport (self ,
0 commit comments