@@ -208,19 +208,23 @@ public IRubyObject set_sync(final ThreadContext context, final IRubyObject sync)
208
208
209
209
@ JRubyMethod
210
210
public IRubyObject connect (final ThreadContext context ) {
211
- return connectImpl (context , true );
211
+ return connectImpl (context , true , true );
212
212
}
213
213
214
214
@ JRubyMethod
215
- public IRubyObject connect_nonblock (ThreadContext context ) {
216
- return connectImpl (context , false );
215
+ public IRubyObject connect_nonblock (final ThreadContext context ) {
216
+ return connectImpl (context , false , true );
217
217
}
218
218
219
- private SSLSocket connectImpl (final ThreadContext context , final boolean blocking ) {
220
- final Ruby runtime = context .runtime ;
219
+ @ JRubyMethod
220
+ public IRubyObject connect_nonblock (final ThreadContext context , IRubyObject opts ) {
221
+ return connectImpl (context , false , getExceptionOpt (context , opts ));
222
+ }
223
+
224
+ private IRubyObject connectImpl (final ThreadContext context , final boolean blocking , final boolean exception ) {
221
225
222
226
if ( ! sslContext .isProtocolForClient () ) {
223
- throw newSSLError (runtime , "called a function you should not call" );
227
+ throw newSSLError (context . runtime , "called a function you should not call" );
224
228
}
225
229
226
230
try {
@@ -231,53 +235,58 @@ private SSLSocket connectImpl(final ThreadContext context, final boolean blockin
231
235
handshakeStatus = engine .getHandshakeStatus ();
232
236
initialHandshake = true ;
233
237
}
234
- doHandshake (blocking );
238
+ final IRubyObject ex = doHandshake (blocking , exception );
239
+ if ( ex != null ) return ex ; // :wait_readable | :wait_writable
235
240
}
236
241
catch (SSLHandshakeException e ) {
237
242
//debugStackTrace(runtime, e);
238
243
// unlike server side, client should close outbound channel even if
239
244
// we have remaining data to be sent.
240
245
forceClose ();
241
- throw newSSLErrorFromHandshake (runtime , e );
246
+ throw newSSLErrorFromHandshake (context . runtime , e );
242
247
}
243
248
catch (NoSuchAlgorithmException e ) {
244
- debugStackTrace (runtime , e );
249
+ debugStackTrace (context . runtime , e );
245
250
forceClose ();
246
- throw newSSLError (runtime , e );
251
+ throw newSSLError (context . runtime , e );
247
252
}
248
253
catch (KeyManagementException e ) {
249
- debugStackTrace (runtime , e );
254
+ debugStackTrace (context . runtime , e );
250
255
forceClose ();
251
- throw newSSLError (runtime , e );
256
+ throw newSSLError (context . runtime , e );
252
257
}
253
258
catch (IOException e ) {
254
- //debugStackTrace(runtime, e);
259
+ //debugStackTrace(context. runtime, e);
255
260
forceClose ();
256
- throw newSSLError (runtime , e );
261
+ throw newSSLError (context . runtime , e );
257
262
}
258
263
return this ;
259
264
}
260
265
261
266
@ JRubyMethod
262
- public IRubyObject accept (ThreadContext context ) {
263
- return acceptImpl (context , true );
267
+ public IRubyObject accept (final ThreadContext context ) {
268
+ return acceptImpl (context , true , true );
269
+ }
270
+
271
+ @ JRubyMethod
272
+ public IRubyObject accept_nonblock (final ThreadContext context ) {
273
+ return acceptImpl (context , false , true );
264
274
}
265
275
266
276
@ JRubyMethod
267
- public IRubyObject accept_nonblock (ThreadContext context ) {
268
- return acceptImpl (context , false );
277
+ public IRubyObject accept_nonblock (final ThreadContext context , IRubyObject opts ) {
278
+ return acceptImpl (context , false , getExceptionOpt ( context , opts ) );
269
279
}
270
280
271
281
@ Deprecated
272
282
public SSLSocket acceptCommon (ThreadContext context , boolean blocking ) {
273
- return acceptImpl (context , blocking );
283
+ return ( SSLSocket ) acceptImpl (context , blocking , true );
274
284
}
275
285
276
- private SSLSocket acceptImpl (final ThreadContext context , final boolean blocking ) {
277
- final Ruby runtime = context .runtime ;
286
+ private IRubyObject acceptImpl (final ThreadContext context , final boolean blocking , final boolean exception ) {
278
287
279
288
if ( ! sslContext .isProtocolForServer () ) {
280
- throw newSSLError (runtime , "called a function you should not call" );
289
+ throw newSSLError (context . runtime , "called a function you should not call" );
281
290
}
282
291
283
292
try {
@@ -302,42 +311,43 @@ private SSLSocket acceptImpl(final ThreadContext context, final boolean blocking
302
311
handshakeStatus = engine .getHandshakeStatus ();
303
312
initialHandshake = true ;
304
313
}
305
- doHandshake (blocking );
314
+ final IRubyObject ex = doHandshake (blocking , exception );
315
+ if ( ex != null ) return ex ; // :wait_readable | :wait_writable
306
316
}
307
317
catch (SSLHandshakeException e ) {
308
318
final String msg = e .getMessage ();
309
319
// updated JDK (>= 1.7.0_75) with deprecated SSL protocols :
310
320
// javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
311
321
if ( e .getCause () == null && msg != null &&
312
322
msg .contains ("(protocol is disabled or cipher suites are inappropriate)" ) ) {
313
- debug (runtime , sslContext .getProtocol () + " protocol has been deactivated and is not available by default\n see the java.security.Security property jdk.tls.disabledAlgorithms in <JRE_HOME>/lib/security/java.security file" );
323
+ debug (context . runtime , sslContext .getProtocol () + " protocol has been deactivated and is not available by default\n see the java.security.Security property jdk.tls.disabledAlgorithms in <JRE_HOME>/lib/security/java.security file" );
314
324
}
315
325
else {
316
- debugStackTrace (runtime , e );
326
+ debugStackTrace (context . runtime , e );
317
327
}
318
- throw newSSLErrorFromHandshake (runtime , e );
328
+ throw newSSLErrorFromHandshake (context . runtime , e );
319
329
}
320
330
catch (NoSuchAlgorithmException e ) {
321
- debugStackTrace (runtime , e );
322
- throw newSSLError (runtime , e );
331
+ debugStackTrace (context . runtime , e );
332
+ throw newSSLError (context . runtime , e );
323
333
}
324
334
catch (KeyManagementException e ) {
325
- debugStackTrace (runtime , e );
326
- throw newSSLError (runtime , e );
335
+ debugStackTrace (context . runtime , e );
336
+ throw newSSLError (context . runtime , e );
327
337
}
328
338
catch (IOException e ) {
329
- debugStackTrace (runtime , e );
330
- throw newSSLError (runtime , e );
339
+ debugStackTrace (context . runtime , e );
340
+ throw newSSLError (context . runtime , e );
331
341
}
332
342
catch (RaiseException e ) {
333
343
throw e ;
334
344
}
335
345
catch (RuntimeException e ) {
336
- debugStackTrace (runtime , e );
346
+ debugStackTrace (context . runtime , e );
337
347
if ( "Could not generate DH keypair" .equals ( e .getMessage () ) ) {
338
- throw SSL .handleCouldNotGenerateDHKeyPairError (runtime , e );
348
+ throw SSL .handleCouldNotGenerateDHKeyPairError (context . runtime , e );
339
349
}
340
- throw newSSLError (runtime , e );
350
+ throw newSSLError (context . runtime , e );
341
351
}
342
352
return this ;
343
353
}
@@ -477,11 +487,17 @@ private static void writeWouldBlock(final Ruby runtime, final boolean exception,
477
487
}
478
488
479
489
private void doHandshake (final boolean blocking ) throws IOException {
490
+ doHandshake (blocking , true );
491
+ }
492
+
493
+ // might return :wait_readable | :wait_writable in case (true, false)
494
+ private IRubyObject doHandshake (final boolean blocking , final boolean exception ) throws IOException {
480
495
while (true ) {
481
- boolean ready = waitSelect (SelectionKey .OP_READ | SelectionKey .OP_WRITE , blocking , true ) == Boolean .TRUE ;
496
+ Object sel = waitSelect (SelectionKey .OP_READ | SelectionKey .OP_WRITE , blocking , exception );
497
+ if ( sel instanceof IRubyObject ) return (IRubyObject ) sel ; // :wait_readable | :wait_writable
482
498
483
499
// if not blocking, raise EAGAIN
484
- if ( ! blocking && ! ready ) {
500
+ if ( ! blocking && sel != Boolean . TRUE ) {
485
501
throw getRuntime ().newErrnoEAGAINError ("Resource temporarily unavailable" );
486
502
}
487
503
@@ -491,7 +507,7 @@ private void doHandshake(final boolean blocking) throws IOException {
491
507
case FINISHED :
492
508
case NOT_HANDSHAKING :
493
509
if ( initialHandshake ) finishInitialHandshake ();
494
- return ;
510
+ return null ; // OK
495
511
case NEED_TASK :
496
512
doTasks ();
497
513
break ;
@@ -503,7 +519,8 @@ private void doHandshake(final boolean blocking) throws IOException {
503
519
// does not mean writable. we explicitly wait for readable channel to avoid
504
520
// busy loop.
505
521
if (initialHandshake && status == SSLEngineResult .Status .BUFFER_UNDERFLOW ) {
506
- waitSelect (SelectionKey .OP_READ , blocking , true );
522
+ sel = waitSelect (SelectionKey .OP_READ , blocking , exception );
523
+ if ( sel instanceof IRubyObject ) return (IRubyObject ) sel ; // :wait_readable
507
524
}
508
525
break ;
509
526
case NEED_WRAP :
0 commit comments