Skip to content

Commit c8df05a

Browse files
committed
wrap Java's SocketChannel into a wrapper due potential JNR UnixChannel support
1 parent 0fe8de8 commit c8df05a

1 file changed

Lines changed: 106 additions & 21 deletions

File tree

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

Lines changed: 106 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import java.io.IOException;
3131
import java.net.Socket;
3232
import java.nio.ByteBuffer;
33+
import java.nio.channels.Channel;
34+
import java.nio.channels.ClosedChannelException;
3335
import java.nio.channels.SelectableChannel;
3436
import java.nio.channels.SelectionKey;
3537
import java.nio.channels.Selector;
@@ -170,11 +172,10 @@ private SSLEngine ossl_ssl_setup(final ThreadContext context)
170172
SSLEngine engine = this.engine;
171173
if ( engine != null ) return engine;
172174

173-
final Socket socket = getSocketChannel().socket();
174175
// Server Name Indication (SNI) RFC 3546
175176
// SNI support will not be attempted unless hostname is explicitly set by the caller
176177
String peerHost = this.callMethod(context, "hostname").toString();
177-
final int peerPort = socket.getPort();
178+
final int peerPort = socketChannelImpl().getSocketPort();
178179
engine = sslContext.createSSLEngine(peerHost, peerPort);
179180

180181
final javax.net.ssl.SSLSession session = engine.getSession();
@@ -264,7 +265,7 @@ private SSLSocket acceptImpl(final ThreadContext context, final boolean blocking
264265

265266
try {
266267
if ( ! initialHandshake ) {
267-
SSLEngine engine = ossl_ssl_setup(context);
268+
final SSLEngine engine = ossl_ssl_setup(context);
268269
engine.setUseClientMode(false);
269270
final IRubyObject verify_mode = sslContext.callMethod(context, "verify_mode");
270271
if ( ! verify_mode.isNil() ) {
@@ -330,16 +331,15 @@ public IRubyObject verify_result(final ThreadContext context) {
330331
// temporarily. SSLSocket requires wrapping IO to be selectable so it should
331332
// be OK to set configureBlocking(false) permanently.
332333
private boolean waitSelect(final int operations, final boolean blocking) throws IOException {
333-
if ( ! ( io.getChannel() instanceof SelectableChannel ) ) {
334-
return true;
335-
}
334+
final SocketChannelImpl channel = socketChannelImpl();
335+
if ( ! channel.isSelectable() ) return true;
336+
336337
final Ruby runtime = getRuntime();
337338
final RubyThread thread = runtime.getCurrentContext().getThread();
338339

339-
SelectableChannel selectable = (SelectableChannel)io.getChannel();
340-
selectable.configureBlocking(false);
340+
channel.configureBlocking(false);
341341
final Selector selector = runtime.getSelectorPool().get();
342-
final SelectionKey key = selectable.register(selector, operations);
342+
final SelectionKey key = channel.register(selector, operations);
343343

344344
try {
345345
io.addBlockingThread(thread);
@@ -506,7 +506,7 @@ private boolean flushData(boolean blocking) throws IOException {
506506
private int writeToChannel(ByteBuffer buffer, boolean blocking) throws IOException {
507507
int totalWritten = 0;
508508
while ( buffer.hasRemaining() ) {
509-
totalWritten += getSocketChannel().write(buffer);
509+
totalWritten += socketChannelImpl().write(buffer);
510510
if ( ! blocking ) break; // don't continue attempting to read
511511
}
512512
return totalWritten;
@@ -521,9 +521,9 @@ public int write(ByteBuffer src, boolean blocking) throws SSLException, IOExcept
521521
throw new IOException("Writing not possible during handshake");
522522
}
523523

524-
SelectableChannel selectable = getSocketChannel();
525-
boolean blockingMode = selectable.isBlocking();
526-
if ( ! blocking ) selectable.configureBlocking(false);
524+
SocketChannelImpl channel = socketChannelImpl();
525+
final boolean blockingMode = channel.isBlocking();
526+
if ( ! blocking ) channel.configureBlocking(false);
527527

528528
try {
529529
if ( netData.hasRemaining() ) {
@@ -539,11 +539,11 @@ public int write(ByteBuffer src, boolean blocking) throws SSLException, IOExcept
539539
return result.bytesConsumed();
540540
}
541541
finally {
542-
if ( ! blocking ) selectable.configureBlocking(blockingMode);
542+
if ( ! blocking ) channel.configureBlocking(blockingMode);
543543
}
544544
}
545545

546-
public int read(ByteBuffer dst, boolean blocking) throws IOException {
546+
public int read(final ByteBuffer dst, final boolean blocking) throws IOException {
547547
if ( initialHandshake ) return 0;
548548
if ( engine.isInboundDone() ) return -1;
549549

@@ -559,8 +559,8 @@ public int read(ByteBuffer dst, boolean blocking) throws IOException {
559559
return limit;
560560
}
561561

562-
private int readAndUnwrap(boolean blocking) throws IOException {
563-
final int bytesRead = getSocketChannel().read(peerNetData);
562+
private int readAndUnwrap(final boolean blocking) throws IOException {
563+
final int bytesRead = socketChannelImpl().read(peerNetData);
564564
if ( bytesRead == -1 ) {
565565
if ( ! peerNetData.hasRemaining() ||
566566
( status == SSLEngineResult.Status.BUFFER_UNDERFLOW ) ) {
@@ -674,7 +674,7 @@ private RubyString sysreadImpl(final ThreadContext context,
674674
// ensure >0 bytes read; sysread is blocking read.
675675
while ( rr <= 0 ) {
676676
if ( engine == null ) {
677-
rr = getSocketChannel().read(dst);
677+
rr = socketChannelImpl().read(dst);
678678
} else {
679679
rr = read(dst, blocking);
680680
}
@@ -754,7 +754,7 @@ public IRubyObject syswrite_nonblock(ThreadContext context, IRubyObject arg, IRu
754754
}
755755

756756
private void checkClosed() {
757-
if ( ! getSocketChannel().isOpen() ) {
757+
if ( ! socketChannelImpl().isOpen() ) {
758758
throw getRuntime().newIOError("closed stream");
759759
}
760760
}
@@ -906,8 +906,93 @@ public IRubyObject ssl_version() {
906906
return getRuntime().newString( engine.getSession().getProtocol() );
907907
}
908908

909-
private SocketChannel getSocketChannel() {
910-
return (SocketChannel) io.getChannel();
909+
private transient SocketChannelImpl socketChannel;
910+
911+
private SocketChannelImpl socketChannelImpl() {
912+
if ( socketChannel != null ) return socketChannel;
913+
914+
final Channel channel = io.getChannel();
915+
if ( channel instanceof SocketChannel ) {
916+
return socketChannel = new JavaSocketChannel((SocketChannel) channel);
917+
}
918+
919+
// TODO JNR
920+
921+
throw new IllegalStateException("unknow channel impl: " + channel + " of type " + channel.getClass().getName());
922+
}
923+
924+
private static interface SocketChannelImpl {
925+
926+
boolean isOpen() ;
927+
928+
int read(ByteBuffer dst) throws IOException ;
929+
930+
int write(ByteBuffer src) throws IOException ;
931+
932+
int getSocketPort() ;
933+
934+
boolean isSelectable() ;
935+
936+
// SelectableChannel
937+
938+
boolean isBlocking() ;
939+
940+
void configureBlocking(boolean block) throws IOException ;
941+
942+
SelectionKey register(Selector selector, int ops) throws IOException ;
943+
944+
//boolean selectionOpsReadable(final int readyOps);
945+
946+
//boolean selectionOpsWritable(final int readyOps) ;
947+
948+
}
949+
950+
private static final class JavaSocketChannel implements SocketChannelImpl {
951+
952+
JavaSocketChannel(final SocketChannel channel) {
953+
this.channel = channel;
954+
}
955+
956+
private final SocketChannel channel;
957+
958+
public boolean isOpen() { return channel.isOpen(); }
959+
960+
public int read(ByteBuffer dst) throws IOException {
961+
return channel.read(dst);
962+
}
963+
964+
public int write(ByteBuffer src) throws IOException {
965+
return channel.write(src);
966+
}
967+
968+
public int getSocketPort() { return channel.socket().getPort(); }
969+
970+
public boolean isSelectable() {
971+
return true; // return channel instanceof SelectableChannel;
972+
}
973+
974+
public boolean isBlocking() { return channel.isBlocking(); }
975+
976+
public void configureBlocking(boolean block) throws IOException {
977+
channel.configureBlocking(block);
978+
}
979+
980+
public SelectionKey register(Selector selector, int ops) throws ClosedChannelException {
981+
return channel.register(selector, ops);
982+
}
983+
984+
public boolean selectionOpsReadable(final int readyOps) {
985+
return (readyOps & SelectionKey.OP_READ) != 0;
986+
}
987+
988+
public boolean selectionOpsWritable(final int readyOps) {
989+
return (readyOps & SelectionKey.OP_WRITE) != 0;
990+
}
991+
992+
}
993+
994+
private static boolean jnrChannel(final Channel channel) {
995+
return channel.getClass().getName().startsWith("jnr.");
911996
}
912997

913998
}// SSLSocket

0 commit comments

Comments
 (0)