Solved

Indy HTTPServer and isapi?

Posted on 2002-03-05
5
956 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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

Enroll in June's Course of the Month

June's Course of the Month is now available! Every 10 seconds, a consumer gets hit with ransomware. Refresh your knowledge of ransomware best practices by enrolling in this month's complimentary course for Premium Members, Team Accounts, and Qualified Experts.

Question has a verified solution.

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

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
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…
If you're a developer or IT admin, you’re probably tasked with managing multiple websites, servers, applications, and levels of security on a daily basis. While this can be extremely time consuming, it can also be frustrating when systems aren't wor…
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…

717 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