Solved

Indy HTTPServer and isapi?

Posted on 2002-03-05
5
949 Views
Last Modified: 2010-05-18
Hi there

I've made a HTTPServer with indy. Is there a way to run external isapi module, from this server?

If so, HOW?

Smilly
0
Comment
Question by:Smilly
  • 3
5 Comments
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6841537
ISPI extensions are nothing but DLL's with a couple of entry functions. So what you have to do is to load the DLL's (using LoadLibrary()), to retrieve its entry points (GetProcAddress()), call them as needed, and finally (when you shutdown the application) unload the library.

In the file ISAPI.PAS (which ships with Delphi) you'll find all the needed ISAPI definitions. I have prepared a base class which will do the basic stuff for you, you only need to fill in the things specific to your self-made web server:

// uses Windows,ISAPI;

type
      TYourWebRequest=class
            Method,                // GET, HEAD, POST, ...
            QueryString,           // the string behind the ? of the URI
            PathInfo,              // the string before the ? in the URI (without host name)
            PathTranslated,        // the physical path on the web server of that request
            ContentType: string;   // the content-type (from the Content-Type header)
            ContentSize,           // number of bytes indicated by client (in the Content-Length header)
            AvailableSize: Integer;// number of bytes currently waiting to be read
            AvailableData: Pointer;// pointer to the data (ReadAvailable bytes)
      end;

      TISAPIExtension=class;
      TGetExtensionVersion=function(var Ver: THSE_VERSION_INFO): BOOL; stdcall;
      THttpExtensionProc=function(var ECB: TEXTENSION_CONTROL_BLOCK): DWORD; stdcall;
      PCONN=^TCONN;
      TCONN=record
            Request: TYourWebRequest;
            Extension: TISAPIExtension;
            ECB: TEXTENSION_CONTROL_BLOCK;
      end;
      TISAPIExtension=class
      private
            FISAPIDLL: string;
            FDLLHandle: THandle;
            FExtensionVersion: THSE_VERSION_INFO;
            FHttpExtensionProc: THttpExtensionProc;
      protected
            function GetServerVariable(Request: TYourWebRequest; VariableName: string; Buffer: Pointer; var Size: Integer): Boolean;
            function WriteClient(Request: TYourWebRequest;      Buffer: Pointer; var Bytes: Integer; dwReserved: Integer): Boolean;
            function ReadClient(Request: TYourWebRequest; Buffer: Pointer;      var Size: Integer): Boolean;
            function ServerSupportFunction(Request: TYourWebRequest; HSERRequest: Integer; Buffer: Pointer; var Size: Integer; var DataType: Integer): Boolean;
      public
            constructor Create(const ISAPIDLL: string);
            procedure Invoke(Request: TYourWebRequest);
            destructor Destroy; override;
      end;

implementation

function GetServerVariableProc(ConnID: HCONN; VariableName: PChar; Buffer: Pointer; var Size: DWORD): BOOL; stdcall;
var
      Conn: PCONN absolute ConnID;
begin
      Result:=Conn.Extension.GetServerVariable(Conn.Request,VariableName,Buffer,Integer(Size));
end;

function WriteClientProc(ConnID: HCONN; Buffer: Pointer; var Bytes: DWORD; dwReserved: DWORD): BOOL; stdcall;
var
      Conn: PCONN absolute ConnID;
begin
      Result:=Conn.Extension.WriteClient(Conn.Request,Buffer,Integer(Bytes),dwReserved);
end;

function ReadClientProc(ConnID: HCONN; Buffer: Pointer;      var Size: DWORD): BOOL; stdcall;
var
      Conn: PCONN absolute ConnID;
begin
      Result:=Conn.Extension.ReadClient(Conn.Request,Buffer,Integer(Size))
end;

function ServerSupportFunctionProc(ConnID: HCONN; HSERRequest: DWORD; Buffer: Pointer; var Size: DWORD; var DataType: DWORD): BOOL; stdcall;
var
      Conn: PCONN absolute ConnID;
begin
      Result:=Conn.Extension.ServerSupportFunction(Conn.Request,HSERRequest,Buffer,Integer(Size),Integer(DataType));
end;

{ TISAPIExtension }

constructor TISAPIExtension.Create(const ISAPIDLL: string);
var
      GetVersion: TGetExtensionVersion;
begin
      FDLLHandle:=LoadLibrary(PChar(ISAPIDLL));
      if FDLLHandle=null then
            raise Exception.Create('Cannot load ISAPI DLL '+ISAPIDLL);
      FISAPIDLL:=ISAPIDLL;
      GetVersion:=TGetExtensionVersion(GetProcAddress(FDLLHandle,'GetExtensionVersion'));
      FHttpExtensionProc:=THttpExtensionProc(GetProcAddress(FDLLHandle,'FHttpExtensionProc'));
      GetVersion(FExtensionVersion);
end;

destructor TISAPIExtension.Destroy;
begin
      FreeLibrary(FDLLHandle);
      inherited;
end;

function TISAPIExtension.GetServerVariable(Request: TYourWebRequest; VariableName: string; Buffer: Pointer; var Size: Integer): Boolean;
begin
      // read a server variable in this requests context here
end;

procedure TISAPIExtension.Invoke(Request: TYourWebRequest);
var
      Connection: TCONN;
begin
      Connection.Request:=Request;
      Connection.Extension:=self;
      with Connection,ECB do begin
            FillChar(ECB,SizeOf(ECB),0);
            cbSize:=SizeOf(ECB);
            dwVersion:=HSE_VERSION_MAJOR shl 16+HSE_VERSION_MINOR;
            ConnID:=Cardinal(@Connection);
            lpszMethod:=PChar(Request.Method);
            lpszQueryString:=PChar(Request.QueryString);
            lpszPathInfo:=PChar(Request.PathInfo);
            lpszPathTranslated:=PChar(Request.PathTranslated);
            cbTotalBytes:=Integer(Request.ContentSize);
            cbAvailable:=Integer(Request.AvailableSize);
            lpbData:=Request.AvailableData;
            lpszContentType:=PChar(Request.ContentType);
            GetServerVariable:=GetServerVariableProc;
            WriteClient:=WriteClientProc;
            ReadClient:=ReadClientProc;
            ServerSupportFunction:=ServerSupportFunctionProc;
      end;
      FHttpExtensionProc(Connection.ECB);
end;

function TISAPIExtension.ReadClient(Request: TYourWebRequest; Buffer: Pointer; var Size: Integer): Boolean;
begin
      // copy data from the request in buffer to the ISAPI DLL
end;

function TISAPIExtension.ServerSupportFunction(Request: TYourWebRequest; HSERRequest: Integer; Buffer: Pointer; var Size, DataType: Integer): Boolean;
begin
      // execute the wanted support function
end;

function TISAPIExtension.WriteClient(Request: TYourWebRequest; Buffer: Pointer; var Bytes: Integer; dwReserved: Integer): Boolean;
begin
      // copy data from the ISAPI DLL to the request out buffer
end;

For further reading on the callback functions and server variables, please read here:
http://msdn.microsoft.com/library/en-us/iisref/html/psdk/asp/isre0wit.asp
0
 
LVL 1

Author Comment

by:Smilly
ID: 6843955
Hi AvonWyss

Thanks for a greate response to my question.
How do I use it? I tried this :

*****************************************
procedure TForm1.IdHTTPServer1CommandGet(AThread: TIdPeerThread;
  RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo);
var
     req : TYourWebRequest;
     MyIsapi: TISAPIExtension;
begin

      MyIsapi := TISAPIExtension.Create('C:\Inetpub\Scripts\sayhello.dll');

      req := TYourWebRequest.Create;

      req.Method := RequestInfo.Command;
      req.QueryString := RequestInfo.Params.Text;
      req.PathInfo := RequestInfo.Host;
      req.PathTranslated := 'D:\delphi\webserver\root';
      req.ContentType := ResponseInfo.ContentType;
      req.ContentSize := ResponseInfo.ContentLength;
      req.AvailableSize := 0;
      req.AvailableData := nil;

      MyIsapi.Invoke(req);

      req.Free;
      MyIsapi.Free;
end;
*****************************************************

But it didn't work.

You wrote :
  -->read
// copy data from the request in buffer to the ISAPI DLL

  -->support
// execute the wanted support function  

  -->write
// copy data from the ISAPI DLL to the request out buffer

But how?

The only thing I want is to run a simple Isapi extension from within my server. The helloworld.dll writes "hello world" in the response context, nothing else.

Please help me a little more, and I increase the point.
0
 
LVL 1

Expert Comment

by:Moondancer
ID: 6853423
Hi, Smilly.

Perhaps you've overlooked these open questions, one quite old, the other without comments for which you may wish a refund.

For special handling needs, please post a zero point question in the link below and include the question QID/link(s) that it regards.
http://www.experts-exchange.com/jsp/qList.jsp?ta=commspt
 
To view your open questions, please click the following link(s) and keep them all current with updates.
http://www.experts-exchange.com/questions/Q.20044308.html
http://www.experts-exchange.com/questions/Q.20144496.html
 
Thank you everyone.
 
Moondancer
Moderator @ Experts Exchange
0
 
LVL 14

Accepted Solution

by:
AvonWyss earned 200 total points
ID: 6853555
You're on the right track. And it's obvious why it does not work.

The ISAPI extensions can communicate with the server through callback functions. These are used for data input and output as well as support functions (like, retrieving a server variable). Internally, the ISAPI extension has to use the Write callback provided by the server to send some test to the client. And this callback leads to the function with the comment:
  // copy data from the ISAPI DLL to the request out buffer

So, what you need to do there, it so send the data the DLL gives you to the client. I'm not familiar with the Indy suite (using self-written stuff), but something like this should work (just for the idea, the actual syntax will differ). First we need to store the request information somewhere:

    TYourWebRequest=class
          Method,                // GET, HEAD, POST, ...
          QueryString,           // the string behind the ? of the URI
          PathInfo,              // the string before the ? in the URI (without host name)
          PathTranslated,        // the physical path on the web server of that request
          ContentType: string;   // the content-type (from the Content-Type header)
          ContentSize,           // number of bytes indicated by client (in the Content-Length header)
          AvailableSize: Integer;// number of bytes currently waiting to be read
          AvailableData: Pointer;// pointer to the data (ReadAvailable bytes)
          RequestInfo: TIdHTTPRequestInfo;
          ResponseInfo: TIdHTTPResponseInfo;
     end;

Actually that's why I called it "YourWebRequest" - you change whatever is needed. I also assume that you assign these two to the req variable in the IdHTTPServer1CommandGet method. Then you can implement the WriteClient:

function TISAPIExtension.WriteClient(Request: TYourWebRequest; Buffer: Pointer; var Bytes: Integer;
dwReserved: Integer): Boolean;
begin
     Bytes:=Request.ResponseInfo.Write(Buffer^,Bytes);
end;

(Or just whatever writes Bytes bytes from the memory location Buffer to the HTTP client.)
Same idea for ReadClient. Note that you also need to implement the different support callbacks for ISAPI extensions to work correctly. For a list of these, go to the link I provided. It explains ISAPI extensions in depth.
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 6853558
And one more thing: Don't load and unload the DLL for each request! Load it (e.g. create the object) when starting your server application, use it and free it only when stopping the server. This will greaty enhance the response time of the server because the DLL does not need to be loaded and unloaded all the time!
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

839 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question