TsgcWebSocketHTTPServer | Sesje

HTTP jest protokołem bezstanowym (przynajmniej do wersji HTTP 1.1), więc klient żąda pliku, serwer wysyła odpowiedź i połączenie jest zamykane (można włączyć keep-alive, aby połączenie nie było zamykane natychmiast, ale wykracza to poza zakres tego artykułu). Sesje umożliwiają przechowywanie informacji o kliencie, które mogą być używane na przykład podczas logowania klienta. Można użyć dowolnego unikalnego identyfikatora sesji, przeszukać listę sesji w celu sprawdzenia, czy już istnieje, a jeśli nie, utworzyć nową sesję. Sesja może zostać zniszczona po okresie bezczynności lub ręcznie po wylogowaniu klienta.

 

Konfiguracja

W komponencie TsgcWebSocketHTTPServer istnieje kilka właściwości włączających/wyłączających sesje. Najważniejsze z nich to:

 

Właściwość Opis
SessionState To pierwsza właściwość, która musi zostać włączona, aby korzystać z sesji. Bez włączenia tej właściwości sesje nie będą działać

SessionTimeout

Należy ustawić wartość większą od zera (w milisekundach) określającą maksymalny czas aktywności sesji.
AutoStartSession Sesje mogą być tworzone automatycznie (AutoStartSession = true) lub ręcznie (AutoStartSession = false). Jeśli sesje są tworzone automatycznie, serwer użyje RemoteIP jako unikalnego identyfikatora do sprawdzenia, czy istnieje aktywna sesja zapisana.
SessionClass Opcjonalne. Klasa, której serwer używa przy tworzeniu nowej sesji. Ustaw ją na własnego potomka TIdHTTPSession, aby przechowywać własne dane w każdej sesji. Musi zostać ustawiona przed aktywacją serwera.
SessionList Lista przechowująca aktywne sesje. Odczytuj ją, aby wyszukiwać, tworzyć lub usuwać sesje z poziomu kodu. Możesz też przypisać własną listę, jeśli potrzebujesz pełnej kontroli nad tym, gdzie sesje są przechowywane. Musi zostać przypisana przed aktywacją serwera.

 

 


TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.AutoStartSession := False;

 

Utwórz sesję

Aby utworzyć nową sesję, należy utworzyć nowy identyfikator sesji, który jest unikalny. Można użyć dowolnej wartości. Przykład: jeśli klient się uwierzytelnia, jako identyfikator sesji można użyć użytkownik + hasło + remoteip.

Następnie przeszukaj listę sesji, aby sprawdzić, czy sesja już istnieje. Jeśli nie istnieje, utwórz nową.

 

Gdy tworzona jest nowa sesja, wywoływane jest zdarzenie OnSessionStart, a gdy sesja jest zamykana, wywoływane jest zdarzenie 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;

Odczyt bieżącej sesji

Gdy sesja już istnieje, serwer dołącza ją do każdego żądania niosącego ciasteczko sesji. Odczytaj ją z ARequestInfo.Session, które jest nil, gdy żądanie nie ma sesji.


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;

Użycie własnej klasy sesji

Jeśli chcesz przechowywać własne pola w każdej sesji, zamiast korzystać z listy ciągów Content, utwórz potomka TIdHTTPSession i każ serwerowi go używać za pomocą właściwości SessionClass. Ustaw ją przed aktywacją serwera.

Serwer nadal zajmuje się całą resztą: generuje unikalny identyfikator sesji, wysyła ciasteczko sesji, stosuje SessionTimeout i usuwa nieaktualne sesje. Przesłoń wirtualny konstruktor CreateInitialized, jeśli chcesz zainicjować własne pola przy tworzeniu sesji.


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;

Użyj własnej listy sesji

Jeśli potrzebujesz pełnej kontroli nad sposobem przechowywania sesji, na przykład przechowywania ich w bazie danych lub współdzielenia między kilkoma serwerami, przypisz własną listę do właściwości SessionList przed aktywacją serwera. Utwórz klasę pochodną od TIdHTTPDefaultSessionList i przesłoń wirtualną metodę CreateSession. Serwer wywołuje ją wewnętrznie z CreateUniqueSession, więc unikalny identyfikator sesji jest nadal generowany automatycznie.

Gdy przypiszesz własną listę SessionList, właściwość SessionClass przestaje obowiązywać, ponieważ to twoja lista decyduje, którą klasę utworzyć.


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;

Uwaga o OnCreateSession

Zdarzenie OnCreateSession pozwala samodzielnie zwrócić instancję sesji, ale nie przypisuje jej identyfikatora sesji. Jeśli utworzysz sesję zwykłym konstruktorem, identyfikator sesji i ciasteczko sesji są puste, a sesji nie da się już odnaleźć przy kolejnym żądaniu. Użyj zamiast tego SessionClass, co jest prostsze i załatwia to wszystko za ciebie.

Ponadto nie wywołuj SessionList.CreateUniqueSession wewnątrz OnCreateSession. Ta metoda już dodaje nową sesję do listy, a serwer dodaje ją ponownie, gdy twoja procedura obsługi zwróci sterowanie, więc ta sama sesja trafia na listę dwukrotnie.