httpsend.pas - int64 support and improvements (by Pepak)
InputStream and OutputStream properties for direct HTTP upload/download to any TStream git-svn-id: https://svn.code.sf.net/p/synalist/code/trunk@227 7c85be65-684b-0410-a082-b2ed4fbef004
This commit is contained in:
		
							
								
								
									
										90
									
								
								httpsend.pas
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								httpsend.pas
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| {==============================================================================| | {==============================================================================| | ||||||
| | Project : Ararat Synapse                                       | 003.012.010 | | | Project : Ararat Synapse                                       | 003.013.000 | | ||||||
| |==============================================================================| | |==============================================================================| | ||||||
| | Content: HTTP client                                                         | | | Content: HTTP client                                                         | | ||||||
| |==============================================================================| | |==============================================================================| | ||||||
| @@ -34,6 +34,7 @@ | |||||||
| |==============================================================================| | |==============================================================================| | ||||||
| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | ||||||
| | Portions created by Lukas Gebauer are Copyright (c) 1999-2021.               | | | Portions created by Lukas Gebauer are Copyright (c) 1999-2021.               | | ||||||
|  | | Portions created by Pepak are Copyright (c) 2020-2021.                       | | ||||||
| | All Rights Reserved.                                                         | | | All Rights Reserved.                                                         | | ||||||
| |==============================================================================| | |==============================================================================| | ||||||
| | Contributor(s):                                                              | | | Contributor(s):                                                              | | ||||||
| @@ -101,18 +102,21 @@ type | |||||||
|     FResultString: string; |     FResultString: string; | ||||||
|     FUserAgent: string; |     FUserAgent: string; | ||||||
|     FCookies: TStringList; |     FCookies: TStringList; | ||||||
|     FDownloadSize: integer; |     FDownloadSize: int64; | ||||||
|     FUploadSize: integer; |     FUploadSize: int64; | ||||||
|     FRangeStart: integer; |     FRangeStart: int64; | ||||||
|     FRangeEnd: integer; |     FRangeEnd: int64; | ||||||
|     FAddPortNumberToHost: Boolean; |     FAddPortNumberToHost: Boolean; | ||||||
|  |     FInputStream, FOutputStream: TStream; | ||||||
|     function ReadUnknown: Boolean; virtual; |     function ReadUnknown: Boolean; virtual; | ||||||
|     function ReadIdentity(Size: Integer): Boolean; virtual; |     function ReadIdentity(Size: int64): Boolean; virtual; | ||||||
|     function ReadChunked: Boolean; virtual; |     function ReadChunked: Boolean; virtual; | ||||||
|     procedure ParseCookies; |     procedure ParseCookies; | ||||||
|     function PrepareHeaders: AnsiString; |     function PrepareHeaders: AnsiString; | ||||||
|     function InternalDoConnect(needssl: Boolean): Boolean; |     function InternalDoConnect(needssl: Boolean): Boolean; | ||||||
|     function InternalConnect(needssl: Boolean): Boolean; |     function InternalConnect(needssl: Boolean): Boolean; | ||||||
|  |     function InputDocument: TStream; | ||||||
|  |     function OutputDocument: TStream; | ||||||
|   public |   public | ||||||
|     constructor Create; |     constructor Create; | ||||||
|     destructor Destroy; override; |     destructor Destroy; override; | ||||||
| @@ -158,13 +162,13 @@ type | |||||||
|  |  | ||||||
|     {:If you need to download only part of a requested document, specify here |     {:If you need to download only part of a requested document, specify here | ||||||
|      the position of subpart begin. If 0, the full document is requested.} |      the position of subpart begin. If 0, the full document is requested.} | ||||||
|     property RangeStart: integer read FRangeStart Write FRangeStart; |     property RangeStart: int64 read FRangeStart Write FRangeStart; | ||||||
|  |  | ||||||
|     {:If you need to download only part of a requested document, specify here |     {:If you need to download only part of a requested document, specify here | ||||||
|      the position of subpart end. If 0, the document from rangeStart to end of |      the position of subpart end. If 0, the document from rangeStart to end of | ||||||
|      document is requested. |      document is requested. | ||||||
|      (Useful for resuming broken downloads, for example.)} |      (Useful for resuming broken downloads, for example.)} | ||||||
|     property RangeEnd: integer read FRangeEnd Write FRangeEnd; |     property RangeEnd: int64 read FRangeEnd Write FRangeEnd; | ||||||
|  |  | ||||||
|     {:Mime type of sending data. Default is: 'text/html'.} |     {:Mime type of sending data. Default is: 'text/html'.} | ||||||
|     property MimeType: string read FMimeType Write FMimeType; |     property MimeType: string read FMimeType Write FMimeType; | ||||||
| @@ -209,12 +213,12 @@ type | |||||||
|     {:if this value is not 0, then data download is pending. In this case you |     {:if this value is not 0, then data download is pending. In this case you | ||||||
|      have here the total size of downloaded data. Useful for drawing download |      have here the total size of downloaded data. Useful for drawing download | ||||||
|      progressbar from OnStatus event.} |      progressbar from OnStatus event.} | ||||||
|     property DownloadSize: integer read FDownloadSize; |     property DownloadSize: int64 read FDownloadSize; | ||||||
|  |  | ||||||
|     {:if this value is not 0, then data upload is pending. In this case you have |     {:if this value is not 0, then data upload is pending. In this case you have | ||||||
|      here the total size of uploaded data. Useful for drawing upload progressbar |      here the total size of uploaded data. Useful for drawing upload progressbar | ||||||
|      from OnStatus event.} |      from OnStatus event.} | ||||||
|     property UploadSize: integer read FUploadSize; |     property UploadSize: int64 read FUploadSize; | ||||||
|  |  | ||||||
|     {:Socket object used for TCP/IP operation. |     {:Socket object used for TCP/IP operation. | ||||||
|      Good for setting OnStatus hook, etc.} |      Good for setting OnStatus hook, etc.} | ||||||
| @@ -223,6 +227,12 @@ type | |||||||
|     {:Allows to switch off port number in 'Host:' HTTP header. By default @TRUE. |     {:Allows to switch off port number in 'Host:' HTTP header. By default @TRUE. | ||||||
|      Some buggy servers do not like port informations in this header.} |      Some buggy servers do not like port informations in this header.} | ||||||
|     property AddPortNumberToHost: Boolean read FAddPortNumberToHost write FAddPortNumberToHost; |     property AddPortNumberToHost: Boolean read FAddPortNumberToHost write FAddPortNumberToHost; | ||||||
|  |   public | ||||||
|  |     {:for direct sending from any TStream. Defalut nil = use Document property instead.} | ||||||
|  |     property InputStream: TStream read FInputStream write FInputStream; | ||||||
|  |  | ||||||
|  |     {:for direct dovnloading into any TStream. Defalut nil = use Document property instead.} | ||||||
|  |     property OutputStream: TStream read FOutputStream write FOutputStream; | ||||||
|   end; |   end; | ||||||
|  |  | ||||||
| {:A very useful function, and example of use can be found in the THTTPSend | {:A very useful function, and example of use can be found in the THTTPSend | ||||||
| @@ -296,6 +306,8 @@ begin | |||||||
|   FUploadSize := 0; |   FUploadSize := 0; | ||||||
|   FAddPortNumberToHost := true; |   FAddPortNumberToHost := true; | ||||||
|   FKeepAliveTimeout := 300; |   FKeepAliveTimeout := 300; | ||||||
|  |   FInputStream := nil; | ||||||
|  |   FOutputStream := nil; | ||||||
|   Clear; |   Clear; | ||||||
| end; | end; | ||||||
|  |  | ||||||
| @@ -308,11 +320,29 @@ begin | |||||||
|   inherited Destroy; |   inherited Destroy; | ||||||
| end; | end; | ||||||
|  |  | ||||||
|  | function THTTPSend.InputDocument: TStream; | ||||||
|  | begin | ||||||
|  |   if InputStream <> nil then | ||||||
|  |     Result := InputStream | ||||||
|  |   else | ||||||
|  |     Result := Document; | ||||||
|  | end; | ||||||
|  |  | ||||||
|  | function THTTPSend.OutputDocument: TStream; | ||||||
|  | begin | ||||||
|  |   if OutputStream <> nil then | ||||||
|  |     Result := OutputStream | ||||||
|  |   else | ||||||
|  |     Result := Document; | ||||||
|  | end; | ||||||
|  |  | ||||||
| procedure THTTPSend.Clear; | procedure THTTPSend.Clear; | ||||||
| begin | begin | ||||||
|   FRangeStart := 0; |   FRangeStart := 0; | ||||||
|   FRangeEnd := 0; |   FRangeEnd := 0; | ||||||
|   FDocument.Clear; |   FDocument.Clear; | ||||||
|  |   InputDocument.Size := 0; | ||||||
|  |   OutputDocument.Size := 0; | ||||||
|   FHeaders.Clear; |   FHeaders.Clear; | ||||||
|   FMimeType := 'text/html'; |   FMimeType := 'text/html'; | ||||||
| end; | end; | ||||||
| @@ -383,7 +413,7 @@ var | |||||||
|   status100: Boolean; |   status100: Boolean; | ||||||
|   status100error: string; |   status100error: string; | ||||||
|   ToClose: Boolean; |   ToClose: Boolean; | ||||||
|   Size: Integer; |   Size: int64; | ||||||
|   Prot, User, Pass, Host, Port, Path, Para, URI: string; |   Prot, User, Pass, Host, Port, Path, Para, URI: string; | ||||||
|   s, su: AnsiString; |   s, su: AnsiString; | ||||||
|   HttpTunnel: Boolean; |   HttpTunnel: Boolean; | ||||||
| @@ -400,7 +430,7 @@ begin | |||||||
|   FDownloadSize := 0; |   FDownloadSize := 0; | ||||||
|   FUploadSize := 0; |   FUploadSize := 0; | ||||||
|  |  | ||||||
|   URI := ParseURL(URL, Prot, User, Pass, Host, Port, Path, Para); |   URI := ParseURL(trim(URL), Prot, User, Pass, Host, Port, Path, Para); | ||||||
|   User := DecodeURL(user); |   User := DecodeURL(user); | ||||||
|   Pass := DecodeURL(pass); |   Pass := DecodeURL(pass); | ||||||
|   if User = '' then |   if User = '' then | ||||||
| @@ -425,14 +455,14 @@ begin | |||||||
|     FSock.HTTPTunnelPass := ''; |     FSock.HTTPTunnelPass := ''; | ||||||
|   end; |   end; | ||||||
|   UsingProxy := (FProxyHost <> '') and not(HttpTunnel); |   UsingProxy := (FProxyHost <> '') and not(HttpTunnel); | ||||||
|   Sending := FDocument.Size > 0; |   Sending := InputDocument.Size > 0; | ||||||
|   {Headers for Sending data} |   {Headers for Sending data} | ||||||
|   status100 := FStatus100 and Sending and (FProtocol = '1.1'); |   status100 := FStatus100 and Sending and (FProtocol = '1.1'); | ||||||
|   if status100 then |   if status100 then | ||||||
|     FHeaders.Insert(0, 'Expect: 100-continue'); |     FHeaders.Insert(0, 'Expect: 100-continue'); | ||||||
|   if Sending then |   if Sending then | ||||||
|   begin |   begin | ||||||
|     FHeaders.Insert(0, 'Content-Length: ' + IntToStr(FDocument.Size)); |     FHeaders.Insert(0, 'Content-Length: ' + IntToStr(InputDocument.Size)); | ||||||
|     if FMimeType <> '' then |     if FMimeType <> '' then | ||||||
|       FHeaders.Insert(0, 'Content-Type: ' + FMimeType); |       FHeaders.Insert(0, 'Content-Type: ' + FMimeType); | ||||||
|   end; |   end; | ||||||
| @@ -514,7 +544,7 @@ begin | |||||||
|   end; |   end; | ||||||
|  |  | ||||||
|   { reading Status } |   { reading Status } | ||||||
|   FDocument.Position := 0; |   InputDocument.Position := 0; | ||||||
|   Status100Error := ''; |   Status100Error := ''; | ||||||
|   if status100 then |   if status100 then | ||||||
|   begin |   begin | ||||||
| @@ -538,23 +568,23 @@ begin | |||||||
|     begin |     begin | ||||||
|       { we can upload content } |       { we can upload content } | ||||||
|       Status100Error := ''; |       Status100Error := ''; | ||||||
|       FUploadSize := FDocument.Size; |       FUploadSize := InputDocument.Size; | ||||||
|       FSock.SendBuffer(FDocument.Memory, FDocument.Size); |       FSock.SendStreamRaw(InputDocument); | ||||||
|     end; |     end; | ||||||
|   end |   end | ||||||
|   else |   else | ||||||
|     { upload content } |     { upload content } | ||||||
|     if sending then |     if sending then | ||||||
|     begin |     begin | ||||||
|       if FDocument.Size >= c64k then |       if InputDocument.Size >= c64k then | ||||||
|       begin |       begin | ||||||
|         FSock.SendString(PrepareHeaders); |         FSock.SendString(PrepareHeaders); | ||||||
|         FUploadSize := FDocument.Size; |         FUploadSize := InputDocument.Size; | ||||||
|         FSock.SendBuffer(FDocument.Memory, FDocument.Size); |         FSock.SendStreamRaw(InputDocument); | ||||||
|       end |       end | ||||||
|       else |       else | ||||||
|       begin |       begin | ||||||
|         s := PrepareHeaders + ReadStrFromStream(FDocument, FDocument.Size); |         s := PrepareHeaders + ReadStrFromStream(InputDocument, InputDocument.Size); | ||||||
|         FUploadSize := Length(s); |         FUploadSize := Length(s); | ||||||
|         FSock.SendString(s); |         FSock.SendString(s); | ||||||
|       end; |       end; | ||||||
| @@ -590,7 +620,7 @@ begin | |||||||
|       begin |       begin | ||||||
|         { old HTTP 0.9 and some buggy servers not send result } |         { old HTTP 0.9 and some buggy servers not send result } | ||||||
|         s := s + CRLF; |         s := s + CRLF; | ||||||
|         WriteStrToStream(FDocument, s); |         WriteStrToStream(OutputDocument, s); | ||||||
|         FResultCode := 0; |         FResultCode := 0; | ||||||
|       end; |       end; | ||||||
|     until (FSock.LastError <> 0) or (FResultCode <> 100); |     until (FSock.LastError <> 0) or (FResultCode <> 100); | ||||||
| @@ -618,7 +648,7 @@ begin | |||||||
|         su := UpperCase(s); |         su := UpperCase(s); | ||||||
|         if Pos('CONTENT-LENGTH:', su) = 1 then |         if Pos('CONTENT-LENGTH:', su) = 1 then | ||||||
|         begin |         begin | ||||||
|           Size := StrToIntDef(Trim(SeparateRight(s, ':')), -1); |           Size := StrToInt64Def(Trim(SeparateRight(s, ':')), -1); | ||||||
|           if (Size <> -1) and (FTransferEncoding = TE_UNKNOWN) then |           if (Size <> -1) and (FTransferEncoding = TE_UNKNOWN) then | ||||||
|             FTransferEncoding := TE_IDENTITY; |             FTransferEncoding := TE_IDENTITY; | ||||||
|         end; |         end; | ||||||
| @@ -671,7 +701,7 @@ begin | |||||||
|         Result := ReadChunked; |         Result := ReadChunked; | ||||||
|     end; |     end; | ||||||
|  |  | ||||||
|   FDocument.Position := 0; |   OutputDocument.Position := 0; | ||||||
|   if ToClose then |   if ToClose then | ||||||
|   begin |   begin | ||||||
|     FSock.CloseSocket; |     FSock.CloseSocket; | ||||||
| @@ -689,7 +719,7 @@ begin | |||||||
|   repeat |   repeat | ||||||
|     s := FSock.RecvPacket(FTimeout); |     s := FSock.RecvPacket(FTimeout); | ||||||
|     if FSock.LastError = 0 then |     if FSock.LastError = 0 then | ||||||
|       WriteStrToStream(FDocument, s); |       WriteStrToStream(OutputDocument, s); | ||||||
|   until FSock.LastError <> 0; |   until FSock.LastError <> 0; | ||||||
|   if FSock.LastError = WSAECONNRESET then |   if FSock.LastError = WSAECONNRESET then | ||||||
|   begin |   begin | ||||||
| @@ -698,13 +728,13 @@ begin | |||||||
|   end; |   end; | ||||||
| end; | end; | ||||||
|  |  | ||||||
| function THTTPSend.ReadIdentity(Size: Integer): Boolean; | function THTTPSend.ReadIdentity(Size: int64): Boolean; | ||||||
| begin | begin | ||||||
|   if Size > 0 then |   if Size > 0 then | ||||||
|   begin |   begin | ||||||
|     FDownloadSize := Size; |     FDownloadSize := Size; | ||||||
|     FSock.RecvStreamSize(FDocument, FTimeout, Size); |     FSock.RecvStreamSize(OutputDocument, FTimeout, Size); | ||||||
|     FDocument.Position := FDocument.Size; |     OutputDocument.Position := OutputDocument.Size; | ||||||
|     Result := FSock.LastError = 0; |     Result := FSock.LastError = 0; | ||||||
|   end |   end | ||||||
|   else |   else | ||||||
| @@ -714,7 +744,7 @@ end; | |||||||
| function THTTPSend.ReadChunked: Boolean; | function THTTPSend.ReadChunked: Boolean; | ||||||
| var | var | ||||||
|   s: ansistring; |   s: ansistring; | ||||||
|   Size: Integer; |   Size: int64; | ||||||
| begin | begin | ||||||
|   repeat |   repeat | ||||||
|     repeat |     repeat | ||||||
| @@ -724,7 +754,7 @@ begin | |||||||
|       Break; |       Break; | ||||||
|     s := Trim(SeparateLeft(s, ' ')); |     s := Trim(SeparateLeft(s, ' ')); | ||||||
|     s := Trim(SeparateLeft(s, ';')); |     s := Trim(SeparateLeft(s, ';')); | ||||||
|     Size := StrToIntDef('$' + s, 0); |     Size := StrToInt64Def('$' + s, 0); | ||||||
|     if Size = 0 then |     if Size = 0 then | ||||||
|       Break; |       Break; | ||||||
|     if not ReadIdentity(Size) then |     if not ReadIdentity(Size) then | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user