4848import javax .net .ssl .SSLHandshakeException ;
4949import javax .net .ssl .SSLPeerUnverifiedException ;
5050
51- import org .jruby .Ruby ;
52- import org .jruby .RubyArray ;
53- import org .jruby .RubyClass ;
54- import org .jruby .RubyHash ;
55- import org .jruby .RubyIO ;
56- import org .jruby .RubyModule ;
57- import org .jruby .RubyNumeric ;
58- import org .jruby .RubyObject ;
59- import org .jruby .RubyString ;
60- import org .jruby .RubyThread ;
51+ import org .jruby .*;
6152import org .jruby .anno .JRubyMethod ;
6253import org .jruby .exceptions .RaiseException ;
6354import org .jruby .ext .openssl .x509store .X509Utils ;
64- import org .jruby .runtime .Arity ;
65- import org .jruby .runtime .Block ;
66- import org .jruby .runtime .ObjectAllocator ;
67- import org .jruby .runtime .ThreadContext ;
55+ import org .jruby .runtime .*;
6856import org .jruby .runtime .builtin .IRubyObject ;
57+ import org .jruby .runtime .callsite .FunctionalCachingCallSite ;
58+ import org .jruby .runtime .callsite .RespondToCallSite ;
6959import org .jruby .util .ByteList ;
70- import org .jruby .runtime .Visibility ;
7160
7261import static org .jruby .ext .openssl .SSL .newSSLErrorWaitReadable ;
7362import static org .jruby .ext .openssl .SSL .newSSLErrorWaitWritable ;
@@ -86,12 +75,45 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
8675 }
8776 };
8877
78+ private enum CallSiteIndex {
79+
80+ // self
81+ hostname ("hostname" ),
82+ //sync_close("sync_close"),
83+ //sync_close_w("sync_close="),
84+ // io
85+ _respond_to_nonblock_w ("nonblock=" ),
86+ nonblock_w ("nonblock=" ),
87+ sync ("sync" ),
88+ sync_w ("sync=" ),
89+ flush ("flush" ),
90+ close ("close" ),
91+ closed_p ("closed?" ),
92+ // ssl_context
93+ verify_mode ("verify_mode" );
94+
95+ final String method ;
96+
97+ CallSiteIndex (String method ) { this .method = method ; }
98+
99+ }
100+
89101 public static void createSSLSocket (final Ruby runtime , final RubyModule SSL ) { // OpenSSL::SSL
102+ CallSiteIndex [] values = CallSiteIndex .values ();
103+ CallSite [] extraCallSites = new CallSite [values .length ];
104+ for (int i =0 ; i <values .length ; i ++) {
105+ if (values [i ].name ().startsWith ("_respond_to" )) {
106+ extraCallSites [i ] = new RespondToCallSite ();
107+ }
108+ else {
109+ extraCallSites [i ] = new FunctionalCachingCallSite (values [i ].method );
110+ }
111+ }
112+
113+ RubyClass SSLSocket = runtime .defineClassUnder ("SSLSocket" , runtime .getObject (), ALLOCATOR , SSL , extraCallSites );
114+
90115 final ThreadContext context = runtime .getCurrentContext ();
91- RubyClass SSLSocket = SSL .defineClassUnder ("SSLSocket" , runtime .getObject (), ALLOCATOR );
92- // SSLSocket.addReadAttribute(context, "io");
93- // SSLSocket.defineAlias("to_io", "io");
94- // SSLSocket.addReadAttribute(context, "context");
116+
95117 SSLSocket .addReadWriteAttribute (context , "sync_close" );
96118 SSLSocket .addReadWriteAttribute (context , "hostname" );
97119 SSLSocket .defineAnnotatedMethods (SSLSocket .class );
@@ -123,6 +145,10 @@ private static RaiseException newSSLErrorFromHandshake(Ruby runtime, SSLHandshak
123145 return SSL .newSSLError (runtime , cause );
124146 }
125147
148+ private CallSite callSite (final CallSiteIndex index ) {
149+ return getMetaClass ().getExtraCallSites ()[ index .ordinal () ];
150+ }
151+
126152 private SSLContext sslContext ;
127153 private SSLEngine engine ;
128154 private RubyIO io ;
@@ -152,29 +178,36 @@ public IRubyObject initialize(final ThreadContext context, final IRubyObject[] a
152178 if ( ! ( args [0 ] instanceof RubyIO ) ) {
153179 throw runtime .newTypeError ("IO expected but got " + args [0 ].getMetaClass ().getName ());
154180 }
155- setInstanceVariable ("@io" , this .io = (RubyIO ) args [0 ]); // compat (we do not read @io)
156- // Ruby 2.3 : @io.nonblock = true if @io.respond_to?(:nonblock=)
157- if (io .respondsTo ("nonblock=" )) {
158- io .callMethod (context , "nonblock=" , runtime .getTrue ());
159- }
160181 setInstanceVariable ("@context" , this .sslContext ); // only compat (we do not use @context)
182+ setInstanceVariable ("@io" , this .io = (RubyIO ) args [0 ]);
183+ set_io_nonblock_checked (context , runtime .getTrue ());
161184 // This is a bit of a hack: SSLSocket should share code with
162185 // RubyBasicSocket, which always sets sync to true.
163186 // Instead we set it here for now.
164- this . set_sync (context , runtime .getTrue ()); // io.sync = true
165- this . callMethod ( context , " sync_close= " , runtime .getFalse ());
187+ set_sync (context , runtime .getTrue ()); // io.sync = true
188+ setInstanceVariable ( "@ sync_close" , runtime .getFalse ()); // self.sync_close = false
166189 sslContext .setup (context );
167190 return Utils .invokeSuper (context , this , args , Block .NULL_BLOCK ); // super()
168191 }
169192
193+ private IRubyObject set_io_nonblock_checked (final ThreadContext context , RubyBoolean value ) {
194+ // @io.nonblock = true if @io.respond_to?(:nonblock=)
195+ IRubyObject respond = callSite (CallSiteIndex ._respond_to_nonblock_w ).call (context , io , io , context .runtime .newSymbol ("nonblock=" ));
196+ if (respond .isTrue ()) {
197+ return callSite (CallSiteIndex .nonblock_w ).call (context , io , io , value );
198+ }
199+ return context .nil ;
200+ }
201+
170202 private SSLEngine ossl_ssl_setup (final ThreadContext context )
171203 throws NoSuchAlgorithmException , KeyManagementException {
172204 SSLEngine engine = this .engine ;
173205 if ( engine != null ) return engine ;
174206
175207 // Server Name Indication (SNI) RFC 3546
176208 // SNI support will not be attempted unless hostname is explicitly set by the caller
177- String peerHost = this .callMethod (context , "hostname" ).toString ();
209+ IRubyObject hostname = callSite (CallSiteIndex .hostname ).call (context , this , this ); // self.hostname
210+ String peerHost = hostname .toString ();
178211 final int peerPort = socketChannelImpl ().getRemotePort ();
179212 engine = sslContext .createSSLEngine (peerHost , peerPort );
180213
@@ -199,12 +232,12 @@ private SSLEngine ossl_ssl_setup(final ThreadContext context)
199232
200233 @ JRubyMethod (name = "sync" )
201234 public IRubyObject sync (final ThreadContext context ) {
202- return this . io . callMethod (context , "sync" );
235+ return callSite ( CallSiteIndex . sync ). call (context , io , io ); // io.sync
203236 }
204237
205238 @ JRubyMethod (name = "sync=" )
206239 public IRubyObject set_sync (final ThreadContext context , final IRubyObject sync ) {
207- return this . io . callMethod (context , "sync=" , sync );
240+ return callSite ( CallSiteIndex . sync_w ). call (context , io , io , sync ); // io.sync = sync
208241 }
209242
210243 @ JRubyMethod
@@ -302,8 +335,8 @@ private IRubyObject acceptImpl(final ThreadContext context, final boolean blocki
302335 if ( ! initialHandshake ) {
303336 final SSLEngine engine = ossl_ssl_setup (context );
304337 engine .setUseClientMode (false );
305- final IRubyObject verify_mode = sslContext . callMethod (context , "verify_mode" );
306- if ( ! verify_mode . isNil () ) {
338+ final IRubyObject verify_mode = callSite ( CallSiteIndex . verify_mode ). call (context , sslContext , sslContext );
339+ if ( verify_mode != context . nil ) {
307340 final int verify = RubyNumeric .fix2int (verify_mode );
308341 if ( verify == 0 ) { // VERIFY_NONE
309342 engine .setNeedClientAuth (false );
@@ -863,7 +896,7 @@ private IRubyObject syswriteImpl(final ThreadContext context,
863896 written = write (buff , blocking );
864897 }
865898
866- this . io . callMethod (context , "flush" );
899+ callSite ( CallSiteIndex . flush ). call (context , io , io ); // io.flush
867900
868901 return runtime .newFixnum (written );
869902 }
@@ -931,17 +964,22 @@ private void close(boolean force) {
931964
932965 @ JRubyMethod
933966 public IRubyObject sysclose (final ThreadContext context ) {
934- //if ( isClosed() ) return context.runtime.getNil();
935- if ( this .io .callMethod (context , "closed?" ).isTrue () ) {
936- return context .runtime .getNil ();
937- } // Ruby 2.3
967+ if ( io_closed_p (context ).isTrue () ) return context .nil ;
968+
938969 // no need to try shutdown when it's a server
939970 close ( sslContext .isProtocolForClient () );
940971
941- if ( this .callMethod (context , "sync_close" ).isTrue () ) {
942- return this .io .callMethod (context , "close" );
943- }
944- return context .runtime .getNil ();
972+ if ( getInstanceVariable ("@sync_close" ).isTrue () ) return io_close (context );
973+
974+ return context .nil ;
975+ }
976+
977+ private IRubyObject io_closed_p (final ThreadContext context ) { // io.closed?
978+ return callSite (CallSiteIndex .closed_p ).call (context , io , io );
979+ }
980+
981+ private IRubyObject io_close (final ThreadContext context ) { // io.close
982+ return callSite (CallSiteIndex .close ).call (context , io , io );
945983 }
946984
947985 @ JRubyMethod
0 commit comments