3030import java .io .IOException ;
3131import java .net .Socket ;
3232import java .nio .ByteBuffer ;
33+ import java .nio .channels .Channel ;
34+ import java .nio .channels .ClosedChannelException ;
3335import java .nio .channels .SelectableChannel ;
3436import java .nio .channels .SelectionKey ;
3537import 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