@@ -208,19 +208,23 @@ public IRubyObject set_sync(final ThreadContext context, final IRubyObject sync)
208208
209209 @ JRubyMethod
210210 public IRubyObject connect (final ThreadContext context ) {
211- return connectImpl (context , true );
211+ return connectImpl (context , true , true );
212212 }
213213
214214 @ 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 );
217217 }
218218
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 ) {
221225
222226 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" );
224228 }
225229
226230 try {
@@ -231,53 +235,58 @@ private SSLSocket connectImpl(final ThreadContext context, final boolean blockin
231235 handshakeStatus = engine .getHandshakeStatus ();
232236 initialHandshake = true ;
233237 }
234- doHandshake (blocking );
238+ final IRubyObject ex = doHandshake (blocking , exception );
239+ if ( ex != null ) return ex ; // :wait_readable | :wait_writable
235240 }
236241 catch (SSLHandshakeException e ) {
237242 //debugStackTrace(runtime, e);
238243 // unlike server side, client should close outbound channel even if
239244 // we have remaining data to be sent.
240245 forceClose ();
241- throw newSSLErrorFromHandshake (runtime , e );
246+ throw newSSLErrorFromHandshake (context . runtime , e );
242247 }
243248 catch (NoSuchAlgorithmException e ) {
244- debugStackTrace (runtime , e );
249+ debugStackTrace (context . runtime , e );
245250 forceClose ();
246- throw newSSLError (runtime , e );
251+ throw newSSLError (context . runtime , e );
247252 }
248253 catch (KeyManagementException e ) {
249- debugStackTrace (runtime , e );
254+ debugStackTrace (context . runtime , e );
250255 forceClose ();
251- throw newSSLError (runtime , e );
256+ throw newSSLError (context . runtime , e );
252257 }
253258 catch (IOException e ) {
254- //debugStackTrace(runtime, e);
259+ //debugStackTrace(context. runtime, e);
255260 forceClose ();
256- throw newSSLError (runtime , e );
261+ throw newSSLError (context . runtime , e );
257262 }
258263 return this ;
259264 }
260265
261266 @ 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 );
264274 }
265275
266276 @ 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 ) );
269279 }
270280
271281 @ Deprecated
272282 public SSLSocket acceptCommon (ThreadContext context , boolean blocking ) {
273- return acceptImpl (context , blocking );
283+ return ( SSLSocket ) acceptImpl (context , blocking , true );
274284 }
275285
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 ) {
278287
279288 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" );
281290 }
282291
283292 try {
@@ -302,42 +311,43 @@ private SSLSocket acceptImpl(final ThreadContext context, final boolean blocking
302311 handshakeStatus = engine .getHandshakeStatus ();
303312 initialHandshake = true ;
304313 }
305- doHandshake (blocking );
314+ final IRubyObject ex = doHandshake (blocking , exception );
315+ if ( ex != null ) return ex ; // :wait_readable | :wait_writable
306316 }
307317 catch (SSLHandshakeException e ) {
308318 final String msg = e .getMessage ();
309319 // updated JDK (>= 1.7.0_75) with deprecated SSL protocols :
310320 // javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
311321 if ( e .getCause () == null && msg != null &&
312322 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" );
314324 }
315325 else {
316- debugStackTrace (runtime , e );
326+ debugStackTrace (context . runtime , e );
317327 }
318- throw newSSLErrorFromHandshake (runtime , e );
328+ throw newSSLErrorFromHandshake (context . runtime , e );
319329 }
320330 catch (NoSuchAlgorithmException e ) {
321- debugStackTrace (runtime , e );
322- throw newSSLError (runtime , e );
331+ debugStackTrace (context . runtime , e );
332+ throw newSSLError (context . runtime , e );
323333 }
324334 catch (KeyManagementException e ) {
325- debugStackTrace (runtime , e );
326- throw newSSLError (runtime , e );
335+ debugStackTrace (context . runtime , e );
336+ throw newSSLError (context . runtime , e );
327337 }
328338 catch (IOException e ) {
329- debugStackTrace (runtime , e );
330- throw newSSLError (runtime , e );
339+ debugStackTrace (context . runtime , e );
340+ throw newSSLError (context . runtime , e );
331341 }
332342 catch (RaiseException e ) {
333343 throw e ;
334344 }
335345 catch (RuntimeException e ) {
336- debugStackTrace (runtime , e );
346+ debugStackTrace (context . runtime , e );
337347 if ( "Could not generate DH keypair" .equals ( e .getMessage () ) ) {
338- throw SSL .handleCouldNotGenerateDHKeyPairError (runtime , e );
348+ throw SSL .handleCouldNotGenerateDHKeyPairError (context . runtime , e );
339349 }
340- throw newSSLError (runtime , e );
350+ throw newSSLError (context . runtime , e );
341351 }
342352 return this ;
343353 }
@@ -477,11 +487,17 @@ private static void writeWouldBlock(final Ruby runtime, final boolean exception,
477487 }
478488
479489 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 {
480495 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
482498
483499 // if not blocking, raise EAGAIN
484- if ( ! blocking && ! ready ) {
500+ if ( ! blocking && sel != Boolean . TRUE ) {
485501 throw getRuntime ().newErrnoEAGAINError ("Resource temporarily unavailable" );
486502 }
487503
@@ -491,7 +507,7 @@ private void doHandshake(final boolean blocking) throws IOException {
491507 case FINISHED :
492508 case NOT_HANDSHAKING :
493509 if ( initialHandshake ) finishInitialHandshake ();
494- return ;
510+ return null ; // OK
495511 case NEED_TASK :
496512 doTasks ();
497513 break ;
@@ -503,7 +519,8 @@ private void doHandshake(final boolean blocking) throws IOException {
503519 // does not mean writable. we explicitly wait for readable channel to avoid
504520 // busy loop.
505521 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
507524 }
508525 break ;
509526 case NEED_WRAP :
0 commit comments