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.
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 podczas tworzenia nowej sesji. Ustaw ją na własnego potomka TIdHTTPSession, aby przechowywać własne dane w każdej sesji. Musi być ustawiona przed aktywacją serwera. |
| SessionList | Lista przechowująca aktywne sesje. Odczytaj ją, aby wyszukiwać, tworzyć lub usuwać sesje z poziomu kodu. Możesz również przypisać własną listę, jeśli potrzebujesz pełnej kontroli nad miejscem przechowywania sesji. Musi być przypisana przed aktywacją serwera. |
TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.AutoStartSession := False;
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;
Gdy sesja już istnieje, serwer dołącza ją do każdego żądania, które niesie plik cookie sesji. Odczytaj ją z ARequestInfo.Session, które ma wartość 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;
Jeśli chcesz przechowywać własne pola w każdej sesji, zamiast używać listy ciągów Content, utwórz klasę potomną TIdHTTPSession i wskaż serwerowi, aby jej używał, poprzez właściwość SessionClass. Ustaw ją przed aktywacją serwera.
Serwer nadal zajmuje się wszystkim innym: generuje unikalny identyfikator sesji, wysyła plik cookie sesji, stosuje SessionTimeout i usuwa nieaktywne sesje. Nadpisz wirtualny konstruktor CreateInitialized, jeśli chcesz zainicjować własne pola podczas tworzenia 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;
Jeśli potrzebujesz pełnej kontroli nad sposobem przechowywania sesji, na przykład trzymając je w bazie danych lub współdzieląc je między kilkoma serwerami, przypisz własną listę do właściwości SessionList, przed aktywacją serwera. Wyprowadź klasę z TIdHTTPDefaultSessionList i nadpisz wirtualną metodę CreateSession. Serwer wywołuje ją wewnętrznie z CreateUniqueSession, więc unikalny identyfikator sesji nadal jest generowany za Ciebie.
Gdy przypiszesz własną SessionList, właściwość SessionClass przestaje obowiązywać, ponieważ to Twoja lista decyduje, jaką 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;
Zdarzenie OnCreateSession pozwala samodzielnie zwrócić instancję sesji, ale nie przypisuje jej identyfikatora sesji. Jeśli utworzysz sesję za pomocą zwykłego konstruktora, identyfikator sesji i plik cookie sesji są puste, a sesji nigdy nie da się ponownie odnaleźć przy następnym żądaniu. Zamiast tego użyj SessionClass, które jest prostsze i wszystko to załatwia 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 zakończy działanie, więc ta sama sesja trafia na listę dwukrotnie.