01 Nov 2014

PhantomJS でネットワークのデバッグと SSL handshake failed

PhantomJS で itunes の url に接続しようとするとエラーになり、デバッグしたいので方法を調べた話。

まず、SSL の通信なので、別のプロセスから tcpdump などで通信をキャプチャすることは難しい。

方法の一つとして、--debug=yes というコマンドラインオプションを渡すと、詳細なログをだしてくれるようだ。(ドキュメント には載っていないオプションだった)

$ phantomjs --debug=yes crawl.js
2014-11-01T15:53:28 [DEBUG] CookieJar - Created but will not store cookies (use option '--cookies-file=' to enable persisten cookie storage)
2014-11-01T15:53:28 [DEBUG] Phantom - execute: Configuration
2014-11-01T15:53:28 [DEBUG]      0 objectName : ""
...

あるいは面倒だけど、onResourceRequested などのイベントを片っ端から Listen して自分でログに落とすこともできる。

こんな感じ:

var page = new WebPage(),
    system = require('system');

page.onResourceRequested = function (requestData, networkRequest) {
    system.stderr.writeLine('[onResourceRequested]');
    system.stderr.writeLine('Request (#' + requestData.id + '): ' + JSON.stringify(requestData));
};

page.onResourceReceived = function(response) {
    system.stderr.writeLine('[onResourceReceived]');
    system.stderr.writeLine('Response (#' + response.id + ', stage "' + response.stage + '"): ' + JSON.stringify(response));
};

page.onLoadStarted = function() {
    var currentUrl = page.evaluate(function() {
        return window.location.href;
    });
    system.stderr.writeLine('Current page ' + currentUrl + ' will gone...');
    system.stderr.writeLine('Now loading a new page...');
};

page.onLoadFinished = function(status) {
    system.stderr.writeLine('[onLoadFinished]');
    system.stderr.writeLine('Status: ' + status);
};

page.onNavigationRequested = function(url, type, willNavigate, main) {
    system.stderr.writeLine('[onNavigationRequested]');
    system.stderr.writeLine('Trying to navigate to: ' + url);
    system.stderr.writeLine('Caused by: ' + type);
    system.stderr.writeLine('Will actually navigate: ' + willNavigate);
    system.stderr.writeLine('Sent from the page\'s main frame: ' + main);
};

page.onResourceTimeout = function(request) {
    system.stderr.writeLine('[onResourceTimeout]');
    console.log('Response (#' + request.id + '): ' + JSON.stringify(request));
};

page.onResourceError = function(resourceError) {
    system.stderr.writeLine('[onResourceError]');
    system.stderr.writeLine('Unable to load resource (#' + resourceError.id + 'URL:' + resourceError.url + ')');
    system.stderr.writeLine('Error code: ' + resourceError.errorCode + '. Description: ' + resourceError.errorString);
};

page.onError = function(msg, trace) {
    var msgStack = ['ERROR: ' + msg];
    if (trace && trace.length) {
        msgStack.push('TRACE:');
        trace.forEach(function(t) {
            msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function +'")' : ''));
        });
    }

    system.stderr.writeLine('[onError]');
    system.stderr.writeLine(msgStack.join('\n'));
};

page.open('https://itunes.apple.com/en/app/instagram/id389801252?mt=8', function (status) {
    console.log('Finished');
});

Web Page Module | PhantomJS

ちゃんと調べていないが、CasperJS や Nightmarejs で同等のことをやろうとすると、後者じゃないとだめかもしれない。

SSL handshake failed

ちなみに itunes の url に接続できなかったのは SSL Protocol が原因だった。上記のスクリプトを実行するとこんなメッセージが出る。

2014-11-01T15:59:40 [DEBUG] Network - Resource request error: 6 ( "SSL handshake failed" ) URL: "https://itunes.apple.com/en/app/instagram/id389801252?mt=8"

または

[onResourceError]
Unable to load resource (#1URL:https://itunes.apple.com/en/app/instagram/id389801252?mt=8)
Error code: 6. Description: SSL handshake failed

curl でアクセスしてみると TLS 1.2 を使っているのがわかる

$ curl -v 'https://itunes.apple.com/en/app/instagram/id389801252?mt=8'
...
* TLS 1.2 connection using TLS_RSA_WITH_AES_256_CBC_SHA
* Server certificate: itunes.apple.com
...

で、PhantomJS のデフォルトのプロトコルは SSLv3 らしい。

Command Line Interface | PhantomJS

--ssl-protocol=tlsv1 (または any) とオプション指定してあげると解決。

$ phantomjs --ssl-protocol=tlsv1 crawl.js

これも、CasperJS や Nightmarejs でも同様だと思う。

もちろんブラウザでも、curl でも問題ないのに PhantomJS でだけ接続できないし、エラーメッセージもほぼ無いしで結構はまった。

ちなみに POODLE 問題をうけて、1.9.8 以降はデフォルトが TLSv1 に変更になった ようだ。

PhantomJS Cookbook
PhantomJS Cookbook
posted with amazlet at 14.11.01
Packt Publishing (2014-06-12)