Opkman: Synchronize with FPC trunk. From FPC311 on OPM no longer depends on opkman_httpclient and opkman_zip.

git-svn-id: trunk@54349 -
This commit is contained in:
balazs 2017-03-06 09:28:48 +00:00
parent 88a333dcd7
commit c5a84bc001
8 changed files with 840 additions and 398 deletions

1
.gitattributes vendored
View File

@ -3253,6 +3253,7 @@ components/onlinepackagemanager/opkman_createjsonforupdates.pas svneol=native#te
components/onlinepackagemanager/opkman_createrepositorypackage.lfm svneol=native#text/plain
components/onlinepackagemanager/opkman_createrepositorypackage.pas svneol=native#text/pascal
components/onlinepackagemanager/opkman_downloader.pas svneol=native#text/pascal
components/onlinepackagemanager/opkman_fpcdef.inc svneol=native#text/pascal
components/onlinepackagemanager/opkman_installer.pas svneol=native#text/pascal
components/onlinepackagemanager/opkman_mainfrm.lfm svneol=native#text/plain
components/onlinepackagemanager/opkman_mainfrm.pas svneol=native#text/pascal

View File

@ -18,21 +18,10 @@ unit opkman_httpclient;
Todo:
* Proxy support ?
---------------------------------------------------------------------}
{
TFPHTTPClient does not implement a timeout/aborting mechanism(2016.10.01), which
is useful when downloading a large file for example. opkman_httpclient and opkman_downloader
fix this issue.
}
{$mode objfpc}{$H+}
{$IF FPC_VERSION = 3}
{$IF FPC_RELEASE > 0}
{$IF FPC_PATCH > 0}
{$DEFINE FPC311}
{$ENDIF}
{$ENDIF}
{$ENDIF}
{$INCLUDE opkman_fpcdef.inc}
interface
@ -83,6 +72,7 @@ Type
FDataRead : Int64;
FContentLength : Int64;
FAllowRedirect: Boolean;
FKeepConnection: Boolean;
FMaxRedirects: Byte;
FOnDataReceived: TDataEvent;
FOnHeaders: TNotifyEvent;
@ -101,9 +91,9 @@ Type
FServerHTTPVersion: String;
FSocket : TInetSocket;
FBuffer : Ansistring;
FTerminated: Boolean;
FUserName: String;
FOnGetSocketHandler : TGetSocketHandlerEvent;
FNeedToBreak: Boolean;
FProxy : TProxyData;
function CheckContentLength: Int64;
function CheckTransferEncoding: string;
@ -111,11 +101,26 @@ Type
function GetProxy: TProxyData;
Procedure ResetResponse;
Procedure SetCookies(const AValue: TStrings);
procedure SetHTTPVersion(const AValue: String);
procedure SetKeepConnection(AValue: Boolean);
procedure SetProxy(AValue: TProxyData);
Procedure SetRequestHeaders(const AValue: TStrings);
procedure SetIOTimeout(AValue: Integer);
Procedure ExtractHostPort(AURI: TURI; Out AHost: String; Out APort: Word);
Procedure CheckConnectionCloseHeader;
protected
Function NoContentAllowed(ACode : Integer) : Boolean;
// Peform a request, close connection.
Procedure DoNormalRequest(const AURI: TURI; const AMethod: string;
AStream: TStream; const AAllowedResponseCodes: array of Integer;
AHeadersOnly, AIsHttps: Boolean); virtual;
// Peform a request, try to keep connection.
Procedure DoKeepConnectionRequest(const AURI: TURI; const AMethod: string;
AStream: TStream; const AAllowedResponseCodes: array of Integer;
AHeadersOnly, AIsHttps: Boolean); virtual;
// Return True if FSocket is assigned
Function IsConnected: Boolean; virtual;
// True if we need to use a proxy: ProxyData Assigned and Hostname Set
Function ProxyActive : Boolean;
// Override this if you want to create a custom instance of proxy.
@ -127,19 +132,23 @@ Type
// Construct server URL for use in request line.
function GetServerURL(URI: TURI): String;
// Read 1 line of response. Fills FBuffer
function ReadString: String;
function ReadString(out S: String): Boolean;
// Check if response code is in AllowedResponseCodes. if not, an exception is raised.
// If AllowRedirect is true, and the result is a Redirect status code, the result is also true
// If the OnPassword event is set, then a 401 will also result in True.
function CheckResponseCode(ACode: Integer; const AllowedResponseCodes: array of Integer): Boolean; virtual;
// Read response from server, and write any document to Stream.
Procedure ReadResponse(Stream: TStream; const AllowedResponseCodes: array of Integer; HeadersOnly: Boolean = False); virtual;
Function ReadResponse(Stream: TStream; const AllowedResponseCodes: array of Integer; HeadersOnly: Boolean = False): Boolean; virtual;
// Read server response line and headers. Returns status code.
Function ReadResponseHeaders : integer; virtual;
// Allow header in request ? (currently checks only if non-empty and contains : token)
function AllowHeader(var AHeader: String): Boolean; virtual;
// Return True if the "connection: close" header is present
Function HasConnectionClose: Boolean; virtual;
// Connect to the server. Must initialize FSocket.
Procedure ConnectToServer(const AHost: String; APort: Integer; UseSSL : Boolean=False); virtual;
// Re-connect to the server. Must reinitialize FSocket.
Procedure ReconnectToServer(const AHost: String; APort: Integer; UseSSL : Boolean=False); virtual;
// Disconnect from server. Must free FSocket.
Procedure DisconnectFromServer; virtual;
// Run method AMethod, using request URL AURL. Write Response to Stream, and headers in ResponseHeaders.
@ -160,13 +169,16 @@ Type
Class Function IndexOfHeader(HTTPHeaders : TStrings; Const AHeader : String) : Integer;
// Return value of header AHeader from httpheaders. Returns empty if it doesn't exist yet.
Class Function GetHeader(HTTPHeaders : TStrings; Const AHeader : String) : String;
{ Terminate the current request.
It will stop the client from trying to send and/or receive data after the current chunk is sent/received. }
Procedure Terminate;
// Request Header management
// Return index of header, -1 if not present.
Function IndexOfHeader(Const AHeader : String) : Integer;
// Add header, replacing an existing one if it exists.
Procedure AddHeader(Const AHeader,AValue : String);
// Return header value, empty if not present.
Function GetHeader(Const AHeader : String) : String;
Function GetHeader(Const AHeader : String) : String;
// General-purpose call. Handles redirect and authorization retry (OnPassword).
Procedure HTTPMethod(Const AMethod,AURL : String; Stream : TStream; Const AllowedResponseCodes : Array of Integer); virtual;
// Execute GET on server, store result in Stream, File, StringList or string
@ -239,7 +251,7 @@ Type
Procedure FormPost(const URL : string; FormData: TStrings; const Response: TStrings);
function FormPost(const URL, FormData: string): String;
function FormPost(const URL: string; FormData : TStrings): String;
// Simple form
// Simple form
Class Procedure SimpleFormPost(const URL, FormData: string; const Response: TStream);
Class Procedure SimpleFormPost(const URL : string; FormData: TStrings; const Response: TStream);
Class Procedure SimpleFormPost(const URL, FormData: string; const Response: TStrings);
@ -256,6 +268,8 @@ Type
Procedure StreamFormPost(const AURL: string; FormData: TStrings; const AFieldName, AFileName: string; const AStream: TStream; const Response: TStream);
// Simple form of Posting a file
Class Procedure SimpleFileFormPost(const AURL, AFieldName, AFileName: string; const Response: TStream);
// Has Terminate been called ?
Property Terminated : Boolean Read FTerminated;
Protected
// Timeouts
Property IOTimeout : Integer read FIOTimeout write SetIOTimeout;
@ -268,7 +282,8 @@ Type
// Optional body to send (mainly in POST request)
Property RequestBody : TStream read FRequestBody Write FRequestBody;
// used HTTP version when constructing the request.
Property HTTPversion : String Read FHTTPVersion Write FHTTPVersion;
// Setting this to any other value than 1.1 will set KeepConnection to False.
Property HTTPversion : String Read FHTTPVersion Write SetHTTPVersion;
// After request properties.
// After request, this contains the headers sent by server.
Property ResponseHeaders : TStrings Read FResponseHeaders;
@ -292,6 +307,10 @@ Type
// They also override any Authenticate: header in Requestheaders.
Property UserName : String Read FUserName Write FUserName;
Property Password : String Read FPassword Write FPassword;
// Is client connected?
Property Connected: Boolean read IsConnected;
// Keep-Alive support. Setting to true will set HTTPVersion to 1.1
Property KeepConnection: Boolean Read FKeepConnection Write SetKeepConnection;
// If a request returns a 401, then the OnPassword event is fired.
// It can modify the username/password and set RepeatRequest to true;
Property OnPassword : TPasswordEvent Read FOnPassword Write FOnPassword;
@ -301,12 +320,14 @@ Type
Property OnHeaders : TNotifyEvent Read FOnHeaders Write FOnHeaders;
// Called to create socket handler. If not set, or Nil is returned, a standard socket handler is created.
Property OnGetSocketHandler : TGetSocketHandlerEvent Read FOnGetSocketHandler Write FOnGetSocketHandler;
Property NeedToBreak: Boolean read FNeedToBreak write FNeedToBreak;
end;
TFPHTTPClient = Class(TFPCustomHTTPClient)
Published
Property KeepConnection;
Property Connected;
Property IOTimeout;
Property RequestHeaders;
Property RequestBody;
@ -326,7 +347,6 @@ Type
Property OnHeaders;
Property OnGetSocketHandler;
Property Proxy;
Property NeedToBreak;
end;
EHTTPClient = Class(EHTTP);
@ -335,19 +355,19 @@ Function EncodeURLElement(S : String) : String;
Function DecodeURLElement(Const S : String) : String;
implementation
{$IFnDEF HASAMIGA}
{$if not defined(hasamiga)}
uses sslsockets;
{$ENDIF}
{$endif}
resourcestring
SErrInvalidProtocol = 'Invalid protocol: "%s"';
SErrInvalidProtocol = 'Invalid protocol : "%s"';
SErrReadingSocket = 'Error reading data from socket';
SErrInvalidProtocolVersion = 'Invalid protocol version in response: "%s"';
SErrInvalidStatusCode = 'Invalid response status code: %s';
SErrUnexpectedResponse = 'Unexpected response status code: %d';
SErrChunkTooBig = 'Chunk too big';
SErrChunkLineEndMissing = 'Chunk line end missing';
SErrMaxRedirectsReached = 'Maximum allowed redirects reached: %d';
SErrMaxRedirectsReached = 'Maximum allowed redirects reached : %d';
//SErrRedirectAborted = 'Redirect aborted.';
Const
@ -475,6 +495,11 @@ begin
{$ENDIF}
end;
function TFPCustomHTTPClient.IsConnected: Boolean;
begin
Result := Assigned(FSocket);
end;
function TFPCustomHTTPClient.NoContentAllowed(ACode: Integer): Boolean;
begin
Result:=((ACode div 100)=1) or ((ACode=204) or (ACode=304))
@ -545,11 +570,11 @@ begin
if Assigned(FonGetSocketHandler) then
FOnGetSocketHandler(Self,UseSSL,Result);
if (Result=Nil) then
{$IFnDEF HASAMIGA}
{$if not defined(HASAMIGA)}
If UseSSL then
Result:=TSSLSocketHandler.Create
else
{$ENDIF}
{$endif}
Result:=TSocketHandler.Create;
end;
@ -561,17 +586,19 @@ Var
begin
If IsConnected Then
DisconnectFromServer; // avoid memory leaks
if (Aport=0) then
if UseSSL then
Aport:=443
else
Aport:=80;
G:=GetSocketHandler(UseSSL);
G:=GetSocketHandler(UseSSL);
FSocket:=TInetSocket.Create(AHost,APort,G);
try
{$IFDEF FPC311}
if FIOTimeout <> 0 then
FSocket.IOTimeout := FIOTimeout;
if FIOTimeout<>0 then
FSocket.IOTimeout:=FIOTimeout;
{$ENDIF}
FSocket.Connect;
except
@ -580,6 +607,13 @@ begin
end;
end;
Procedure TFPCustomHTTPClient.ReconnectToServer(const AHost: String;
APort: Integer; UseSSL: Boolean);
begin
DisconnectFromServer;
ConnectToServer(AHost, APort, UseSSL);
end;
procedure TFPCustomHTTPClient.DisconnectFromServer;
begin
@ -592,6 +626,11 @@ begin
Result:=(AHeader<>'') and (Pos(':',AHeader)<>0);
end;
Function TFPCustomHTTPClient.HasConnectionClose: Boolean;
begin
Result := CompareText(GetHeader('Connection'), 'close') = 0;
end;
procedure TFPCustomHTTPClient.SendRequest(const AMethod: String; URI: TURI);
Var
@ -626,6 +665,7 @@ begin
S:=S+CRLF;
If Assigned(RequestBody) and (IndexOfHeader('Content-Length')=-1) then
AddHeader('Content-Length',IntToStr(RequestBody.Size));
CheckConnectionCloseHeader;
For I:=0 to FRequestHeaders.Count-1 do
begin
l:=FRequestHeaders[i];
@ -648,55 +688,60 @@ begin
FSentCookies:=FCookies;
FCookies:=Nil;
S:=S+CRLF;
FSocket.WriteBuffer(S[1],Length(S));
If Assigned(FRequestBody) then
if not Terminated then
FSocket.WriteBuffer(S[1],Length(S));
If Assigned(FRequestBody) and not Terminated then
FSocket.CopyFrom(FRequestBody,FRequestBody.Size);
end;
function TFPCustomHTTPClient.ReadString : String;
function TFPCustomHTTPClient.ReadString(out S: String): Boolean;
Procedure FillBuffer;
Function FillBuffer: Boolean;
Var
R : Integer;
begin
if Terminated then
Exit(False);
SetLength(FBuffer,ReadBufLen);
r:=FSocket.Read(FBuffer[1],ReadBufLen);
If r<0 then
If (r=0) or Terminated Then
Exit(False);
If (r<0) then
Raise EHTTPClient.Create(SErrReadingSocket);
if (r<ReadBuflen) then
SetLength(FBuffer,r);
FDataRead:=FDataRead+R;
DoDataRead;
Result:=r>0;
end;
Var
CheckLF,Done : Boolean;
CheckLF: Boolean;
P,L : integer;
begin
Result:='';
Done:=False;
S:='';
Result:=False;
CheckLF:=False;
Repeat
if NeedToBreak then
Break;
if Length(FBuffer)=0 then
FillBuffer;
if not FillBuffer then
Break;
if Length(FBuffer)=0 then
Done:=True
Result:=True
else if CheckLF then
begin
If (FBuffer[1]<>#10) then
Result:=Result+#13
S:=S+#13
else
begin
System.Delete(FBuffer,1,1);
Done:=True;
Result:=True;
end;
end;
if not Done then
if not Result then
begin
P:=Pos(#13#10,FBuffer);
If P=0 then
@ -704,20 +749,21 @@ begin
L:=Length(FBuffer);
CheckLF:=FBuffer[L]=#13;
if CheckLF then
Result:=Result+Copy(FBuffer,1,L-1)
S:=S+Copy(FBuffer,1,L-1)
else
Result:=Result+FBuffer;
S:=S+FBuffer;
FBuffer:='';
end
else
begin
Result:=Result+Copy(FBuffer,1,P-1);
S:=S+Copy(FBuffer,1,P-1);
System.Delete(FBuffer,1,P+1);
Done:=True;
Result:=True;
end;
end;
until Done;
until Result or Terminated;
end;
Function GetNextWord(Var S : String) : string;
Const
@ -770,15 +816,13 @@ function TFPCustomHTTPClient.ReadResponseHeaders: integer;
P:=Pos(':',S);
System.Delete(S,1,P);
Repeat
if NeedToBreak then
Break;
P:=Pos(';',S);
If (P=0) then
P:=Length(S)+1;
C:=Trim(Copy(S,1,P-1));
Cookies.Add(C);
System.Delete(S,1,P);
Until (S='');
Until (S='') or Terminated;
end;
Const
@ -788,20 +832,18 @@ Var
StatusLine,S : String;
begin
StatusLine:=ReadString;
if not ReadString(StatusLine) then
Exit(0);
Result:=ParseStatusLine(StatusLine);
Repeat
if NeedToBreak then
Break;
S:=ReadString;
if (S<>'') then
if ReadString(S) and (S<>'') then
begin
ResponseHeaders.Add(S);
If (LowerCase(Copy(S,1,Length(SetCookie)))=SetCookie) then
DoCookies(S);
end
Until (S='');
If Assigned(FOnHeaders) then
Until (S='') or Terminated;
If Assigned(FOnHeaders) and not Terminated then
FOnHeaders(Self);
end;
@ -902,14 +944,33 @@ begin
GetCookies.Assign(AValue);
end;
procedure TFPCustomHTTPClient.SetHTTPVersion(const AValue: String);
begin
if FHTTPVersion = AValue then Exit;
FHTTPVersion := AValue;
if (AValue<>'1.1') then
KeepConnection:=False;
end;
procedure TFPCustomHTTPClient.SetKeepConnection(AValue: Boolean);
begin
if FKeepConnection=AValue then Exit;
FKeepConnection:=AValue;
if AValue then
HTTPVersion:='1.1'
else if IsConnected then
DisconnectFromServer;
CheckConnectionCloseHeader;
end;
procedure TFPCustomHTTPClient.SetProxy(AValue: TProxyData);
begin
if (AValue=FProxy) then exit;
Proxy.Assign(AValue);
end;
procedure TFPCustomHTTPClient.ReadResponse(Stream: TStream;
const AllowedResponseCodes: array of Integer; HeadersOnly: Boolean);
Function TFPCustomHTTPClient.ReadResponse(Stream: TStream;
const AllowedResponseCodes: array of Integer; HeadersOnly: Boolean): Boolean;
Function Transfer(LB : Integer) : Integer;
@ -944,6 +1005,9 @@ procedure TFPCustomHTTPClient.ReadResponse(Stream: TStream;
function FetchData(out Cnt: integer): boolean;
begin
Result:=False;
If Terminated then
exit;
SetLength(FBuffer,ReadBuflen);
Cnt:=FSocket.Read(FBuffer[1],length(FBuffer));
If Cnt<0 then
@ -984,31 +1048,28 @@ procedure TFPCustomHTTPClient.ReadResponse(Stream: TStream;
begin
BufPos:=1;
repeat
if NeedToBreak then
Break;
// read ChunkSize
ChunkSize:=0;
repeat
if NeedToBreak then
Break;
if ReadData(@c,1)<1 then exit;
case c of
'0'..'9': ChunkSize:=ChunkSize*16+ord(c)-ord('0');
'a'..'f': ChunkSize:=ChunkSize*16+ord(c)-ord('a')+10;
'A'..'F': ChunkSize:=ChunkSize*16+ord(c)-ord('A')+10;
else break;
else
break;
end;
if ChunkSize>1000000 then
Raise EHTTPClient.Create(SErrChunkTooBig);
until false;
until Terminated;
// read till line end
while (c<>#10) do
while (c<>#10) and not Terminated do
if ReadData(@c,1)<1 then exit;
if ChunkSize=0 then exit;
// read data
repeat
if NeedToBreak then
Break;
if Terminated then
exit;
l:=length(FBuffer)-BufPos+1;
if l=0 then
if not FetchData(l) then
@ -1024,14 +1085,18 @@ procedure TFPCustomHTTPClient.ReadResponse(Stream: TStream;
end;
until ChunkSize=0;
// read #13#10
if ReadData(@c,1)<1 then exit;
if c<>#13 then
Raise EHTTPClient.Create(SErrChunkLineEndMissing);
if ReadData(@c,1)<1 then exit;
if c<>#10 then
Raise EHTTPClient.Create(SErrChunkLineEndMissing);
// next chunk
until false;
if ReadData(@c,1)<1 then
exit;
if Not Terminated then
begin
if c<>#13 then
Raise EHTTPClient.Create(SErrChunkLineEndMissing);
if ReadData(@c,1)<1 then exit;
if c<>#10 then
Raise EHTTPClient.Create(SErrChunkLineEndMissing);
// next chunk
end;
until Terminated;
end;
Var
@ -1043,6 +1108,9 @@ begin
FContentLength:=0;
SetLength(FBuffer,0);
FResponseStatusCode:=ReadResponseHeaders;
Result := FResponseStatusCode > 0;
if not Result then
Exit;
if not CheckResponseCode(FResponseStatusCode,AllowedResponseCodes) then
Raise EHTTPClient.CreateFmt(SErrUnexpectedResponse,[ResponseStatusCode]);
if HeadersOnly Or (AllowRedirect and IsRedirect(FResponseStatusCode)) then
@ -1064,34 +1132,122 @@ begin
// We cannot use copyfrom, it uses ReadBuffer, and this is dangerous with sockets
L:=L-LB;
Repeat
if NeedToBreak then
Break;
LB:=ReadBufLen;
If (LB>L) then
LB:=L;
R:=Transfer(LB);
L:=L-R;
until (L=0) or (R=0);
until (L=0) or (R=0) or Terminated;
end
else if (L<0) and (Not NoContentAllowed(ResponseStatusCode)) then
begin
// No content-length, so we read till no more data available.
Repeat
if NeedToBreak then
Break;
R:=Transfer(ReadBufLen);
until (R=0);
until (R=0) or Terminated;
end;
end;
end;
procedure TFPCustomHTTPClient.DoMethod(const AMethod, AURL: String;
Stream: TStream; const AllowedResponseCodes: array of Integer);
Procedure TFPCustomHTTPClient.ExtractHostPort(AURI: TURI; Out AHost: String;
Out APort: Word);
Begin
if ProxyActive then
begin
AHost:=Proxy.Host;
APort:=Proxy.Port;
end
else
begin
AHost:=AURI.Host;
APort:=AURI.Port;
end;
End;
procedure TFPCustomHTTPClient.CheckConnectionCloseHeader;
Var
URI : TURI;
P,CHost : String;
CPort : Word;
I : integer;
N,V : String;
begin
V:=GetHeader('Connection');
If FKeepConnection Then
begin
I:=IndexOfHeader(FRequestHeaders,'Connection');
If i>-1 Then
begin
// It can be keep-alive, check value
FRequestHeaders.GetNameValue(I,N,V);
If CompareText(V,'close')=0 then
FRequestHeaders.Delete(i);
end
end
Else
AddHeader('Connection', 'close');
end;
Procedure TFPCustomHTTPClient.DoNormalRequest(const AURI: TURI;
const AMethod: string; AStream: TStream;
const AAllowedResponseCodes: array of Integer;
AHeadersOnly, AIsHttps: Boolean);
Var
CHost: string;
CPort: Word;
begin
ExtractHostPort(AURI, CHost, CPort);
ConnectToServer(CHost,CPort,AIsHttps);
Try
SendRequest(AMethod,AURI);
if not Terminated then
ReadResponse(AStream,AAllowedResponseCodes,AHeadersOnly);
Finally
DisconnectFromServer;
End;
end;
Procedure TFPCustomHTTPClient.DoKeepConnectionRequest(const AURI: TURI;
const AMethod: string; AStream: TStream;
const AAllowedResponseCodes: array of Integer;
AHeadersOnly, AIsHttps: Boolean);
Var
T: Boolean;
CHost: string;
CPort: Word;
begin
ExtractHostPort(AURI, CHost, CPort);
T := False;
Repeat
If Not IsConnected Then
ConnectToServer(CHost,CPort,AIsHttps);
Try
if not Terminated then
SendRequest(AMethod,AURI);
if not Terminated then
begin
T := ReadResponse(AStream,AAllowedResponseCodes,AHeadersOnly);
If Not T Then
ReconnectToServer(CHost,CPort,AIsHttps);
end;
Finally
// On terminate, we close the request
If HasConnectionClose or Terminated Then
DisconnectFromServer;
End;
Until T or Terminated;
end;
Procedure TFPCustomHTTPClient.DoMethod(Const AMethod, AURL: String;
Stream: TStream; Const AllowedResponseCodes: Array of Integer);
Var
URI: TURI;
P: String;
IsHttps, HeadersOnly: Boolean;
begin
ResetResponse;
@ -1099,23 +1255,12 @@ begin
p:=LowerCase(URI.Protocol);
If Not ((P='http') or (P='https')) then
Raise EHTTPClient.CreateFmt(SErrInvalidProtocol,[URI.Protocol]);
if ProxyActive then
begin
CHost:=Proxy.Host;
CPort:=Proxy.Port;
end
IsHttps:=P='https';
HeadersOnly:=CompareText(AMethod,'HEAD')=0;
if FKeepConnection then
DoKeepConnectionRequest(URI,AMethod,Stream,AllowedResponseCodes,HeadersOnly,IsHttps)
else
begin
CHost:=URI.Host;
CPort:=URI.Port;
end;
ConnectToServer(CHost,CPort,P='https');
try
SendRequest(AMethod,URI);
ReadResponse(Stream,AllowedResponseCodes,CompareText(AMethod,'HEAD')=0);
finally
DisconnectFromServer;
end;
DoNormalRequest(URI,AMethod,Stream,AllowedResponseCodes,HeadersOnly,IsHttps);
end;
constructor TFPCustomHTTPClient.Create(AOwner: TComponent);
@ -1124,13 +1269,17 @@ begin
// Infinite timeout on most platforms
FIOTimeout:=0;
FRequestHeaders:=TStringList.Create;
FRequestHeaders.NameValueSeparator:=':';
FResponseHeaders:=TStringList.Create;
FHTTPVersion:='1.1';
FResponseHeaders.NameValueSeparator:=':';
HTTPVersion:='1.1';
FMaxRedirects:=DefMaxRedirects;
end;
destructor TFPCustomHTTPClient.Destroy;
begin
if IsConnected then
DisconnectFromServer;
FreeAndNil(FProxy);
FreeAndNil(FCookies);
FreeAndNil(FSentCookies);
@ -1184,6 +1333,11 @@ begin
end;
end;
procedure TFPCustomHTTPClient.Terminate;
begin
FTerminated:=True;
end;
procedure TFPCustomHTTPClient.ResetResponse;
begin
@ -1204,19 +1358,19 @@ Var
RR : Boolean; // Repeat request ?
begin
// Reset Terminated
FTerminated:=False;
L:=AURL;
RC:=0;
RR:=False;
M:=AMethod;
Repeat
if FNeedToBreak then
Break;
if Not AllowRedirect then
DoMethod(M,L,Stream,AllowedResponseCodes)
else
begin
DoMethod(M,L,Stream,AllowedResponseCodes);
if IsRedirect(FResponseStatusCode) then
if IsRedirect(FResponseStatusCode) and not Terminated then
begin
Inc(RC);
if (RC>MaxRedirects) then
@ -1242,8 +1396,8 @@ begin
FOnPassword(Self,RR);
end
else
RR:=AllowRedirect and IsRedirect(FResponseStatusCode) and (L<>'')
until not RR;
RR:=AllowRedirect and IsRedirect(FResponseStatusCode) and (L<>'');
until Terminated or not RR ;
end;
procedure TFPCustomHTTPClient.Get(const AURL: String; Stream: TStream);
@ -1310,7 +1464,7 @@ class procedure TFPCustomHTTPClient.SimpleGet(const AURL: String;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Get(AURL,Stream);
finally
Free;
@ -1324,7 +1478,7 @@ class procedure TFPCustomHTTPClient.SimpleGet(const AURL: String;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Get(AURL,LocalFileName);
finally
Free;
@ -1338,7 +1492,7 @@ class procedure TFPCustomHTTPClient.SimpleGet(const AURL: String;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Get(AURL,Response);
finally
Free;
@ -1347,7 +1501,7 @@ end;
class function TFPCustomHTTPClient.SimpleGet(const AURL: String): String;
begin
With Self.Create(nil) do
try
@ -1406,7 +1560,7 @@ class procedure TFPCustomHTTPClient.SimplePost(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Post(URL,Response);
finally
Free;
@ -1420,7 +1574,7 @@ class procedure TFPCustomHTTPClient.SimplePost(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Post(URL,Response);
finally
Free;
@ -1434,7 +1588,7 @@ class procedure TFPCustomHTTPClient.SimplePost(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Post(URL,LocalFileName);
finally
Free;
@ -1447,7 +1601,7 @@ class function TFPCustomHTTPClient.SimplePost(const URL: string): String;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Result:=Post(URL);
finally
Free;
@ -1498,7 +1652,7 @@ class procedure TFPCustomHTTPClient.SimplePut(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Put(URL,Response);
finally
Free;
@ -1511,7 +1665,7 @@ class procedure TFPCustomHTTPClient.SimplePut(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Put(URL,Response);
finally
Free;
@ -1524,7 +1678,7 @@ class procedure TFPCustomHTTPClient.SimplePut(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Put(URL,LocalFileName);
finally
Free;
@ -1536,7 +1690,7 @@ class function TFPCustomHTTPClient.SimplePut(const URL: string): String;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Result:=Put(URL);
finally
Free;
@ -1588,7 +1742,7 @@ class procedure TFPCustomHTTPClient.SimpleDelete(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Delete(URL,Response);
finally
Free;
@ -1601,7 +1755,7 @@ class procedure TFPCustomHTTPClient.SimpleDelete(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Delete(URL,Response);
finally
Free;
@ -1614,7 +1768,7 @@ class procedure TFPCustomHTTPClient.SimpleDelete(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Delete(URL,LocalFileName);
finally
Free;
@ -1626,7 +1780,7 @@ class function TFPCustomHTTPClient.SimpleDelete(const URL: string): String;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Result:=Delete(URL);
finally
Free;
@ -1678,7 +1832,7 @@ class procedure TFPCustomHTTPClient.SimpleOptions(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Options(URL,Response);
finally
Free;
@ -1691,7 +1845,7 @@ class procedure TFPCustomHTTPClient.SimpleOptions(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Options(URL,Response);
finally
Free;
@ -1704,7 +1858,7 @@ class procedure TFPCustomHTTPClient.SimpleOptions(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Options(URL,LocalFileName);
finally
Free;
@ -1716,7 +1870,7 @@ class function TFPCustomHTTPClient.SimpleOptions(const URL: string): String;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Result:=Options(URL);
finally
Free;
@ -1727,7 +1881,7 @@ class procedure TFPCustomHTTPClient.Head(AURL: String; Headers: TStrings);
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
HTTPMethod('HEAD', AURL, Nil, [200]);
Headers.Assign(ResponseHeaders);
Finally
@ -1812,7 +1966,7 @@ class procedure TFPCustomHTTPClient.SimpleFormPost(const URL, FormData: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
FormPost(URL,FormData,Response);
Finally
Free;
@ -1826,7 +1980,7 @@ class procedure TFPCustomHTTPClient.SimpleFormPost(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
FormPost(URL,FormData,Response);
Finally
Free;
@ -1840,7 +1994,7 @@ class procedure TFPCustomHTTPClient.SimpleFormPost(const URL, FormData: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
FormPost(URL,FormData,Response);
Finally
Free;
@ -1853,7 +2007,7 @@ class procedure TFPCustomHTTPClient.SimpleFormPost(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
FormPost(URL,FormData,Response);
Finally
Free;
@ -1866,7 +2020,7 @@ class function TFPCustomHTTPClient.SimpleFormPost(const URL, FormData: string
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Result:=FormPost(URL,FormData);
Finally
Free;
@ -1879,7 +2033,7 @@ class function TFPCustomHTTPClient.SimpleFormPost(const URL: string;
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
Result:=FormPost(URL,FormData);
Finally
Free;
@ -1958,7 +2112,7 @@ class procedure TFPCustomHTTPClient.SimpleFileFormPost(const AURL, AFieldName,
begin
With Self.Create(nil) do
try
RequestHeaders.Add('Connection: Close');
KeepConnection := False;
FileFormPost(AURL,AFieldName,AFileName,Response);
Finally
Free;

File diff suppressed because it is too large Load Diff

View File

@ -26,11 +26,14 @@ unit opkman_downloader;
{$mode objfpc}{$H+}
{$INCLUDE opkman_fpcdef.inc}
interface
uses
Classes, SysUtils, fpjson, opkman_httpclient, opkman_timer, opkman_common,
opkman_serializablepackages;
Classes, SysUtils, fpjson, opkman_timer, opkman_common, opkman_serializablepackages,
{$IFDEF FPC311}fphttpclient{$ELSE}opkman_httpclient{$ENDIF};
type
TDownloadType = (dtJSON, dtPackage, dtUpdate);
@ -270,7 +273,7 @@ procedure TThreadDownload.DoOnTimer(Sender: TObject);
begin
if FDownloadType = dtJSON then
begin
FHTTPClient.NeedToBreak := True;
FHTTPClient.Terminate;
FErrMsg := rsMainFrm_rsMessageError2;
FErrTyp := etTimeOut;
FTimer.StopTimer;
@ -679,7 +682,7 @@ procedure TPackageDownloader.CancelDownloadPackages;
begin
if Assigned(FDownload) then
begin
FDownload.FHTTPClient.NeedToBreak := True;
FDownload.FHTTPClient.Terminate;
FDownload.FTimer.StopTimer;
FDownload.NeedToBreak := True;
end;
@ -700,7 +703,7 @@ procedure TPackageDownloader.CancelUpdatePackages;
begin
if Assigned(FDownload) then
begin
FDownload.FHTTPClient.NeedToBreak := True;
FDownload.FHTTPClient.Terminate;
FDownload.FTimer.StopTimer;
FDownload.NeedToBreak := True;
end;

View File

@ -0,0 +1,7 @@
{$IF FPC_VERSION = 3}
{$IF FPC_RELEASE > 0}
{$IF FPC_PATCH > 0}
{$DEFINE FPC311}
{$ENDIF}
{$ENDIF}
{$ENDIF}

View File

@ -25,11 +25,14 @@ unit opkman_updates;
{$mode objfpc}{$H+}
{$INCLUDE opkman_fpcdef.inc}
interface
uses
Classes, SysUtils, LazIDEIntf, Laz2_XMLCfg, LazFileUtils, fpjson, fpjsonrtti,
opkman_httpclient, opkman_timer, opkman_serializablepackages, dateutils;
opkman_timer, opkman_serializablepackages, dateutils,
{$IFDEF FPC311}fphttpclient{$ELSE}opkman_httpclient{$ENDIF};
const
OpkVersion = 1;
@ -128,7 +131,8 @@ var
implementation
uses opkman_options, opkman_common, opkman_const, opkman_zip;
uses opkman_options, opkman_common, opkman_const,
{$IFDEF FPC311}zipper{$ELSE}opkman_zip{$ENDIF};
{ TUpdatePackage }
@ -527,7 +531,7 @@ begin
ResetPackageData(SerializablePackages.Items[I]);
end
else
FHTTPClient.NeedToBreak := True;
FHTTPClient.Terminate;
end;
if Assigned(FOnUpdate) and (not FNeedToBreak) and (not FPaused) then
Synchronize(@DoOnUpdate);
@ -561,7 +565,7 @@ begin
Save;
FTimer.StopTimer;
FStarted := False;
FHTTPClient.NeedToBreak := True;
FHTTPClient.Terminate;
end;
procedure TUpdates.PauseUpdate;

View File

@ -26,12 +26,15 @@ unit opkman_uploader;
{$mode objfpc}{$H+}
{$INCLUDE opkman_fpcdef.inc}
interface
uses
Classes, SysUtils, fpjson, base64, LazFileUtils, opkman_httpclient,
Classes, SysUtils, fpjson, base64, LazFileUtils,
{$IFDEF FPC311}fphttpclient{$ELSE}opkman_httpclient{$ENDIF};
dialogs;
type
TOnUploadProgress = procedure(Sender: TObject; AFileName: String) of object;
@ -210,7 +213,7 @@ end;
procedure TUploader.StopUpload;
begin
if Assigned(FHTTPClient) then
FHTTPClient.NeedToBreak := True;
FHTTPClient.Terminate;
FNeedToBreak := True;
end;

View File

@ -26,10 +26,13 @@ unit opkman_zipper;
{$mode objfpc}{$H+}
{$INCLUDE opkman_fpcdef.inc}
interface
uses
Classes, SysUtils, FileUtil, LazFileUtils, strutils, opkman_timer, opkman_zip;
Classes, SysUtils, FileUtil, LazFileUtils, strutils, opkman_timer,
{$IFDEF FPC311}zipper{$ELSE}opkman_zip{$ENDIF};
type
TOnProgressEx = procedure(Sender : TObject; const ATotPos, ATotSize: Int64);
@ -69,6 +72,7 @@ type
procedure DoOnZipProgress;
procedure DoOnZipError;
procedure DoOnZipCompleted;
function GetZipSize(var AIsDirZipped: Boolean; var ABaseDir: String): Int64;
protected
procedure Execute; override;
public
@ -226,6 +230,42 @@ begin
Sleep(5);
end;
function TPackageUnzipper.GetZipSize(var AIsDirZipped: Boolean; var ABaseDir: String): Int64;
var
I: Integer;
Item: TFullZipFileEntry;
AllFiles: Boolean;
P: Integer;
begin
FUnZipper.Examine;
AllFiles := (FUnZipper.Files.Count = 0);
Result := 0;
if FUnZipper.Entries.Count > 0 then
begin
P := Pos('/', TZipFileEntry(FUnZipper.Entries.Items[0]).ArchiveFileName);
if P = 0 then
P := Pos('\', TZipFileEntry(FUnZipper.Entries.Items[0]).ArchiveFileName);
if P <> 0 then
ABaseDir := Copy(TZipFileEntry(FUnZipper.Entries.Items[0]).ArchiveFileName, 1, P);
end;
for I := 0 to FUnZipper.Entries.Count-1 do
begin
Item := FUnZipper.Entries[i];
if AllFiles or (FUnZipper.Files.IndexOf(Item.ArchiveFileName)<>-1) then
begin
Result := Result + TZipFileEntry(Item).Size;
if AIsDirZipped then
if Pos(ABaseDir, Item.ArchiveFileName) = 0 then
AIsDirZipped := False;
end;
end;
if not AIsDirZipped then
ABaseDir := ''
else
ABaseDir := Copy(ABaseDir, 1, Length(ABaseDir) - 1);
end;
procedure TPackageUnzipper.StartUnZip(const ASrcDir, ADstDir: String;
const AIsUpdate: Boolean);
var
@ -250,7 +290,7 @@ begin
FUnZipper.Examine;
IsDirZipped := True;
BaseDir := '';
FTotSize := FTotSize + FUnZipper.GetZipSize(IsDirZipped, BaseDir);
FTotSize := FTotSize + GetZipSize(IsDirZipped, BaseDir);
SerializablePackages.Items[I].IsDirZipped := IsDirZipped;
if BaseDir <> '' then
BaseDir := AppendPathDelim(BaseDir);
@ -276,7 +316,7 @@ end;
procedure TPackageUnzipper.StopUnZip;
begin
if Assigned(FUnZipper) then
FUnZipper.NeedToBreak := True;
FUnZipper.Terminate;
if Assigned(FTimer) then
FTimer.StopTimer;
FNeedToBreak := True;
@ -373,7 +413,7 @@ end;
procedure TPackageZipper.StopZip;
begin
if Assigned(FZipper) then
FZipper.NeedToBreak := True;
FZipper.Terminate;
FNeedToBreak := True;
FStarted := False;
end;