WebSocketとCGI::Sessionのハイブリッドシステムで起こる怪奇現象
以前作った「Perl Net::WebSocket::Server + CGI::Session で認証付きソケット作成」のシステムで、接続中の全セッションのタイムアウト処理にCGI::Session->load()を使ってタイムアウト判別を行っていました。
こんなシステムで、セッション管理と有効期限の判別にCGI::Sessionを利用。
誰かがWeb認証経由でWebSocketへアクセスする
-> セッションの有効期限が切れてる他のメンバーがいれば強制切断
セッションチェック部分
sub check_session {
# IPアドレスとポートからセッションIDを取り出す
my $conn = $_ && $_->can("socket") || !$_[1] ? $_: $_[0];
my $addr = $conn->socket->peerhost(). ":". $conn->socket->peerport();
my $sid = $_ && $_->can("socket") || !$_[1] ? $clients{$addr} : $_[1];
my $session = CGI::Session->load(undef, $sid, {Directory=>'/.session'});
# 生存チェック
if (!(defined $session->id) || $sid ne $session->id) {
$conn->disconnect($conn->socket); #お前はもう死んでいる
print "dead session: ",$sid,"\n";
undef $user_ids{$sid};
undef $clients{$addr}; #セッションIDも消す
return 0;
}
if(!defined($user_ids{$sid})){
$user_ids{$sid} = $session->param('u');
}
return 1;
}
しかしセッションの有効時間中に他のアカウントがアクセスすると、接続中の全セッションの_SESSION_ATIMEが更新されてしまい誰もタイムアウトになりません。
「load()
関数は有効期限を延長させない」と過信していたのが災いしました・・・。
やはり、「Node.jsでPerl CGI::Sessionで生成されたSessionIDからセッションを取得する」でやったように、perlでもCGI::Sessionを介さず、直接セッションファイルを読んだほうが確実かもしれませんね・・・。
こんな用途にCGI::Session使う人は少ないと思いますが、アクセス時間を更新しないメソッドを追加して欲しいものです。
追記
CGI-Sessionを使わずにセッションを読む
sub getSession {
my ($sid, $dir) = @_;
my $filename = $dir . '/cgisess_' . $sid;
my $text;
if (-e $filename) {
open (my $FILE, "<$filename") or die;
$text = do { local $/; <$FILE> };
close($FILE);
}
if($text && length($text) > 0){
my $session = eval($text);
#現在時刻
my $now = time;
#制限時間
my $limit = $session->{_SESSION_ATIME} + $session->{_SESSION_ETIME};
#現在時刻が制限時間を超えていたらアウト
if($now >= $limit){
return undef;
}
return $session;
} else {
return undef;
}
}
ううんダサい。。。他にいい方法はないものか