10

質問は簡単です。 SOCKS 対応のソケット実装が、抽象クラスの実装のデフォルトの選択肢であるのはなぜですか? java.net.Socket 素朴に私は期待していjava.net.PlainSocketImplます。

背景はもう少し複雑です。

私はGLASSFISH-12213を殺そうとしています(または実際に回避しようとしています)。バグ自体の詳細はあまり重要ではありません。スレッド セーフではないネイティブ ライブラリがあり、GlassFish で作成された LDAP レルムからそれを同時に使用すると、JVM がクラッシュします。

それを回避するために、私は逆方向に作業を始めました: そもそもネイティブ ライブラリが呼び出されないようにすることはできますか? これにより、さまざまな理由で、特定の URI に対して適切なプロキシを見つける (またはしない) ことsun.net.spi.DefaultProxySelectorを担当するを調べました。ネイティブ メソッド呼び出しを行う場所がそこにあり、そこで JVM クラッシュが発生しますもし私がその電話を避けることができれば、私は仕事をしていただろう.

sun.net.spi.NetProperties.getBoolean("java.net.useSystemProxies")その呼び出しを回避できる 1 つの方法は、値がfalseであることを確認できる場合です。その場合、先ほど述べたネイティブ メソッドは呼び出されません。 falseがデフォルトであり、この点に関しては何も変更していません。

(つまり、実際にそうです。私がバグを観察したマシンで実行されている GlassFish インスタンスで明らかに証明されています。したがって、このコードがどのようにしてネイティブ ライブラリをまだロードしている可能性があるのか​​ はわかりません。それは別の日の話題です。 .)

それで、そのパスをパントして、さらにバックアップしました:呼び出しでどのプロトコルが渡されURI schemeましたか? DefaultProxySelector.select(uri)たぶん、私はどういうわけか愚かなことに影響を与えて、そのネイティブコールをスキップすることができます.

結局のところ、プロトコルはsocket(おそらく のようなものだと思っていましたが、そうではldapありませんでした)。この事実と私の反証された仮定は、LDAP-realm-land のどこかで直接ソケットが手動で開かれていることを示唆していました (つまり、HttpUrlConnectionまたは他の抽象化のようなものを使用していません)。案の定、Sun が作成した LDAP-JNDI ブリッジはまさにこれを行います。渡される URI はsocket://somehost:389.

したがって、これらすべてから、プロキシ情報を設定していない、何も構成していない、または直接のデフォルトを使用する以外のことをしていないにもかかわらず、JDK が SOCKS プロキシを使用しようとしていることがわかります。 詳細については、 364 行目あたりsetImpl()メソッドをjava.net.Socket参照し、トレースしてください。SocksSocketImpl.java

(これは、システム プロパティをミックスに追加するだけで、このコードパス全体をスキップできる可能性があることを最終的に示唆していますsocksNonProxyHosts=*。うわあ、その動作がデフォルトであってはなりませんか?)

その結果、また、私や GlassFish による構成の変更がないにもかかわらずDefaultProxySelector、なんらかの理由で のhasSystemProxiesフィールドが に設定されていると信じてtrue、さまざまな Sun LDAP 接続によって作成されたさまざまなソケットがネイティブ ルックアップを引き起こしています。 SOCKS プロキシ サーバー用。それは私だけかもしれませんが、それは狂気のように私を襲います.

では、これを読んでいる人なら誰でも、おそらく JDK チームに所属している、あるいは JDK チームに所属している人を知っている、またはここで歴史を知っている人は、 のデフォルトの実装がjava.net.Socket常に SOCKS プロキシを探す理由を知っていますか?

更新:答えは次のようになることがわかります。システムプロキシがどこかに設定されていて、SOCKS が有効になっている場合、すべてのものはそれを通過します。しかし、 のデフォルト値がjava.net.useSystemProxiesそのままfalseである場合、(デフォルトで) SOCKS プロキシを探すポイントは何ですか?

4

1 に答える 1

4

デフォルトのソケット実装はSocksSocketImpl、JREがシステム プロパティ -Dおよび -Dを介して、またはJRE によってインストールされた デフォルトを介して、SOCKS を使用するように外部的に構成されている可能性があるためです。socksProxyHostsocksProxyPortProxySelector.setDefault()ProxySelector

PlainSocketImplはこれらのプロパティまたはクラスを参照しませ(これは単純なソケットであり、プロキシについて何も認識しないため)。したがって、これらの外部構成は無視さSocksSocketImplれ、それらをチェックするために常に呼び出されるとは限りません。JRE に対して SOCKS について誰も何も言わなかったのに、奇妙に思われることに同意しますが、ソケットのインスタンス化時に正しい impl を事前に選択することを許可していない アーキテクチャだとSocksSocketImpl思います。java.net.SocketProxySelector

あなた (または Glassfish のバグに関する現在の調査を主導している人物) は、これについて間違った方向に進んでいる可能性があると思います: JRE がソケット実装とプロキシを選択する方法を覆そうとするのではなく、デフォルトProxySelectorおよび/またはOS ネイティブ呼び出しにより、Java がプロキシに関する情報を求めて標準クエリを実行するときに問題が発生しません。修正は、上位ではなく、プロキシ ルックアップ プロセスとクラスの内部にあると思います。


私が求めていることを尋ねる別の方法は、次のとおりです。DefaultProxySelectorソケット接続に使用するプロキシを教えてもらえれば、Socket は最初にそれを呼び出して、適切な実装を選択できるようにしないのはなぜですか?

java.net.Socket問題は、複数のプログラミング モデルをサポートしていることだと思います。明らかなコンストラクターがありますが、最初に構築でき、その後任意の時点でそのメソッドを呼び出す ことができるnew Socket(host, port)デフォルトのコンストラクターもあります。new Socket()connect(SocketAddress)

ProxySelectorプロキシを使用するかどうかを決定するために が使用できる基準の一部は、接続先のリモート ホストとリモート ポートの名前 (つまり、 に提供される情報connect) であるため、java.net.Socketコンストラクターは早すぎて判断できません。プロキシが必要かどうか。

なんらかの理由で (単に歴史的な理由を想定できますか? / 後方互換性の理由でしょうか? :)、java.net.Socketimpl が設定される唯一の場所のコンストラクターであり、私が言ったように、場合によってはプロキシが必要かどうかを判断するには時期尚早です。か否か。

于 2013-11-01T21:31:26.640 に答える