TsgcWebSocketHTTPServer | Sessions

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.

 

Configuração

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 utiliza quando cria uma nova sessão. Defina-a como seu próprio descendente de TIdHTTPSession para armazenar 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 pesquisar, criar ou remover sessões via 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;

 

Criar Sessão

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;

Leia a Sessão Atual

Uma vez que uma sessão existe, o servidor a anexa a cada requisição que carrega o cookie de sessão. Leia-a a partir de ARequestInfo.Session, que é nil quando a requisição não possui 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;

Use Sua Própria Classe de Sessão

Se desejar manter seus próprios campos dentro de cada sessão, em vez de usar a string list Content, crie um descendente de TIdHTTPSession e diga ao servidor para utilizá-lo através da propriedade SessionClass. Defina-a antes de o servidor ser ativado.

O servidor continua cuidando de todo o resto: ele gera o session ID único, envia o cookie de sessão, aplica SessionTimeout e remove as sessões obsoletas. Sobrescreva o construtor virtual CreateInitialized se desejar inicializar seus próprios campos quando a sessão é 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;

Use Sua Própria Lista de Sessões

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 de o servidor ser ativado. Descenda de TIdHTTPDefaultSessionList e sobrescreva o método virtual CreateSession. O servidor o chama internamente a partir de CreateUniqueSession, portanto o session ID ú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;

Uma Observação Sobre OnCreateSession

O evento OnCreateSession permite que você mesmo retorne uma instância de sessão, mas ele não atribui um session ID a ela. Se você criar a sessão com um construtor comum, o session ID e o cookie de sessão ficam vazios e a sessão nunca poderá ser encontrada novamente na próxima requisição. Utilize 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 seu manipulador retorna, de modo que a mesma sessão acaba duas vezes na lista.