Solved

Indy HTTPServer and isapi?

Posted on 2002-03-05
5
944 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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Suggested Solutions

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
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…
Both in life and business – not all partnerships are created equal. As the demand for cloud services increases, so do the number of self-proclaimed cloud partners. Asking the right questions up front in the partnership, will enable both parties …
As a trusted technology advisor to your customers you are likely getting the daily question of, ‘should I put this in the cloud?’ As customer demands for cloud services increases, companies will see a shift from traditional buying patterns to new…

911 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

20 Experts available now in Live!

Get 1:1 Help Now