@@ -278,7 +278,6 @@ cdef class SSLProtocol:
278
278
self ._incoming_high_water = 0
279
279
self ._incoming_low_water = 0
280
280
self ._set_read_buffer_limits()
281
- self ._eof_received = False
282
281
283
282
self ._app_writing_paused = False
284
283
self ._outgoing_high_water = 0
@@ -392,27 +391,24 @@ cdef class SSLProtocol:
392
391
will close itself. If it returns a true value, closing the
393
392
transport is up to the protocol.
394
393
"""
395
- self ._eof_received = True
396
394
try :
397
395
if self ._loop.get_debug():
398
396
aio_logger.debug(" %r received EOF" , self )
399
397
400
398
if self ._state == DO_HANDSHAKE:
401
399
self ._on_handshake_complete(ConnectionResetError)
402
400
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".
406
407
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 )
413
409
414
410
elif self ._state == SHUTDOWN:
415
- self ._do_shutdown( )
411
+ self ._on_shutdown_complete( None )
416
412
417
413
except Exception :
418
414
self ._transport.close()
@@ -443,6 +439,9 @@ cdef class SSLProtocol:
443
439
elif self ._state == WRAPPED and new_state == FLUSHING:
444
440
allowed = True
445
441
442
+ elif self ._state == WRAPPED and new_state == SHUTDOWN:
443
+ allowed = True
444
+
446
445
elif self ._state == FLUSHING and new_state == SHUTDOWN:
447
446
allowed = True
448
447
@@ -560,54 +559,70 @@ cdef class SSLProtocol:
560
559
self ._transport._force_close(
561
560
aio_TimeoutError(' SSL shutdown timed out' ))
562
561
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
+ """
564
567
cdef:
565
568
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()
566
580
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
+ """
567
589
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()
581
593
self ._control_ssl_reading()
582
594
except Exception as ex:
583
- self ._fatal_error (ex, ' Fatal error on SSL protocol ' )
595
+ self ._on_shutdown_complete (ex)
584
596
else :
585
597
if not self ._get_write_buffer_size():
586
598
self ._set_state(SHUTDOWN)
587
599
self ._do_shutdown()
588
600
589
601
cdef _do_shutdown(self ):
602
+ """ Send close_notify and wait for the same from the peer."""
590
603
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 :
592
607
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 )
601
616
602
617
cdef _on_shutdown_complete(self , shutdown_exc):
603
618
if self ._shutdown_timeout_handle is not None :
604
619
self ._shutdown_timeout_handle.cancel()
605
620
self ._shutdown_timeout_handle = None
606
621
607
622
if shutdown_exc:
608
- self ._fatal_error(shutdown_exc)
623
+ self ._fatal_error(shutdown_exc, ' Error occurred during shutdown ' )
609
624
else :
610
- self ._loop.call_soon( self . _transport.close)
625
+ self ._transport.close( )
611
626
612
627
cdef _abort(self , exc):
613
628
self ._set_state(UNWRAPPED)
@@ -630,11 +645,14 @@ cdef class SSLProtocol:
630
645
try :
631
646
if self ._state == WRAPPED:
632
647
self ._do_write()
648
+ self ._process_outgoing()
649
+ self ._control_app_writing()
633
650
634
651
except Exception as ex:
635
652
self ._fatal_error(ex, ' Fatal error on SSL protocol' )
636
653
637
654
cdef _do_write(self ):
655
+ """ Do SSL write, consumes write backlog and fills outgoing BIO."""
638
656
cdef size_t data_len, count
639
657
try :
640
658
while self ._write_backlog:
@@ -651,14 +669,13 @@ cdef class SSLProtocol:
651
669
self ._write_buffer_size -= data_len
652
670
except ssl_SSLAgainErrors as exc:
653
671
pass
654
- self ._process_outgoing()
655
672
656
673
cdef _process_outgoing(self ):
674
+ """ Send bytes from the outgoing BIO."""
657
675
if not self ._ssl_writing_paused:
658
676
data = self ._outgoing_read()
659
677
if len (data):
660
678
self ._transport.write(data)
661
- self ._control_app_writing()
662
679
663
680
# Incoming flow
664
681
@@ -673,8 +690,8 @@ cdef class SSLProtocol:
673
690
self ._do_read__copied()
674
691
if self ._write_backlog:
675
692
self ._do_write()
676
- else :
677
- self ._process_outgoing ()
693
+ self ._process_outgoing()
694
+ self ._control_app_writing ()
678
695
self ._control_ssl_reading()
679
696
except Exception as ex:
680
697
self ._fatal_error(ex, ' Fatal error on SSL protocol' )
@@ -860,7 +877,16 @@ cdef class SSLProtocol:
860
877
"""
861
878
assert self ._ssl_writing_paused
862
879
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()
864
890
865
891
cdef _fatal_error(self , exc, message = ' Fatal error on transport' ):
866
892
if self ._transport:
0 commit comments