HTTPはステートレスプロトコルです(少なくともHTTP 1.1まで)。クライアントはファイルをリクエストし、サーバーはレスポンスを送信し、接続が閉じられます(keep-aliveを有効にすることで接続が即座に閉じられないようにできますが、これはこの記事の範囲外です)。セッションを使用すると、クライアントに関する情報を保存できます。例えば、クライアントのログイン時に使用できます。任意の一意のセッションIDを使用し、セッションリストを検索して既存のセッションがあるかどうかを確認し、なければ新しいセッションを作成できます。セッションは一定期間の非アクティブ後に破棄するか、クライアントのログアウト後に手動で破棄できます。
TsgcWebSocketHTTPServerには、サーバーコンポーネントでセッションを有効/無効にするいくつかのプロパティがあります。最も重要なものは次のとおりです。
| プロパティ | 説明 |
| SessionState | Tこれはセッションを使用するために最初に有効にする必要があるプロパティです。このプロパティが有効になっていない場合、セッションは機能しません。 |
|
SessionTimeout |
ここでは、セッションがアクティブになる最大時間(ミリ秒)としてゼロより大きい値を設定する必要があります。 |
| AutoStartSession | セッションは自動的に作成(AutoStartSession = true)または手動で作成(AutoStartSession = false)できます。セッションが自動的に作成される場合、サーバーはアクティブなセッションが格納されているかどうかを確認するために RemoteIP を一意識別子として使用します。 |
| SessionClass | オプション。サーバーが新しいセッションを作成するときに使用するクラスです。すべてのセッション内に独自のデータを格納するには、独自の TIdHTTPSession 派生クラスに設定します。サーバーがアクティブになる前に設定する必要があります。 |
| SessionList | アクティブなセッションを保持するリストです。コードでセッションを検索、作成、削除するために読み取ります。セッションの格納場所を完全に制御する必要がある場合は、独自のリストを割り当てることもできます。サーバーがアクティブになる前に割り当てる必要があります。 |
TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.AutoStartSession := False;
新しいセッションを作成するには、一意の新しいセッション ID を作成する必要があります。任意の値を使用できます。使用例: クライアントが認証する場合、セッション ID としてユーザー名 + パスワード + リモート IP を使用できます。
次に、セッションリストを検索してすでに存在するか確認します。存在しない場合は新しいセッションを作成します。
新しいセッションが作成されると OnSessionStart イベントが呼び出され、セッションが閉じられると OnSessionEnd イベントが発生します。
procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo);
var
vID: String;
oSession: TIdHTTPSession;
begin
if ARequestInfo.Document = '/' then
AResponseInfo.ServeFile(AContext, 'yourpathhere\index.html')
else
begin
// check if user is valid
if not ((ARequestInfo.AuthUsername = 'user') and (ARequestInfo.AuthPassword = 'pass')) then
AResponseInfo.AuthRealm := 'Authenticate'
else
begin
// create a new session id with authentication data
vID := ARequestInfo.AuthUsername + '_' + ARequestInfo.AuthPassword + '_' + ARequestInfo.RemoteIP;
// search session
oSession := TsgcWebSocketHTTPServer1.SessionList.GetSession(vID, ARequestInfo.RemoteIP);
// create new session if not exists
if not Assigned(oSession) then
oSession := TsgcWebSocketHTTPServer1.SessionList.CreateSession(ARequestInfo.RemoteIP, vID);
AResponseInfo.ContentText := '<html><head></head><body>Authenticated</body></html>';
AResponseInfo.ResponseNo := 200;
end;
end;
end;
セッションが存在すると、サーバーはセッションクッキーを持つすべてのリクエストにそれを添付します。ARequestInfo.Session から読み取ります。リクエストにセッションがない場合、これは nil です。
procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo);
begin
if Assigned(ARequestInfo.Session) then
begin
// Content is a TStrings you can use to store your own values
ARequestInfo.Session.Content.Values['visits'] :=
IntToStr(StrToIntDef(ARequestInfo.Session.Content.Values['visits'], 0) + 1);
AResponseInfo.ContentText := 'Session ' + ARequestInfo.Session.SessionID +
' visits: ' + ARequestInfo.Session.Content.Values['visits'];
end
else
AResponseInfo.ContentText := 'No session';
end;
Content 文字列リストを使用する代わりに、すべてのセッション内に独自のフィールドを保持したい場合は、TIdHTTPSession の派生クラスを作成し、SessionClass プロパティを介してそれを使用するようサーバーに指示します。サーバーがアクティブになる前に設定してください。
サーバーはその他のすべてを引き続き処理します。一意のセッション ID を生成し、セッションクッキーを送信し、SessionTimeout を適用し、古いセッションを削除します。セッションが作成されるときに独自のフィールドを初期化したい場合は、仮想コンストラクタ CreateInitialized をオーバーライドしてください。
type
TMySession = class(TIdHTTPSession)
private
FUserName: String;
FLoginTime: TDateTime;
public
constructor CreateInitialized(AOwner: TIdHTTPCustomSessionList;
const SessionID, RemoteIP: string); override;
property UserName: String read FUserName write FUserName;
property LoginTime: TDateTime read FLoginTime write FLoginTime;
end;
constructor TMySession.CreateInitialized(AOwner: TIdHTTPCustomSessionList;
const SessionID, RemoteIP: string);
begin
inherited CreateInitialized(AOwner, SessionID, RemoteIP);
FLoginTime := Now;
end;
// configure the server before it starts
TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.SessionClass := TMySession;
TsgcWebSocketHTTPServer1.Active := True;
// and read it back in any request
procedure OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo);
begin
if ARequestInfo.Session is TMySession then
AResponseInfo.ContentText := TMySession(ARequestInfo.Session).UserName;
end;
セッションの格納方法を完全に制御する必要がある場合、たとえばデータベースに保存したり、複数のサーバー間で共有したりする場合は、サーバーがアクティブになる前に、SessionList プロパティに独自のリストを割り当てます。TIdHTTPDefaultSessionList から派生し、仮想メソッド CreateSession をオーバーライドします。サーバーは CreateUniqueSession から内部でそれを呼び出すため、一意のセッション ID は引き続き自動的に生成されます。
独自の SessionList を割り当てると、SessionClass プロパティは適用されなくなります。どのクラスを作成するかはリストが決定するためです。
type
TMySessionList = class(TIdHTTPDefaultSessionList)
public
function CreateSession(const RemoteIP, SessionID: string)
: TIdHTTPSession; override;
end;
function TMySessionList.CreateSession(const RemoteIP, SessionID: string)
: TIdHTTPSession;
begin
Result := TMySession.CreateInitialized(Self, SessionID, RemoteIP);
SessionList.Add(Result);
end;
// assign it before the server starts
TsgcWebSocketHTTPServer1.SessionList := TMySessionList.Create(nil);
TsgcWebSocketHTTPServer1.Active := True;
OnCreateSession イベントを使うと自分でセッションインスタンスを返すことができますが、それにセッション ID は割り当てられません。プレーンなコンストラクタでセッションを作成すると、セッション ID とセッションクッキーが空になり、次のリクエストでそのセッションを二度と見つけられなくなります。代わりに SessionClass を使用してください。こちらの方がシンプルで、それらすべてを自動的に処理します。
また、OnCreateSession 内で SessionList.CreateUniqueSession を呼び出さないでください。このメソッドはすでに新しいセッションをリストに追加しており、ハンドラーが戻るときにサーバーが再度追加するため、同じセッションがリストに二度登録されてしまいます。