home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Vcl / httpintr.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  14KB  |  572 lines

  1. {*******************************************************}
  2. {                                                       }
  3. {       Borland Delphi Visual Component Library         }
  4. {                                                       }
  5. {       Copyright (c) 1995,99 Inprise Corporation       }
  6. {                                                       }
  7. {*******************************************************}
  8.  
  9. unit HTTPIntr;
  10.  
  11. interface
  12.  
  13. uses
  14.   Windows, Messages, SysUtils, Classes, HTTPApp, SConnect;
  15.  
  16. type
  17.  
  18.   { THTTPServer }
  19.  
  20.   THTTPServer = class(TWebModule, ISendDataBlock)
  21.     procedure InterpreterAction(Sender: TObject; Request: TWebRequest;
  22.       Response: TWebResponse; var Handled: Boolean);
  23.   private
  24.     FInterpreter: TDataBlockInterpreter;
  25.     FData: IDataBlock;
  26.   protected
  27.     function Send(const Data: IDataBlock; WaitForResult: Boolean): IDataBlock; stdcall;
  28.   end;
  29.  
  30. var
  31.   HTTPServer: THTTPServer;
  32.  
  33. implementation
  34.  
  35. uses
  36.   ComObj, ActiveX, MidConst, IsapiApp;
  37.  
  38. {$R *.DFM}
  39.  
  40. type
  41.  
  42.   { TPooledDataInterpreter }
  43.  
  44.   TPooledDataInterpreter = class(TDataBlockInterpreter)
  45.   protected
  46.     function InternalCreateObject(const ClassID: TGUID): OleVariant; override;
  47.     function StoreObject(const Value: OleVariant): Integer; override;
  48.     function LockObject(ID: Integer): IDispatch; override;
  49.     procedure UnLockObject(ID: Integer; const Disp: IDispatch); override;
  50.     procedure ReleaseObject(ID: Integer); override;
  51.   end;
  52.  
  53.   { TObject List }
  54.  
  55.   PObjectInfo = ^TObjectInfo;
  56.   TObjectInfo = record
  57.     Obj: IDispatch;
  58.     LastAccessed: TDateTime;
  59.     Locked: Boolean;
  60.   end;
  61.  
  62.   TObjectList = class
  63.   private
  64.     FClassID: string;
  65.     FLock: TRTLCriticalSection;
  66.     FOwner: TStringList;
  67.     FIndex: Integer;
  68.     FList: TList;
  69.     FMaxObjects: Integer;
  70.     FSingleton: Boolean;
  71.     FTimeout: TDateTime;
  72.   public
  73.     constructor Create(List: TStringList; const ClassID: string);
  74.     destructor Destroy; override;
  75.     procedure Lock;
  76.     procedure UnLock;
  77.     function LockObject: IDispatch;
  78.     procedure UnlockObject(const Dispatch: IDispatch);
  79.     property MaxObjects: Integer read FMaxObjects;
  80.     property Singleton: Boolean read FSingleton;
  81.     property Timeout: TDateTime read FTimeout;
  82.   end;
  83.  
  84.   { TGarbageCollector }
  85.  
  86.   TGarbageCollector = class(TThread)
  87.   private
  88.     FEvent: THandle;
  89.   protected
  90.     procedure Execute; override;
  91.   public
  92.     constructor Create;
  93.     destructor Destroy; override;
  94.     property Event: THandle read FEvent;
  95.   end;
  96.  
  97.   { TObjectManager }
  98.  
  99.   TObjectManager = class
  100.   private
  101.     FStateObjects: OleVariant;
  102.     FLock: TRTLCriticalSection;
  103.     FList: TStringList;
  104.     FNextID: Integer;
  105.     FSemaphore: THandle;
  106.     FGarbageCollector: TGarbageCollector;
  107.   protected
  108.     procedure Lock;
  109.     procedure Unlock;
  110.     function GetCatID(const ClassID: TGUID): Integer;
  111.     function LockList(CatID: Integer): TObjectList;
  112.   public
  113.     constructor Create;
  114.     destructor Destroy; override;
  115.     property Semaphore: THandle read FSemaphore;
  116.     function CreateObject(const ClassID: TGUID): OleVariant;
  117.     function StoreObject(const Value: OleVariant): Integer;
  118.     function LockObject(ID: Integer): OleVariant;
  119.     procedure UnLockObject(ID: Integer; const Disp: IDispatch);
  120.     procedure ReleaseObject(ID: Integer);
  121.   end;
  122.  
  123. var
  124.   ObjectManager: TObjectManager;
  125.  
  126. { THTTPServer }
  127.  
  128. function THTTPServer.Send(const Data: IDataBlock; WaitForResult: Boolean): IDataBlock;
  129. begin
  130.   FData := Data;
  131.   Result := nil;
  132. end;
  133.  
  134. procedure THTTPServer.InterpreterAction(Sender: TObject;
  135.   Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  136. var
  137.   DataBlock: IDataBlock;
  138.   S: string;
  139.   BytesRead, ChunkSize: Integer;
  140.   DataPacket: array of Byte;
  141. begin
  142.   FData := nil;
  143.   if not Assigned(FInterpreter) then
  144.     FInterpreter := TPooledDataInterpreter.Create(Self, SWeb);
  145.   S := Request.Content;
  146.   BytesRead := Length(S);
  147.   DataBlock := TDataBlock.Create;
  148.   if BytesRead < Request.ContentLength then
  149.   begin
  150.     SetLength(DataPacket, Request.ContentLength);
  151.     Move(S[1], DataPacket[0], BytesRead);
  152.     repeat
  153.       ChunkSize := TISAPIRequest(Request).ReadClient(Pointer(@Datapacket[BytesRead])^, Request.ContentLength - BytesRead);
  154.       if ChunkSize > 0 then
  155.       begin
  156.         Inc(BytesRead, ChunkSize);
  157.       end;
  158.     until ChunkSize = -1;
  159.     DataBlock.InitData(@DataPacket[0], Request.ContentLength, True);
  160.   end else
  161.     DataBlock.InitData(@S[1], Request.ContentLength, True);
  162.   FInterpreter.InterpretData(DataBlock);
  163.   if Assigned(FData) then
  164.   begin
  165.     Response.ContentStream := FData.Stream;
  166.     FData.IgnoreStream;
  167.   end;
  168. end;
  169.  
  170. { TPooledDataInterpreter }
  171.  
  172. function TPooledDataInterpreter.InternalCreateObject(const ClassID: TGUID): OleVariant;
  173. begin
  174.   Result := ObjectManager.CreateObject(ClassID);
  175. end;
  176.  
  177. function TPooledDataInterpreter.StoreObject(const Value: OleVariant): Integer;
  178. begin
  179.   Result := ObjectManager.StoreObject(Value);
  180. end;
  181.  
  182. function TPooledDataInterpreter.LockObject(ID: Integer): IDispatch;
  183. begin
  184.   Result := ObjectManager.LockObject(ID);
  185. end;
  186.  
  187. procedure TPooledDataInterpreter.UnLockObject(ID: Integer; const Disp: IDispatch);
  188. begin
  189.   ObjectManager.UnLockObject(ID, Disp);
  190. end;
  191.  
  192. procedure TPooledDataInterpreter.ReleaseObject(ID: Integer);
  193. begin
  194.   ObjectManager.ReleaseObject(ID);
  195. end;
  196.  
  197. { TObjectList }
  198.  
  199. constructor TObjectList.Create(List: TStringList; const ClassID: string);
  200. var
  201.   i: Integer;
  202. begin
  203.   InitializeCriticalSection(FLock);
  204.   FList := TList.Create;
  205.   FClassID := ClassID;
  206.   FOwner := List;
  207.   try
  208.     FMaxObjects := StrToInt(GetRegStringValue(SClsid + ClassID, SMaxObjects));
  209.     if FMaxObjects = 0 then FMaxObjects := MaxInt - 1;
  210.   except
  211.     FMaxObjects := MaxInt;
  212.   end;
  213.   FSingleton := GetRegStringValue(SClsid + ClassID, SSingleton) = SFlagOn;
  214.   try
  215.     i := StrToInt(GetRegStringValue(SClsid + ClassID, STimeout));
  216.     FTimeout := EncodeTime(i div 60, i mod 60, 0, 0);
  217.   except
  218.     FTimeout := 0;
  219.   end;
  220.   FIndex := FOwner.AddObject(ClassID, Self);
  221. end;
  222.  
  223. destructor TObjectList.Destroy;
  224. var
  225.   i: Integer;
  226. begin
  227.   Lock;
  228.   try
  229.     for i := 0 to FList.Count - 1 do
  230.       Dispose(PObjectInfo(FList[i]));
  231.     FOwner.Delete(FIndex);
  232.     inherited Destroy;
  233.   finally
  234.     UnLock;
  235.     DeleteCriticalSection(FLock);
  236.   end;
  237. end;
  238.  
  239. procedure TObjectList.Lock;
  240. begin
  241.   EnterCriticalSection(FLock);
  242. end;
  243.  
  244. procedure TObjectList.UnLock;
  245. begin
  246.   LeaveCriticalSection(FLock);
  247. end;
  248.  
  249. function TObjectList.LockObject: IDispatch;
  250.  
  251.   function CreateInfo: PObjectInfo;
  252.   begin
  253.     New(Result);
  254.     try
  255.       Result.LastAccessed := Now;
  256.       { Singleton Object is never locked }
  257.       Result.Locked := not Singleton;
  258.       { For singleton objects, the object create needs to be blocked,
  259.         for pooled objects, the creation shouldn't be blocked }
  260.       if Singleton then
  261.         Result.Obj := CreateComObject(StringToGUID(FClassID)) as IDispatch else
  262.         Result.Obj := nil;
  263.       FList.Add(Result);
  264.     except
  265.       Dispose(Result);
  266.       raise;
  267.     end;
  268.   end;
  269.  
  270. var
  271.   i: Integer;
  272.   P: PObjectInfo;
  273. begin
  274.   if Singleton then
  275.   begin
  276.     if FList.Count < 1 then
  277.     begin
  278.       Lock;
  279.       try
  280.         if FList.Count < 1 then
  281.           CreateInfo;
  282.       finally
  283.         Unlock;
  284.       end;
  285.     end;
  286.     with PObjectInfo(FList[0])^ do
  287.     begin
  288.       LastAccessed := Now;
  289.       Result := Obj;
  290.     end;
  291.   end else
  292.   begin
  293.     P := nil;
  294.     Lock;
  295.     try
  296.       for i := 0 to FList.Count - 1 do
  297.         with PObjectInfo(FList[i])^ do
  298.           if not Locked then
  299.           begin
  300.             Locked := True;
  301.             LastAccessed := Now;
  302.             Result := Obj;
  303.             Exit;
  304.           end;
  305.       if FList.Count >= MaxObjects then
  306.         raise Exception.CreateRes(@SServerIsBusy);
  307.       P := CreateInfo;
  308.     finally
  309.       Unlock;
  310.     end;
  311.     if Assigned(P) then
  312.     begin
  313.       try
  314.         P.Obj := CreateComObject(StringToGUID(FClassID)) as IDispatch;
  315.       except
  316.         Lock;
  317.         try
  318.           FList.Remove(P);
  319.           Dispose(P);
  320.         finally
  321.           Unlock;
  322.         end;
  323.         raise;
  324.       end;
  325.       Result := P.Obj;
  326.     end;
  327.   end;
  328. end;
  329.  
  330. procedure TObjectList.UnlockObject(const Dispatch: IDispatch);
  331. var
  332.   i: Integer;
  333. begin
  334.   if Singleton then
  335.   begin
  336.     PObjectInfo(FList[0]).LastAccessed := Now;
  337.   end else
  338.   begin
  339.     Lock;
  340.     try
  341.       for i := 0 to FList.Count - 1 do
  342.         with PObjectInfo(FList[i])^ do
  343.           if Obj = Dispatch then
  344.           begin
  345.             Locked := False;
  346.             LastAccessed := Now;
  347.             Exit;
  348.           end;
  349.     finally
  350.       Unlock;
  351.     end;
  352.   end;
  353. end;
  354.  
  355. constructor TGarbageCollector.Create;
  356. begin
  357.   FEvent := CreateEvent(nil, False, False, nil);
  358.   inherited Create(False);
  359. end;
  360.  
  361. destructor TGarbageCollector.Destroy;
  362. begin
  363.   CloseHandle(FEvent);
  364.   inherited Destroy;
  365. end;
  366.  
  367. procedure TGarbageCollector.Execute;
  368.  
  369.   function CheckObject(ObjectInfo: PObjectInfo; Timeout: TDateTime): Boolean;
  370.   begin
  371.     Result := False;
  372.     with ObjectInfo^ do
  373.     begin
  374.       if not Locked then
  375.       begin
  376.         Result := (Timeout > 0) and ((Now - LastAccessed) > Timeout);
  377.         if Result then Obj := nil;
  378.       end;
  379.     end;
  380.   end;
  381.  
  382.   procedure CheckObjectList(ObjectList: TObjectList);
  383.   var
  384.     i: Integer;
  385.   begin
  386.     with ObjectList do
  387.     begin
  388.       if not Singleton then
  389.       begin
  390.         Lock;
  391.         try
  392.           for i := 0 to FList.Count - 1 do
  393.             if CheckObject(PObjectInfo(FList[i]), Timeout) then
  394.             begin
  395.               Dispose(PObjectInfo(FList[i]));
  396.               FList.Delete(i);
  397.             end;
  398.         finally
  399.           Unlock;
  400.         end;
  401.       end;
  402.     end;
  403.   end;
  404.  
  405. var
  406.   i: Integer;
  407. begin
  408.   while not Terminated do
  409.     if WaitForSingleObject(FEvent, 360000) = WAIT_TIMEOUT then
  410.     begin
  411.       ObjectManager.Lock;
  412.       try
  413.         for i := 0 to ObjectManager.FList.Count - 1 do
  414.           CheckObjectList(TObjectList(ObjectManager.FList.Objects[i]));
  415.       finally
  416.         ObjectManager.Unlock;
  417.       end;
  418.     end else
  419.       Exit;
  420. end;
  421.  
  422. { TObjectManager }
  423.  
  424. constructor TObjectManager.Create;
  425. begin
  426.   InitializeCriticalSection(FLock);
  427.   FNextID := 0;
  428.   FList := TStringList.Create;
  429.   FList.Sorted := True;
  430.   FGarbageCollector := TGarbageCollector.Create;
  431. end;
  432.  
  433. destructor TObjectManager.Destroy;
  434. var
  435.   i: Integer;
  436. begin
  437.   FGarbageCollector.Terminate;
  438.   PulseEvent(FGarbageCollector.Event);
  439.   FGarbageCollector.WaitFor;
  440.   Lock;
  441.   try
  442.     for i := 0 to FList.Count - 1 do
  443.       TObjectList(FList[i]).Free;
  444.     FList.Free;
  445.     inherited Destroy;
  446.   finally
  447.     UnLock;
  448.     DeleteCriticalSection(FLock);
  449.   end;
  450. end;
  451.  
  452. procedure TObjectManager.Lock;
  453. begin
  454.   EnterCriticalSection(FLock);
  455. end;
  456.  
  457. procedure TObjectManager.Unlock;
  458. begin
  459.   LeaveCriticalSection(FLock);
  460. end;
  461.  
  462. function TObjectManager.GetCatID(const ClassID: TGUID): Integer;
  463. var
  464.   S: string;
  465. begin
  466.   Lock;
  467.   try
  468.     S := GuidToString(ClassID);
  469.     if not FList.Find(S, Result) then
  470.       Result := FList.AddObject(S, TObjectList.Create(FList, S));
  471.     Result := (Result + 1) shl 16;
  472.   finally
  473.     Unlock;
  474.   end;
  475. end;
  476.  
  477. function TObjectManager.LockList(CatID: Integer): TObjectList;
  478. begin
  479.   Lock;
  480.   try
  481.     Result := TObjectList(FList.Objects[HiWord(CatID) - 1]);
  482.   finally
  483.     Unlock;
  484.   end;
  485. end;
  486.  
  487. function TObjectManager.CreateObject(const ClassID: TGUID): OleVariant;
  488.  
  489.   function CreateObject(const ClassID: TGUID): IDispatch;
  490.   var
  491.     Unk: IUnknown;
  492.   begin
  493.     OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
  494.       CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER, IUnknown, Unk));
  495.     Result := Unk as IDispatch;
  496.   end;
  497.  
  498.   function IsClassPooled(ClassID: TGUID): Boolean;
  499.   begin
  500.     Result := GetRegStringValue(SClsid + GuidToString(ClassID), SPooled) = SFlagOn;
  501.   end;
  502.  
  503. begin
  504.   if IsClassPooled(ClassID) then
  505.     Result := GetCatID(ClassID) else
  506.     Result := CreateObject(ClassID);
  507. end;
  508.  
  509. function TObjectManager.StoreObject(const Value: OleVariant): Integer;
  510. begin
  511.   { This is only used for statefull objects }
  512.   Lock;
  513.   try
  514.     if not VarIsArray(FStateObjects) then
  515.       FStateObjects := VarArrayCreate([0,10], varVariant);
  516.     Result := FNextID;
  517.     if Result > VarArrayHighBound(FStateObjects, 1) then
  518.       VarArrayRedim(FStateObjects, Result + 10);
  519.     if VarIsEmpty(FStateObjects[Result]) then
  520.       FNextID := Result + 1 else
  521.       FNextID := FStateObjects[Result];
  522.     FStateObjects[Result] := Value;
  523.   finally
  524.     UnLock;
  525.   end;
  526. end;
  527.  
  528. function TObjectManager.LockObject(ID: Integer): OleVariant;
  529. begin
  530.   if HiWord(ID) = 0 then
  531.   begin
  532.     Lock;
  533.     try
  534.       Result := FStateObjects[ID];
  535.     finally
  536.       UnLock;
  537.     end;
  538.   end else
  539.     Result := LockList(ID).LockObject;
  540. end;
  541.  
  542. procedure TObjectManager.UnLockObject(ID: Integer; const Disp: IDispatch);
  543. begin
  544.   { Only used for stateless objects }
  545.   if HiWord(ID) > 0 then LockList(ID).UnlockObject(Disp);
  546. end;
  547.  
  548. procedure TObjectManager.ReleaseObject(ID: Integer);
  549. begin
  550.   { This is only used for statefull objects }
  551.   if HiWord(ID) > 0 then Exit;
  552.   Lock;
  553.   try
  554.     if (ID >= 0) and (VarIsArray(FStateObjects)) and
  555.        (ID < VarArrayHighBound(FStateObjects, 1)) then
  556.     begin
  557.       FStateObjects[ID] := FNextID;
  558.       FNextID := ID;
  559.     end;
  560.   finally
  561.     UnLock;
  562.   end;
  563. end;
  564.  
  565. initialization
  566.   CoInitializeEx(nil, COINIT_MULTITHREADED);
  567.   ObjectManager := TObjectManager.Create;
  568. finalization
  569.   ObjectManager.Free;
  570.   CoUninitialize;
  571. end.
  572.