Node.jsでPerl CGI::Sessionで生成されたSessionIDからセッションを取得する

またアホな事を試してみる。

Windows 10 環境のPCでのテスト
Perl v5.20.1
Node v7.2.1

PerlのCGIが動く既存サーバー内でNode.jsのWebSocketを追加して共存させることを想定。
※もしCGI::Session::MySQLなどで動いているならファイルではなく直接MySQLを読み込んで同じことすればいいかな。

とりあえずセッションファイルを読み込んでみる

とりあえず読み込むところから。
セッションファイルはcgisess_ + セッションIDのファイル名で、CGI::Sessionの使用時に指定したパスに保存されているので
後からファイル名部分を加工して読み取ろうと思う。

test.js
var fs = require('fs');
fs.readFile('C:\\.session\\cgisess_0adf7392151ba60afcc05e65f398df23', 'utf8', function(err, text) {
    if(text){
        console.log(text);
    } else {
        console.log(err);
    }
});
出力結果
$D = {'_SESSION_ETIME' => 7200,'_SESSION_ID' => '0adf7392151ba60afcc05e65f398df23','_SESSION_REMOTE_ADDR' => '::1','_SESSION_ATIME' => 1481824332,'_SESSION_CTIME' => 1481824330};;$D

セッションファイルの中はPerlのハッシュ定義みたいなのが書いてある。

これをごにょごにょしてjson化してオブジェクト化してみる。

test.js
var fs = require('fs');
fs.readFile('C:\\.session\\cgisess_0adf7392151ba60afcc05e65f398df23', 'utf8', function(err, text) {
    if(text){
        var m = text.match(/^\$D = (.+);;\$D$/);
        var data = m[1].replace(/=>/g, ':').replace(/'/g, '"');
        console.log(JSON.parse(data));
    } else {
        console.log(err);
    }
});
出力結果
{ _SESSION_ETIME: 7200,
  _SESSION_ID: '0adf7392151ba60afcc05e65f398df23',
  _SESSION_REMOTE_ADDR: '::1',
  _SESSION_ATIME: 1481824332,
  _SESSION_CTIME: 1481824330 }

オブジェクト化もできた。

CGI::Sessionの説明書きによると、各項目は以下の通り。

_SESSION_ID
そのデータに対するセッションidです。id()メソッドを通してアクセスできます。

_SESSION_CTIME
セッション作成時刻です。ctime()メソッドを通してアクセスできます。

_SESSION_ATIME
セッションの最終アクセス時刻です。atime()メソッドを通すことでアクセスできます。

_SESSION_ETIME
もしあれば、セッションの有効期限を示します。expire()メソッドを通してアクセス できます。

_SESSION_REMOTE_ADDR
そのセッションを作成したユーザーのIPアドレスです。remote_addr()メソッドを通して アクセスできます。

_SESSION_EXPIRE_LIST
もしあれば、それぞれの有効期限付き公開レコードに関する有効期限の情報を入れた もう一つの内部的なハッシュテーブルとなります。このテーブルは2つの引数を持つ 方のexpires()メソッドの文法を用いることによって更新されます。

Node.js側の現在時刻と比較する場合は_SESSION_ETIME _SESSION_ATIME _SESSION_CTIMEをそれぞれ1000倍してミリ秒化してDate()に読み込ませれば、現在時刻との比較ができそう。

関数化

function getSession(sid){
    var fs = require('fs');
    var text = fs.readFileSync('C:\\.session\\cgisess_' + sid, 'utf8');
    if(text){
            var m = text.match(/^\$D = (.+);;\$D$/);
            var data = m[1].replace(/=>/g, ':').replace(/'/g, '"');
            var session = JSON.parse(data);
            session._SESSION_CTIME *= 1000;
            session._SESSION_ATIME *= 1000;
            session._SESSION_ETIME *= 1000;
            //現在時刻
            var now = new Date();
            //制限時間
            var limit = new Date(session._SESSION_ATIME + session._SESSION_ETIME);
            //現在時刻が制限時間を超えていたらアウト
            if(now.getTime() >= limit.getTime()){
                return undefined;
            }
            return session;
    } else {
        return undefined;
    }
}

これでPerlのCGIとNode.jsで動かしているWebアプリケーションで同じセッションIDを共有できちゃうかもしれない。

わくわく

追記
Perl側でSessionにSetしたユーザーの情報もこのファイルにハッシュとして保存されるているので、Node側で取得できるようオブジェクトで返しています。IPアドレスの比較をしたいときは_SESSION_REMOTE_ADDRを見てで自前でやる感じで(手抜き)
fsオブジェクトがreadFileSyncでファイルをopenする時にread onlyにするかどうかの情報が無かったのでロックの可能性の確認はできてないので、もし気になるなら、fs.open(path, "r")で開いて下さい。