Solved

Indy HTTPServer and isapi?

Posted on 2002-03-05
5
940 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
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

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

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…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

772 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

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now