* fphttpclient: add new OnIdle event to keep the client responsive in case the server needs a lot of time to respond (and to be able to terminate the request while waiting for data)

This commit is contained in:
Ondrej Pokorny 2021-08-29 13:33:39 +02:00 committed by Ondrej Pokorny
parent 56c3b5b2cd
commit df6d289693

View File

@ -19,7 +19,7 @@ unit fphttpclient;
interface interface
uses uses
Classes, SysUtils, ssockets, httpdefs, uriparser, base64, sslsockets; Classes, SysUtils, ssockets, httpdefs, uriparser, base64, sslsockets, DateUtils;
Const Const
// Socket Read buffer size // Socket Read buffer size
@ -73,6 +73,7 @@ Type
FKeepConnection: Boolean; FKeepConnection: Boolean;
FMaxChunkSize: SizeUInt; FMaxChunkSize: SizeUInt;
FMaxRedirects: Byte; FMaxRedirects: Byte;
FOnIdle: TNotifyEvent;
FOnDataReceived: TDataEvent; FOnDataReceived: TDataEvent;
FOnDataSent: TDataEvent; FOnDataSent: TDataEvent;
FOnHeaders: TNotifyEvent; FOnHeaders: TNotifyEvent;
@ -131,6 +132,10 @@ Type
Function ProxyActive : Boolean; Function ProxyActive : Boolean;
// Override this if you want to create a custom instance of proxy. // Override this if you want to create a custom instance of proxy.
Function CreateProxyData : TProxyData; Function CreateProxyData : TProxyData;
// Called before data is read.
Procedure DoBeforeDataRead; virtual;
// Called when the client is waiting for the server.
Procedure DoOnIdle; virtual;
// Called whenever data is read. // Called whenever data is read.
Procedure DoDataRead; virtual; Procedure DoDataRead; virtual;
// Called whenever data is written. // Called whenever data is written.
@ -345,6 +350,8 @@ Type
Property OnPassword : TPasswordEvent Read FOnPassword Write FOnPassword; Property OnPassword : TPasswordEvent Read FOnPassword Write FOnPassword;
// Called whenever data is read from the connection. // Called whenever data is read from the connection.
Property OnDataReceived : TDataEvent Read FOnDataReceived Write FOnDataReceived; Property OnDataReceived : TDataEvent Read FOnDataReceived Write FOnDataReceived;
// Called when the client is waiting for the server
Property OnIdle : TNotifyEvent Read FOnIdle Write FOnIdle;
// Called whenever data is written to the connection. // Called whenever data is written to the connection.
Property OnDataSent : TDataEvent Read FOnDataSent Write FOnDataSent; Property OnDataSent : TDataEvent Read FOnDataSent Write FOnDataSent;
// Called when headers have been processed. // Called when headers have been processed.
@ -380,6 +387,7 @@ Type
Property OnPassword; Property OnPassword;
Property OnDataReceived; Property OnDataReceived;
Property OnDataSent; Property OnDataSent;
Property OnIdle;
Property OnHeaders; Property OnHeaders;
Property OnGetSocketHandler; Property OnGetSocketHandler;
Property Proxy; Property Proxy;
@ -689,6 +697,21 @@ begin
FreeAndNil(FSocket); FreeAndNil(FSocket);
end; end;
procedure TFPCustomHTTPClient.DoBeforeDataRead;
var
BreakUTC: TDateTime;
begin
// use CanRead to keep the client responsive in case the server needs a lot of time to respond
if IOTimeout>0 then
BreakUTC := IncMilliSecond(NowUTC, IOTimeout);
while not Terminated and not FSocket.CanRead(10) do
begin
DoOnIdle;
if (IOTimeout>0) and (CompareDateTime(NowUTC, BreakUTC)>0) then // we exceeded the timeout -> read error
Raise EHTTPClientSocketRead.Create(SErrReadingSocket);
end;
end;
function TFPCustomHTTPClient.AllowHeader(var AHeader: String): Boolean; function TFPCustomHTTPClient.AllowHeader(var AHeader: String): Boolean;
begin begin
@ -780,6 +803,7 @@ function TFPCustomHTTPClient.ReadString(out S: String): Boolean;
R : Integer; R : Integer;
begin begin
DoBeforeDataRead;
if Terminated then if Terminated then
Exit(False); Exit(False);
SetLength(FBuffer,ReadBufLen); SetLength(FBuffer,ReadBufLen);
@ -1121,6 +1145,9 @@ Function TFPCustomHTTPClient.ReadResponse(Stream: TStream;
Function Transfer(LB : Integer) : Integer; Function Transfer(LB : Integer) : Integer;
begin begin
DoBeforeDataRead;
if Terminated then
Exit(0);
Result:=FSocket.Read(FBuffer[1],LB); Result:=FSocket.Read(FBuffer[1],LB);
If Result<0 then If Result<0 then
Raise EHTTPClientSocketRead.Create(SErrReadingSocket); Raise EHTTPClientSocketRead.Create(SErrReadingSocket);
@ -1152,6 +1179,7 @@ Function TFPCustomHTTPClient.ReadResponse(Stream: TStream;
begin begin
Result:=False; Result:=False;
DoBeforeDataRead;
If Terminated then If Terminated then
exit; exit;
SetLength(FBuffer,ReadBuflen); SetLength(FBuffer,ReadBuflen);
@ -1356,6 +1384,12 @@ begin
End; End;
end; end;
procedure TFPCustomHTTPClient.DoOnIdle;
begin
If Assigned(FOnIdle) Then
FOnIdle(Self);
end;
Procedure TFPCustomHTTPClient.DoKeepConnectionRequest(const AURI: TURI; Procedure TFPCustomHTTPClient.DoKeepConnectionRequest(const AURI: TURI;
const AMethod: string; AStream: TStream; const AMethod: string; AStream: TStream;
const AAllowedResponseCodes: array of Integer; const AAllowedResponseCodes: array of Integer;