通信先によってプロキシを切り替える(社外はプロキシ、社内はダイレクトみたいな)要件に出くわしたので、この機にJavaのプロキシの設定方法をいろいろ調べてみた。
System.setPropertyで設定したり、URLのopenConnection時に設定したり、ProxySelectorを使ったり、いろいろな方法があるようだ。
パケットキャプチャを導入すると、プロキシが使われているか簡単にチェックできる。本例ではWiresharkを利用。
以下はUbuntuでのインストールと簡易な実行例。
// インストール
$ sudo apt-get install tshark
// eth0のポート80とプロキシで使う予定のポート(下記例では8080)のhttp通信を監視してみる
$ sudo tshark -i eth0 -R "(tcp.port == 80 or tcp.port == 8080 ) and http"
これでリクエスト時に流れるIPを見てプロキシが利用されていることを確認できる。
練習用にApacheでプロキシサーバを立てておく。
Ubuntuだとhttpd.confに以下のような感じで記述する。LoadModuleのパスとかログの出力先は環境に応じて適宜変更すること。
NameVirtualHost *:8080
Listen 8080
LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so
<VirtualHost *:8080>
ProxyRequests On
ProxyVia On
CustomLog /var/log/apache2/proxy.log combined
<Proxy *>
Order deny,allow
Deny from all
Allow from 127.0.0.1 192.168.
</Proxy>
</VirtualHost>
プログラムを実行するマシンとプロキシサーバにするマシンが同一の場合は、パケットキャプチャで確認し辛いので、CustomLogのところで設定したログファイルを見てプロキシが利用されていることを確認する。
我が家のテスト環境では192.168.1.33にプロキシサーバを作った。以後のサンプルコードではプロキシが192.168.1.33:8080で稼働しているものとして扱う。
これで準備万端。さっそくプロキシ経由でHTTP通信をするJavaのコードを書いてみる。
プロキシはSystem.setPropertyで設定できる。
// プロキシの情報を設定
System.setProperty("proxySet", "true");
System.setProperty("proxyHost", "192.168.1.33");
System.setProperty("proxyPort", "8080");
// 以下の通信は上で設定したプロキシが利用される
URL url = new URL("http://www.yahoo.co.jp/")
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
上記のようにプロキシ情報を設定した後で、URLのopenConnectionを利用すると、指定したプロキシで通信が行われる。
HttpClientを利用した場合は、この方法ではプロキシは反映されない。
System.setPropertyを使うとシステム全体に設定が反映されてしまうので、通信ごとに別のプロキシに切り替えるような用途には向かない。
個別にプロキシを適用する場合は、URLのopenConnection時に引数にProxyクラス(1.5より導入)を指定する。
URL url = new URL("http://www.yahoo.co.jp/");
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("192.168.1.33", 8080));
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
HttpClientでのプロキシ利用は、バージョンによって書き方がかなり違う。
バージョン3系の場合はHostConfigurationのsetProxyを利用する。
HttpClient client = new HttpClient();
client.getHostConfiguration().setProxy("192.168.1.33", 8080);
バージョン4系の場合はHttpParamsのsetParameterで設定する。
HttpClient client = new DefaultHttpClient();
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
new HttpHost("192.168.1.33", 8080));
複数のHTTPClientのインスタンスを生成し、それぞれに別のプロキシを指定すれば、複数のプロキシを使い分けることはできそう。
システムのデフォルトのプロキシを利用する場合は、ProxySelectorRoutePlannerに設定する。
// プロキシの情報を設定
System.setProperty("proxySet", "true");
System.setProperty("proxyHost", "192.168.1.33");
System.setProperty("proxyPort", "8080");
DefaultHttpClient client = new DefaultHttpClient();
// デフォルトのProxySelectorを利用するよう設定
ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
client.getConnectionManager().getSchemeRegistry(), ProxySelector.getDefault());
client.setRoutePlanner(routePlanner);
上記のように記述すれば、HttpClientでもSystem.setPropertyで設定したプロキシが利用される。
コード内に出てくるProxySelectorに関する説明は後述。
java.netパッケージにはProxySelectorというプロキシを選択するためのクラスが存在する。
これを利用すれば通信するURLのプロトコルやホストなどの情報に応じてプロキシを変更することができる。
ProxySelector
http://java.sun.com/javase/ja/6/docs/ja/api/java/net/ProxySelector.html
例として、Yahooに繋ぐ時はプロキシAを、Googleに繋ぐ時はプロキシBを、その他の場合はプロキシを用いず通信するようなProxySelectorを書いてみる。
class MyProxySelector extends ProxySelector {
// yahooかgoogleの場合だけプロキシを返すメソッド
public List<Proxy> select(URI uri) {
String proxyHost = null;
// Yahooだったら
if ("www.yahoo.co.jp".equals(uri.getHost()))
proxyHost = "192.168.1.33";
// Googleだったら
else if ("www.google.co.jp".equals(uri.getHost()))
proxyHost = "192.168.1.36";
if (proxyHost != null)
return Arrays.asList(new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyHost, 8080)));
else
return Arrays.asList(Proxy.NO_PROXY);
}
// Proxyへの接続に失敗すると呼ばれるメソッド
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
ioe.printStackTrace();
}
}
上記のクラスをProxySelector.setDefaultでデフォルトに設定すると、それ以降の通信は指定したProxySelectorからプロキシをselectすることになる。
// 上記のカスタムクラスをデフォルトに設定
ProxySelector.setDefault(new MyProxySelector());
// Yahooに繋ぐ通信は、192.168.1.33:8080が利用される
URL url1 = new URL("http://www.yahoo.co.jp/");
HttpURLConnection conn1 = (HttpURLConnection) url1.openConnection();
System.out.println(conn1.getResponseCode());
// Googleに繋ぐ通信は、192.168.1.36:8080が利用される
URL url2 = new URL("http://www.google.co.jp/");
HttpURLConnection conn2 = (HttpURLConnection) url2.openConnection();
System.out.println(conn2.getResponseCode());
ProxySelectorのselectメソッドはListで複数のプロキシの情報を返す。1つ目のプロキシが死んでいたら自動で次のプロキシが利用される。
システムのデフォルトのプロキシを適用する際のコードで既出だが、HttpClientの場合はProxySelectorRoutePlannerを用いることで、任意のProxySelectorを利用することができる。
DefaultHttpClient client = new DefaultHttpClient();
ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
client.getConnectionManager().getSchemeRegistry(), new MyProxySelector());
client.setRoutePlanner(routePlanner);
Java ネットワークとプロキシ
http://java.sun.com/javase/ja/6/docs/ja/technotes/guides/net/proxies.html
HttpClient Tutorial
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/