mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-09-16 13:49:40 +02:00
* Better error handling, continue to serve requests (bug ID 22260)
git-svn-id: trunk@23238 -
This commit is contained in:
parent
764b132645
commit
91c8177890
@ -48,6 +48,7 @@ Type
|
|||||||
var ARequest: TFPHTTPConnectionRequest;
|
var ARequest: TFPHTTPConnectionRequest;
|
||||||
var AResponse: TFPHTTPConnectionResponse);
|
var AResponse: TFPHTTPConnectionResponse);
|
||||||
Private
|
Private
|
||||||
|
FOnRequestError: TRequestErrorHandler;
|
||||||
FServer: TEmbeddedHTTPServer;
|
FServer: TEmbeddedHTTPServer;
|
||||||
function GetAllowConnect: TConnectQuery;
|
function GetAllowConnect: TConnectQuery;
|
||||||
function GetPort: Word;
|
function GetPort: Word;
|
||||||
@ -58,6 +59,7 @@ Type
|
|||||||
procedure SetQueueSize(const AValue: Word);
|
procedure SetQueueSize(const AValue: Word);
|
||||||
procedure SetThreaded(const AValue: Boolean);
|
procedure SetThreaded(const AValue: Boolean);
|
||||||
protected
|
protected
|
||||||
|
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;
|
||||||
function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; override;
|
function WaitForRequest(out ARequest : TRequest; out AResponse : TResponse) : boolean; override;
|
||||||
@ -75,6 +77,8 @@ Type
|
|||||||
Property OnAllowConnect : TConnectQuery Read GetAllowConnect Write SetOnAllowConnect;
|
Property OnAllowConnect : TConnectQuery Read GetAllowConnect Write SetOnAllowConnect;
|
||||||
// Use a thread to handle a connection ?
|
// Use a thread to handle a connection ?
|
||||||
property Threaded : Boolean read GetThreaded Write SetThreaded;
|
property Threaded : Boolean read GetThreaded Write SetThreaded;
|
||||||
|
// Handle On Request error. If not set, error is logged.
|
||||||
|
Property OnRequestError : TRequestErrorHandler Read FOnRequestError Write FOnRequestError;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TCustomHTTPApplication }
|
{ TCustomHTTPApplication }
|
||||||
@ -102,14 +106,6 @@ Type
|
|||||||
property Threaded : Boolean read GetThreaded Write SetThreaded;
|
property Threaded : Boolean read GetThreaded Write SetThreaded;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ResourceString
|
|
||||||
SNoInputHandle = 'Failed to open input-handle passed from server. Socket Error: %d';
|
|
||||||
SNoSocket = 'Failed to open socket. Socket Error: %d';
|
|
||||||
SBindFailed = 'Failed to bind to port %d. Socket Error: %d';
|
|
||||||
SListenFailed = 'Failed to listen to port %d. Socket Error: %d';
|
|
||||||
SErrReadingSocket = 'Failed to read data from socket. Error: %d';
|
|
||||||
SErrReadingHeader = 'Failed to read FastCGI header. Read only %d bytes';
|
|
||||||
SErrWritingSocket = 'Failed to write data to socket. Error: %d';
|
|
||||||
|
|
||||||
Implementation
|
Implementation
|
||||||
|
|
||||||
@ -185,6 +181,19 @@ end;
|
|||||||
|
|
||||||
{ TFPHTTPServerHandler }
|
{ TFPHTTPServerHandler }
|
||||||
|
|
||||||
|
procedure TFPHTTPServerHandler.HandleRequestError(Sender: TObject; E: Exception
|
||||||
|
);
|
||||||
|
begin
|
||||||
|
Try
|
||||||
|
If Assigned(FOnRequestError) then
|
||||||
|
FOnRequestError(Sender,E)
|
||||||
|
else
|
||||||
|
Log(etError,Format('Error (%s) handling request : %s',[E.ClassName,E.Message]));
|
||||||
|
except
|
||||||
|
// Do not let errors escape
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TFPHTTPServerHandler.HTTPHandleRequest(Sender: TObject;
|
procedure TFPHTTPServerHandler.HTTPHandleRequest(Sender: TObject;
|
||||||
var ARequest: TFPHTTPConnectionRequest;
|
var ARequest: TFPHTTPConnectionRequest;
|
||||||
var AResponse: TFPHTTPConnectionResponse);
|
var AResponse: TFPHTTPConnectionResponse);
|
||||||
@ -273,6 +282,7 @@ begin
|
|||||||
FServer:=CreateServer;
|
FServer:=CreateServer;
|
||||||
FServer.FWebHandler:=Self;
|
FServer.FWebHandler:=Self;
|
||||||
FServer.OnRequest:=@HTTPHandleRequest;
|
FServer.OnRequest:=@HTTPHandleRequest;
|
||||||
|
Fserver.OnRequestError:=@HandleRequestError;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TFPHTTPServerHandler.Destroy;
|
destructor TFPHTTPServerHandler.Destroy;
|
||||||
|
@ -29,6 +29,7 @@ Type
|
|||||||
TFPHTTPConnection = Class;
|
TFPHTTPConnection = Class;
|
||||||
TFPHTTPConnectionThread = Class;
|
TFPHTTPConnectionThread = Class;
|
||||||
TFPCustomHttpServer = Class;
|
TFPCustomHttpServer = Class;
|
||||||
|
TRequestErrorHandler = Procedure (Sender : TObject; E : Exception) of object;
|
||||||
|
|
||||||
{ TFPHTTPConnectionRequest }
|
{ TFPHTTPConnectionRequest }
|
||||||
|
|
||||||
@ -61,6 +62,7 @@ Type
|
|||||||
|
|
||||||
TFPHTTPConnection = Class(TObject)
|
TFPHTTPConnection = Class(TObject)
|
||||||
private
|
private
|
||||||
|
FOnError: TRequestErrorHandler;
|
||||||
FServer: TFPCustomHTTPServer;
|
FServer: TFPCustomHTTPServer;
|
||||||
FSocket: TSocketStream;
|
FSocket: TSocketStream;
|
||||||
FBuffer : Ansistring;
|
FBuffer : Ansistring;
|
||||||
@ -69,6 +71,8 @@ Type
|
|||||||
Protected
|
Protected
|
||||||
procedure ReadRequestContent(ARequest: TFPHTTPConnectionRequest); virtual;
|
procedure ReadRequestContent(ARequest: TFPHTTPConnectionRequest); virtual;
|
||||||
procedure UnknownHeader(ARequest: TFPHTTPConnectionRequest; const AHeader: String); virtual;
|
procedure UnknownHeader(ARequest: TFPHTTPConnectionRequest; const AHeader: String); virtual;
|
||||||
|
procedure HandleRequestError(E : Exception); virtual;
|
||||||
|
Procedure SetupSocket; virtual;
|
||||||
Function ReadRequestHeaders : TFPHTTPConnectionRequest;
|
Function ReadRequestHeaders : TFPHTTPConnectionRequest;
|
||||||
Public
|
Public
|
||||||
Constructor Create(AServer : TFPCustomHTTPServer; ASocket : TSocketStream);
|
Constructor Create(AServer : TFPCustomHTTPServer; ASocket : TSocketStream);
|
||||||
@ -76,6 +80,7 @@ Type
|
|||||||
Procedure HandleRequest; virtual;
|
Procedure HandleRequest; virtual;
|
||||||
Property Socket : TSocketStream Read FSocket;
|
Property Socket : TSocketStream Read FSocket;
|
||||||
Property Server : TFPCustomHTTPServer Read FServer;
|
Property Server : TFPCustomHTTPServer Read FServer;
|
||||||
|
Property OnRequestError : TRequestErrorHandler Read FOnError Write FOnError;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TFPHTTPConnectionThread }
|
{ TFPHTTPConnectionThread }
|
||||||
@ -102,6 +107,7 @@ Type
|
|||||||
FAdminName: string;
|
FAdminName: string;
|
||||||
FOnAllowConnect: TConnectQuery;
|
FOnAllowConnect: TConnectQuery;
|
||||||
FOnRequest: THTTPServerRequestHandler;
|
FOnRequest: THTTPServerRequestHandler;
|
||||||
|
FOnRequestError: TRequestErrorHandler;
|
||||||
FPort: Word;
|
FPort: Word;
|
||||||
FQueueSize: Word;
|
FQueueSize: Word;
|
||||||
FServer : TInetServer;
|
FServer : TInetServer;
|
||||||
@ -114,6 +120,8 @@ Type
|
|||||||
procedure SetPort(const AValue: Word);
|
procedure SetPort(const AValue: Word);
|
||||||
procedure SetQueueSize(const AValue: Word);
|
procedure SetQueueSize(const AValue: Word);
|
||||||
procedure SetThreaded(const AValue: Boolean);
|
procedure SetThreaded(const AValue: Boolean);
|
||||||
|
procedure SetupSocket;
|
||||||
|
procedure StartServerSocket;
|
||||||
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;
|
||||||
@ -135,6 +143,8 @@ Type
|
|||||||
// 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.
|
||||||
|
procedure HandleRequestError(Sender: TObject; E: Exception); virtual;
|
||||||
public
|
public
|
||||||
Constructor Create(AOwner : TComponent); override;
|
Constructor Create(AOwner : TComponent); override;
|
||||||
Destructor Destroy; override;
|
Destructor Destroy; override;
|
||||||
@ -151,7 +161,8 @@ Type
|
|||||||
property Threaded : Boolean read FThreaded Write SetThreaded;
|
property Threaded : Boolean read FThreaded Write SetThreaded;
|
||||||
// Called to handle the request. If Threaded=True, it is called in a the connection thread.
|
// Called to handle the request. If Threaded=True, it is called in a the connection thread.
|
||||||
Property OnRequest : THTTPServerRequestHandler Read FOnRequest Write FOnRequest;
|
Property OnRequest : THTTPServerRequestHandler Read FOnRequest Write FOnRequest;
|
||||||
|
// Called when an unexpected error occurs during handling of the request. Sender is the TFPHTTPConnection.
|
||||||
|
Property OnRequestError : TRequestErrorHandler Read FOnRequestError Write FOnRequestError;
|
||||||
published
|
published
|
||||||
//aditional server information
|
//aditional server information
|
||||||
property AdminMail: string read FAdminMail write FAdminMail;
|
property AdminMail: string read FAdminMail write FAdminMail;
|
||||||
@ -167,6 +178,7 @@ Type
|
|||||||
Property OnAllowConnect;
|
Property OnAllowConnect;
|
||||||
property Threaded;
|
property Threaded;
|
||||||
Property OnRequest;
|
Property OnRequest;
|
||||||
|
Property OnRequestError;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
EHTTPServer = Class(Exception);
|
EHTTPServer = Class(Exception);
|
||||||
@ -175,6 +187,8 @@ Type
|
|||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
uses sockets;
|
||||||
|
|
||||||
resourcestring
|
resourcestring
|
||||||
SErrSocketActive = 'Operation not allowed while server is active';
|
SErrSocketActive = 'Operation not allowed while server is active';
|
||||||
SErrReadingSocket = 'Error reading data from the socket';
|
SErrReadingSocket = 'Error reading data from the socket';
|
||||||
@ -230,6 +244,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure HandleRequestError(Sender: TObject; E: Exception);
|
||||||
|
begin
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TFPHTTPConnectionRequest.InitRequestVars;
|
procedure TFPHTTPConnectionRequest.InitRequestVars;
|
||||||
Var
|
Var
|
||||||
P : Integer;
|
P : Integer;
|
||||||
@ -357,6 +376,24 @@ begin
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TFPHTTPConnection.HandleRequestError(E: Exception);
|
||||||
|
begin
|
||||||
|
If Assigned(FOnError) then
|
||||||
|
try
|
||||||
|
FOnError(Self,E);
|
||||||
|
except
|
||||||
|
// We really cannot handle this...
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TFPHTTPConnection.SetupSocket;
|
||||||
|
begin
|
||||||
|
{$ifdef unix}
|
||||||
|
FSocket.ReadFlags:=MSG_NOSIGNAL;
|
||||||
|
FSocket.WriteFlags:=MSG_NOSIGNAL;
|
||||||
|
{$endif}
|
||||||
|
end;
|
||||||
|
|
||||||
Procedure TFPHTTPConnection.InterPretHeader(ARequest : TFPHTTPConnectionRequest; Const AHeader : String);
|
Procedure TFPHTTPConnection.InterPretHeader(ARequest : TFPHTTPConnectionRequest; Const AHeader : String);
|
||||||
|
|
||||||
Var
|
Var
|
||||||
@ -446,15 +483,20 @@ Var
|
|||||||
StartLine,S : String;
|
StartLine,S : String;
|
||||||
begin
|
begin
|
||||||
Result:=Server.CreateRequest;
|
Result:=Server.CreateRequest;
|
||||||
Server.InitRequest(Result);
|
try
|
||||||
Result.FConnection:=Self;
|
Server.InitRequest(Result);
|
||||||
StartLine:=ReadString;
|
Result.FConnection:=Self;
|
||||||
ParseStartLine(Result,StartLine);
|
StartLine:=ReadString;
|
||||||
Repeat
|
ParseStartLine(Result,StartLine);
|
||||||
S:=ReadString;
|
Repeat
|
||||||
if (S<>'') then
|
S:=ReadString;
|
||||||
InterPretHeader(Result,S);
|
if (S<>'') then
|
||||||
Until (S='');
|
InterPretHeader(Result,S);
|
||||||
|
Until (S='');
|
||||||
|
except
|
||||||
|
FreeAndNil(Result);
|
||||||
|
Raise;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
constructor TFPHTTPConnection.Create(AServer: TFPCustomHttpServer; ASocket: TSocketStream);
|
constructor TFPHTTPConnection.Create(AServer: TFPCustomHttpServer; ASocket: TSocketStream);
|
||||||
@ -476,30 +518,36 @@ Var
|
|||||||
Resp : TFPHTTPConnectionResponse;
|
Resp : TFPHTTPConnectionResponse;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
// Read headers.
|
Try
|
||||||
Req:=ReadRequestHeaders;
|
SetupSocket;
|
||||||
//set port
|
// Read headers.
|
||||||
Req.ServerPort := Server.Port;
|
Req:=ReadRequestHeaders;
|
||||||
try
|
|
||||||
// Read content, if any
|
|
||||||
If Req.ContentLength>0 then
|
|
||||||
ReadRequestContent(Req);
|
|
||||||
Req.InitRequestVars;
|
|
||||||
// Create Response
|
|
||||||
Resp:= Server.CreateResponse(Req);
|
|
||||||
try
|
try
|
||||||
Server.InitResponse(Resp);
|
//set port
|
||||||
Resp.FConnection:=Self;
|
Req.ServerPort := Server.Port;
|
||||||
// And dispatch
|
// Read content, if any
|
||||||
if Server.Active then
|
If Req.ContentLength>0 then
|
||||||
Server.HandleRequest(Req,Resp);
|
ReadRequestContent(Req);
|
||||||
if Assigned(Resp) and (not Resp.ContentSent) then
|
Req.InitRequestVars;
|
||||||
Resp.SendContent;
|
// Create Response
|
||||||
finally
|
Resp:= Server.CreateResponse(Req);
|
||||||
FreeAndNil(Resp);
|
try
|
||||||
|
Server.InitResponse(Resp);
|
||||||
|
Resp.FConnection:=Self;
|
||||||
|
// And dispatch
|
||||||
|
if Server.Active then
|
||||||
|
Server.HandleRequest(Req,Resp);
|
||||||
|
if Assigned(Resp) and (not Resp.ContentSent) then
|
||||||
|
Resp.SendContent;
|
||||||
|
finally
|
||||||
|
FreeAndNil(Resp);
|
||||||
|
end;
|
||||||
|
Finally
|
||||||
|
FreeAndNil(Req);
|
||||||
end;
|
end;
|
||||||
Finally
|
Except
|
||||||
FreeAndNil(Req);
|
On E : Exception do
|
||||||
|
HandleRequestError(E);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -528,6 +576,18 @@ end;
|
|||||||
|
|
||||||
{ TFPCustomHttpServer }
|
{ TFPCustomHttpServer }
|
||||||
|
|
||||||
|
procedure TFPCustomHttpServer.HandleRequestError(Sender: TObject; E: Exception);
|
||||||
|
begin
|
||||||
|
If Assigned(FOnRequestError) then
|
||||||
|
try
|
||||||
|
FOnRequestError(Sender,E);
|
||||||
|
except
|
||||||
|
// Do not let errors in user code escape.
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Writeln('Unhandled exception : ',E.ClassName,' : ',E.Message);
|
||||||
|
end;
|
||||||
|
|
||||||
function TFPCustomHttpServer.GetActive: Boolean;
|
function TFPCustomHttpServer.GetActive: Boolean;
|
||||||
begin
|
begin
|
||||||
if (csDesigning in ComponentState) then
|
if (csDesigning in ComponentState) then
|
||||||
@ -542,7 +602,11 @@ begin
|
|||||||
FLoadActivate:=AValue;
|
FLoadActivate:=AValue;
|
||||||
if not (csDesigning in Componentstate) then
|
if not (csDesigning in Componentstate) then
|
||||||
if AValue then
|
if AValue then
|
||||||
CreateServerSocket
|
begin
|
||||||
|
CreateServerSocket;
|
||||||
|
SetupSocket;
|
||||||
|
StartServerSocket;
|
||||||
|
end
|
||||||
else
|
else
|
||||||
FreeServerSocket;
|
FreeServerSocket;
|
||||||
end;
|
end;
|
||||||
@ -622,6 +686,7 @@ begin
|
|||||||
Con:=CreateConnection(Data);
|
Con:=CreateConnection(Data);
|
||||||
try
|
try
|
||||||
Con.FServer:=Self;
|
Con.FServer:=Self;
|
||||||
|
Con.OnRequestError:=@HandleRequestError;
|
||||||
if Threaded then
|
if Threaded then
|
||||||
CreateConnectionThread(Con)
|
CreateConnectionThread(Con)
|
||||||
else
|
else
|
||||||
@ -634,13 +699,23 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TFPCustomHttpServer.SetupSocket;
|
||||||
|
|
||||||
|
begin
|
||||||
|
FServer.QueueSize:=Self.QueueSize;
|
||||||
|
FServer.ReuseAddress:=true;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TFPCustomHttpServer.CreateServerSocket;
|
procedure TFPCustomHttpServer.CreateServerSocket;
|
||||||
begin
|
begin
|
||||||
FServer:=TInetServer.Create(FPort);
|
FServer:=TInetServer.Create(FPort);
|
||||||
FServer.MaxConnections:=-1;
|
FServer.MaxConnections:=-1;
|
||||||
FServer.OnConnectQuery:=OnAllowConnect;
|
FServer.OnConnectQuery:=OnAllowConnect;
|
||||||
FServer.OnConnect:=@DOConnect;
|
FServer.OnConnect:=@DOConnect;
|
||||||
FServer.QueueSize:=Self.QueueSize;
|
end;
|
||||||
|
|
||||||
|
procedure TFPCustomHttpServer.StartServerSocket;
|
||||||
|
begin
|
||||||
FServer.Bind;
|
FServer.Bind;
|
||||||
FServer.Listen;
|
FServer.Listen;
|
||||||
FServer.StartAccepting;
|
FServer.StartAccepting;
|
||||||
|
Loading…
Reference in New Issue
Block a user