ktty1220 / cheerio-httpcli

iconvによる文字コード変換とcheerioによるHTMLパースを組み込んだNode.js用HTTPクライアントモジュール
MIT License
262 stars 28 forks source link

Cookieが新しいインスタンスに引き継がれてしまう #12

Closed iciclize closed 8 years ago

iciclize commented 8 years ago

以下のコードは、クライアントからPOSTリクエストを受け取って、クライアントの代理でとあるサイトのログインフォームにユーザーIDとパスワードを入力するものなのですが、1回でもログインに成功すると、その後var client = require('cheerio-httpcli');とやり直しても、前のCookieのセッション情報が残ってしまい、どんなID/パスワードを入力してもログインできるようになってしまいました。Node.jsを再起動するとセッション情報はリセットされるのですが、require('cheerio-httpcli')では新しいインスタンスを作ったことにならないのでしょうか?

http.createServer(function (req, res) {
    var client = require('cheerio-httpcli');
    var postdata = '';

    client.headers['Authorization'] = authorization;

    req.setEncoding('utf8');
    req.on('data', function (data) {
        postdata += data;
    });
    req.on('end', function () {
        postdata = qs.parse(postdata);

        client.fetch('https://ログイン画面', function (err, $, response, body) {
            $('#login_id_0').val(postdata.userid);
            $('#password_0').val(postdata.password);
            $('#login_form_0').submit({}, function (err, $, response, body) {
                client.fetch('http://ログイン後に見られるようになる画面', function (err, $, response, body) {
                    console.log(response.cookies); 
                    res.writeHead('Content-Type', 'text/html');
                    res.end(body);
                });
            });
        });

    });

}).listen(3015);
ktty1220 commented 8 years ago

cheerio-httpcliはシングルインスタンスで動作するモジュールなので、そのプロセスが動作している間は各種設定やクッキーを共有して保持し続けます。

また、Node.jsのrequireは1回実行したら2回目以降はキャッシュが使用される仕様になっています。

参考URL: http://qiita.com/kacchan6@github/items/3b76c0db790c8c1f2d06

ただ、cheerio-httpcliの内部でrequireしているrequestモジュールがクッキーを管理しているため、上記URLで説明されている対策はおそらくcheerio-httpcliでは効かないような気がします(require.cache内のcheerio-httpcli絡みのモジュールのキャッシュを全部消せばいけそうな気もしないでもないですが未検証です)。

解決策としては、cheerio-httpcliによるクライアント代理処理を外部スクリプトとして切り出して、サーバーからchild_process.spawn()などで起動する(stdin経由で引数的な情報を渡してstdout経由で処理結果を受け取る)

といったあたりになるかと思います。

また、バージョン0.6.7reset()というメソッドを実装しました。cilent.reset()とかすると各種プロパティやクッキーが初期化されます。

しかし、cheerio-httpcliはシングルインスタンスという前提があるので、代理ログインといった機能を有するサーバーでクライアントからのアクセスの度にreset()を実行したとしても、その後、ほぼ同タイミングで別ユーザーからそれぞれアクセスがあった場合、片方がもう一方のログイン情報を引き継いでしまう、もしくは今ログイン中のユーザーのログイン情報がクリアされてしまうといった可能性があり、今回のケースの解決には結びつかないかと思います(実装してから気付きました)。

なので、先ほど挙げた解決策が現実的かと思います(結構面倒ですが)。

将来的には複数インスタンスで動作するような形にすることも考えられますが、割と大改造になりそうなので、取り急ぎの対応策としてはこんな感じです。

iciclize commented 8 years ago

よく分かりました。迅速なコメントありがとうございます。