11/*
2- * Copyright (c) 2017, 2023 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2017, 2026 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
@@ -273,7 +273,7 @@ final class Reader extends SubscriberWrapper implements FlowTube.TubeSubscriber
273273
274274 final SequentialScheduler scheduler ;
275275 volatile ByteBuffer readBuf ;
276- volatile boolean completing ;
276+ boolean completing ;
277277 final ReentrantLock readBufferLock = new ReentrantLock ();
278278 final Logger debugr = Utils .getDebugLogger (this ::dbgString , Utils .DEBUG );
279279
@@ -301,6 +301,11 @@ protected SchedulingAction enterScheduling() {
301301 return enterReadScheduling ();
302302 }
303303
304+ @ Override
305+ public boolean closing () {
306+ return closeNotifyReceived ();
307+ }
308+
304309 public final String dbgString () {
305310 return "SSL Reader(" + tubeName + ")" ;
306311 }
@@ -505,7 +510,7 @@ else if (this.completing) {
505510 if (result .handshaking ()) {
506511 handshaking = true ;
507512 if (debugr .on ()) debugr .log ("handshaking" );
508- if (doHandshake (result , READER )) continue ; // need unwrap
513+ if (doHandshake (result . handshakeStatus () , READER )) continue ; // need unwrap
509514 else break ; // doHandshake will have triggered the write scheduler if necessary
510515 } else {
511516 if (trySetALPN ()) {
@@ -550,6 +555,7 @@ else if (this.completing) {
550555
551556 private volatile Status lastUnwrapStatus ;
552557 EngineResult unwrapBuffer (ByteBuffer src ) throws IOException {
558+ assert readBufferLock .isHeldByCurrentThread ();
553559 ByteBuffer dst = getAppBuffer ();
554560 int len = src .remaining ();
555561 while (true ) {
@@ -573,6 +579,8 @@ EngineResult unwrapBuffer(ByteBuffer src) throws IOException {
573579 break ;
574580 case CLOSED :
575581 assert dst .position () == 0 ;
582+ src .position (src .limit ());
583+ completing = true ;
576584 return doClosure (new EngineResult (sslResult ));
577585 case BUFFER_UNDERFLOW :
578586 // handled implicitly by compaction/reallocation of readBuf
@@ -834,7 +842,7 @@ private void processData() {
834842 boolean handshaking = false ;
835843 if (result .handshaking ()) {
836844 if (debugw .on ()) debugw .log ("handshaking" );
837- doHandshake (result , WRITER ); // ok to ignore return
845+ doHandshake (result . handshakeStatus () , WRITER ); // ok to ignore return
838846 handshaking = true ;
839847 } else {
840848 if (trySetALPN ()) {
@@ -1090,14 +1098,14 @@ private void resumeActivity() {
10901098 return (current & HANDSHAKING );
10911099 };
10921100
1093- private boolean doHandshake (EngineResult r , int caller ) {
1101+ private boolean doHandshake (HandshakeStatus handshakeStatus , int caller ) {
10941102 // unconditionally sets the HANDSHAKING bit, while preserving task bits
10951103 handshakeState .getAndAccumulate (0 , (current , unused ) -> HANDSHAKING | (current & TASK_BITS ));
10961104 if (stateList != null && debug .on ()) {
1097- stateList .add (r . handshakeStatus () .toString ());
1105+ stateList .add (handshakeStatus .toString ());
10981106 stateList .add (Integer .toString (caller ));
10991107 }
1100- switch (r . handshakeStatus () ) {
1108+ switch (handshakeStatus ) {
11011109 case NEED_TASK :
11021110 int s = handshakeState .accumulateAndGet (0 , REQUEST_OR_DO_TASKS );
11031111 if ((s & REQUESTING_TASKS ) > 0 ) { // someone else is or will do tasks
@@ -1125,7 +1133,7 @@ private boolean doHandshake(EngineResult r, int caller) {
11251133 break ;
11261134 default :
11271135 throw new InternalError ("Unexpected handshake status:"
1128- + r . handshakeStatus () );
1136+ + handshakeStatus );
11291137 }
11301138 return true ;
11311139 }
@@ -1182,34 +1190,20 @@ boolean trySetALPN() {
11821190 return false ;
11831191 }
11841192
1185- // FIXME: acknowledge a received CLOSE request from peer
11861193 EngineResult doClosure (EngineResult r ) throws IOException {
11871194 if (debug .on ())
11881195 debug .log ("doClosure(%s): %s [isOutboundDone: %s, isInboundDone: %s]" ,
11891196 r .result , engine .getHandshakeStatus (),
11901197 engine .isOutboundDone (), engine .isInboundDone ());
1198+ if (debug .on ()) debug .log ("doClosure: close_notify received" );
1199+ close_notify_received = true ;
1200+ engine .closeOutbound ();
11911201 if (engine .getHandshakeStatus () == HandshakeStatus .NEED_WRAP ) {
11921202 // we have received TLS close_notify and need to send
11931203 // an acknowledgement back. We're calling doHandshake
11941204 // to finish the close handshake.
1195- if (engine .isInboundDone () && !engine .isOutboundDone ()) {
1196- if (debug .on ()) debug .log ("doClosure: close_notify received" );
1197- close_notify_received = true ;
1198- if (!writer .scheduler .isStopped ()) {
1199- doHandshake (r , READER );
1200- } else {
1201- // We have received closed notify, but we
1202- // won't be able to send the acknowledgement.
1203- // Nothing more will come from the socket either,
1204- // so mark the reader as completed.
1205- var readerLock = reader .readBufferLock ;
1206- readerLock .lock ();
1207- try {
1208- reader .completing = true ;
1209- } finally {
1210- readerLock .unlock ();
1211- }
1212- }
1205+ if (!writer .scheduler .isStopped ()) {
1206+ doHandshake (HandshakeStatus .NEED_WRAP , READER );
12131207 }
12141208 }
12151209 return r ;
0 commit comments