TsgcWebSocketHTTPServer | Sessioni

HTTP è un protocollo senza stato (almeno fino a HTTP 1.1), quindi il client richiede un file, il server invia una risposta e la connessione viene chiusa (è possibile abilitare il keep-alive in modo che la connessione non venga chiusa immediatamente, ma ciò va oltre lo scopo di questo articolo). Le sessioni consentono di memorizzare informazioni sul client, che possono essere utilizzate durante un login del client, ad esempio. È possibile utilizzare qualsiasi ID sessione univoco, cercare nell'elenco delle sessioni se ne esiste già una e, in caso contrario, crearne una nuova. Una sessione può essere distrutta dopo un periodo di inattività o manualmente dopo il logout del client.

 

Configurazione

Esistono alcune proprietà in TsgcWebSocketHTTPServer che abilitano/disabilitano le sessioni nel componente server. Le più importanti sono:

 

Property Descrizione
SessionState Questa è la prima proprietà che deve essere abilitata per poter utilizzare le Sessioni. Senza questa proprietà abilitata, le sessioni non funzioneranno

SessionTimeout

Qui è necessario impostare un valore maggiore di zero (in millisecondi) per il tempo massimo di attività di una sessione.
AutoStartSession Le sessioni possono essere create automaticamente (AutoStartSession = true) o manualmente (AutoStartSession = false). Se le sessioni vengono create automaticamente, il server utilizzerà RemoteIP come identificatore univoco per verificare se è presente una sessione attiva memorizzata.
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 The list that holds the active sessions. Read it to search, create or remove sessions by code. You can also assign your own list if you need full control over where sessions are stored. Must be assigned before the server is activated.

 

 


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

 

Crea sessione

Per creare una nuova sessione, è necessario creare un nuovo ID sessione che sia univoco. È possibile utilizzare qualsiasi valore. Esempio: se il client si sta autenticando, è possibile utilizzare utente + password + IP remoto come ID sessione.

Quindi, cercare nell'elenco delle sessioni se esiste già. Se non esiste, crearne una nuova.

 

Quando viene creata una nuova sessione viene chiamato l'evento OnSessionStart e quando la sessione viene chiusa viene generato l'evento 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

If you want to keep your own fields inside every session, instead of using the Content string list, create a descendant of TIdHTTPSession and tell the server to use it through the SessionClass property. Set it before the server is activated.

The server keeps taking care of everything else: it generates the unique session ID, it sends the session cookie, it applies SessionTimeout and it removes stale sessions. Override the virtual constructor CreateInitialized if you want to initialize your own fields when the session is created.


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

The OnCreateSession event lets you return a session instance yourself, but it does not assign a session ID to it. If you create the session with a plain constructor, the session ID and the session cookie are empty and the session can never be found again on the next request. Use SessionClass instead, which is simpler and handles all of that for you.

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.