问题很简短。 为什么支持SOCKS的套接字实现是抽象java.net.Socket
类实现的默认选择?天真地我期望java.net.PlainSocketImpl
。
背景有点复杂。
我正试图杀死GLASSFISH-12213(或者我真的想要解决它)。错误本身的细节并不是很重要 - 有一个本地库不是线程安全的,并且来自GlassFish编写的LDAP领域的一些并发使用会使JVM崩溃。
为了解决这个问题,我开始倒退了:我可以避免首先调用本机库吗?这导致我出于各种原因来查看负责sun.net.spi.DefaultProxySelector
的finding an appropriate proxy (or not) for a given URI。它有一个spot in there进行本机方法调用,这就是发生JVM崩溃的地方。如果我可以避免这个电话,我会做生意。
如果我可以确保sun.net.spi.NetProperties.getBoolean("java.net.useSystemProxies")
的值为false,那么我可以避免该调用的一种方法。如果是这种情况,那么我之前提到的本机方法将不会被调用。 false
是默认值,我根本没有修改任何内容。
(所以实际上它是的情况;在我在机器上运行的GlassFish实例中证明了这一点我已经观察到了这个bug。我不确定这段代码是如何可能仍然加载的本土图书馆;这是另一天的主题。)
所以,在那条道路上,我进一步补充:在URI
电话中,scheme
DefaultProxySelector.select(uri)
传递的协议是什么?也许我可以以某种方式影响愚蠢的事情仍然以某种方式跳过原生呼叫。
事实证明,协议是socket
(我以为它可能类似于ldap
,但没有)。这个事实和我的错误假设向我提出,在LDAP-realm-land中的某个地方正在手工打开一个直接套接字(即不使用类似HttpUrlConnection
或其他抽象的东西)。果然,Sun编写的LDAP-JNDI桥就是这样做的;传入的URI是socket://somehost:389
。
所以从所有这些尽管我没有设置任何代理信息,配置任何东西或做除了使用直接默认值以外的任何事情,但事实证明 JDK尝试使用SOCKS代理。请参阅the setImpl()
method in java.net.Socket
和line 364 or so of SocksSocketImpl.java
并查看详细信息。
(这最终表明我可以通过简单地在混合中添加socksNonProxyHosts=*
系统属性来跳过整个代码路径.Jeez,这个行为不应该是默认行为吗?)
结果 - 并且再次认为DefaultProxySelector
由于某种原因java.net.Socket
有hasSystemProxies
field set to true
,尽管我或GlassFish没有改变配置,花园种类插座由花园种类Sun LDAP连接创建,导致SOCKS代理服务器的本机查找。也许这只是我,但这让我感到疯狂。
所有读过这篇文章的人 - 也许你是JDK团队的成员,或者知道某人是谁,或者知道这里的历史 - 知道为什么java.net.useSystemProxies
的默认实现总是寻找SOCKS代理?< / p>
更新:我可以看到答案是:所以,如果您在某处设置了系统代理,并且启用了SOCKS,则所有内容都会通过它。但是,如果false
的默认值为{{1}},那么SOCKS代理的搜索点(默认情况下)是什么?
答案 0 :(得分:4)
默认套接字实现为SocksSocketImpl
,因为JRE 可能已通过系统属性外部配置为使用SOCKS -D socksProxyHost
和-D socksProxyPort
或ProxySelector.setDefault()
或通过JRE安装的默认ProxySelector
。
PlainSocketImpl
不会查询这些属性或类(因为它是一个普通的套接字,不应该对代理有任何了解),因此这些外部配置将被忽略不是SocksSocketImpl
总是被调用来检查它们。我同意,当没有人向JRE提及有关SOCKS的任何内容时,你得到SocksSocketImpl
似乎很奇怪,但我想java.net.Socket
和ProxySelector
的架构不允许它预先选择正确的在Socket实例化时的impl。
我认为你(或任何领导当前调查Glassfish bug的人)可能会采用错误的方式:而不是试图颠覆JRE选择套接字impls和代理的方式,为什么不修复默认值ProxySelector
和/或OS本机调用,以便当Java对代理的信息进行标准查询时,事情不会中断。我认为修复是在代理查找过程和类的内部,而不是更高。
也许另一种方式来问我要问的是:如果DefaultProxySelector
可以告诉我用于套接字连接的代理,为什么Socket不首先调用它来帮助它选择理智执行?
我认为问题在于java.net.Socket
支持多种编程模型。有明显的new Socket(host, port)
构造函数,但也有一个默认的构造函数new Socket()
可以先构造,然后在将来的任意时间可以有connect(SocketAddress)
方法叫做。
由于ProxySelector
可用于确定是否应使用代理的部分标准是要连接的远程主机和远程端口的名称(即提供给{的信息) {1}}),connect
构造函数为时尚早,无法知道是否需要代理。
无论出于何种原因(我们可以假设历史?/向后兼容性原因?:),java.net.Socket
的构造函数是impl集的唯一位置,就像我说的那样,在某些情况下太早了判断是否需要代理。