TsgcWebSocketHTTPServer | セッション

HTTPはステートレスプロトコルです(少なくともHTTP 1.1まで)。クライアントはファイルをリクエストし、サーバーはレスポンスを送信し、接続が閉じられます(keep-aliveを有効にすることで接続が即座に閉じられないようにできますが、これはこの記事の範囲外です)。セッションを使用すると、クライアントに関する情報を保存できます。例えば、クライアントのログイン時に使用できます。任意の一意のセッションIDを使用し、セッションリストを検索して既存のセッションがあるかどうかを確認し、なければ新しいセッションを作成できます。セッションは一定期間の非アクティブ後に破棄するか、クライアントのログアウト後に手動で破棄できます。

 

設定

TsgcWebSocketHTTPServerには、サーバーコンポーネントでセッションを有効/無効にするいくつかのプロパティがあります。最も重要なものは次のとおりです。

 

プロパティ 説明
SessionState Tこれはセッションを使用するために最初に有効にする必要があるプロパティです。このプロパティが有効になっていない場合、セッションは機能しません。

SessionTimeout

ここでは、セッションがアクティブになる最大時間(ミリ秒)としてゼロより大きい値を設定する必要があります。
AutoStartSession セッションは自動的に作成(AutoStartSession = true)または手動で作成(AutoStartSession = false)できます。セッションが自動的に作成される場合、サーバーはアクティブなセッションが格納されているかどうかを確認するために RemoteIP を一意識別子として使用します。
SessionClass Optional. The class the server uses when it creates a new session. Set it to your own TIdHTTPSession descendant to store your own data inside every session. Must be set before the server is activated.
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;

Read the Current Session

Once a session exists, the server attaches it to every request that carries the session cookie. Read it from ARequestInfo.Session, which is nil when the request has no session.


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;

Use Your Own Session Class

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;

Use Your Own Session List

If you need full control over how sessions are stored, for example keeping them in a database or sharing them between several servers, assign your own list to the SessionList property, before the server is activated. Descend from TIdHTTPDefaultSessionList and override the virtual CreateSession method. The server calls it internally from CreateUniqueSession, so the unique session ID is still generated for you.

When you assign your own SessionList, the SessionClass property no longer applies, because your list decides which class to create.


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;

A Note About OnCreateSession

OnCreateSession イベントを使うと、セッションインスタンスを自分で返すことができますが、そのインスタンスにセッション ID は割り当てられません。単純なコンストラクタでセッションを作成すると、セッション ID とセッションクッキーは空になり、次のリクエストでそのセッションを二度と見つけられなくなります。代わりに SessionClass を使ってください。こちらの方がシンプルで、すべてを自動的に処理してくれます。

Also, do not call SessionList.CreateUniqueSession inside OnCreateSession. That method already adds the new session to the list, and the server adds it again when your handler returns, so the same session ends up twice in the list.