Delphi WebBroker apps and OmniHTTPd

By: Dave Nottage

Abstract: This article describes fixes and issues associated with running Delphi WebBroker apps with the OmniHTTPd Web server. By Dave Nottage.

OmniHTTPd is an ISAPI compliant Web server. It is very easy to install and configure, and Web apps made with Delphi are very easily debugged with it.

You can find OmniHTTPd here.

At least since version 5 of Delphi, there have been some features of Delphi WebBroker apps that haven't worked well with OmniHTTPd, due to the fact that OmniHTTPd doesn't implement ISAPI exactly as IIS does.

This article focuses on one such feature, and also focuses on Delphi 6 features that require changes for WebBroker apps (including Web services) to work with OmniHTTPd.

TWebRequest.Host property

If you build a WebBroker app with Delphi 5 or greater (possibly Delphi 4 also; I haven't checked), using the Host property of TWebRequest does not work under OmniHTTPd. Let's see why by examining how the Host property is accessed.

From TWebRequest in HTTPApp.PAS:

property Host: string index 10 read GetStringVariable;

Host is an "indexed" property, which means that the index value of 10 is passed to the GetStringVariable method. Examining the GetStringVariable method of TISAPIRequest in ISAPIHTTP.PAS:

function TISAPIRequest.GetStringVariable(Index: Integer): string;
begin
  case Index of
    0: Result := ECB.lpszMethod;
    3: Result := ECB.lpszQueryString;
    4: Result := ECB.lpszPathInfo;
    5: Result := ECB.lpszPathTranslated;
    1..2, 6..24, 26..28: Result := GetFieldByName(ServerVariables[Index]);
    25: if ECB.cbAvailable > 0 then
      SetString(Result, PChar(ECB.lpbData), ECB.cbAvailable);
   else
      Result := '';
  end;
end;

This code shows that GetFieldByName is called, passing the value of the ServerVariables array with an index of 10, which is HTTP_HOST. GetFieldByName calls the GetServerVariable method of the ECB (Extension Control Block) structure, which is supplied by the ISAPI server.

In the case of OmniHTTPd, for some reason or another, the ECB does not return the HTTP_HOST variable. Fortunately, an alternative is to use the ALL_HTTP value, which lists HTTP_HOST within it.

Fixing the TWebRequest.Host property for OmniHTTPd

To fix this problem, I made a number of changes to ISAPIHTTP.PAS:

type
  TISAPIRequest = class(TWebRequest)
  private
    FECB: PEXTENSION_CONTROL_BLOCK;
// Fix for OmniHTTPd - DPN SEP 2001
    function GetHostName: string;
    function GetAllHTTPValue(const VarName: string): string;

...

function TISAPIRequest.GetStringVariable(Index: Integer): string;
begin
  case Index of
    0: Result := ECB.lpszMethod;
    3: Result := ECB.lpszQueryString;
    4: Result := ECB.lpszPathInfo;
    5: Result := ECB.lpszPathTranslated;
// Fix for OmniHTTPd - DPN SEP 2001
//    1..2, 6..24, 26..28: Result := GetFieldByName(ServerVariables[Index]);
    1..2, 6..9, 11..24, 26..28: Result := GetFieldByName(ServerVariables[Index]);
    10: Result := GetHostName;
    25: if ECB.cbAvailable > 0 then
      SetString(Result, PChar(ECB.lpbData), ECB.cbAvailable);
   else
      Result := '';
  end;
end;

// Fix for OmniHTTPd - DPN SEP 2001
function TISAPIRequest.GetHostName: string;
begin
  Result := GetFieldByName(ServerVariables[10]);
  if Result = '' then
    Result := GetAllHTTPValue(ServerVariables[10]);
end;

function TISAPIRequest.GetAllHTTPValue(const VarName: string): string;
var
  Str: TStrings;
  i: integer;
begin
  Result := '';
  Str := TStringList.Create;
  try
    Str.Text := GetFieldByName('ALL_HTTP');
    for i := 0 to Str.Count - 1 do
      if Pos(VarName, Str[i]) > 0 then
      begin
        Result := Trim(Copy(Str[i], Pos(':', Str[i]) + 1, Length(Str[i])));
        break;
      end;
  finally
    Str.Free;
  end;
end;
// End fix

Note that I created a new method, GetHostName, so that any future changes to the way in which the host name is retrieved are localized to that method.

The necessity for this change becomes obvious when you create Web services with Delphi 6 and run them under OmniHTTPd.

The above outlines changes to fix the Host property for ISAPI apps only. The changes required for CGI apps are almost identical. These are left as an exercise for the reader!

Delphi 6 Web services

When the WSDL for Web services in a SOAP server is published, the WSDLPublish component uses the Host property of TWebRequest to publish links to the WSDL for each Web service. Without the fix as outlined above, the Host value is empty, thus creating an invalid link.

With the fix applied, you can successfully navigate the WSDL in your SOAP server.

ISAPI Thread Pooling

Delphi 6 introduces a new unit to support the ISAPI thread pooling features of IIS. As this feature is for IIS only, the unit needs to be removed from the project's uses clause when using Web servers other than IIS, including OmniHTTPd:

library Project1;

uses
  WebBroker,
  ISAPIThreadPool,  // <----- Remove this unit
  ISAPIApp,
  Unit1 in 'Unit1.pas' {WebModule1: TWebModule};

Remember to return the unit to your code if you decide to deploy on IIS.

Dave Nottage is CTO of Pure Software Technology, a software development company specializing in Delphi. He can be reached at dave@b3.com.au and the company's Web site is http://www.puresoftwaretech.com.


Server Response from: ETNASC04