44import java .nio .ByteBuffer ;
55import java .nio .channels .SelectionKey ;
66import java .nio .channels .Selector ;
7+ import java .util .concurrent .CountDownLatch ;
8+ import java .util .concurrent .TimeUnit ;
79import javax .net .ssl .SSLEngine ;
810
911import org .jruby .Ruby ;
@@ -58,9 +60,12 @@ public void syswriteNonblockDataIntegrity() throws Exception {
5860 SSLSocket server = (SSLSocket ) pair .entry (1 ).toJava (SSLSocket .class );
5961
6062 try {
61- // Server: read all data in a background thread, counting bytes
62- final long [] serverReceived = { 0 };
63- Thread serverReader = startServerReader (server , serverReceived );
63+ final int expectedBytes = 64 * 4096 ;
64+
65+ // Server: read an exact payload size in the background so the assertion
66+ // does not depend on EOF timing or close-path behavior
67+ final ServerReadResult serverResult = new ServerReadResult ();
68+ Thread serverReader = startServerReader (server , expectedBytes , serverResult );
6469
6570 // Client: write 256KB in 4KB chunks via syswrite_nonblock
6671 byte [] chunk = new byte [4096 ];
@@ -85,42 +90,62 @@ public void syswriteNonblockDataIntegrity() throws Exception {
8590 }
8691 }
8792 assertTrue (totalSent > 0 , "should have sent data" );
93+ assertEquals (expectedBytes , totalSent , "test must send the full payload" );
8894
89- // Close client to signal EOF, let server finish reading
90- client .callMethod (currentContext (), "close" );
91- serverReader .join (10_000 );
95+ assertTrue (serverResult .await (10 , TimeUnit .SECONDS ),
96+ "server must finish reading the expected payload"
97+ );
98+ serverReader .join (1_000 );
99+ assertFalse (serverReader .isAlive (), "server reader must exit after reading expected bytes" );
100+ if (serverResult .failure != null ) {
101+ throw new AssertionError ("server reader failed unexpectedly" , serverResult .failure );
102+ }
92103
93- assertEquals (totalSent , serverReceived [ 0 ] ,
104+ assertEquals (totalSent , serverResult . bytesRead ,
94105 "server must receive exactly what client sent — mismatch means encrypted bytes were lost!"
95106 );
96107 } finally {
97108 closeQuietly (pair );
98109 }
99110 }
100111
101- private Thread startServerReader (final SSLSocket server , final long [] serverReceived ) {
112+ private Thread startServerReader (final SSLSocket server , final long expectedBytes ,
113+ final ServerReadResult serverResult ) {
102114 Thread serverReader = new Thread (() -> {
103115 try {
104116 RubyFixnum len = RubyFixnum .newFixnum (runtime , 8192 );
105- while (true ) {
117+ while (serverResult . bytesRead < expectedBytes ) {
106118 IRubyObject data = server .sysread (currentContext (), len );
107- serverReceived [ 0 ] += ((RubyString ) data ).getByteList ().getRealSize ();
119+ serverResult . bytesRead += ((RubyString ) data ).getByteList ().getRealSize ();
108120 }
109- } catch (RaiseException e ) {
110- String errorName = e .getException ().getMetaClass ().getName ();
111- if ("EOFError" .equals (errorName ) || "IOError" .equals (errorName )) { // client closes connection
112- System .out .println ("server-reader expected: " + e .getMessage ());
113- } else {
114- System .err .println ("server-reader unexpected: " + e .getMessage ());
115- e .printStackTrace (System .err );
116- throw e ;
121+ if (serverResult .bytesRead != expectedBytes ) {
122+ throw new AssertionError ("server read " + serverResult .bytesRead +
123+ " bytes, expected " + expectedBytes );
117124 }
125+ } catch (Throwable t ) {
126+ serverResult .failure = t ;
127+ } finally {
128+ serverResult .finish ();
118129 }
119- });
130+ }, "ssl-server-reader" );
120131 serverReader .start ();
121132 return serverReader ;
122133 }
123134
135+ private static final class ServerReadResult {
136+ private final CountDownLatch done = new CountDownLatch (1 );
137+ private volatile long bytesRead ;
138+ private volatile Throwable failure ;
139+
140+ private boolean await (final long timeout , final TimeUnit unit ) throws InterruptedException {
141+ return done .await (timeout , unit );
142+ }
143+
144+ private void finish () {
145+ done .countDown ();
146+ }
147+ }
148+
124149 /**
125150 * After saturating the TCP send buffer with {@code syswrite_nonblock},
126151 * inspect {@code netWriteData} to verify the buffer is consistent.
0 commit comments