@@ -308,6 +308,9 @@ def __init__(self, loop, app_protocol, sslcontext, waiter,
308308 # App data write buffering
309309 self ._write_backlog = collections .deque ()
310310 self ._write_buffer_size = 0
311+ # How much of self._outgoing was produced by _do_write(): encrypted
312+ # app data, plus anything else OpenSSL flushed along with it
313+ self ._outgoing_app_size = 0
311314
312315 self ._waiter = waiter
313316 self ._loop = loop
@@ -710,6 +713,7 @@ def _write_appdata(self, list_of_data):
710713 self ._fatal_error (ex , 'Fatal error on SSL protocol' )
711714
712715 def _do_write (self ):
716+ pending = self ._outgoing .pending
713717 try :
714718 while self ._write_backlog :
715719 data = self ._write_backlog [0 ]
@@ -723,11 +727,13 @@ def _do_write(self):
723727 self ._write_buffer_size -= data_len
724728 except SSLAgainErrors :
725729 pass
730+ self ._outgoing_app_size += self ._outgoing .pending - pending
726731 self ._process_outgoing ()
727732
728733 def _process_outgoing (self ):
729734 if not self ._ssl_writing_paused :
730735 data = self ._outgoing .read ()
736+ self ._outgoing_app_size = 0
731737 if len (data ):
732738 self ._transport .write (data )
733739 self ._control_app_writing ()
@@ -889,7 +895,15 @@ def resume():
889895 # Flow control for reads from SSL socket
890896
891897 def _control_ssl_reading (self ):
892- size = self ._get_read_buffer_size ()
898+ # Also count the records OpenSSL writes on its own while reading
899+ # (alerts, KeyUpdate replies, ...) which the peer does not read:
900+ # the app is not involved in producing them, so pausing it does
901+ # not bound them; only pausing reads from the peer does. Whatever
902+ # was produced while writing app data (_outgoing_app_size) is left
903+ # out so that two peers which both write before they read cannot
904+ # deadlock.
905+ size = (self ._get_read_buffer_size () +
906+ self ._outgoing .pending - self ._outgoing_app_size )
893907 if size >= self ._incoming_high_water and not self ._ssl_reading_paused :
894908 self ._ssl_reading_paused = True
895909 self ._transport .pause_reading ()
@@ -922,6 +936,7 @@ def resume_writing(self):
922936 assert self ._ssl_writing_paused
923937 self ._ssl_writing_paused = False
924938 self ._process_outgoing ()
939+ self ._control_ssl_reading ()
925940
926941 def _fatal_error (self , exc , message = 'Fatal error on transport' ):
927942 if self ._transport :
0 commit comments