Jsoupでプロキシを使えるようにする

Androidをいじっています。

開発用端末がしているSSL通信を見るために、fiddler2 *1という
キャプチャソフトのプロキシを経由させようとして手間取ったしたので記録。

ルート証明書をインストールして*2、開発用端末のWiFi設定でプロキシを設定して
動作確認したところ、プロキシを経由していないのか、通信がキャプチャされませんでした。

Androidのプロキシ設定の注意点

なぜキャプチャされないのか?原因はAndroidの仕様でした。
Androidでは、WiFiの設定画面でプロキシの設定ができますが、この設定が適用されるのは標準ブラウザ(とWebView)だけだそうです。
アプリがプロキシを使うには、各アプリのパッケージ内で処理を実装する必要があります。

びっくり。

これ、プロキシが必須の環境下でAndroidバイスを使う場合は、使用する各アプリがプロキシに
対応していることを事前に確認しないと、後で大変な事になりそうですね。

とにかく、アプリの通信をFiddler2で見るには、アプリのソースに手を入れてプロキシに対応させないと不可能。

Jsoupがプロキシで通信できない

通信にJsoup*3というライブラリを使っていたので、公式ドキュメントでproxyを設定するメソッドを探したところ、そもそもこのライブラリではプロキシが使えないようです。ウソだろって感じですが。


別のライブラリに切替えようか迷いましたが、Jsoupはソース全体が小さくまとまっているので、手を入れやすく、またライブラリを切り替えるのもしんどいので、Jsoupでプロキシを使えるようにライブラリ側を修正することにしました。


その成果物をgithubに上げたので、同じような状況になってしまった人はどうぞ。
特にAndroidのライブラリに依存しているわけではないので、サーバーで動かす場合なんかにも使えます。
github
https://github.com/yatt/jsoup

使い方

通常のJsoupの通信に、 .proxy( ... ) という呼び出しを追加するだけです。
BASIC認証には対応していません。

String proxyHost = "XXX.XXX.XXX.XXX";
int proxyPort = 8888;

Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));

Response res = Jsoup.connect("http://example.com/")
    .proxy(proxy)
    .data("parameter01", "XXXXXX")
    .method(Method.POST)
    .execute()

Document doc = res.parse();

ボタンを押したら通信するような処理はこんな感じになります。

Button btnViaProxy = (Button)findViewById(R.id.btnViaProxy);
btnViaProxy.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        (new AsyncTask<Void, Void, String >() {
            @Override
            protected String doInBackground(Void... params) {
                final String address = "XXX.XXX.XXX.XXX";
                final int port = 8888;
                Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(address, port));

                try
                {
                    
                    Response res = Jsoup.connect("http://example.com/")
                            .proxy(proxy)
                            .method(Method.GET)
                            .execute()
                            ;
                    return res.body();
                }
                catch (Exception e)
                {
                    return e.getMessage();
                }
            }
            protected void onPostExecute(String result) 
            {
                    Log.v("result", result);
            }
        }).execute();
    }
});