The question is brief. Why is a SOCKS-aware socket implementation the default choice for the implementation of the abstract java.net.Socket
class? Naïvely I'd expect java.net.PlainSocketImpl
.
The background is a bit more complicated.
I'm trying to kill off GLASSFISH-12213 (or really I'm trying to work around it). The details of the bug itself aren't very important--there's a native library that is not thread safe, and some concurrent usages of it from a GlassFish-authored LDAP realm crash the JVM.
To work around it, I started working backwards: can I avoid having the native library called in the first place? This led me for various reasons to look at sun.net.spi.DefaultProxySelector
, which is in charge of finding an appropriate proxy (or not) for a given URI. There's a spot in there where it makes a native method call, and that is where the JVM crash occurs. If I could avoid that call, I'd be in business.
One way I could avoid that call would be if I could ensure that the value of sun.net.spi.NetProperties.getBoolean("java.net.useSystemProxies")
false. If that were the case, then the native method I mentioned earlier wouldn't be called. false
is the default and I haven't modified anything in this regard at all.
(So actually it is the case; demonstrably proven in the GlassFish instance running on that machine where I observed the bug. I'm not sure how this code, therefore, could possibly still be loading the native library; that's a subject for another day.)
So, punting on that path, I backed up further: what protocol was being passed as the URI
scheme
in the DefaultProxySelector.select(uri)
call? Maybe I could then somehow influence the silly thing to still skip that native call somehow.
As it turns out, the protocol was socket
(I had assumed it was probably something like ldap
, but no). This fact and my disproven assumption suggested to me that somewhere something in LDAP-realm-land was opening up a direct socket by hand (i.e. not using something like an HttpUrlConnection
or some other abstraction). Sure enough, the Sun-authored LDAP-JNDI bridge does just this; the URI that is passed in is socket://somehost:389
.
So from all this despite the fact I haven't set up any proxy information, configured anything or done anything other than use straight defaults, it turns out that the JDK attempts to use a SOCKS proxy. See the setImpl()
method in java.net.Socket
and line 364 or so of SocksSocketImpl.java
and trace it through for details.
(This finally suggests that I might be able to skip this whole codepath by simply adding a socksNonProxyHosts=*
system property to the mix. Jeez, shouldn't that behavior be the default?)
As a result--and again, taking it on faith that the DefaultProxySelector
for some reason is having its hasSystemProxies
field set to true
despite no changes in configuration by me or GlassFish, a garden variety socket created by a garden variety Sun LDAP connection is causing a native lookup for a SOCKS proxy server. Maybe it's just me, but that strikes me as madness.
So does anyone reading this--perhaps you are on the JDK team, or know someone who is, or know the history here--know why the default implementation of java.net.Socket
always looks for a SOCKS proxy?
Update: I can see that the answer would be: so that if you have a system proxy set somewhere, and it's SOCKS-enabled, all stuff flows through it. But if the default value of java.net.useSystemProxies
is false
, as it is, then what's the point of hunting (by default) for a SOCKS proxy?
The default socket implementation is SocksSocketImpl
because the JRE might have been externally configured to use SOCKS via the system properties -DsocksProxyHost
and -DsocksProxyPort
or ProxySelector.setDefault()
or via the default ProxySelector
installed by the JRE.
PlainSocketImpl
does not consult these properties or classes (because it is a plain socket and should not know anything about proxies) and so these external configurations would be ignored were not SocksSocketImpl
always invoked to check on them. I agree it seems odd that you get a SocksSocketImpl
when nobody has said anything about SOCKS to the JRE, but I guess the architecture of java.net.Socket
and ProxySelector
does not allow it to preselect the correct impl at the time of Socket instantiation.
I think you (or whoever is leading the current line of investigation on that Glassfish bug) may be going about this the wrong way: rather than trying to subvert the way the JRE selects socket impls and proxies, why not fix the default ProxySelector
and/or OS native calls so that when Java does its standard queries for information about proxies, things don't break. I think the fix is in the guts of the proxy lookup process and classes, not higher up.
Maybe another way to ask what I'm asking is: if DefaultProxySelector
can tell me the proxies to use for a socket connection, why doesn't Socket invoke that first to help it pick a sane implementation?
I think the problem is that java.net.Socket
supports several programming models. There's the obvious new Socket(host, port)
constructor, but there's also a default constructor new Socket()
which can be constructed first and then at some arbitrary time in the future can have its connect(SocketAddress)
method called.
Since part of the criteria that a ProxySelector
can use to determine whether a proxy should be used or not is the name of the remote host and remote port to be connected to (that is, the information supplied to connect
), the java.net.Socket
constructor is too early to know whether a proxy will be needed or not.
For whatever reason (can we just assume historical? / backwards compatibility reasons? :), the constructor for java.net.Socket
the only place where the impl set, and, like I said, that is in some cases too early to tell whether a proxy will be needed or not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With