Skip to content

Commit 9bd2336

Browse files
committed
support SSLSocket#accept_nonblock/connect_nonblock with exception: false
adding Ruby 2.3 compatibility
1 parent 32e7fd6 commit 9bd2336

File tree

1 file changed

+56
-39
lines changed

1 file changed

+56
-39
lines changed

src/main/java/org/jruby/ext/openssl/SSLSocket.java

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)