home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 November / Chip_2002-11_cd1.bin / zkuste / delphi / kompon / d3456 / SYNAPSE.ZIP / source / lib / HTTPSend.pas < prev    next >
Pascal/Delphi Source File  |  2002-07-07  |  16KB  |  531 lines

  1. {==============================================================================|
  2. | Project : Delphree - Synapse                                   | 003.002.000 |
  3. |==============================================================================|
  4. | Content: HTTP client                                                         |
  5. |==============================================================================|
  6. | Copyright (c)1999-2002, Lukas Gebauer                                        |
  7. | All rights reserved.                                                         |
  8. |                                                                              |
  9. | Redistribution and use in source and binary forms, with or without           |
  10. | modification, are permitted provided that the following conditions are met:  |
  11. |                                                                              |
  12. | Redistributions of source code must retain the above copyright notice, this  |
  13. | list of conditions and the following disclaimer.                             |
  14. |                                                                              |
  15. | Redistributions in binary form must reproduce the above copyright notice,    |
  16. | this list of conditions and the following disclaimer in the documentation    |
  17. | and/or other materials provided with the distribution.                       |
  18. |                                                                              |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may      |
  20. | be used to endorse or promote products derived from this software without    |
  21. | specific prior written permission.                                           |
  22. |                                                                              |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE    |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE   |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR  |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL       |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR   |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER   |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT           |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY    |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH  |
  33. | DAMAGE.                                                                      |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c) 1999-2002.               |
  37. | All Rights Reserved.                                                         |
  38. |==============================================================================|
  39. | Contributor(s):                                                              |
  40. |==============================================================================|
  41. | History: see HISTORY.HTM from distribution package                           |
  42. |          (Found at URL: http://www.ararat.cz/synapse/)                       |
  43. |==============================================================================}
  44.  
  45. {$WEAKPACKAGEUNIT ON}
  46.  
  47. unit HTTPSend;
  48.  
  49. interface
  50.  
  51. uses
  52.   SysUtils, Classes,
  53.   blcksock, SynaUtil, SynaCode;
  54.  
  55. const
  56.   cHttpProtocol = '80';
  57.  
  58. type
  59.   TTransferEncoding = (TE_UNKNOWN, TE_IDENTITY, TE_CHUNKED);
  60.  
  61.   THTTPSend = class(TSynaClient)
  62.   private
  63.     FSock: TTCPBlockSocket;
  64.     FTransferEncoding: TTransferEncoding;
  65.     FAliveHost: string;
  66.     FAlivePort: string;
  67.     FHeaders: TStringList;
  68.     FDocument: TMemoryStream;
  69.     FMimeType: string;
  70.     FProtocol: string;
  71.     FKeepAlive: Boolean;
  72.     FProxyHost: string;
  73.     FProxyPort: string;
  74.     FProxyUser: string;
  75.     FProxyPass: string;
  76.     FResultCode: Integer;
  77.     FResultString: string;
  78.     function ReadUnknown: Boolean;
  79.     function ReadIdentity(Size: Integer): Boolean;
  80.     function ReadChunked: Boolean;
  81.   public
  82.     constructor Create;
  83.     destructor Destroy; override;
  84.     procedure Clear;
  85.     procedure DecodeStatus(const Value: string);
  86.     function HTTPMethod(const Method, URL: string): Boolean;
  87.   published
  88.     property Headers: TStringList read FHeaders Write FHeaders;
  89.     property Document: TMemoryStream read FDocument Write FDocument;
  90.     property MimeType: string read FMimeType Write FMimeType;
  91.     property Protocol: string read FProtocol Write FProtocol;
  92.     property KeepAlive: Boolean read FKeepAlive Write FKeepAlive;
  93.     property ProxyHost: string read FProxyHost Write FProxyHost;
  94.     property ProxyPort: string read FProxyPort Write FProxyPort;
  95.     property ProxyUser: string read FProxyUser Write FProxyUser;
  96.     property ProxyPass: string read FProxyPass Write FProxyPass;
  97.     property ResultCode: Integer read FResultCode;
  98.     property ResultString: string read FResultString;
  99.     property Sock: TTCPBlockSocket read FSock;
  100.   end;
  101.  
  102. function HttpGetText(const URL: string; const Response: TStrings): Boolean;
  103. function HttpGetBinary(const URL: string; const Response: TStream): Boolean;
  104. function HttpPostBinary(const URL: string; const Data: TStream): Boolean;
  105. function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean;
  106. function HttpPostFile(const URL, FieldName, FileName: string;
  107.   const Data: TStream; const ResultData: TStrings): Boolean;
  108.  
  109. implementation
  110.  
  111. const
  112.   CRLF = #13#10;
  113.  
  114. constructor THTTPSend.Create;
  115. begin
  116.   inherited Create;
  117.   FHeaders := TStringList.Create;
  118.   FDocument := TMemoryStream.Create;
  119.   FSock := TTCPBlockSocket.Create;
  120.   FSock.SizeRecvBuffer := 65536;
  121.   FSock.SizeSendBuffer := 65536;
  122.   FSock.ConvertLineEnd := True;
  123.   FTimeout := 300000;
  124.   FTargetPort := cHttpProtocol;
  125.   FProxyHost := '';
  126.   FProxyPort := '8080';
  127.   FProxyUser := '';
  128.   FProxyPass := '';
  129.   FAliveHost := '';
  130.   FAlivePort := '';
  131.   FProtocol := '1.0';
  132.   FKeepAlive := True;
  133.   Clear;
  134. end;
  135.  
  136. destructor THTTPSend.Destroy;
  137. begin
  138.   FSock.Free;
  139.   FDocument.Free;
  140.   FHeaders.Free;
  141.   inherited Destroy;
  142. end;
  143.  
  144. procedure THTTPSend.Clear;
  145. begin
  146.   FDocument.Clear;
  147.   FHeaders.Clear;
  148.   FMimeType := 'text/html';
  149. end;
  150.  
  151. procedure THTTPSend.DecodeStatus(const Value: string);
  152. var
  153.   s, su: string;
  154. begin
  155.   s := SeparateRight(Value, ' ');
  156.   su := SeparateLeft(s, ' ');
  157.   FResultCode := StrToIntDef(su, 0);
  158.   FResultString := SeparateRight(s, ' ');
  159.   if FResultString = s then
  160.     FResultString := '';
  161. end;
  162.  
  163. function THTTPSend.HTTPMethod(const Method, URL: string): Boolean;
  164. var
  165.   Sending, Receiving: Boolean;
  166.   status100: Boolean;
  167.   status100error: string;
  168.   ToClose: Boolean;
  169.   Size: Integer;
  170.   Prot, User, Pass, Host, Port, Path, Para, URI: string;
  171.   s, su: string;
  172.   HttpTunnel: Boolean;
  173. begin
  174.   {initial values}
  175.   Result := False;
  176.   FResultCode := 500;
  177.   FResultString := '';
  178.  
  179.   URI := ParseURL(URL, Prot, User, Pass, Host, Port, Path, Para);
  180.  
  181.   if UpperCase(Prot) = 'HTTPS' then
  182.   begin
  183.     FSock.SSLEnabled := True;
  184.     HttpTunnel := FProxyHost <> '';
  185.     FSock.HTTPTunnelIP := FProxyHost;
  186.     FSock.HTTPTunnelPort := FProxyPort;
  187.     FSock.HTTPTunnelUser := FProxyUser;
  188.     FSock.HTTPTunnelPass := FProxyPass;
  189.   end
  190.   else
  191.   begin
  192.     FSock.SSLEnabled := False;
  193.     HttpTunnel := False;
  194.     FSock.HTTPTunnelIP := '';
  195.     FSock.HTTPTunnelPort := '';
  196.     FSock.HTTPTunnelUser := '';
  197.     FSock.HTTPTunnelPass := '';
  198.   end;
  199.  
  200.   Sending := Document.Size > 0;
  201.   {Headers for Sending data}
  202.   status100 := Sending and (FProtocol = '1.1');
  203.   if status100 then
  204.     FHeaders.Insert(0, 'Expect: 100-continue');
  205.   if Sending then
  206.   begin
  207.     FHeaders.Insert(0, 'Content-Length: ' + IntToStr(FDocument.Size));
  208.     if FMimeType <> '' then
  209.       FHeaders.Insert(0, 'Content-Type: ' + FMimeType);
  210.   end;
  211.   { setting KeepAlives }
  212.   if not FKeepAlive then
  213.     FHeaders.Insert(0, 'Connection: close');
  214.   { set target servers/proxy, authorizations, etc... }
  215.   if User <> '' then
  216.     FHeaders.Insert(0, 'Authorization: Basic ' + EncodeBase64(user + ':' + pass));
  217.   if (FProxyHost <> '') and (FProxyUser <> '') and not(HttpTunnel) then
  218.     FHeaders.Insert(0, 'Proxy-Authorization: Basic ' +
  219.       EncodeBase64(FProxyUser + ':' + FProxyPass));
  220.   if Port<>'80' then
  221.      FHeaders.Insert(0, 'Host: ' + Host + ':' + Port)
  222.   else
  223.      FHeaders.Insert(0, 'Host: ' + Host);
  224.   if (FProxyHost <> '') and not(HttpTunnel)then
  225.     URI := Prot + '://' + Host + ':' + Port + URI;
  226.   if URI = '/*' then
  227.     URI := '*';
  228.   if FProtocol = '0.9' then
  229.     FHeaders.Insert(0, UpperCase(Method) + ' ' + URI)
  230.   else
  231.     FHeaders.Insert(0, UpperCase(Method) + ' ' + URI + ' HTTP/' + FProtocol);
  232.   if (FProxyHost <> '') and not(HttpTunnel) then
  233.   begin
  234.     FTargetHost := FProxyHost;
  235.     FTargetPort := FProxyPort;
  236.   end
  237.   else
  238.   begin
  239.     FTargetHost := Host;
  240.     FTargetPort := Port;
  241.   end;
  242.   if FHeaders[FHeaders.Count - 1] <> '' then
  243.     FHeaders.Add('');
  244.  
  245.   { connect }
  246.   if (FAliveHost <> FTargetHost) or (FAlivePort <> FTargetPort) then
  247.   begin
  248.     FSock.CloseSocket;
  249.     FSock.CreateSocket;
  250.     FSock.Bind(FIPInterface, cAnyPort);
  251.     if FSock.LastError <> 0 then
  252.       Exit;
  253.     FSock.Connect(FTargetHost, FTargetPort);
  254.     if FSock.LastError <> 0 then
  255.       Exit;
  256.     FAliveHost := FTargetHost;
  257.     FAlivePort := FTargetPort;
  258.   end
  259.   else
  260.   begin
  261.     if FSock.CanRead(0) then
  262.     begin
  263.       FSock.CloseSocket;
  264.       FSock.CreateSocket;
  265.       FSock.Bind(FIPInterface, cAnyPort);
  266.       if FSock.LastError <> 0 then
  267.         Exit;
  268.       FSock.Connect(FTargetHost, FTargetPort);
  269.       if FSock.LastError <> 0 then
  270.         Exit;
  271.     end;
  272.   end;
  273.  
  274.   { send Headers }
  275.   if FProtocol = '0.9' then
  276.     FSock.SendString(FHeaders[0] + CRLF)
  277.   else
  278. {$IFDEF LINUX}
  279.     FSock.SendString(AdjustLineBreaks(FHeaders.Text, tlbsCRLF));
  280. {$ELSE}
  281.     FSock.SendString(FHeaders.Text);
  282. {$ENDIF}
  283.   if FSock.LastError <> 0 then
  284.     Exit;
  285.  
  286.   { reading Status }
  287.   Status100Error := '';
  288.   if status100 then
  289.   begin
  290.     repeat
  291.       s := FSock.RecvString(FTimeout);
  292.       if s <> '' then
  293.         Break;
  294.     until FSock.LastError <> 0;
  295.     DecodeStatus(s);
  296.     if (FResultCode >= 100) and (FResultCode < 200) then
  297.       repeat
  298.         s := FSock.recvstring(FTimeout);
  299.         if s = '' then
  300.           Break;
  301.       until FSock.LastError <> 0
  302.     else
  303.     begin
  304.       Sending := False;
  305.       Status100Error := s;
  306.     end;
  307.   end;
  308.  
  309.   { send document }
  310.   if Sending then
  311.   begin
  312.     FSock.SendBuffer(FDocument.Memory, FDocument.Size);
  313.     if FSock.LastError <> 0 then
  314.       Exit;
  315.   end;
  316.  
  317.   Clear;
  318.   Size := -1;
  319.   FTransferEncoding := TE_UNKNOWN;
  320.  
  321.   { read status }
  322.   if Status100Error = '' then
  323.   begin
  324.     repeat
  325.       s := FSock.RecvString(FTimeout);
  326.       if s <> '' then
  327.         Break;
  328.     until FSock.LastError <> 0;
  329.     if Pos('HTTP/', UpperCase(s)) = 1 then
  330.     begin
  331.       FHeaders.Add(s);
  332.       DecodeStatus(s);
  333.     end
  334.     else
  335.     begin
  336.       { old HTTP 0.9 and some buggy servers not send result }
  337.       s := s + CRLF;
  338.       FDocument.Write(Pointer(s)^, Length(s));
  339.       FResultCode := 0;
  340.     end;
  341.   end
  342.   else
  343.     FHeaders.Add(Status100Error);
  344.  
  345.   { if need receive headers, receive and parse it }
  346.   ToClose := FProtocol <> '1.1';
  347.   if FHeaders.Count > 0 then
  348.     repeat
  349.       s := FSock.RecvString(FTimeout);
  350.       FHeaders.Add(s);
  351.       if s = '' then
  352.         Break;
  353.       su := UpperCase(s);
  354.       if Pos('CONTENT-LENGTH:', su) = 1 then
  355.       begin
  356.         Size := StrToIntDef(SeparateRight(s, ' '), -1);
  357.         if Size <> -1 then
  358.           FTransferEncoding := TE_IDENTITY;
  359.       end;
  360.       if Pos('CONTENT-TYPE:', su) = 1 then
  361.         FMimeType := SeparateRight(s, ' ');
  362.       if Pos('TRANSFER-ENCODING:', su) = 1 then
  363.       begin
  364.         s := SeparateRight(su, ' ');
  365.         if Pos('CHUNKED', s) > 0 then
  366.           FTransferEncoding := TE_CHUNKED;
  367.       end;
  368.       if Pos('CONNECTION: CLOSE', su) = 1 then
  369.         ToClose := True;
  370.     until FSock.LastError <> 0;
  371.  
  372.   {if need receive response body, read it}
  373.   Receiving := Method <> 'HEAD';
  374.   Receiving := Receiving and (FResultCode <> 204);
  375.   Receiving := Receiving and (FResultCode <> 304);
  376.   if Receiving then
  377.     case FTransferEncoding of
  378.       TE_UNKNOWN:
  379.         ReadUnknown;
  380.       TE_IDENTITY:
  381.         ReadIdentity(Size);
  382.       TE_CHUNKED:
  383.         ReadChunked;
  384.     end;
  385.  
  386.   FDocument.Seek(0, soFromBeginning);
  387.   Result := True;
  388.   if ToClose then
  389.   begin
  390.     FSock.CloseSocket;
  391.     FAliveHost := '';
  392.     FAlivePort := '';
  393.   end;
  394. end;
  395.  
  396. function THTTPSend.ReadUnknown: Boolean;
  397. var
  398.   s: string;
  399. begin
  400.   repeat
  401.     s := FSock.RecvPacket(FTimeout);
  402.     if FSock.LastError = 0 then
  403.       FDocument.Write(Pointer(s)^, Length(s));
  404.   until FSock.LastError <> 0;
  405.   Result := True;
  406. end;
  407.  
  408. function THTTPSend.ReadIdentity(Size: Integer): Boolean;
  409. var
  410.   mem: TMemoryStream;
  411. begin
  412.   mem := TMemoryStream.Create;
  413.   try
  414.     mem.SetSize(Size);
  415.     FSock.RecvBufferEx(mem.Memory, Size, FTimeout);
  416.     Result := FSock.LastError = 0;
  417.     FDocument.CopyFrom(mem, 0);
  418.   finally
  419.     mem.Free;
  420.   end;
  421. end;
  422.  
  423. function THTTPSend.ReadChunked: Boolean;
  424. var
  425.   s: string;
  426.   Size: Integer;
  427. begin
  428.   repeat
  429.     repeat
  430.       s := FSock.RecvString(FTimeout);
  431.     until s <> '';
  432.     if FSock.LastError <> 0 then
  433.       Break;
  434.     s := SeparateLeft(s, ' ');
  435.     Size := StrToIntDef('$' + s, 0);
  436.     if Size = 0 then
  437.       Break;
  438.     ReadIdentity(Size);
  439.   until False;
  440.   Result := FSock.LastError = 0;
  441. end;
  442.  
  443. {==============================================================================}
  444.  
  445. function HttpGetText(const URL: string; const Response: TStrings): Boolean;
  446. var
  447.   HTTP: THTTPSend;
  448. begin
  449.   HTTP := THTTPSend.Create;
  450.   try
  451.     Result := HTTP.HTTPMethod('GET', URL);
  452.     Response.LoadFromStream(HTTP.Document);
  453.   finally
  454.     HTTP.Free;
  455.   end;
  456. end;
  457.  
  458. function HttpGetBinary(const URL: string; const Response: TStream): Boolean;
  459. var
  460.   HTTP: THTTPSend;
  461. begin
  462.   HTTP := THTTPSend.Create;
  463.   try
  464.     Result := HTTP.HTTPMethod('GET', URL);
  465.     Response.Seek(0, soFromBeginning);
  466.     Response.CopyFrom(HTTP.Document, 0);
  467.   finally
  468.     HTTP.Free;
  469.   end;
  470. end;
  471.  
  472. function HttpPostBinary(const URL: string; const Data: TStream): Boolean;
  473. var
  474.   HTTP: THTTPSend;
  475. begin
  476.   HTTP := THTTPSend.Create;
  477.   try
  478.     HTTP.Document.CopyFrom(Data, 0);
  479.     HTTP.MimeType := 'Application/octet-stream';
  480.     Result := HTTP.HTTPMethod('POST', URL);
  481.     Data.Seek(0, soFromBeginning);
  482.     Data.CopyFrom(HTTP.Document, 0);
  483.   finally
  484.     HTTP.Free;
  485.   end;
  486. end;
  487.  
  488. function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean;
  489. var
  490.   HTTP: THTTPSend;
  491. begin
  492.   HTTP := THTTPSend.Create;
  493.   try
  494.     HTTP.Document.Write(Pointer(URLData)^, Length(URLData));
  495.     HTTP.MimeType := 'application/x-www-form-urlencoded';
  496.     Result := HTTP.HTTPMethod('POST', URL);
  497.     Data.CopyFrom(HTTP.Document, 0);
  498.   finally
  499.     HTTP.Free;
  500.   end;
  501. end;
  502.  
  503. function HttpPostFile(const URL, FieldName, FileName: string;
  504.   const Data: TStream; const ResultData: TStrings): Boolean;
  505. const
  506.   CRLF = #$0D + #$0A;
  507. var
  508.   HTTP: THTTPSend;
  509.   Bound, s: string;
  510. begin
  511.   Bound := IntToHex(Random(MaxInt), 8) + '_Synapse_boundary';
  512.   HTTP := THTTPSend.Create;
  513.   try
  514.     s := '--' + Bound + CRLF;
  515.     s := s + 'content-disposition: form-data; name="' + FieldName + '";';
  516.     s := s + ' filename="' + FileName +'"' + CRLF;
  517.     s := s + 'Content-Type: Application/octet-string' + CRLF + CRLF;
  518.     HTTP.Document.Write(Pointer(s)^, Length(s));
  519.     HTTP.Document.CopyFrom(Data, 0);
  520.     s := CRLF + '--' + Bound + '--' + CRLF;
  521.     HTTP.Document.Write(Pointer(s)^, Length(s));
  522.     HTTP.MimeType := 'multipart/form-data, boundary=' + Bound;
  523.     Result := HTTP.HTTPMethod('POST', URL);
  524.     ResultData.LoadFromStream(HTTP.Document);
  525.   finally
  526.     HTTP.Free;
  527.   end;
  528. end;
  529.  
  530. end.
  531.