|
@@ -36,12 +36,20 @@ Const
|
|
Type
|
|
Type
|
|
TRedirectEvent = Procedure (Sender : TObject; Const ASrc : String; Var ADest: String) of object;
|
|
TRedirectEvent = Procedure (Sender : TObject; Const ASrc : String; Var ADest: String) of object;
|
|
TPasswordEvent = Procedure (Sender : TObject; Var RepeatRequest : Boolean) of object;
|
|
TPasswordEvent = Procedure (Sender : TObject; Var RepeatRequest : Boolean) of object;
|
|
|
|
+ // During read of headers, ContentLength equals 0.
|
|
|
|
+ // During read of content, of Server did not specify contentlength, -1 is passed.
|
|
|
|
+ // CurrentPos is reset to 0 when the actual content is read, i.e. it is the position in the data, discarding header size.
|
|
|
|
+ TDataEvent = Procedure (Sender : TObject; Const ContentLength, CurrentPos : Int64) of object;
|
|
|
|
|
|
{ TFPCustomHTTPClient }
|
|
{ TFPCustomHTTPClient }
|
|
TFPCustomHTTPClient = Class(TComponent)
|
|
TFPCustomHTTPClient = Class(TComponent)
|
|
private
|
|
private
|
|
|
|
+ FDataRead : Int64;
|
|
|
|
+ FContentLength : Int64;
|
|
FAllowRedirect: Boolean;
|
|
FAllowRedirect: Boolean;
|
|
FMaxRedirects: Byte;
|
|
FMaxRedirects: Byte;
|
|
|
|
+ FOnDataReceived: TDataEvent;
|
|
|
|
+ FOnHeaders: TNotifyEvent;
|
|
FOnPassword: TPasswordEvent;
|
|
FOnPassword: TPasswordEvent;
|
|
FOnRedirect: TRedirectEvent;
|
|
FOnRedirect: TRedirectEvent;
|
|
FPassword: String;
|
|
FPassword: String;
|
|
@@ -57,13 +65,15 @@ Type
|
|
FSocket : TInetSocket;
|
|
FSocket : TInetSocket;
|
|
FBuffer : Ansistring;
|
|
FBuffer : Ansistring;
|
|
FUserName: String;
|
|
FUserName: String;
|
|
- function CheckContentLength: Integer;
|
|
|
|
|
|
+ function CheckContentLength: Int64;
|
|
function CheckTransferEncoding: string;
|
|
function CheckTransferEncoding: string;
|
|
function GetCookies: TStrings;
|
|
function GetCookies: TStrings;
|
|
Procedure ResetResponse;
|
|
Procedure ResetResponse;
|
|
Procedure SetCookies(const AValue: TStrings);
|
|
Procedure SetCookies(const AValue: TStrings);
|
|
Procedure SetRequestHeaders(const AValue: TStrings);
|
|
Procedure SetRequestHeaders(const AValue: TStrings);
|
|
protected
|
|
protected
|
|
|
|
+ // Called whenever data is read.
|
|
|
|
+ Procedure DoDataRead; virtual;
|
|
// Parse response status line. Saves status text and protocol, returns numerical code. Exception if invalid line.
|
|
// Parse response status line. Saves status text and protocol, returns numerical code. Exception if invalid line.
|
|
Function ParseStatusLine(AStatusLine : String) : Integer;
|
|
Function ParseStatusLine(AStatusLine : String) : Integer;
|
|
// Construct server URL for use in request line.
|
|
// Construct server URL for use in request line.
|
|
@@ -225,6 +235,10 @@ Type
|
|
// If a request returns a 401, then the OnPassword event is fired.
|
|
// If a request returns a 401, then the OnPassword event is fired.
|
|
// It can modify the username/password and set RepeatRequest to true;
|
|
// It can modify the username/password and set RepeatRequest to true;
|
|
Property OnPassword : TPasswordEvent Read FOnPassword Write FOnPassword;
|
|
Property OnPassword : TPasswordEvent Read FOnPassword Write FOnPassword;
|
|
|
|
+ // Called whenever data is read from the connection.
|
|
|
|
+ Property OnDataReceived : TDataEvent Read FOnDataReceived Write FOnDataReceived;
|
|
|
|
+ // Called when headers have been processed.
|
|
|
|
+ Property OnHeaders : TNotifyEvent Read FOnHeaders Write FOnHeaders;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
@@ -244,6 +258,8 @@ Type
|
|
Property UserName;
|
|
Property UserName;
|
|
Property Password;
|
|
Property Password;
|
|
Property OnPassword;
|
|
Property OnPassword;
|
|
|
|
+ Property OnDataReceived;
|
|
|
|
+ Property OnHeaders;
|
|
end;
|
|
end;
|
|
EHTTPClient = Class(Exception);
|
|
EHTTPClient = Class(Exception);
|
|
|
|
|
|
@@ -351,6 +367,12 @@ begin
|
|
FRequestHeaders.Assign(AValue);
|
|
FRequestHeaders.Assign(AValue);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TFPCustomHTTPClient.DoDataRead;
|
|
|
|
+begin
|
|
|
|
+ If Assigned(FOnDataReceived) Then
|
|
|
|
+ FOnDataReceived(Self,FContentLength,FDataRead);
|
|
|
|
+end;
|
|
|
|
+
|
|
function TFPCustomHTTPClient.IndexOfHeader(const AHeader: String): Integer;
|
|
function TFPCustomHTTPClient.IndexOfHeader(const AHeader: String): Integer;
|
|
begin
|
|
begin
|
|
Result:=IndexOfHeader(RequestHeaders,AHeader);
|
|
Result:=IndexOfHeader(RequestHeaders,AHeader);
|
|
@@ -477,6 +499,8 @@ function TFPCustomHTTPClient.ReadString : String;
|
|
Raise EHTTPClient.Create(SErrReadingSocket);
|
|
Raise EHTTPClient.Create(SErrReadingSocket);
|
|
if (r<ReadBuflen) then
|
|
if (r<ReadBuflen) then
|
|
SetLength(FBuffer,r);
|
|
SetLength(FBuffer,r);
|
|
|
|
+ FDataRead:=FDataRead+R;
|
|
|
|
+ DoDataRead;
|
|
end;
|
|
end;
|
|
|
|
|
|
Var
|
|
Var
|
|
@@ -603,6 +627,8 @@ begin
|
|
DoCookies(S);
|
|
DoCookies(S);
|
|
end
|
|
end
|
|
Until (S='');
|
|
Until (S='');
|
|
|
|
+ If Assigned(FOnHeaders) then
|
|
|
|
+ FOnHeaders(Self);
|
|
end;
|
|
end;
|
|
|
|
|
|
function TFPCustomHTTPClient.CheckResponseCode(ACode: Integer;
|
|
function TFPCustomHTTPClient.CheckResponseCode(ACode: Integer;
|
|
@@ -631,7 +657,7 @@ begin
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TFPCustomHTTPClient.CheckContentLength: Integer;
|
|
|
|
|
|
+function TFPCustomHTTPClient.CheckContentLength: Int64;
|
|
|
|
|
|
Const CL ='content-length:';
|
|
Const CL ='content-length:';
|
|
|
|
|
|
@@ -648,10 +674,11 @@ begin
|
|
If (Copy(S,1,Length(Cl))=Cl) then
|
|
If (Copy(S,1,Length(Cl))=Cl) then
|
|
begin
|
|
begin
|
|
System.Delete(S,1,Length(CL));
|
|
System.Delete(S,1,Length(CL));
|
|
- Result:=StrToIntDef(Trim(S),-1);
|
|
|
|
|
|
+ Result:=StrToInt64Def(Trim(S),-1);
|
|
end;
|
|
end;
|
|
Inc(I);
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
|
|
+ FContentLength:=Result;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TFPCustomHTTPClient.CheckTransferEncoding: string;
|
|
function TFPCustomHTTPClient.CheckTransferEncoding: string;
|
|
@@ -701,7 +728,11 @@ procedure TFPCustomHTTPClient.ReadResponse(Stream: TStream;
|
|
If Result<0 then
|
|
If Result<0 then
|
|
Raise EHTTPClient.Create(SErrReadingSocket);
|
|
Raise EHTTPClient.Create(SErrReadingSocket);
|
|
if (Result>0) then
|
|
if (Result>0) then
|
|
|
|
+ begin
|
|
|
|
+ FDataRead:=FDataRead+Result;
|
|
|
|
+ DoDataRead;
|
|
Stream.Write(FBuffer[1],Result);
|
|
Stream.Write(FBuffer[1],Result);
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
Procedure ReadChunkedResponse;
|
|
Procedure ReadChunkedResponse;
|
|
@@ -730,7 +761,9 @@ procedure TFPCustomHTTPClient.ReadResponse(Stream: TStream;
|
|
SetLength(FBuffer,Cnt);
|
|
SetLength(FBuffer,Cnt);
|
|
BufPos:=1;
|
|
BufPos:=1;
|
|
Result:=Cnt>0;
|
|
Result:=Cnt>0;
|
|
- end;
|
|
|
|
|
|
+ FDataRead:=FDataRead+Cnt;
|
|
|
|
+ DoDataRead;
|
|
|
|
+ end;
|
|
|
|
|
|
Function ReadData(Data: PByte; Cnt: integer): integer;
|
|
Function ReadData(Data: PByte; Cnt: integer): integer;
|
|
|
|
|
|
@@ -806,8 +839,12 @@ procedure TFPCustomHTTPClient.ReadResponse(Stream: TStream;
|
|
end;
|
|
end;
|
|
|
|
|
|
Var
|
|
Var
|
|
- L,LB,R : Integer;
|
|
|
|
|
|
+ L : Int64;
|
|
|
|
+ LB,R : Integer;
|
|
|
|
+
|
|
begin
|
|
begin
|
|
|
|
+ FDataRead:=0;
|
|
|
|
+ FContentLength:=0;
|
|
SetLength(FBuffer,0);
|
|
SetLength(FBuffer,0);
|
|
FResponseStatusCode:=ReadResponseHeaders;
|
|
FResponseStatusCode:=ReadResponseHeaders;
|
|
if not CheckResponseCode(FResponseStatusCode,AllowedResponseCodes) then
|
|
if not CheckResponseCode(FResponseStatusCode,AllowedResponseCodes) then
|
|
@@ -820,6 +857,7 @@ begin
|
|
begin
|
|
begin
|
|
// Write remains of buffer to output.
|
|
// Write remains of buffer to output.
|
|
LB:=Length(FBuffer);
|
|
LB:=Length(FBuffer);
|
|
|
|
+ FDataRead:=LB;
|
|
If (LB>0) then
|
|
If (LB>0) then
|
|
Stream.WriteBuffer(FBuffer[1],LB);
|
|
Stream.WriteBuffer(FBuffer[1],LB);
|
|
// Now read the rest, if any.
|
|
// Now read the rest, if any.
|