httpclient.pp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. {
  2. HTTPClient: HTTP client component
  3. Copyright (C) 2000-2003 by Sebastian Guenther ([email protected])
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. }
  10. unit HTTPClient;
  11. interface
  12. uses Classes, HTTPBase, fpSock, fpAsync;
  13. type
  14. TCustomHTTPClient = class(TCustomTCPClient)
  15. protected
  16. SendBuffer: TAsyncWriteStream;
  17. FOnPrepareSending: TNotifyEvent;
  18. FOnHeaderSent: TNotifyEvent;
  19. FOnStreamSent: TNotifyEvent;
  20. FOnPrepareReceiving: TNotifyEvent;
  21. FOnHeaderReceived: TNotifyEvent;
  22. FOnStreamReceived: TNotifyEvent;
  23. FOnDestroy: TNotifyEvent;
  24. RecvSize: Integer; // How many bytes are still to be read. -1 if unknown.
  25. DataAvailableNotifyHandle: Pointer;
  26. ReceivedHTTPVersion: String;
  27. procedure HeaderToSendCompleted(Sender: TObject);
  28. procedure StreamToSendCompleted(Sender: TObject);
  29. procedure ReceivedHeaderCompleted(Sender: TObject);
  30. procedure ReceivedHeaderEOF(Sender: TObject);
  31. procedure DataAvailable(Sender: TObject);
  32. procedure ReceivedStreamCompleted(Sender: TObject);
  33. property OnPrepareSending: TNotifyEvent read FOnPrepareSending write FOnPrepareSending;
  34. property OnHeaderSent: TNotifyEvent read FOnHeaderSent write FOnHeaderSent;
  35. property OnStreamSent: TNotifyEvent read FOnStreamSent write FOnStreamSent;
  36. property OnPrepareReceiving: TNotifyEvent read FOnPrepareReceiving write FOnPrepareReceiving;
  37. property OnHeaderReceived: TNotifyEvent read FOnHeaderReceived write FOnHeaderReceived;
  38. property OnStreamReceived: TNotifyEvent read FOnStreamReceived write FOnStreamReceived;
  39. property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
  40. public
  41. HeaderToSend: THttpHeader;
  42. StreamToSend: TStream;
  43. ReceivedHeader: THttpHeader;
  44. ReceivedStream: TStream;
  45. DoDestroy: Boolean;
  46. destructor Destroy; override;
  47. procedure Receive;
  48. procedure Send;
  49. end;
  50. THttpClient = class(TCustomHttpClient)
  51. public
  52. property OnPrepareSending;
  53. property OnHeaderSent;
  54. property OnStreamSent;
  55. property OnPrepareReceiving;
  56. property OnHeaderReceived;
  57. property OnStreamReceived;
  58. property OnDestroy;
  59. end;
  60. {TCustomHTTPClient = class
  61. protected
  62. FEventLoop: TEventLoop;
  63. FSocket: TInetSocket;
  64. SendBuffer: TAsyncWriteStream;
  65. FOnPrepareSending: TNotifyEvent;
  66. FOnHeaderSent: TNotifyEvent;
  67. FOnStreamSent: TNotifyEvent;
  68. FOnPrepareReceiving: TNotifyEvent;
  69. FOnHeaderReceived: TNotifyEvent;
  70. FOnStreamReceived: TNotifyEvent;
  71. FOnDestroy: TNotifyEvent;
  72. RecvSize: Integer; // How many bytes are still to be read. -1 if unknown.
  73. DataAvailableNotifyHandle: Pointer;
  74. ReceivedHTTPVersion: String;
  75. procedure HeaderToSendCompleted(Sender: TObject);
  76. procedure StreamToSendCompleted(Sender: TObject);
  77. procedure ReceivedHeaderCompleted(Sender: TObject);
  78. procedure ReceivedHeaderEOF(Sender: TObject);
  79. procedure DataAvailable(Sender: TObject);
  80. procedure ReceivedStreamCompleted(Sender: TObject);
  81. property OnPrepareSending: TNotifyEvent read FOnPrepareSending write FOnPrepareSending;
  82. property OnHeaderSent: TNotifyEvent read FOnHeaderSent write FOnHeaderSent;
  83. property OnStreamSent: TNotifyEvent read FOnStreamSent write FOnStreamSent;
  84. property OnPrepareReceiving: TNotifyEvent read FOnPrepareReceiving write FOnPrepareReceiving;
  85. property OnHeaderReceived: TNotifyEvent read FOnHeaderReceived write FOnHeaderReceived;
  86. property OnStreamReceived: TNotifyEvent read FOnStreamReceived write FOnStreamReceived;
  87. property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
  88. public
  89. HeaderToSend: THttpHeader;
  90. StreamToSend: TStream;
  91. ReceivedHeader: THttpHeader;
  92. ReceivedStream: TStream;
  93. DoDestroy: Boolean;
  94. constructor Create(AEventLoop: TEventLoop; ASocket: TInetSocket);
  95. destructor Destroy; override;
  96. procedure Receive;
  97. procedure Send;
  98. end;}
  99. implementation
  100. uses SysUtils;
  101. procedure TCustomHttpClient.HeaderToSendCompleted(Sender: TObject);
  102. begin
  103. // WriteLn('TCustomHttpClient.HeaderToSendCompleted');
  104. if Assigned(FOnHeaderSent) then
  105. FOnHeaderSent(Self);
  106. if Assigned(StreamToSend) then
  107. begin
  108. SendBuffer := TAsyncWriteStream.Create(EventLoop, Stream);
  109. SendBuffer.CopyFrom(StreamToSend, StreamToSend.Size);
  110. SendBuffer.OnBufferSent := @StreamToSendCompleted;
  111. end else
  112. begin
  113. StreamToSendCompleted(nil);
  114. if DoDestroy then
  115. Self.Free;
  116. end;
  117. end;
  118. procedure TCustomHttpClient.StreamToSendCompleted(Sender: TObject);
  119. begin
  120. // WriteLn('TCustomHttpClient.StreamToSendCompleted');
  121. if Assigned(FOnStreamSent) then
  122. FOnStreamSent(Self);
  123. FreeAndNil(SendBuffer);
  124. if DoDestroy then
  125. Self.Free
  126. else
  127. Receive;
  128. end;
  129. procedure TCustomHttpClient.ReceivedHeaderCompleted(Sender: TObject);
  130. var
  131. BytesInBuffer: Integer;
  132. NeedMoreData: Boolean;
  133. begin
  134. // WriteLn('TCustomHttpClient.ReceivedHeaderCompleted');
  135. ReceivedHeader.DataReceived := False;
  136. ReceivedHTTPVersion := ReceivedHeader.HttpVersion;
  137. BytesInBuffer := ReceivedHeader.Reader.BytesInBuffer;
  138. //WriteLn('BytesInBuffer: ', BytesInBuffer, ', Content length: ', ReceivedHeader.ContentLength);
  139. if Assigned(FOnHeaderReceived) then
  140. FOnHeaderReceived(Self);
  141. RecvSize := ReceivedHeader.ContentLength;
  142. if Assigned(ReceivedStream) then
  143. begin
  144. if BytesInBuffer = 0 then
  145. NeedMoreData := True
  146. else
  147. begin
  148. ReceivedStream.Write(ReceivedHeader.Reader.Buffer^, BytesInBuffer);
  149. if RecvSize > 0 then
  150. Dec(RecvSize, BytesInBuffer);
  151. if BytesInBuffer = ReceivedHeader.ContentLength then
  152. NeedMoreData := False
  153. else
  154. NeedMoreData := (not ReceivedHeader.InheritsFrom(THttpRequestHeader)) or
  155. (THttpRequestHeader(ReceivedHeader).Command <> 'GET');
  156. end;
  157. end else
  158. NeedMoreData := False;
  159. if NeedMoreData then
  160. DataAvailableNotifyHandle :=
  161. EventLoop.SetDataAvailableNotify(Stream.Handle, @DataAvailable, Stream)
  162. else
  163. ReceivedStreamCompleted(nil);
  164. if DoDestroy then
  165. Self.Free;
  166. end;
  167. procedure TCustomHttpClient.ReceivedHeaderEOF(Sender: TObject);
  168. begin
  169. Self.Free;
  170. end;
  171. procedure TCustomHttpClient.DataAvailable(Sender: TObject);
  172. var
  173. FirstRun: Boolean;
  174. ReadNow, BytesRead: Integer;
  175. buf: array[0..1023] of Byte;
  176. begin
  177. FirstRun := True;
  178. while True do
  179. begin
  180. if RecvSize >= 0 then
  181. begin
  182. ReadNow := RecvSize;
  183. if ReadNow > 1024 then
  184. ReadNow := 1024;
  185. end else
  186. ReadNow := 1024;
  187. BytesRead := Stream.Read(buf, ReadNow);
  188. // WriteLn('TCustomHttpClient.DataAvailable: Read ', BytesRead, ' bytes; RecvSize=', RecvSize);
  189. if BytesRead <= 0 then
  190. begin
  191. if FirstRun then
  192. ReceivedStreamCompleted(nil);
  193. break;
  194. end;
  195. FirstRun := False;
  196. ReceivedStream.Write(buf, BytesRead);
  197. if RecvSize > 0 then
  198. Dec(RecvSize, BytesRead);
  199. if RecvSize = 0 then
  200. begin
  201. ReceivedStreamCompleted(nil);
  202. break;
  203. end;
  204. end;
  205. if DoDestroy then
  206. Self.Free;
  207. end;
  208. procedure TCustomHttpClient.ReceivedStreamCompleted(Sender: TObject);
  209. begin
  210. // WriteLn('TCustomHttpClient.ReceivedStreamCompleted');
  211. if Assigned(DataAvailableNotifyHandle) then
  212. begin
  213. EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  214. DataAvailableNotifyHandle := nil;
  215. end;
  216. if Assigned(FOnStreamReceived) then
  217. FOnStreamReceived(Self);
  218. if DoDestroy then
  219. Self.Free
  220. else
  221. Send;
  222. end;
  223. {constructor TCustomHttpClient.Create(AManager: TEventLoop; ASocket: TInetSocket);
  224. begin
  225. inherited Create;
  226. EventLoop := AManager;
  227. Stream := ASocket;
  228. end;}
  229. destructor TCustomHttpClient.Destroy;
  230. begin
  231. if Assigned(DataAvailableNotifyHandle) then
  232. EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  233. if Assigned(OnDestroy) then
  234. OnDestroy(Self);
  235. FreeAndNil(SendBuffer);
  236. inherited Destroy;
  237. end;
  238. procedure TCustomHttpClient.Receive;
  239. begin
  240. // Start receiver
  241. ReceivedHttpVersion := '';
  242. if Assigned(OnPrepareReceiving) then
  243. OnPrepareReceiving(Self);
  244. if Assigned(ReceivedHeader) then
  245. begin
  246. ReceivedHeader.OnCompleted := @ReceivedHeaderCompleted;
  247. ReceivedHeader.OnEOF := @ReceivedHeaderEOF;
  248. ReceivedHeader.AsyncReceive(EventLoop, Stream);
  249. end;
  250. end;
  251. procedure TCustomHttpClient.Send;
  252. begin
  253. // Start sender
  254. if Assigned(OnPrepareSending) then
  255. OnPrepareSending(Self);
  256. if Assigned(HeaderToSend) then
  257. begin
  258. if ReceivedHttpVersion <> '' then
  259. begin
  260. HeaderToSend.HttpVersion := ReceivedHttpVersion;
  261. ReceivedHttpVersion := '';
  262. end;
  263. HeaderToSend.OnCompleted := @HeaderToSendCompleted;
  264. HeaderToSend.AsyncSend(EventLoop, Stream);
  265. end;
  266. end;
  267. end.