@@ -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
@@ -720,6 +723,7 @@ def _write_appdata(self, list_of_data):
720723 self ._fatal_error (ex , 'Fatal error on SSL protocol' )
721724
722725 def _do_write (self ):
726+ pending = self ._outgoing .pending
723727 try :
724728 while self ._write_backlog :
725729 data = self ._write_backlog [0 ]
@@ -733,11 +737,13 @@ def _do_write(self):
733737 self ._write_buffer_size -= data_len
734738 except SSLAgainErrors :
735739 pass
740+ self ._outgoing_app_size += self ._outgoing .pending - pending
736741 self ._process_outgoing ()
737742
738743 def _process_outgoing (self ):
739744 if not self ._ssl_writing_paused :
740745 data = self ._outgoing .read ()
746+ self ._outgoing_app_size = 0
741747 if len (data ):
742748 self ._transport .write (data )
743749 self ._control_app_writing ()
@@ -904,7 +910,15 @@ def resume():
904910 # Flow control for reads from SSL socket
905911
906912 def _control_ssl_reading (self ):
907- size = self ._get_read_buffer_size ()
913+ # Also count the records OpenSSL writes on its own while reading
914+ # (alerts, KeyUpdate replies, ...) which the peer does not read:
915+ # the app is not involved in producing them, so pausing it does
916+ # not bound them; only pausing reads from the peer does. Whatever
917+ # was produced while writing app data (_outgoing_app_size) is left
918+ # out so that two peers which both write before they read cannot
919+ # deadlock.
920+ size = (self ._get_read_buffer_size () +
921+ self ._outgoing .pending - self ._outgoing_app_size )
908922 if size >= self ._incoming_high_water and not self ._ssl_reading_paused :
909923 self ._ssl_reading_paused = True
910924 self ._transport .pause_reading ()
@@ -937,6 +951,7 @@ def resume_writing(self):
937951 assert self ._ssl_writing_paused
938952 self ._ssl_writing_paused = False
939953 self ._process_outgoing ()
954+ self ._control_ssl_reading ()
940955
941956 def _fatal_error (self , exc , message = 'Fatal error on transport' ):
942957 if self ._transport :
0 commit comments