home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 December / Chip_2001-12_cd1.bin / zkuste / delphi / unity / d23456 / SYNAPSE.ZIP / source / lib / HTTPSend.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2001-09-16  |  13.8 KB  |  488 lines

  1. {==============================================================================|
  2. | Project : Delphree - Synapse                                   | 002.003.000 |
  3. |==============================================================================|
  4. | Content: HTTP client                                                         |
  5. |==============================================================================|
  6. | The contents of this file are subject to the Mozilla Public License Ver. 1.1 |
  7. | (the "License"); you may not use this file except in compliance with the     |
  8. | License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ |
  9. |                                                                              |
  10. | Software distributed under the License is distributed on an "AS IS" basis,   |
  11. | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for |
  12. | the specific language governing rights and limitations under the License.    |
  13. |==============================================================================|
  14. | The Original Code is Synapse Delphi Library.                                 |
  15. |==============================================================================|
  16. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  17. | Portions created by Lukas Gebauer are Copyright (c) 1999,2000,2001.          |
  18. | All Rights Reserved.                                                         |
  19. |==============================================================================|
  20. | Contributor(s):                                                              |
  21. |==============================================================================|
  22. | History: see HISTORY.HTM from distribution package                           |
  23. |          (Found at URL: http://www.ararat.cz/synapse/)                       |
  24. |==============================================================================}
  25.  
  26. {$WEAKPACKAGEUNIT ON}
  27.  
  28. unit HTTPSend;
  29.  
  30. interface
  31.  
  32. uses
  33.   SysUtils, Classes,
  34.   blcksock, SynaUtil, SynaCode;
  35.  
  36. const
  37.   cHttpProtocol = '80';
  38.  
  39. type
  40.   TTransferEncoding = (TE_UNKNOWN, TE_IDENTITY, TE_CHUNKED);
  41.  
  42.   THTTPSend = class(TObject)
  43.   private
  44.     FSock: TTCPBlockSocket;
  45.     FTransferEncoding: TTransferEncoding;
  46.     FAliveHost: string;
  47.     FAlivePort: string;
  48.     FHeaders: TStringList;
  49.     FDocument: TMemoryStream;
  50.     FMimeType: string;
  51.     FProtocol: string;
  52.     FKeepAlive: Boolean;
  53.     FTimeout: Integer;
  54.     FHTTPHost: string;
  55.     FHTTPPort: string;
  56.     FProxyHost: string;
  57.     FProxyPort: string;
  58.     FProxyUser: string;
  59.     FProxyPass: string;
  60.     FResultCode: Integer;
  61.     FResultString: string;
  62.     function ReadUnknown: Boolean;
  63.     function ReadIdentity(Size: Integer): Boolean;
  64.     function ReadChunked: Boolean;
  65.   public
  66.     constructor Create;
  67.     destructor Destroy; override;
  68.     procedure Clear;
  69.     procedure DecodeStatus(const Value: string);
  70.     function HTTPMethod(const Method, URL: string): Boolean;
  71.   published
  72.     property Headers: TStringList read FHeaders Write FHeaders;
  73.     property Document: TMemoryStream read FDocument Write FDocument;
  74.     property MimeType: string read FMimeType Write FMimeType;
  75.     property Protocol: string read FProtocol Write FProtocol;
  76.     property KeepAlive: Boolean read FKeepAlive Write FKeepAlive;
  77.     property Timeout: Integer read FTimeout Write FTimeout;
  78.     property HTTPHost: string read FHTTPHost;
  79.     property HTTPPort: string read FHTTPPort;
  80.     property ProxyHost: string read FProxyHost Write FProxyHost;
  81.     property ProxyPort: string read FProxyPort Write FProxyPort;
  82.     property ProxyUser: string read FProxyUser Write FProxyUser;
  83.     property ProxyPass: string read FProxyPass Write FProxyPass;
  84.     property ResultCode: Integer read FResultCode;
  85.     property ResultString: string read FResultString;
  86.     property Sock: TTCPBlockSocket read FSock;
  87.   end;
  88.  
  89. function HttpGetText(const URL: string; const Response: TStrings): Boolean;
  90. function HttpGetBinary(const URL: string; const Response: TStream): Boolean;
  91. function HttpPostBinary(const URL: string; const Data: TStream): Boolean;
  92. function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean;
  93. function HttpPostFile(const URL, FieldName, FileName: string;
  94.   const Data: TStream; const ResultData: TStringList): Boolean;
  95.  
  96. implementation
  97.  
  98. const
  99.   CRLF = #13#10;
  100.  
  101. constructor THTTPSend.Create;
  102. begin
  103.   inherited Create;
  104.   FHeaders := TStringList.Create;
  105.   FDocument := TMemoryStream.Create;
  106.   FSock := TTCPBlockSocket.Create;
  107.   FSock.SizeRecvBuffer := 65536;
  108.   FSock.SizeSendBuffer := 65536;
  109.   FTimeout := 300000;
  110.   FHTTPHost := cLocalhost;
  111.   FHTTPPort := cHttpProtocol;
  112.   FProxyHost := '';
  113.   FProxyPort := '8080';
  114.   FProxyUser := '';
  115.   FProxyPass := '';
  116.   FAliveHost := '';
  117.   FAlivePort := '';
  118.   FProtocol := '1.1';
  119.   FKeepAlive := True;
  120.   Clear;
  121. end;
  122.  
  123. destructor THTTPSend.Destroy;
  124. begin
  125.   FSock.Free;
  126.   FDocument.Free;
  127.   FHeaders.Free;
  128.   inherited Destroy;
  129. end;
  130.  
  131. procedure THTTPSend.Clear;
  132. begin
  133.   FDocument.Clear;
  134.   FHeaders.Clear;
  135.   FMimeType := 'text/html';
  136. end;
  137.  
  138. procedure THTTPSend.DecodeStatus(const Value: string);
  139. var
  140.   s, su: string;
  141. begin
  142.   s := SeparateRight(Value, ' ');
  143.   su := SeparateLeft(s, ' ');
  144.   FResultCode := StrToIntDef(su, 0);
  145.   FResultString := SeparateRight(s, ' ');
  146.   if FResultString = s then
  147.     FResultString := '';
  148. end;
  149.  
  150. function THTTPSend.HTTPMethod(const Method, URL: string): Boolean;
  151. var
  152.   Sending, Receiving: Boolean;
  153.   status100: Boolean;
  154.   status100error: string;
  155.   ToClose: Boolean;
  156.   Size: Integer;
  157.   Prot, User, Pass, Host, Port, Path, Para, URI: string;
  158.   n: Integer;
  159.   s, su: string;
  160. begin
  161.   {initial values}
  162.   Result := False;
  163.   FResultCode := 500;
  164.   FResultString := '';
  165.  
  166.   URI := ParseURL(URL, Prot, User, Pass, Host, Port, Path, Para);
  167.   Sending := Document.Size > 0;
  168.   {Headers for Sending data}
  169.   status100 := Sending and (FProtocol = '1.1');
  170.   if status100 then
  171.     FHeaders.Insert(0, 'Expect: 100-continue');
  172.   if Sending then
  173.   begin
  174.     FHeaders.Insert(0, 'Content-Length: ' + IntToStr(FDocument.Size));
  175.     if FMimeType <> '' then
  176.       FHeaders.Insert(0, 'Content-Type: ' + FMimeType);
  177.   end;
  178.   { setting KeepAlives }
  179.   if not FKeepAlive then
  180.     FHeaders.Insert(0, 'Connection: close');
  181.   { set target servers/proxy, authorisations, etc... }
  182.   if User <> '' then
  183.     FHeaders.Insert(0, 'Authorization: Basic ' + EncodeBase64(user + ':' + pass));
  184.   if (FProxyHost <> '') and (FProxyUser <> '') then
  185.     FHeaders.Insert(0, 'Proxy-Authorization: Basic ' +
  186.       EncodeBase64(FProxyUser + ':' + FProxyPass));
  187.   if Port<>'80' then
  188.      FHeaders.Insert(0, 'Host: ' + Host + ':' + Port)
  189.   else
  190.      FHeaders.Insert(0, 'Host: ' + Host);
  191.   if FProxyHost <> '' then
  192.     URI := Prot + '://' + Host + ':' + Port + URI;
  193.   if URI = '/*' then
  194.     URI := '*';
  195.   if FProtocol = '0.9' then
  196.     FHeaders.Insert(0, UpperCase(Method) + ' ' + URI)
  197.   else
  198.     FHeaders.Insert(0, UpperCase(Method) + ' ' + URI + ' HTTP/' + FProtocol);
  199.   if FProxyHost = '' then
  200.   begin
  201.     FHTTPHost := Host;
  202.     FHTTPPort := Port;
  203.   end
  204.   else
  205.   begin
  206.     FHTTPHost := FProxyHost;
  207.     FHTTPPort := FProxyPort;
  208.   end;
  209.   if FHeaders[FHeaders.Count - 1] <> '' then
  210.     FHeaders.Add('');
  211.  
  212.   { connect }
  213.   if (FAliveHost <> FHTTPHost) or (FAlivePort <> FHTTPPort) then
  214.   begin
  215.     FSock.CloseSocket;
  216.     FSock.CreateSocket;
  217.     FSock.Connect(FHTTPHost, FHTTPPort);
  218.     if FSock.LastError <> 0 then
  219.       Exit;
  220.     FAliveHost := FHTTPHost;
  221.     FAlivePort := FHTTPPort;
  222.   end
  223.   else
  224.   begin
  225.     if FSock.CanRead(0) then
  226.     begin
  227.       FSock.CloseSocket;
  228.       FSock.CreateSocket;
  229.       FSock.Connect(FHTTPHost, FHTTPPort);
  230.       if FSock.LastError <> 0 then
  231.         Exit;
  232.     end;
  233.   end;
  234.  
  235.   { send Headers }
  236.   FSock.SendString(Headers[0] + CRLF);
  237.   if FProtocol <> '0.9' then
  238.     for n := 1 to FHeaders.Count - 1 do
  239.       FSock.SendString(FHeaders[n] + CRLF);
  240.   if FSock.LastError <> 0 then
  241.     Exit;
  242.  
  243.   { reading Status }
  244.   Status100Error := '';
  245.   if status100 then
  246.   begin
  247.     repeat
  248.       s := FSock.RecvString(FTimeout);
  249.       if s <> '' then
  250.         Break;
  251.     until FSock.LastError <> 0;
  252.     DecodeStatus(s);
  253.     if (FResultCode >= 100) and (FResultCode < 200) then
  254.       repeat
  255.         s := FSock.recvstring(FTimeout);
  256.         if s = '' then
  257.           Break;
  258.       until FSock.LastError <> 0
  259.     else
  260.     begin
  261.       Sending := False;
  262.       Status100Error := s;
  263.     end;
  264.   end;
  265.  
  266.   { send document }
  267.   if Sending then
  268.   begin
  269.     FSock.SendBuffer(FDocument.Memory, FDocument.Size);
  270.     if FSock.LastError <> 0 then
  271.       Exit;
  272.   end;
  273.  
  274.   Clear;
  275.   Size := -1;
  276.   FTransferEncoding := TE_UNKNOWN;
  277.  
  278.   { read status }
  279.   if Status100Error = '' then
  280.   begin
  281.     repeat
  282.       s := FSock.RecvString(FTimeout);
  283.       if s <> '' then
  284.         Break;
  285.     until FSock.LastError <> 0;
  286.     if Pos('HTTP/', UpperCase(s)) = 1 then
  287.     begin
  288.       FHeaders.Add(s);
  289.       DecodeStatus(s);
  290.     end
  291.     else
  292.     begin
  293.       { old HTTP 0.9 and some buggy servers not send result }
  294.       s := s + CRLF;
  295.       FDocument.Write(Pointer(s)^, Length(s));
  296.       FResultCode := 0;
  297.     end;
  298.   end
  299.   else
  300.     FHeaders.Add(Status100Error);
  301.  
  302.   { if need receive hedaers, receive and parse it }
  303.   ToClose := FProtocol <> '1.1';
  304.   if FHeaders.Count > 0 then
  305.     repeat
  306.       s := FSock.RecvString(FTimeout);
  307.       FHeaders.Add(s);
  308.       if s = '' then
  309.         Break;
  310.       su := UpperCase(s);
  311.       if Pos('CONTENT-LENGTH:', su) = 1 then
  312.       begin
  313.         Size := StrToIntDef(SeparateRight(s, ' '), -1);
  314.         if Size <> -1 then
  315.           FTransferEncoding := TE_IDENTITY;
  316.       end;
  317.       if Pos('CONTENT-TYPE:', su) = 1 then
  318.         FMimeType := SeparateRight(s, ' ');
  319.       if Pos('TRANSFER-ENCODING:', su) = 1 then
  320.       begin
  321.         s := SeparateRight(su, ' ');
  322.         if Pos('CHUNKED', s) > 0 then
  323.           FTransferEncoding := TE_CHUNKED;
  324.       end;
  325.       if Pos('CONNECTION: CLOSE', su) = 1 then
  326.         ToClose := True;
  327.     until FSock.LastError <> 0;
  328.  
  329.   {if need receive response body, read it}
  330.   Receiving := Method <> 'HEAD';
  331.   Receiving := Receiving and (FResultCode <> 204);
  332.   Receiving := Receiving and (FResultCode <> 304);
  333.   if Receiving then
  334.     case FTransferEncoding of
  335.       TE_UNKNOWN:
  336.         ReadUnknown;
  337.       TE_IDENTITY:
  338.         ReadIdentity(Size);
  339.       TE_CHUNKED:
  340.         ReadChunked;
  341.     end;
  342.  
  343.   FDocument.Seek(0, soFromBeginning);
  344.   Result := True;
  345.   if ToClose then
  346.   begin
  347.     FSock.CloseSocket;
  348.     FAliveHost := '';
  349.     FAlivePort := '';
  350.   end;
  351. end;
  352.  
  353. function THTTPSend.ReadUnknown: Boolean;
  354. var
  355.   s: string;
  356. begin
  357.   repeat
  358.     s := FSock.RecvPacket(FTimeout);
  359.     if FSock.LastError = 0 then
  360.       FDocument.Write(Pointer(s)^, Length(s));
  361.   until FSock.LastError <> 0;
  362.   Result := True;
  363. end;
  364.  
  365. function THTTPSend.ReadIdentity(Size: Integer): Boolean;
  366. var
  367.   mem: TMemoryStream;
  368. begin
  369.   mem := TMemoryStream.Create;
  370.   try
  371.     mem.SetSize(Size);
  372.     FSock.RecvBufferEx(mem.Memory, Size, FTimeout);
  373.     Result := FSock.LastError = 0;
  374.     FDocument.CopyFrom(mem, 0);
  375.   finally
  376.     mem.Free;
  377.   end;
  378. end;
  379.  
  380. function THTTPSend.ReadChunked: Boolean;
  381. var
  382.   s: string;
  383.   Size: Integer;
  384. begin
  385.   repeat
  386.     repeat
  387.       s := FSock.RecvString(FTimeout);
  388.     until s <> '';
  389.     if FSock.LastError <> 0 then
  390.       Break;
  391.     s := SeparateLeft(s, ' ');
  392.     Size := StrToIntDef('$' + s, 0);
  393.     if Size = 0 then
  394.       Break;
  395.     ReadIdentity(Size);
  396.   until False;
  397.   Result := FSock.LastError = 0;
  398. end;
  399.  
  400. {==============================================================================}
  401.  
  402. function HttpGetText(const URL: string; const Response: TStrings): Boolean;
  403. var
  404.   HTTP: THTTPSend;
  405. begin
  406.   HTTP := THTTPSend.Create;
  407.   try
  408.     Result := HTTP.HTTPMethod('GET', URL);
  409.     Response.LoadFromStream(HTTP.Document);
  410.   finally
  411.     HTTP.Free;
  412.   end;
  413. end;
  414.  
  415. function HttpGetBinary(const URL: string; const Response: TStream): Boolean;
  416. var
  417.   HTTP: THTTPSend;
  418. begin
  419.   HTTP := THTTPSend.Create;
  420.   try
  421.     Result := HTTP.HTTPMethod('GET', URL);
  422.     Response.Seek(0, soFromBeginning);
  423.     Response.CopyFrom(HTTP.Document, 0);
  424.   finally
  425.     HTTP.Free;
  426.   end;
  427. end;
  428.  
  429. function HttpPostBinary(const URL: string; const Data: TStream): Boolean;
  430. var
  431.   HTTP: THTTPSend;
  432. begin
  433.   HTTP := THTTPSend.Create;
  434.   try
  435.     HTTP.Document.CopyFrom(Data, 0);
  436.     HTTP.MimeType := 'Application/octet-stream';
  437.     Result := HTTP.HTTPMethod('POST', URL);
  438.     Data.Seek(0, soFromBeginning);
  439.     Data.CopyFrom(HTTP.Document, 0);
  440.   finally
  441.     HTTP.Free;
  442.   end;
  443. end;
  444.  
  445. function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean;
  446. var
  447.   HTTP: THTTPSend;
  448. begin
  449.   HTTP := THTTPSend.Create;
  450.   try
  451.     HTTP.Document.Write(Pointer(URLData)^, Length(URLData));
  452.     HTTP.MimeType := 'application/x-url-encoded';
  453.     Result := HTTP.HTTPMethod('POST', URL);
  454.     Data.CopyFrom(HTTP.Document, 0);
  455.   finally
  456.     HTTP.Free;
  457.   end;
  458. end;
  459.  
  460. function HttpPostFile(const URL, FieldName, FileName: string;
  461.   const Data: TStream; const ResultData: TStringList): Boolean;
  462. const
  463.   CRLF = #$0D + #$0A;
  464. var
  465.   HTTP: THTTPSend;
  466.   Bound, s: string;
  467. begin
  468.   Bound := '--' + IntToHex(Random(MaxInt), 8) + '_Synapse_boundary';
  469.   HTTP := THTTPSend.Create;
  470.   try
  471.     s := Bound + CRLF;
  472.     s := s + 'content-disposition: form-data; name="' + FieldName + '";';
  473.     s := s + ' filename="' + FileName +'"' + CRLF;
  474.     s := s + 'Content-Type: Application/octet-string' + CRLF + CRLF;
  475.     HTTP.Document.Write(Pointer(s)^, Length(s));
  476.     HTTP.Document.CopyFrom(Data, 0);
  477.     s := CRLF + Bound + '--' + CRLF;
  478.     HTTP.Document.Write(Pointer(s)^, Length(s));
  479.     HTTP.MimeType := 'multipart/form-data, boundary=' + Bound;
  480.     Result := HTTP.HTTPMethod('POST', URL);
  481.     ResultData.LoadFromStream(HTTP.Document);
  482.   finally
  483.     HTTP.Free;
  484.   end;
  485. end;
  486.  
  487. end.
  488.