Browse Source

* Changes as suggested in bug ID #24810, so a threaded web application can be stopped correctly, even from a request

git-svn-id: trunk@25571 -
michael 12 years ago
parent
commit
973c0687fc

+ 16 - 9
packages/fcl-web/src/base/custhttpapp.pp

@@ -33,8 +33,8 @@ Type
   Private
   Private
     FWebHandler: TFPHTTPServerHandler;
     FWebHandler: TFPHTTPServerHandler;
   protected
   protected
-    Procedure InitRequest(ARequest : TFPHTTPConnectionRequest); virtual;
-    Procedure InitResponse(AResponse : TFPHTTPConnectionResponse); virtual;
+    Procedure InitRequest(ARequest : TFPHTTPConnectionRequest); override;
+    Procedure InitResponse(AResponse : TFPHTTPConnectionResponse); override;
     Property WebHandler : TFPHTTPServerHandler Read FWebHandler;
     Property WebHandler : TFPHTTPServerHandler Read FWebHandler;
     Property Active;
     Property Active;
   end;
   end;
@@ -44,9 +44,6 @@ Type
   { TFPHTTPServerHandler }
   { TFPHTTPServerHandler }
 
 
   TFPHTTPServerHandler = class(TWebHandler)
   TFPHTTPServerHandler = class(TWebHandler)
-    procedure HTTPHandleRequest(Sender: TObject;
-      var ARequest: TFPHTTPConnectionRequest;
-      var AResponse: TFPHTTPConnectionResponse);
   Private
   Private
     FOnRequestError: TRequestErrorHandler;
     FOnRequestError: TRequestErrorHandler;
     FServer: TEmbeddedHTTPServer;
     FServer: TEmbeddedHTTPServer;
@@ -61,6 +58,7 @@ Type
     function GetLookupHostNames : Boolean;
     function GetLookupHostNames : Boolean;
     Procedure SetLookupHostnames(Avalue : Boolean);
     Procedure SetLookupHostnames(Avalue : Boolean);
   protected
   protected
+    procedure HTTPHandleRequest(Sender: TObject; var ARequest: TFPHTTPConnectionRequest; var AResponse: TFPHTTPConnectionResponse); virtual;
     procedure HandleRequestError(Sender: TObject; E: Exception); virtual;
     procedure HandleRequestError(Sender: TObject; E: Exception); virtual;
     Procedure InitRequest(ARequest : TRequest); override;
     Procedure InitRequest(ARequest : TRequest); override;
     Procedure InitResponse(AResponse : TResponse); override;
     Procedure InitResponse(AResponse : TResponse); override;
@@ -69,6 +67,7 @@ Type
     Property HTTPServer : TEmbeddedHttpServer Read FServer;
     Property HTTPServer : TEmbeddedHttpServer Read FServer;
   Public
   Public
     Procedure Run; override;
     Procedure Run; override;
+    Procedure Terminate; override;
     constructor Create(AOwner: TComponent); override;
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
     // Port to listen on.
     // Port to listen on.
@@ -225,8 +224,6 @@ begin
     ARequest:=Nil;
     ARequest:=Nil;
     AResponse:=Nil;
     AResponse:=Nil;
   end;    
   end;    
-  If Terminated And Assigned(FServer) then
-    FServer.Active:=False;
   if Assigned(OnIdle) then
   if Assigned(OnIdle) then
     OnIdle(Self);
     OnIdle(Self);
 end;
 end;
@@ -311,6 +308,13 @@ begin
   Fserver.Active:=True;
   Fserver.Active:=True;
 end;
 end;
 
 
+procedure TFPHTTPServerHandler.Terminate;
+begin
+  Inherited;
+  if Assigned(FServer) then
+    Fserver.Active:=False;
+end;
+
 constructor TFPHTTPServerHandler.Create(AOwner: TComponent);
 constructor TFPHTTPServerHandler.Create(AOwner: TComponent);
 begin
 begin
   inherited Create(AOwner);
   inherited Create(AOwner);
@@ -322,8 +326,11 @@ end;
 
 
 destructor TFPHTTPServerHandler.Destroy;
 destructor TFPHTTPServerHandler.Destroy;
 begin
 begin
-  FServer.Active:=False;
-  FreeAndNil(FServer);
+  if Assigned(FServer) then
+    begin
+    FServer.Active:=False;
+    FreeAndNil(FServer);
+    end;
   inherited Destroy;
   inherited Destroy;
 
 
 end;
 end;

+ 1 - 1
packages/fcl-web/src/base/custweb.pp

@@ -106,7 +106,7 @@ Type
     FOnLog : TLogEvent;
     FOnLog : TLogEvent;
     FPreferModuleName : Boolean;
     FPreferModuleName : Boolean;
   protected
   protected
-    procedure Terminate;
+    procedure Terminate; virtual;
     Function GetModuleName(Arequest : TRequest) : string;
     Function GetModuleName(Arequest : TRequest) : string;
     function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; virtual; abstract;
     function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; virtual; abstract;
     procedure EndRequest(ARequest : TRequest;AResponse : TResponse); virtual;
     procedure EndRequest(ARequest : TRequest;AResponse : TResponse); virtual;

+ 52 - 4
packages/fcl-web/src/base/fphttpserver.pp

@@ -119,6 +119,7 @@ Type
     FServerBanner: string;
     FServerBanner: string;
     FLookupHostNames,
     FLookupHostNames,
     FThreaded: Boolean;
     FThreaded: Boolean;
+    FConnectionCount : Integer;
     function GetActive: Boolean;
     function GetActive: Boolean;
     procedure SetActive(const AValue: Boolean);
     procedure SetActive(const AValue: Boolean);
     procedure SetOnAllowConnect(const AValue: TConnectQuery);
     procedure SetOnAllowConnect(const AValue: TConnectQuery);
@@ -126,13 +127,15 @@ Type
     procedure SetQueueSize(const AValue: Word);
     procedure SetQueueSize(const AValue: Word);
     procedure SetThreaded(const AValue: Boolean);
     procedure SetThreaded(const AValue: Boolean);
     procedure SetupSocket;
     procedure SetupSocket;
-    procedure StartServerSocket;
+    procedure WaitForRequests;
   Protected
   Protected
     // Override these to create descendents of the request/response instead.
     // Override these to create descendents of the request/response instead.
     Function CreateRequest : TFPHTTPConnectionRequest; virtual;
     Function CreateRequest : TFPHTTPConnectionRequest; virtual;
     Function CreateResponse(ARequest : TFPHTTPConnectionRequest) : TFPHTTPConnectionResponse; virtual;
     Function CreateResponse(ARequest : TFPHTTPConnectionRequest) : TFPHTTPConnectionResponse; virtual;
     Procedure InitRequest(ARequest : TFPHTTPConnectionRequest); virtual;
     Procedure InitRequest(ARequest : TFPHTTPConnectionRequest); virtual;
     Procedure InitResponse(AResponse : TFPHTTPConnectionResponse); virtual;
     Procedure InitResponse(AResponse : TFPHTTPConnectionResponse); virtual;
+    // Called on accept errors
+    procedure DoAcceptError(Sender: TObject; ASocket: Longint; E: Exception;  var ErrorAction: TAcceptErrorAction);
     // Create a connection handling object.
     // Create a connection handling object.
     function CreateConnection(Data : TSocketStream) : TFPHTTPConnection; virtual;
     function CreateConnection(Data : TSocketStream) : TFPHTTPConnection; virtual;
     // Create a connection handling thread.
     // Create a connection handling thread.
@@ -143,13 +146,19 @@ Type
     Procedure DoConnect(Sender : TObject; Data : TSocketStream); virtual;
     Procedure DoConnect(Sender : TObject; Data : TSocketStream); virtual;
     // Create and configure TInetServer
     // Create and configure TInetServer
     Procedure CreateServerSocket; virtual;
     Procedure CreateServerSocket; virtual;
-    // Stop and free TInetServer
+    // Start server socket
+    procedure StartServerSocket; virtual;
+    // Stop server stocket
+    procedure StopServerSocket; virtual;
+    // free server socket instance
     Procedure FreeServerSocket; virtual;
     Procedure FreeServerSocket; virtual;
     // Handle request. This calls OnRequest. It can be overridden by descendants to provide standard handling.
     // Handle request. This calls OnRequest. It can be overridden by descendants to provide standard handling.
     procedure HandleRequest(Var ARequest: TFPHTTPConnectionRequest;
     procedure HandleRequest(Var ARequest: TFPHTTPConnectionRequest;
                             Var AResponse : TFPHTTPConnectionResponse); virtual;
                             Var AResponse : TFPHTTPConnectionResponse); virtual;
     // Called when a connection encounters an unexpected error. Will call OnRequestError when set.
     // Called when a connection encounters an unexpected error. Will call OnRequestError when set.
     procedure HandleRequestError(Sender: TObject; E: Exception); virtual;
     procedure HandleRequestError(Sender: TObject; E: Exception); virtual;
+    // Connection count
+    Property ConnectionCount : Integer Read FConnectionCount;
   public
   public
     Constructor Create(AOwner : TComponent); override;
     Constructor Create(AOwner : TComponent); override;
     Destructor Destroy; override;
     Destructor Destroy; override;
@@ -542,10 +551,14 @@ constructor TFPHTTPConnection.Create(AServer: TFPCustomHttpServer; ASocket: TSoc
 begin
 begin
   FSocket:=ASocket;
   FSocket:=ASocket;
   FServer:=AServer;
   FServer:=AServer;
+  If Assigned(FServer) then
+    InterLockedIncrement(FServer.FConnectionCount)
 end;
 end;
 
 
 destructor TFPHTTPConnection.Destroy;
 destructor TFPHTTPConnection.Destroy;
 begin
 begin
+  If Assigned(FServer) then
+    InterLockedDecrement(FServer.FConnectionCount);
   FreeAndNil(FSocket);
   FreeAndNil(FSocket);
   Inherited;
   Inherited;
 end;
 end;
@@ -634,6 +647,15 @@ begin
     end
     end
 end;
 end;
 
 
+procedure TFPCustomHttpServer.DoAcceptError(Sender: TObject; ASocket: Longint;
+  E: Exception; var ErrorAction: TAcceptErrorAction);
+begin
+  If Not Active then
+    ErrorAction:=AEAStop
+  else
+    ErrorAction:=AEARaise
+end;
+
 function TFPCustomHttpServer.GetActive: Boolean;
 function TFPCustomHttpServer.GetActive: Boolean;
 begin
 begin
   if (csDesigning in ComponentState) then
   if (csDesigning in ComponentState) then
@@ -642,6 +664,11 @@ begin
     Result:=Assigned(FServer);
     Result:=Assigned(FServer);
 end;
 end;
 
 
+procedure TFPCustomHttpServer.StopServerSocket;
+begin
+  FServer.StopAccepting(True);
+end;
+
 procedure TFPCustomHttpServer.SetActive(const AValue: Boolean);
 procedure TFPCustomHttpServer.SetActive(const AValue: Boolean);
 begin
 begin
   If AValue=GetActive then exit;
   If AValue=GetActive then exit;
@@ -652,9 +679,10 @@ begin
       CreateServerSocket;
       CreateServerSocket;
       SetupSocket;
       SetupSocket;
       StartServerSocket;
       StartServerSocket;
+      FreeServerSocket;
       end
       end
     else
     else
-      FreeServerSocket;
+      StopServerSocket;
 end;
 end;
 
 
 procedure TFPCustomHttpServer.SetOnAllowConnect(const AValue: TConnectQuery);
 procedure TFPCustomHttpServer.SetOnAllowConnect(const AValue: TConnectQuery);
@@ -758,6 +786,7 @@ begin
   FServer.MaxConnections:=-1;
   FServer.MaxConnections:=-1;
   FServer.OnConnectQuery:=OnAllowConnect;
   FServer.OnConnectQuery:=OnAllowConnect;
   FServer.OnConnect:=@DOConnect;
   FServer.OnConnect:=@DOConnect;
+  FServer.OnAcceptError:=@DoAcceptError;
 end;
 end;
 
 
 procedure TFPCustomHttpServer.StartServerSocket;
 procedure TFPCustomHttpServer.StartServerSocket;
@@ -769,7 +798,6 @@ end;
 
 
 procedure TFPCustomHttpServer.FreeServerSocket;
 procedure TFPCustomHttpServer.FreeServerSocket;
 begin
 begin
-  FServer.StopAccepting;
   FreeAndNil(FServer);
   FreeAndNil(FServer);
 end;
 end;
 
 
@@ -788,9 +816,29 @@ begin
   FServerBanner := 'Freepascal';
   FServerBanner := 'Freepascal';
 end;
 end;
 
 
+Procedure TFPCustomHttpServer.WaitForRequests;
+
+Var
+  FLastCount,ACount : Integer;
+
+begin
+  ACount:=0;
+  FLastCount:=FConnectionCount;
+  While (FConnectionCount>0) and (ACount<10) do
+    begin
+    Sleep(100);
+    if (FConnectionCount=FLastCount) then
+      Dec(ACount)
+    else
+      FLastCount:=FConnectionCount;
+    end;
+end;
+
 destructor TFPCustomHttpServer.Destroy;
 destructor TFPCustomHttpServer.Destroy;
 begin
 begin
   Active:=False;
   Active:=False;
+  if Threaded and (FConnectionCount>0) then
+    WaitForRequests;
   inherited Destroy;
   inherited Destroy;
 end;
 end;