O HTTP é um protocolo stateless (pelo menos até o HTTP 1.1), portanto o cliente solicita um arquivo, o servidor envia uma resposta, e a conexão é fechada (você pode habilitar keep-alive para que a conexão não seja fechada imediatamente, mas isso está além do escopo deste artigo). As sessões permitem que você armazene informações sobre o cliente, que podem ser utilizadas durante um login de cliente, por exemplo. Você pode utilizar qualquer session ID único, pesquisar a lista de sessões para ver se uma já existe e, caso não exista, criar uma nova sessão. Uma sessão pode ser destruída após um período de inatividade ou manualmente após o logout do cliente.
Existem algumas propriedades no TsgcWebSocketHTTPServer que habilitam/desabilitam sessões no componente servidor. As mais importantes são:
| Propriedade | Descrição |
| SessionState | Testa é a primeira propriedade que deve ser habilitada para usar Sessions. Sem esta propriedade habilitada, as sessions não funcionarão |
|
SessionTimeout |
Aqui você deve definir um valor maior que zero (em milissegundos) para o tempo máximo em que uma sessão estará ativa. |
| AutoStartSession | As sessões podem ser criadas automaticamente (AutoStartSession = true) ou manualmente (AutoStartSession = false). Se as sessões forem criadas automaticamente, o servidor utilizará RemoteIP como identificador único para verificar se há uma sessão ativa armazenada. |
| SessionClass | Opcional. A classe que o servidor usa quando cria uma nova sessão. Defina-a com o seu próprio descendente de TIdHTTPSession para armazenar os seus próprios dados dentro de cada sessão. Deve ser definida antes de o servidor ser ativado. |
| SessionList | A lista que contém as sessões ativas. Leia-a para buscar, criar ou remover sessões por código. Você também pode atribuir sua própria lista se precisar de controle total sobre onde as sessões são armazenadas. Deve ser atribuída antes de o servidor ser ativado. |
TsgcWebSocketHTTPServer1.SessionState := True;
TsgcWebSocketHTTPServer1.SessionTimeout := 600000;
TsgcWebSocketHTTPServer1.AutoStartSession := False;
Para criar uma nova sessão, você deve criar um novo session ID que seja único. Você pode usar qualquer valor. Exemplo: se o cliente está se autenticando, você pode usar usuário + senha + remoteip como o session ID.
Em seguida, pesquise a lista de sessões para verificar se ela já existe. Se não existir, crie uma nova.
Quando uma nova sessão é criada, o evento OnSessionStart é chamado e, quando a sessão é fechada, o evento OnSessionEnd é gerado.
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;
Uma vez que a sessão existe, o servidor a anexa a todas as requisições que carregam o cookie de sessão. Leia-a em ARequestInfo.Session, que é nil quando a requisição não tem sessão.
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 você quiser manter os seus próprios campos dentro de cada sessão, em vez de usar a lista de strings Content, crie um descendente de TIdHTTPSession e diga ao servidor para usá-lo por meio da propriedade SessionClass. Defina-a antes de o servidor ser ativado.
O servidor continua cuidando de todo o resto: ele gera o ID único de sessão, envia o cookie de sessão, aplica SessionTimeout e remove sessões obsoletas. Sobrescreva o construtor virtual CreateInitialized se quiser inicializar seus próprios campos quando a sessão for criada.
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;
Se você precisar de controle total sobre como as sessões são armazenadas, por exemplo, mantendo-as em um banco de dados ou compartilhando-as entre vários servidores, atribua sua própria lista à propriedade SessionList, antes que o servidor seja ativado. Derive de TIdHTTPDefaultSessionList e sobrescreva o método virtual CreateSession. O servidor o chama internamente a partir de CreateUniqueSession, então o ID de sessão único ainda é gerado para você.
Quando você atribui sua própria SessionList, a propriedade SessionClass deixa de se aplicar, porque sua lista decide qual classe criar.
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;
O evento OnCreateSession permite que você mesmo retorne uma instância de sessão, mas ele não atribui um ID de sessão a ela. Se você criar a sessão com um construtor simples, o ID de sessão e o cookie de sessão ficam vazios e a sessão nunca mais poderá ser encontrada na próxima requisição. Use SessionClass em vez disso, que é mais simples e cuida de tudo isso para você.
Além disso, não chame SessionList.CreateUniqueSession dentro de OnCreateSession. Esse método já adiciona a nova sessão à lista, e o servidor a adiciona novamente quando o seu manipulador retorna, portanto a mesma sessão acaba duas vezes na lista.