I've just finished converting a 10 year old web site that previously ran
on Microsoft IIS to a full Delphi web application using the new
THttpAppSrv web application server, and thought some comments might be of
interest.  

Old Technology
--------------
The old site ran on Microsoft IIS/5/6/7 using ASP v3 VBScript pages,
supported by Delphi COM objects for DNS lookups and non-SQL database
lookups, and a Delphi ISAPI Filter (PassDir) for authentication
restricting paid members to certain sub-directories.  It also used
various Microsoft COM objects, to send email, look-up file tile stamps,
etc.  Many of the site static pages were created offline by a Delphi
application from a non-SQL database, originating from various CSV, HTML
and Excel files.  

The site was originally on a shared web server at a hosting facility that
allowed COM objects and filters to be installed, which not everyone does.
The original site is at:

http://www.magsys.co.uk/telecom/

New Technology
--------------
The new site is a Delphi web application, written with ICS v7 and Delphi
2007, using the THttpAppSrv, THttpServer and TSmtpCli components.
The new site is on my own hosted server (as is the old site now) at: 

http://www.telecom-tariffs.co.uk/

The main reason for the conversion to Delphi was maintainability.  The
old site had a mix of technologies and languages, and updating the COM
objects and ISAPI Filters was not easy.  I did look at converting to
ASPX/.NET using Delphi .NET or Prism, but it's a major learning curve
with a massive rewrite necessary, and dependence upon ever changing
Microsoft runtimes.  I do have a commercial .NET application on my server,
and it takes up to 30 seconds to wake up and offer the index page, unless
accessed regularly to keep it in memory. 

Create a Web Server
-------------------
Before the site could be converted, I needed a base web application
server, which means hanging a lot of code around the THttpServer
component.  Servers must run as Windows services, and I use the proven
SvCom environment that allows debugging and running under Delphi and
deployment as a service.  I added a configuration file, debug log files,
and W3C extended log files acceptable to web statistics analysis
applications.  I had to update both OverbyteIcsHttpSrv and
OverbyteIcsWSocket to get an event showing how much data was actually
sent to the client when the request completed and with the result code. I
also added content encoding since some of my static pages are 300K of
HTML that compress down to 20K, speeding up downloads. 

Converting the Site
-------------------
ASP pages generally comprise both server side code (VBScript) and the
HTML and often client side code (Javascript) in the same file, in the
public web directory.  An ICS web application still has a public root for
static HTML pages, images and other files, then a template directory for
dynamic pages comprising pure HTML and server side code, with the server
side code in a Delphi unit.  

My ASP included dynamic content with the HTML tag <%=name%> where name
could be a variable or function name.  For ICS, these tags become <#name>
which are replaced by the ICS function HtmlPageProducerToStream with
application content.  I also have server side includes (SSI) in ASP pages,
ie <!--#include file="addcopy.inc"--> inserts a file at that point in the
page, which I changed to <#addcopyinc> and then loaded the file into a
string of similar name which is returned dynamically.  
  
Each dynamic page with a template needs a matching UrlHandler class:

TUrlHandlerTcomlink = class(TUrlHandler)
public
    procedure Execute; override;
end;

which is added to the application server GET or POST lists: 

AddGetHandler('/tcomlink.htm', TUrlHandlerTcomlink);

then the actual code is simple for this page which shows the file time
stamp for the template, and two SSIs: 

procedure TUrlHandlerTcomlink.Execute;
begin
  AnswerPage('', NO_CACHE, 'tcomlink.htm', nil,
   ['PageLastMod', GetTempLastMod (Client, 'tcomlink.htm'),
    'OtherLinksInc', SsiOtherLinksInc,
    'AddCopyInc', SsiAddCopyInc ]);
  Finish;
end;
            
GetTempLastMod is a function that checks the file and format the date
into a string. In the response, NO_CACHE is literal for a header to stop
the page being cached, but other for pages I add cookies to this header.
The [] parameters are paired, first being the tag name, then the variable
for replacement, three in this example. 

Other pages need more complicated UrlHandlers, the web application has a
server information page at:  

http://www.telecom-tariffs.co.uk/serverinfo.htm

that uses TWSocket and a timer to look-up the client host name with a
five second timeout to avoid long DNS delays: 

   TUrlHandlerServerInfo = class(TUrlHandler)
    private
        WSocket: TWSocket;
        AbortTimer: TTimer;
        sIPAddr: string ;
        sUserIPHost: string ;
    public
        destructor  Destroy; override;
        procedure Execute; override;
        procedure DoneDnsLookup (Sender: TObject; Error: Word);
        procedure TimerAbortTimer(Sender: TObject);
    end;

With AnswerPage being in the DoneDnsLookup event.  It's important not to
use blocking internet functions since these stop the web server
responding to other clients.  

The site email form:

http://www.telecom-tariffs.co.uk/mailer.htm?telecom

does a similar DNS lookup, but checks if the form is GET or POST, and in
the latter case then uses the SMTP component to send an email, with a
different page being returned if successful, or the same page showing an
error.  

The main database lookup page also checks for GET or POST, and looks up
telephone numbering data in Robert Marsh's TQDB database component, this
code was previously in a COM object that was hard to change and which
regularly crashed the IIS application pool, but now it's simple Delphi I
can expand the searches offered and return more useful data.  
 
http://www.telecom-tariffs.co.uk/codelook.htm

Server Events
-------------
Part of the site conversion needs code in various server events.  

BeforeProcessRequest - check for login cookie and set Client.AuthUserName
and Client.AuthPasswordr, and virtual logoff page that clears login
cookie.  Also check for URLs without files, and default home page. 

AuthGetType - check if user path is a protected directory and a login
cookie not provided, then set Client.AuthTypes := [atBasic]; 

TMagHttpConnection.AuthBasicCheckPassword override - check authentication
from cookie or Basic login (also set cookie), allow access or 401 error. 

GetDocument - check Flags = hg401 then return custom page or if login has
expired redirect to a different page. Also check redirection of various
old URLs to new ones, such as codelook.asp to codelook.htm. 

HeadDocument and PostDocument - same event handler as GetDocument

AfterAnswer - update W3C extended log, includes data sent and received,
request and response codes, time taken for request, etc. 

Future
------
I need to create a lot of the static pages dynamically from the database,
either the current QDB or SQL server, offering more queries for dynamic
content. 

Find a better way to update the running web server, automatically
stopping it, replacing the EXE and restarting it.  Also continually
checking the server has not crashed or stopped and restarting it.  
 
Summary
-------
Benefits - much easier to maintain and enhance, low learning curve,
flexible

Disadvantages - needs co-operative hosting company or dedicated server,
currently uses one (rare) public IP per site since virtual hosts not
really supported in THttpAppSrv, no SSL in THttpAppSrv yet

The site conversion was a very satisfying project, something I should
really have done 10 years ago instead of messing with COM and such like.

Angus


--
To unsubscribe or change your settings for TWSocket mailing list
please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket
Visit our website at http://www.overbyte.be

Reply via email to