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 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;

 

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;

Ler a sessão atual

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;

Use a sua própria classe de sessão

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;

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 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;

Uma nota sobre OnCreateSession

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.