@@ -278,7 +278,6 @@ cdef class SSLProtocol:
278278 self ._incoming_high_water = 0
279279 self ._incoming_low_water = 0
280280 self ._set_read_buffer_limits()
281- self ._eof_received = False
282281
283282 self ._app_writing_paused = False
284283 self ._outgoing_high_water = 0
@@ -392,27 +391,24 @@ cdef class SSLProtocol:
392391 will close itself. If it returns a true value, closing the
393392 transport is up to the protocol.
394393 """
395- self ._eof_received = True
396394 try :
397395 if self ._loop.get_debug():
398396 aio_logger.debug(" %r received EOF" , self )
399397
400398 if self ._state == DO_HANDSHAKE:
401399 self ._on_handshake_complete(ConnectionResetError)
402400
403- elif self ._state == WRAPPED:
404- self ._set_state(FLUSHING)
405- self ._do_write()
401+ elif self ._state == WRAPPED or self ._state == FLUSHING:
402+ # we treat low-level EOF as a critical situation similar as a
403+ # broken connection - just send whatever in the buffer and
404+ # close up. No application level eof_received() is called -
405+ # because we don't want the user to think that this is a
406+ # graceful shutdown triggered by SSL "close_notify".
406407 self ._set_state(SHUTDOWN)
407- self ._do_shutdown()
408-
409- elif self ._state == FLUSHING:
410- self ._do_write()
411- self ._set_state(SHUTDOWN)
412- self ._do_shutdown()
408+ self ._on_shutdown_complete(None )
413409
414410 elif self ._state == SHUTDOWN:
415- self ._do_shutdown( )
411+ self ._on_shutdown_complete( None )
416412
417413 except Exception :
418414 self ._transport.close()
@@ -443,6 +439,9 @@ cdef class SSLProtocol:
443439 elif self ._state == WRAPPED and new_state == FLUSHING:
444440 allowed = True
445441
442+ elif self ._state == WRAPPED and new_state == SHUTDOWN:
443+ allowed = True
444+
446445 elif self ._state == FLUSHING and new_state == SHUTDOWN:
447446 allowed = True
448447
@@ -560,54 +559,70 @@ cdef class SSLProtocol:
560559 self ._transport._force_close(
561560 aio_TimeoutError(' SSL shutdown timed out' ))
562561
563- cdef _do_flush(self ):
562+ cdef _do_read_into_void(self ):
563+ """ Consume and discard incoming application data.
564+
565+ If close_notify is received for the first time, call eof_received.
566+ """
564567 cdef:
565568 bint close_notify = False
569+ try :
570+ while True :
571+ if not self ._sslobj_read(SSL_READ_MAX_SIZE):
572+ close_notify = True
573+ break
574+ except ssl_SSLAgainErrors as exc:
575+ pass
576+ except ssl_SSLZeroReturnError:
577+ close_notify = True
578+ if close_notify:
579+ self ._call_eof_received()
566580
581+ cdef _do_flush(self ):
582+ """ Flush the write backlog, discarding new data received.
583+
584+ We don't send close_notify in FLUSHING because we still want to send
585+ the remaining data over SSL, even if we received a close_notify. Also,
586+ no application-level resume_writing() or pause_writing() will be called
587+ in FLUSHING, as we could fully manage the flow control internally.
588+ """
567589 try :
568- try :
569- while True :
570- if not self ._sslobj_read(SSL_READ_MAX_SIZE):
571- close_notify = True
572- break
573- except ssl_SSLAgainErrors as exc:
574- pass
575- if close_notify:
576- self ._call_eof_received()
577- if self ._write_backlog:
578- self ._do_write()
579- else :
580- self ._process_outgoing()
590+ self ._do_read_into_void()
591+ self ._do_write()
592+ self ._process_outgoing()
581593 self ._control_ssl_reading()
582594 except Exception as ex:
583- self ._fatal_error (ex, ' Fatal error on SSL protocol ' )
595+ self ._on_shutdown_complete (ex)
584596 else :
585597 if not self ._get_write_buffer_size():
586598 self ._set_state(SHUTDOWN)
587599 self ._do_shutdown()
588600
589601 cdef _do_shutdown(self ):
602+ """ Send close_notify and wait for the same from the peer."""
590603 try :
591- if not self ._eof_received:
604+ # we must skip all application data (if any) before unwrap
605+ self ._do_read_into_void()
606+ try :
592607 self ._sslobj.unwrap()
593- except ssl_SSLAgainErrors as exc:
594- self ._process_outgoing()
595- except ssl_SSLError as exc :
596- self ._on_shutdown_complete(exc )
597- else :
598- self ._process_outgoing( )
599- self ._call_eof_received()
600- self ._on_shutdown_complete(None )
608+ except ssl_SSLAgainErrors as exc:
609+ self ._process_outgoing()
610+ else :
611+ self ._process_outgoing( )
612+ if not self ._get_write_buffer_size() :
613+ self ._on_shutdown_complete( None )
614+ except Exception as ex:
615+ self ._on_shutdown_complete(ex )
601616
602617 cdef _on_shutdown_complete(self , shutdown_exc):
603618 if self ._shutdown_timeout_handle is not None :
604619 self ._shutdown_timeout_handle.cancel()
605620 self ._shutdown_timeout_handle = None
606621
607622 if shutdown_exc:
608- self ._fatal_error(shutdown_exc)
623+ self ._fatal_error(shutdown_exc, ' Error occurred during shutdown ' )
609624 else :
610- self ._loop.call_soon( self . _transport.close)
625+ self ._transport.close( )
611626
612627 cdef _abort(self , exc):
613628 self ._set_state(UNWRAPPED)
@@ -630,11 +645,14 @@ cdef class SSLProtocol:
630645 try :
631646 if self ._state == WRAPPED:
632647 self ._do_write()
648+ self ._process_outgoing()
649+ self ._control_app_writing()
633650
634651 except Exception as ex:
635652 self ._fatal_error(ex, ' Fatal error on SSL protocol' )
636653
637654 cdef _do_write(self ):
655+ """ Do SSL write, consumes write backlog and fills outgoing BIO."""
638656 cdef size_t data_len, count
639657 try :
640658 while self ._write_backlog:
@@ -651,14 +669,13 @@ cdef class SSLProtocol:
651669 self ._write_buffer_size -= data_len
652670 except ssl_SSLAgainErrors as exc:
653671 pass
654- self ._process_outgoing()
655672
656673 cdef _process_outgoing(self ):
674+ """ Send bytes from the outgoing BIO."""
657675 if not self ._ssl_writing_paused:
658676 data = self ._outgoing_read()
659677 if len (data):
660678 self ._transport.write(data)
661- self ._control_app_writing()
662679
663680 # Incoming flow
664681
@@ -673,8 +690,8 @@ cdef class SSLProtocol:
673690 self ._do_read__copied()
674691 if self ._write_backlog:
675692 self ._do_write()
676- else :
677- self ._process_outgoing ()
693+ self ._process_outgoing()
694+ self ._control_app_writing ()
678695 self ._control_ssl_reading()
679696 except Exception as ex:
680697 self ._fatal_error(ex, ' Fatal error on SSL protocol' )
@@ -860,7 +877,16 @@ cdef class SSLProtocol:
860877 """
861878 assert self ._ssl_writing_paused
862879 self ._ssl_writing_paused = False
863- self ._process_outgoing()
880+
881+ if self ._state == WRAPPED:
882+ self ._process_outgoing()
883+ self ._control_app_writing()
884+
885+ elif self ._state == FLUSHING:
886+ self ._do_flush()
887+
888+ elif self ._state == SHUTDOWN:
889+ self ._do_shutdown()
864890
865891 cdef _fatal_error(self , exc, message = ' Fatal error on transport' ):
866892 if self ._transport:
0 commit comments