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
