http.pp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. {
  2. $Id$
  3. HTTP: Classes for dealing with HTTP requests
  4. Copyright (C) 2000 by Sebastian Guenther ([email protected])
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. {$MODE objfpc}
  12. {$H+}
  13. unit HTTP;
  14. interface
  15. uses Classes, AsyncIO, SSockets;
  16. const
  17. fieldAccept = 'Accept';
  18. fieldAcceptCharset = 'Accept-Charset';
  19. fieldAcceptEncoding = 'Accept-Encoding';
  20. fieldAcceptLanguage = 'Accept-Language';
  21. fieldAuthorization = 'Authorization';
  22. fieldContentEncoding = 'Content-Encoding';
  23. fieldContentLanguage = 'Content-Language';
  24. fieldContentLength = 'Content-Length';
  25. fieldContentType = 'Content-Type';
  26. fieldCookie = 'Cookie';
  27. fieldDate = 'Date';
  28. fieldExpires = 'Expires';
  29. fieldFrom = 'From';
  30. fieldIfModifiedSince = 'If-Modified-Since';
  31. fieldLastModified = 'Last-Modified';
  32. fieldLocation = 'Location';
  33. fieldPragma = 'Pragma';
  34. fieldReferer = 'Referer';
  35. fieldRetryAfter = 'Retry-After';
  36. fieldServer = 'Server';
  37. fieldSetCookie = 'Set-Cookie';
  38. fieldUserAgent = 'User-Agent';
  39. fieldWWWAuthenticate = 'WWW-Authenticate';
  40. type
  41. PHttpField = ^THttpField;
  42. THttpField = record
  43. Name, Value: String;
  44. end;
  45. THttpHeader = class
  46. protected
  47. FReader: TAsyncStreamLineReader;
  48. FWriter: TAsyncWriteStream;
  49. FOnCompleted: TNotifyEvent;
  50. FFields: TList;
  51. CmdReceived: Boolean;
  52. procedure ParseFirstHeaderLine(const line: String); virtual; abstract;
  53. procedure LineReceived(const line: String);
  54. function GetFirstHeaderLine: String; virtual; abstract;
  55. procedure WriterCompleted(ASender: TObject);
  56. function GetFieldCount: Integer;
  57. function GetFields(AIndex: Integer): String;
  58. function GetFieldNames(AIndex: Integer): String;
  59. procedure SetFieldNames(AIndex: Integer; const AName: String);
  60. function GetFieldValues(AIndex: Integer): String;
  61. procedure SetFieldValues(AIndex: Integer; const AValue: String);
  62. function GetAccept: String;
  63. procedure SetAccept(const AValue: String);
  64. function GetAcceptCharset: String;
  65. procedure SetAcceptCharset(const AValue: String);
  66. function GetAcceptEncoding: String;
  67. procedure SetAcceptEncoding(const AValue: String);
  68. function GetAcceptLanguage: String;
  69. procedure SetAcceptLanguage(const AValue: String);
  70. function GetAuthorization: String;
  71. procedure SetAuthorization(const AValue: String);
  72. function GetContentEncoding: String;
  73. procedure SetContentEncoding(const AValue: String);
  74. function GetContentLanguage: String;
  75. procedure SetContentLanguage(const AValue: String);
  76. function GetContentLength: Integer;
  77. procedure SetContentLength(AValue: Integer);
  78. function GetContentType: String;
  79. procedure SetContentType(const AValue: String);
  80. function Get_Cookie: String;
  81. procedure Set_Cookie(const AValue: String);
  82. function GetDate: String;
  83. procedure SetDate(const AValue: String);
  84. function GetExpires: String;
  85. procedure SetExpires(const AValue: String);
  86. function GetFrom: String;
  87. procedure SetFrom(const AValue: String);
  88. function GetIfModifiedSince: String;
  89. procedure SetIfModifiedSince(const AValue: String);
  90. function GetLastModified: String;
  91. procedure SetLastModified(const AValue: String);
  92. function GetLocation: String;
  93. procedure SetLocation(const AValue: String);
  94. function GetPragma: String;
  95. procedure SetPragma(const AValue: String);
  96. function GetReferer: String;
  97. procedure SetReferer(const AValue: String);
  98. function GetRetryAfter: String;
  99. procedure SetRetryAfter(const AValue: String);
  100. function GetServer: String;
  101. procedure SetServer(const AValue: String);
  102. function Get_SetCookie: String;
  103. procedure Set_SetCookie(const AValue: String);
  104. function GetUserAgent: String;
  105. procedure SetUserAgent(const AValue: String);
  106. function GetWWWAuthenticate: String;
  107. procedure SetWWWAuthenticate(const AValue: String);
  108. public
  109. HttpVersion: String;
  110. constructor Create;
  111. destructor Destroy; override;
  112. procedure SetFieldByName(const AName, AValue: String);
  113. function GetFieldByName(const AName: String): String;
  114. procedure AsyncSend(AManager: TAsyncIOManager; AStream: THandleStream);
  115. procedure AsyncReceive(AManager: TAsyncIOManager; AStream: THandleStream);
  116. property Reader: TAsyncStreamLineReader read FReader;
  117. property Writer: TAsyncWriteStream read FWriter;
  118. property FieldCount: Integer read GetFieldCount;
  119. property Fields[AIndex: Integer]: String read GetFields;
  120. property FieldNames[AIndex: Integer]: String read GetFieldNames write SetFieldNames;
  121. property FieldValues[AIndex: Integer]: String read GetFieldValues write SetFieldValues;
  122. property OnCompleted: TNotifyEvent read FOnCompleted write FOnCompleted;
  123. property Accept: String read GetAccept write SetAccept;
  124. property AcceptCharset: String read GetAcceptCharset write SetAcceptCharset;
  125. property AcceptEncoding: String read GetAcceptEncoding write SetAcceptEncoding;
  126. property AcceptLanguage: String read GetAcceptLanguage write SetAcceptLanguage;
  127. property Authorization: String read GetAuthorization write SetAuthorization;
  128. property ContentEncoding: String read GetContentEncoding write SetContentEncoding;
  129. property ContentLanguage: String read GetContentLanguage write SetContentLanguage;
  130. property ContentLength: Integer read GetContentLength write SetContentLength;
  131. property ContentType: String read GetContentType write SetContentType;
  132. property Cookie: String read Get_Cookie write Set_Cookie;
  133. property Date: String read GetDate write SetDate;
  134. property Expires: String read GetExpires write SetExpires;
  135. property From: String read GetFrom write SetFrom;
  136. property IfModifiedSince: String read GetIfModifiedSince write SetIfModifiedSince;
  137. property LastModified: String read GetLastModified write SetLastModified;
  138. property Location: String read GetLocation write SetLocation;
  139. property Pragma: String read GetPragma write SetPragma;
  140. property Referer: String read GetReferer write SetReferer;
  141. property RetryAfter: String read GetRetryAfter write SetRetryAfter;
  142. property Server: String read GetServer write SetServer;
  143. property SetCookie: String read Get_SetCookie write Set_SetCookie;
  144. property UserAgent: String read GetUserAgent write SetUserAgent;
  145. property WWWAuthenticate: String read GetWWWAuthenticate write SetWWWAuthenticate;
  146. end;
  147. THttpRequestHeader = class(THttpHeader)
  148. protected
  149. procedure ParseFirstHeaderLine(const line: String); override;
  150. function GetFirstHeaderLine: String; override;
  151. public
  152. CommandLine: String;
  153. Command: String;
  154. URI: String; // Uniform Resource Identifier
  155. end;
  156. THttpAnswerHeader = class(THttpHeader)
  157. protected
  158. procedure ParseFirstHeaderLine(const line: String); override;
  159. function GetFirstHeaderLine: String; override;
  160. public
  161. Code: Integer;
  162. CodeText: String;
  163. constructor Create;
  164. end;
  165. TCustomHttpConnection = class
  166. protected
  167. FManager: TAsyncIOManager;
  168. FSocket: TInetSocket;
  169. SendBuffer: TAsyncWriteStream;
  170. FOnHeaderSent, FOnStreamSent, FOnHeaderReceived, FOnStreamReceived: TNotifyEvent;
  171. RecvSize: Integer; // How many bytes are still to be read. -1 if unknown.
  172. procedure HeaderToSendCompleted(Sender: TObject);
  173. procedure StreamToSendCompleted(Sender: TObject);
  174. procedure ReceivedHeaderCompleted(Sender: TObject);
  175. procedure DataAvailable(Sender: TObject);
  176. procedure ReceivedStreamCompleted(Sender: TObject);
  177. property OnHeaderSent: TNotifyEvent read FOnHeaderSent write FOnHeaderSent;
  178. property OnStreamSent: TNotifyEvent read FOnStreamSent write FOnStreamSent;
  179. property OnHeaderReceived: TNotifyEvent read FOnHeaderReceived write FOnHeaderReceived;
  180. property OnStreamReceived: TNotifyEvent read FOnStreamReceived write FOnStreamReceived;
  181. public
  182. HeaderToSend: THttpHeader;
  183. StreamToSend: TStream;
  184. ReceivedHeader: THttpHeader;
  185. ReceivedStream: TStream;
  186. constructor Create(AManager: TAsyncIOManager; ASocket: TInetSocket);
  187. destructor Destroy; override;
  188. procedure Start;
  189. end;
  190. THttpConnection = class(TCustomHttpConnection)
  191. public
  192. property OnHeaderSent;
  193. property OnStreamSent;
  194. property OnHeaderReceived;
  195. property OnStreamReceived;
  196. end;
  197. // ===================================================================
  198. // ===================================================================
  199. implementation
  200. uses SysUtils;
  201. // -------------------------------------------------------------------
  202. // THttpHeader
  203. // -------------------------------------------------------------------
  204. procedure THttpHeader.LineReceived(const line: String);
  205. var
  206. i: Integer;
  207. begin
  208. if Length(line) = 0 then
  209. begin
  210. FReader.OnLine := nil; // Stop receiving
  211. if Assigned(FOnCompleted) then
  212. FOnCompleted(Self);
  213. FReader.Free;
  214. FReader := nil;
  215. end else
  216. if not CmdReceived then
  217. begin
  218. CmdReceived := True;
  219. ParseFirstHeaderLine(line);
  220. end else
  221. begin
  222. i := Pos(':', line);
  223. SetFieldByName(Trim(Copy(line, 1, i - 1)), Trim(Copy(line, i + 1, Length(line))));
  224. end;
  225. end;
  226. procedure THttpHeader.WriterCompleted(ASender: TObject);
  227. begin
  228. if Assigned(FOnCompleted) then
  229. FOnCompleted(Self);
  230. end;
  231. function THttpHeader.GetFieldCount: Integer;
  232. begin
  233. Result := FFields.Count;
  234. end;
  235. function THttpHeader.GetFields(AIndex: Integer): String;
  236. begin
  237. Result := FieldNames[AIndex] + ': ' + FieldValues[AIndex];
  238. end;
  239. function THttpHeader.GetFieldNames(AIndex: Integer): String;
  240. begin
  241. Result := PHttpField(FFields.Items[AIndex])^.Name;
  242. end;
  243. procedure THttpHeader.SetFieldNames(AIndex: Integer; const AName: String);
  244. begin
  245. PHttpField(FFields.Items[AIndex])^.Name := AName;
  246. end;
  247. function THttpHeader.GetFieldValues(AIndex: Integer): String;
  248. begin
  249. Result := PHttpField(FFields.Items[AIndex])^.Value;
  250. end;
  251. procedure THttpHeader.SetFieldValues(AIndex: Integer; const AValue: String);
  252. begin
  253. PHttpField(FFields.Items[AIndex])^.Name := AValue;
  254. end;
  255. function THttpHeader.GetAccept: String; begin Result := GetFieldByName(fieldAccept) end;
  256. procedure THttpHeader.SetAccept(const AValue: String); begin SetFieldByName(fieldAccept, AValue) end;
  257. function THttpHeader.GetAcceptCharset: String; begin Result := GetFieldByName(fieldAcceptCharset) end;
  258. procedure THttpHeader.SetAcceptCharset(const AValue: String); begin SetFieldByName(fieldAcceptCharset, AValue) end;
  259. function THttpHeader.GetAcceptEncoding: String; begin Result := GetFieldByName(fieldAcceptEncoding) end;
  260. procedure THttpHeader.SetAcceptEncoding(const AValue: String); begin SetFieldByName(fieldAcceptEncoding, AValue) end;
  261. function THttpHeader.GetAcceptLanguage: String; begin Result := GetFieldByName(fieldAcceptLanguage) end;
  262. procedure THttpHeader.SetAcceptLanguage(const AValue: String); begin SetFieldByName(fieldAcceptLanguage, AValue) end;
  263. function THttpHeader.GetAuthorization: String; begin Result := GetFieldByName(fieldAuthorization) end;
  264. procedure THttpHeader.SetAuthorization(const AValue: String); begin SetFieldByName(fieldAuthorization, AValue) end;
  265. function THttpHeader.GetContentEncoding: String; begin Result := GetFieldByName(fieldContentEncoding) end;
  266. procedure THttpHeader.SetContentEncoding(const AValue: String); begin SetFieldByName(fieldContentEncoding, AValue) end;
  267. function THttpHeader.GetContentLanguage: String; begin Result := GetFieldByName(fieldContentLanguage) end;
  268. procedure THttpHeader.SetContentLanguage(const AValue: String); begin SetFieldByName(fieldContentLanguage, AValue) end;
  269. function THttpHeader.GetContentLength: Integer; var s: String; begin s := GetFieldByName(fieldContentLength); if Length(s) = 0 then Result := -1 else Result := StrToInt(s) end;
  270. procedure THttpHeader.SetContentLength(AValue: Integer); begin SetFieldByName(fieldContentLength, IntToStr(AValue)) end;
  271. function THttpHeader.GetContentType: String; begin Result := GetFieldByName(fieldContentType) end;
  272. procedure THttpHeader.SetContentType(const AValue: String); begin SetFieldByName(fieldContentType, AValue) end;
  273. function THttpHeader.Get_Cookie: String; begin Result := GetFieldByName(fieldCookie) end;
  274. procedure THttpHeader.Set_Cookie(const AValue: String); begin SetFieldByName(fieldCookie, AValue) end;
  275. function THttpHeader.GetDate: String; begin Result := GetFieldByName(fieldDate) end;
  276. procedure THttpHeader.SetDate(const AValue: String); begin SetFieldByName(fieldDate, AValue) end;
  277. function THttpHeader.GetExpires: String; begin Result := GetFieldByName(fieldExpires) end;
  278. procedure THttpHeader.SetExpires(const AValue: String); begin SetFieldByName(fieldExpires, AValue) end;
  279. function THttpHeader.GetFrom: String; begin Result := GetFieldByName(fieldFrom) end;
  280. procedure THttpHeader.SetFrom(const AValue: String); begin SetFieldByName(fieldFrom, AValue) end;
  281. function THttpHeader.GetIfModifiedSince: String; begin Result := GetFieldByName(fieldIfModifiedSince) end;
  282. procedure THttpHeader.SetIfModifiedSince(const AValue: String); begin SetFieldByName(fieldIfModifiedSince, AValue) end;
  283. function THttpHeader.GetLastModified: String; begin Result := GetFieldByName(fieldLastModified) end;
  284. procedure THttpHeader.SetLastModified(const AValue: String); begin SetFieldByName(fieldLastModified, AValue) end;
  285. function THttpHeader.GetLocation: String; begin Result := GetFieldByName(fieldLocation) end;
  286. procedure THttpHeader.SetLocation(const AValue: String); begin SetFieldByName(fieldLocation, AValue) end;
  287. function THttpHeader.GetPragma: String; begin Result := GetFieldByName(fieldPragma) end;
  288. procedure THttpHeader.SetPragma(const AValue: String); begin SetFieldByName(fieldPragma, AValue) end;
  289. function THttpHeader.GetReferer: String; begin Result := GetFieldByName(fieldReferer) end;
  290. procedure THttpHeader.SetReferer(const AValue: String); begin SetFieldByName(fieldReferer, AValue) end;
  291. function THttpHeader.GetRetryAfter: String; begin Result := GetFieldByName(fieldRetryAfter) end;
  292. procedure THttpHeader.SetRetryAfter(const AValue: String); begin SetFieldByName(fieldRetryAfter, AValue) end;
  293. function THttpHeader.GetServer: String; begin Result := GetFieldByName(fieldServer) end;
  294. procedure THttpHeader.SetServer(const AValue: String); begin SetFieldByName(fieldServer, AValue) end;
  295. function THttpHeader.Get_SetCookie: String; begin Result := GetFieldByName(fieldSetCookie) end;
  296. procedure THttpHeader.Set_SetCookie(const AValue: String); begin SetFieldByName(fieldSetCookie, AValue) end;
  297. function THttpHeader.GetUserAgent: String; begin Result := GetFieldByName(fieldUserAgent) end;
  298. procedure THttpHeader.SetUserAgent(const AValue: String); begin SetFieldByName(fieldUserAgent, AValue) end;
  299. function THttpHeader.GetWWWAuthenticate: String; begin Result := GetFieldByName(fieldWWWAuthenticate) end;
  300. procedure THttpHeader.SetWWWAuthenticate(const AValue: String); begin SetFieldByName(fieldWWWAuthenticate, AValue) end;
  301. constructor THttpHeader.Create;
  302. begin
  303. inherited Create;
  304. FFields := TList.Create;
  305. HttpVersion := '1.0';
  306. end;
  307. destructor THttpHeader.Destroy;
  308. var
  309. i: Integer;
  310. field: PHttpField;
  311. begin
  312. FReader.Free;
  313. FWriter.Free;
  314. for i := 0 to FFields.Count - 1 do begin
  315. field := PHttpField(FFields.Items[i]);
  316. SetLength(field^.Name, 0);
  317. SetLength(field^.Value, 0);
  318. Dispose(field);
  319. end;
  320. FFields.Free;
  321. inherited Destroy;
  322. end;
  323. function THttpHeader.GetFieldByName(const AName: String): String;
  324. var
  325. i: Integer;
  326. name: String;
  327. begin
  328. name := UpperCase(AName);
  329. for i := 0 to FFields.Count - 1 do
  330. if UpperCase(FieldNames[i]) = name then begin
  331. Result := FieldValues[i];
  332. exit;
  333. end;
  334. SetLength(Result, 0);
  335. end;
  336. procedure THttpHeader.SetFieldByName(const AName, AValue: String);
  337. var
  338. i: Integer;
  339. name: String;
  340. field: PHttpField;
  341. begin
  342. name := UpperCase(AName);
  343. for i := 0 to FFields.Count - 1 do
  344. if UpperCase(FieldNames[i]) = name then begin
  345. FieldNames[i] := AName; // preserve case
  346. FieldValues[i] := AValue;
  347. exit;
  348. end;
  349. New(field);
  350. FillChar(field^, SizeOf(field^), 0);
  351. field^.Name := AName;
  352. field^.Value := AValue;
  353. FFields.Add(field);
  354. end;
  355. procedure THttpHeader.AsyncSend(AManager: TAsyncIOManager; AStream: THandleStream);
  356. var
  357. i: Integer;
  358. begin
  359. FWriter.Free;
  360. FWriter := TAsyncWriteStream.Create(AManager, AStream);
  361. FWriter.OnBufferEmpty := @WriterCompleted;
  362. FWriter.EndOfLineMarker := #13#10;
  363. FWriter.WriteLine(GetFirstHeaderLine);
  364. for i := 0 to FFields.Count - 1 do
  365. FWriter.WriteLine(Fields[i]);
  366. FWriter.WriteLine('');
  367. end;
  368. procedure THttpHeader.AsyncReceive(AManager: TAsyncIOManager; AStream: THandleStream);
  369. begin
  370. CmdReceived := False;
  371. FReader.Free;
  372. FReader := TAsyncStreamLineReader.Create(AManager, AStream);
  373. FReader.OnLine := @LineReceived;
  374. end;
  375. // -------------------------------------------------------------------
  376. // THttpRequestHeader
  377. // -------------------------------------------------------------------
  378. procedure THttpRequestHeader.ParseFirstHeaderLine(const line: String);
  379. var
  380. i: Integer;
  381. begin
  382. CommandLine := line;
  383. i := Pos(' ', line);
  384. Command := Copy(line, 1, i - 1);
  385. URI := Copy(line, i + 1, Length(line));
  386. i := Pos(' ', URI);
  387. if i > 0 then begin
  388. HttpVersion := Copy(URI, i + 1, Length(URI));
  389. URI := Copy(URI, 1, i - 1);
  390. HttpVersion := Copy(HttpVersion, Pos('/', HttpVersion) + 1, Length(HttpVersion));
  391. end;
  392. end;
  393. function THttpRequestHeader.GetFirstHeaderLine: String;
  394. begin
  395. Result := Command + ' ' + URI;
  396. if Length(HttpVersion) > 0 then
  397. Result := Result + ' HTTP/' + HttpVersion;
  398. end;
  399. // -------------------------------------------------------------------
  400. // THttpAnswerHeader
  401. // -------------------------------------------------------------------
  402. procedure THttpAnswerHeader.ParseFirstHeaderLine(const line: String);
  403. var
  404. i: Integer;
  405. s: String;
  406. begin
  407. i := Pos('/', line);
  408. s := Copy(line, i + 1, Length(line));
  409. i := Pos(' ', s);
  410. HttpVersion := Copy(s, 1, i - 1);
  411. s := Copy(s, i + 1, Length(s));
  412. i := Pos(' ', s);
  413. if i > 0 then begin
  414. CodeText := Copy(s, i + 1, Length(s));
  415. s := Copy(s, 1, i - 1);
  416. end;
  417. Code := StrToInt(s);
  418. end;
  419. function THttpAnswerHeader.GetFirstHeaderLine: String;
  420. begin
  421. Result := Format('HTTP/%s %d %s', [HttpVersion, Code, CodeText]);
  422. end;
  423. constructor THttpAnswerHeader.Create;
  424. begin
  425. inherited Create;
  426. Code := 200;
  427. CodeText := 'OK';
  428. end;
  429. // -------------------------------------------------------------------
  430. // TCustomHttpConnection
  431. // -------------------------------------------------------------------
  432. procedure TCustomHttpConnection.HeaderToSendCompleted(Sender: TObject);
  433. begin
  434. //WriteLn('TCustomHttpConnection.HeaderToSendCompleted');
  435. if Assigned(FOnHeaderSent) then
  436. FOnHeaderSent(Self);
  437. if Assigned(StreamToSend) then begin
  438. SendBuffer := TAsyncWriteStream.Create(FManager, FSocket);
  439. SendBuffer.CopyFrom(StreamToSend, StreamToSend.Size);
  440. SendBuffer.OnBufferEmpty := @StreamToSendCompleted;
  441. SendBuffer.Run;
  442. end else
  443. StreamToSendCompleted(nil);
  444. end;
  445. procedure TCustomHttpConnection.StreamToSendCompleted(Sender: TObject);
  446. begin
  447. if Assigned(FOnStreamSent) then
  448. FOnStreamSent(Self);
  449. //WriteLn('TCustomHttpConnection.StreamToSendCompleted');
  450. SendBuffer.Free;
  451. SendBuffer := nil;
  452. end;
  453. procedure TCustomHttpConnection.ReceivedHeaderCompleted(Sender: TObject);
  454. var
  455. BytesInBuffer: Integer;
  456. begin
  457. //WriteLn('TCustomHttpConnection.ReceivedHeaderCompleted');
  458. BytesInBuffer := ReceivedHeader.Reader.BytesInBuffer;
  459. //WriteLn('BytesInBuffer: ', BytesInBuffer, ', Content length: ', ReceivedHeader.ContentLength);
  460. if Assigned(FOnHeaderReceived) then
  461. FOnHeaderReceived(Self);
  462. RecvSize := ReceivedHeader.ContentLength;
  463. if Assigned(ReceivedStream) then
  464. begin
  465. if BytesInBuffer > 0 then
  466. begin
  467. ReceivedStream.Write(ReceivedHeader.Reader.Buffer^, BytesInBuffer);
  468. if RecvSize > 0 then
  469. Dec(RecvSize, BytesInBuffer);
  470. if BytesInBuffer = ReceivedHeader.ContentLength then
  471. begin
  472. ReceivedStreamCompleted(nil);
  473. exit;
  474. end;
  475. end;
  476. FManager.SetReadHandler(FSocket.Handle, @DataAvailable, nil);
  477. end else
  478. ReceivedStreamCompleted(nil);
  479. end;
  480. procedure TCustomHttpConnection.DataAvailable(Sender: TObject);
  481. var
  482. FirstRun: Boolean;
  483. ReadNow, BytesRead: Integer;
  484. buf: array[0..1023] of Byte;
  485. begin
  486. FirstRun := True;
  487. while True do
  488. begin
  489. if RecvSize >= 0 then
  490. begin
  491. ReadNow := RecvSize;
  492. if ReadNow > 1024 then
  493. ReadNow := 1024;
  494. end else
  495. ReadNow := 1024;
  496. BytesRead := FSocket.Read(buf, ReadNow);
  497. //WriteLn('TCustomHttpConnection.DataAvailable: Read ', BytesRead, ' bytes; RecvSize=', RecvSize);
  498. if BytesRead <= 0 then
  499. begin
  500. if FirstRun then
  501. ReceivedStreamCompleted(nil);
  502. break;
  503. end;
  504. FirstRun := False;
  505. ReceivedStream.Write(buf, BytesRead);
  506. if RecvSize > 0 then
  507. Dec(RecvSize, BytesRead);
  508. if RecvSize = 0 then
  509. begin
  510. ReceivedStreamCompleted(nil);
  511. break;
  512. end;
  513. end;
  514. end;
  515. procedure TCustomHttpConnection.ReceivedStreamCompleted(Sender: TObject);
  516. begin
  517. //WriteLn('TCustomHttpConnection.ReceivedStreamCompleted');
  518. if Assigned(FOnStreamReceived) then
  519. FOnStreamReceived(Self);
  520. FManager.ClearReadHandler(FSocket.Handle);
  521. end;
  522. constructor TCustomHttpConnection.Create(AManager: TAsyncIOManager; ASocket: TInetSocket);
  523. begin
  524. inherited Create;
  525. FManager := AManager;
  526. FSocket := ASocket;
  527. end;
  528. destructor TCustomHttpConnection.Destroy;
  529. begin
  530. FManager.ClearReadHandler(FSocket.Handle);
  531. inherited Destroy;
  532. end;
  533. procedure TCustomHttpConnection.Start;
  534. begin
  535. // Start receiver
  536. if Assigned(ReceivedHeader) then begin
  537. ReceivedHeader.OnCompleted := @ReceivedHeaderCompleted;
  538. ReceivedHeader.AsyncReceive(FManager, FSocket);
  539. end;
  540. // Start sender
  541. if Assigned(HeaderToSend) then begin
  542. // Set the 'Content-Length' field automatically, if possible
  543. if (HeaderToSend.ContentLength = -1) and Assigned(StreamToSend) then
  544. HeaderToSend.ContentLength := StreamToSend.Size;
  545. HeaderToSend.OnCompleted := @HeaderToSendCompleted;
  546. HeaderToSend.AsyncSend(FManager, FSocket)
  547. end;
  548. end;
  549. end.
  550. {
  551. $Log$
  552. Revision 1.3 2002-09-07 15:15:24 peter
  553. * old logs removed and tabs fixed
  554. }