This week I had to maintain an IntraWeb application. For those who are not familiar with IntraWeb it allows you to create a web application with Delphi. This means that you can design your web application with components and their properties in the same way as a Delphi desktop application. At runtime the controls render themselves, they create html code so that they look and feel in the same way as in the designer preview. That’s why the IntraWeb version that ships with Delphi is called the VCL for the Web.
I have to mention that there are huge differences between the various IntraWeb versions. In this case I used IntraWeb 12 in Delphi XE2 with full source code. In the version that ships with Delphi there are some limitations so that maybe the code I will show will not work. Currently Atozed is shipping a new IntraWeb version, the version 14.
If you create a new IntraWeb application you normally create two different apps: A stand-alone webserver for debugging purpose and a ISAPI dll that will be used in the real world.
The IntraWeb project wizard creates amongst others two important units: A server controller and a user session unit. Its is not surprising, the server controller controls your IntraWeb server app. Since web apps are stateless the user session gives you the possibility to save data that can be exchanged between your forms.
Let’s go back to the original problem: If your web apps shows some confidential data the app should get into a timeout after several minutes of inactivity. Therefore you can simply set the SessionTimeout property of the ServerController to a value in minutes after which your session gets into a timeout. In order to show the user a webesite after a timeout you can put a TIWURLResponderRedirect onto the ServerController and set the TimeoutResponse property to the TIWURLResponderRedirect. The TIWURLResponderRedirect has a property URL that will be shown in the case of a timeout.
This approach has two disadvantages: On the one hand the URL property points to a static website and on the other hand the client shows the timeout website not till then the client sends the next request to the server. The first point could be a deployment problem, the second point could be a problem since confidential data will rest on the screen.
That’s why I prefer a strategy in which the client polls the server, the server checks the inactivity time and if necessary redirects the client to a dynamic timeout website. Therefore I would like to place a timer on every form. The time polls the server, the server differs between the special timer request and a normal request, saves the last access in the session and redirects to the timeout form in the case the session has expired.
First I added the property LastAccess to the UserSession.
TIWUserSession = class(TIWUserSessionBase) strict private FLastAccess: TDateTime; public property LastAccess: TDateTime read FLastAccess write FLastAccess; end;
Since Delphi supports visual inheritance I derived every form from a base form and placed a timer on it. In the timer event I checked if the session has expired.
procedure TfrmBase.tiSessionAsyncTimer(Sender: TObject; EventParams: TStringList); const cMinuteToDateTime = 60 * 24; begin if (Now - TIWServerController.UserSession.LastAccess) >= (TIWServerController.IWServerController.SessionTimeOut / cMinuteToDateTime) then TfrmTimeout.Create(WebApplication).Show; end;
In the ServerController there is after every request an event fired in which I can decide if it was a normal request or a timer request.
procedure TIWServerController.IWServerControllerBaseAfterDispatch(Request: THttpRequest; aReply: THttpReply); begin if (WebApplication <> nil) and (UserSession <> nil) and (Pos('TISESSION', Request.Query) <= 0) then UserSession.LastAccess := Now; end;
In the timeout form I added a TIWURL component so that the user can navigate back to the login dialog. I set the URL property dynamically so that there is no problem concerning deployment.
procedure TfrmTimeout.IWAppFormCreate(Sender: TObject); begin inherited; urlLogin.URL := WebApplication.ApplicationURL; end; procedure TfrmTimeout.urlWebWorkflowHTMLTag(ASender: TObject; ATag: TIWHTMLTag); begin if Assigned(ATag) and (ATag.Params.IndexOfName('href') >= 0) then ATag.Params.Values['href'] := urlLogin.URL; end;
Last bust not least I terminate the session manually (Please guys from Atozed don’t look at this code).
type THackApp = class(TIWApplication); procedure TfrmTimeout.IWAppFormRender(Sender: TObject); begin THackApp(WebApplication).FTerminated := True; end;