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.
Esistono alcune proprietà in TsgcHTTPServer che abilitano/disabilitano le sessioni nel componente server. Le più importanti sono:
| Property | Description |
| 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 | Facoltativo. La classe utilizzata dal server quando crea una nuova sessione. Impostarla su una propria discendente di TIdHTTPSession per memorizzare dati personalizzati all'interno di ogni sessione. Deve essere impostata prima che il server venga attivato. |
| SessionList | L'elenco che contiene le sessioni attive. Leggerlo per cercare, creare o rimuovere sessioni via codice. È anche possibile assegnare un proprio elenco se è necessario il pieno controllo su dove vengono memorizzate le sessioni. Deve essere assegnato prima che il server venga attivato. |
TsgcHTTPServer1.SessionState := True;
TsgcHTTPServer1.SessionTimeout := 600000;
TsgcHTTPServer1.AutoStartSession := False;
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 := TsgcHTTPServer1.SessionList.GetSession(vID, ARequestInfo.RemoteIP);
// create new session if not exists
if not Assigned(oSession) then
oSession := TsgcHTTPServer1.SessionList.CreateSession(ARequestInfo.RemoteIP, vID);
AResponseInfo.ContentText := '<html><head></head><body>Authenticated</body></html>';
AResponseInfo.ResponseNo := 200;
end;
end;
end;
Una volta che una sessione esiste, il server la collega a ogni richiesta che porta il cookie di sessione. Leggerla da ARequestInfo.Session, che è nil quando la richiesta non ha alcuna sessione.
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;
Se si desidera mantenere i propri campi all'interno di ogni sessione, invece di utilizzare la string list Content, creare una discendente di TIdHTTPSession e indicare al server di utilizzarla tramite la proprietà SessionClass. Impostarla prima che il server venga attivato.
Il server continua a occuparsi di tutto il resto: genera l'ID di sessione univoco, invia il cookie di sessione, applica SessionTimeout e rimuove le sessioni obsolete. Sovrascrivere il costruttore virtuale CreateInitialized se si desidera inizializzare i propri campi quando la sessione viene creata.
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
TsgcHTTPServer1.SessionState := True;
TsgcHTTPServer1.SessionTimeout := 600000;
TsgcHTTPServer1.SessionClass := TMySession;
TsgcHTTPServer1.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;
Se è necessario il pieno controllo su come vengono memorizzate le sessioni, ad esempio conservandole in un database o condividendole tra più server, assegnare un proprio elenco alla proprietà SessionList, prima che il server venga attivato. Discendere da TIdHTTPDefaultSessionList e sovrascrivere il metodo virtuale CreateSession. Il server lo richiama internamente da CreateUniqueSession, quindi l'ID di sessione univoco viene comunque generato automaticamente.
Quando si assegna un proprio SessionList, la proprietà SessionClass non si applica più, perché è il proprio elenco a decidere quale classe creare.
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
TsgcHTTPServer1.SessionList := TMySessionList.Create(nil);
TsgcHTTPServer1.Active := True;
L'evento OnCreateSession consente di restituire da soli un'istanza di sessione, ma non le assegna un ID di sessione. Se si crea la sessione con un costruttore semplice, l'ID di sessione e il cookie di sessione risultano vuoti e la sessione non potrà più essere trovata alla richiesta successiva. Utilizzare invece SessionClass, che è più semplice e gestisce tutto questo automaticamente.
Inoltre, non chiamare SessionList.CreateUniqueSession all'interno di OnCreateSession. Quel metodo aggiunge già la nuova sessione all'elenco, e il server la aggiunge nuovamente quando il gestore restituisce il controllo, per cui la stessa sessione finisce per comparire due volte nell'elenco.