2016-06-22 17:49:16 +02:00
|
|
|
// ***************************************************************************
|
|
|
|
//
|
|
|
|
// Delphi MVC Framework
|
|
|
|
//
|
2020-01-06 16:49:18 +01:00
|
|
|
// Copyright (c) 2010-2020 Daniele Teti and the DMVCFramework Team
|
2016-06-22 17:49:16 +02:00
|
|
|
//
|
|
|
|
// https://github.com/danieleteti/delphimvcframework
|
|
|
|
//
|
|
|
|
// ***************************************************************************
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// ***************************************************************************
|
2015-12-22 12:38:17 +01:00
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
unit MVCFramework.Session;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
{$I dmvcframework.inc}
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
interface
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
uses
|
|
|
|
System.SyncObjs,
|
|
|
|
System.SysUtils,
|
|
|
|
System.DateUtils,
|
2013-10-30 00:48:23 +01:00
|
|
|
System.Generics.Collections;
|
|
|
|
|
2017-01-20 17:29:09 +01:00
|
|
|
const
|
|
|
|
|
|
|
|
DEFAULT_SESSION_INACTIVITY = 60; // in minutes
|
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
type
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2013-10-30 00:48:23 +01:00
|
|
|
TWebSession = class abstract
|
2017-03-23 18:51:25 +01:00
|
|
|
private
|
|
|
|
FSessionId: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
FLastAccess: TDateTime;
|
2014-04-01 19:39:28 +02:00
|
|
|
FTimeout: UInt64;
|
2017-03-23 18:51:25 +01:00
|
|
|
protected
|
|
|
|
function GetItems(const AKey: string): string; virtual; abstract;
|
|
|
|
procedure SetItems(const AKey, AValue: string); virtual; abstract;
|
2013-10-30 00:48:23 +01:00
|
|
|
public
|
2017-03-23 18:51:25 +01:00
|
|
|
constructor Create(const ASessionId: string; const ATimeout: UInt64); virtual;
|
2013-10-30 00:48:23 +01:00
|
|
|
destructor Destroy; override;
|
2018-10-30 13:53:01 +01:00
|
|
|
procedure MarkAsUsed; virtual;
|
2013-10-30 00:48:23 +01:00
|
|
|
function ToString: string; override;
|
2018-10-30 13:53:01 +01:00
|
|
|
function IsExpired: Boolean; virtual;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
|
|
|
property Items[const AKey: string]: string read GetItems write SetItems; default;
|
|
|
|
property SessionId: string read FSessionId;
|
2013-10-30 00:48:23 +01:00
|
|
|
property LastAccess: TDateTime read FLastAccess;
|
|
|
|
property Timeout: UInt64 read FTimeout;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TWebSessionClass = class of TWebSession;
|
|
|
|
|
|
|
|
TWebSessionMemory = class(TWebSession)
|
2017-03-23 18:51:25 +01:00
|
|
|
private
|
2013-10-30 00:48:23 +01:00
|
|
|
FData: TDictionary<string, string>;
|
2017-03-23 18:51:25 +01:00
|
|
|
protected
|
|
|
|
function GetItems(const AKey: string): string; override;
|
|
|
|
procedure SetItems(const AKey, AValue: string); override;
|
2013-10-30 00:48:23 +01:00
|
|
|
public
|
2017-03-23 18:51:25 +01:00
|
|
|
constructor Create(const ASessionId: string; const ATimeout: UInt64); override;
|
2013-10-30 00:48:23 +01:00
|
|
|
destructor Destroy; override;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
2018-10-30 13:53:01 +01:00
|
|
|
function ToString: string; override;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
|
|
|
property Data: TDictionary<string, string> read FData;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
TMVCSessionFactory = class sealed
|
2017-03-23 18:51:25 +01:00
|
|
|
private
|
2018-10-30 13:53:01 +01:00
|
|
|
FRegisteredSessionTypes: TDictionary<string, TWebSessionClass>;
|
|
|
|
protected
|
|
|
|
class var cInstance: TMVCSessionFactory;
|
2017-03-23 18:51:25 +01:00
|
|
|
constructor Create;
|
2018-10-30 13:53:01 +01:00
|
|
|
public
|
2015-04-01 17:01:23 +02:00
|
|
|
destructor Destroy; override;
|
2018-10-30 13:53:01 +01:00
|
|
|
procedure RegisterSessionType(const AName: string; AWebSessionClass: TWebSessionClass);
|
2017-03-23 18:51:25 +01:00
|
|
|
function CreateNewByType(const AName, ASessionId: string; const ATimeout: UInt64): TWebSession;
|
|
|
|
|
|
|
|
class function GetInstance: TMVCSessionFactory; static;
|
2018-10-30 13:53:01 +01:00
|
|
|
// class procedure DestroyInstance; static;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
function GlobalSessionList: TObjectDictionary<string, TWebSession>;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
var
|
2017-03-23 18:51:25 +01:00
|
|
|
GlSessionList: TObjectDictionary<string, TWebSession> = nil;
|
|
|
|
GlLastSessionListClear: TDateTime;
|
|
|
|
GlCriticalSection: TCriticalSection;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
function GlobalSessionList: TObjectDictionary<string, TWebSession>;
|
2013-10-30 00:48:23 +01:00
|
|
|
var
|
2017-03-23 18:51:25 +01:00
|
|
|
S: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
if not Assigned(GlSessionList) then
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
GlCriticalSection.Enter;
|
2013-10-30 00:48:23 +01:00
|
|
|
try
|
2017-03-23 18:51:25 +01:00
|
|
|
if not Assigned(GlSessionList) then
|
|
|
|
GlSessionList := TObjectDictionary<string, TWebSession>.Create([doOwnsValues]);
|
2013-10-30 00:48:23 +01:00
|
|
|
finally
|
2017-03-23 18:51:25 +01:00
|
|
|
GlCriticalSection.Leave;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
if MinutesBetween(Now, GlLastSessionListClear) >= 1 then
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
TMonitor.Enter(GlSessionList);
|
2013-10-30 00:48:23 +01:00
|
|
|
try
|
2017-03-23 18:51:25 +01:00
|
|
|
for S in GlSessionList.Keys do
|
|
|
|
if TWebSession(GlSessionList.Items[S]).IsExpired then
|
|
|
|
GlSessionList.Remove(S);
|
|
|
|
GlLastSessionListClear := Now;
|
2013-10-30 00:48:23 +01:00
|
|
|
finally
|
2017-03-23 18:51:25 +01:00
|
|
|
TMonitor.Exit(GlSessionList);
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
end;
|
2017-03-23 18:51:25 +01:00
|
|
|
|
|
|
|
Result := GlSessionList;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
{ TWebSession }
|
|
|
|
|
|
|
|
constructor TWebSession.Create(const ASessionId: string; const ATimeout: UInt64);
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
|
|
|
inherited Create;
|
2017-03-23 18:51:25 +01:00
|
|
|
FSessionId := ASessionId;
|
|
|
|
FTimeout := ATimeout;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TWebSession.Destroy;
|
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
inherited Destroy;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
function TWebSession.IsExpired: Boolean;
|
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
if (FTimeout = 0) then
|
|
|
|
Result := MinutesBetween(Now, LastAccess) > DEFAULT_SESSION_INACTIVITY
|
2017-01-20 17:29:09 +01:00
|
|
|
else
|
2017-03-23 18:51:25 +01:00
|
|
|
Result := MinutesBetween(Now, LastAccess) > FTimeout;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TWebSession.MarkAsUsed;
|
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
FLastAccess := Now;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
function TWebSession.ToString: string;
|
|
|
|
begin
|
|
|
|
Result := '';
|
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
{ TWebSessionMemory }
|
|
|
|
|
|
|
|
constructor TWebSessionMemory.Create(const ASessionId: string; const ATimeout: UInt64);
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
inherited Create(ASessionId, ATimeout);
|
2013-10-30 00:48:23 +01:00
|
|
|
FData := TDictionary<string, string>.Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TWebSessionMemory.Destroy;
|
|
|
|
begin
|
|
|
|
FData.Free;
|
2017-03-23 18:51:25 +01:00
|
|
|
inherited Destroy;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
function TWebSessionMemory.GetItems(const AKey: string): string;
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
2014-04-01 19:39:28 +02:00
|
|
|
TMonitor.Enter(Self);
|
|
|
|
try
|
2017-03-23 18:51:25 +01:00
|
|
|
if not FData.TryGetValue(AKey, Result) then
|
2014-04-01 19:39:28 +02:00
|
|
|
Result := '';
|
|
|
|
finally
|
|
|
|
TMonitor.Exit(Self);
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
procedure TWebSessionMemory.SetItems(const AKey, AValue: string);
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
2014-04-01 19:39:28 +02:00
|
|
|
TMonitor.Enter(Self);
|
|
|
|
try
|
2017-03-23 18:51:25 +01:00
|
|
|
FData.AddOrSetValue(AKey, AValue);
|
2014-04-01 19:39:28 +02:00
|
|
|
finally
|
|
|
|
TMonitor.Exit(Self);
|
|
|
|
end;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
2018-10-30 13:53:01 +01:00
|
|
|
function TWebSessionMemory.ToString: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
var
|
2017-03-23 18:51:25 +01:00
|
|
|
LKey: string;
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
|
|
|
Result := '';
|
2017-03-23 18:51:25 +01:00
|
|
|
for LKey in FData.Keys do
|
|
|
|
Result := Result + LKey + '=' + QuotedStr(FData.Items[LKey]) + sLineBreak;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
{ TMVCSessionFactory }
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
constructor TMVCSessionFactory.Create;
|
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
inherited Create;
|
2013-10-30 00:48:23 +01:00
|
|
|
FRegisteredSessionTypes := TDictionary<string, TWebSessionClass>.Create;
|
|
|
|
end;
|
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
function TMVCSessionFactory.CreateNewByType(const AName, ASessionId: string; const ATimeout: UInt64): TWebSession;
|
2013-10-30 00:48:23 +01:00
|
|
|
var
|
2017-03-23 18:51:25 +01:00
|
|
|
Clazz: TWebSessionClass;
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
2017-03-23 18:51:25 +01:00
|
|
|
if not FRegisteredSessionTypes.TryGetValue(AName, Clazz) then
|
|
|
|
raise Exception.Create('Unknown application session type');
|
|
|
|
Result := Clazz.Create(ASessionId, ATimeout);
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TMVCSessionFactory.Destroy;
|
|
|
|
begin
|
|
|
|
FRegisteredSessionTypes.Free;
|
2017-03-23 18:51:25 +01:00
|
|
|
inherited Destroy;
|
2013-10-30 00:48:23 +01:00
|
|
|
end;
|
|
|
|
|
2018-10-30 13:53:01 +01:00
|
|
|
// class procedure TMVCSessionFactory.DestroyInstance;
|
|
|
|
// begin
|
|
|
|
// if Assigned(cInstance) then
|
|
|
|
// cInstance.Free;
|
|
|
|
// end;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
2017-03-23 18:51:25 +01:00
|
|
|
class function TMVCSessionFactory.GetInstance: TMVCSessionFactory;
|
|
|
|
begin
|
2018-10-30 13:53:01 +01:00
|
|
|
if not Assigned(cInstance) then
|
|
|
|
cInstance := TMVCSessionFactory.Create;
|
|
|
|
Result := cInstance;
|
2017-03-23 18:51:25 +01:00
|
|
|
end;
|
|
|
|
|
2018-10-30 13:53:01 +01:00
|
|
|
procedure TMVCSessionFactory.RegisterSessionType(const AName: string; AWebSessionClass: TWebSessionClass);
|
2013-10-30 00:48:23 +01:00
|
|
|
begin
|
|
|
|
FRegisteredSessionTypes.AddOrSetValue(AName, AWebSessionClass);
|
|
|
|
end;
|
|
|
|
|
|
|
|
initialization
|
|
|
|
|
|
|
|
TMVCSessionFactory.GetInstance.RegisterSessionType('memory', TWebSessionMemory);
|
2017-03-23 18:51:25 +01:00
|
|
|
GlCriticalSection := TCriticalSection.Create;
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
finalization
|
|
|
|
|
2018-10-30 13:53:01 +01:00
|
|
|
FreeAndNil(TMVCSessionFactory.cInstance);
|
2017-03-23 18:51:25 +01:00
|
|
|
FreeAndNil(GlCriticalSection);
|
|
|
|
|
|
|
|
if Assigned(GlSessionList) then
|
|
|
|
FreeAndNil(GlSessionList);
|
2013-10-30 00:48:23 +01:00
|
|
|
|
|
|
|
end.
|
2019-07-08 19:25:56 +02:00
|
|
|
|