httpbase.pp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. {
  2. $Id$
  3. HTTPBase: Common HTTP utility declarations and classes
  4. Copyright (C) 2000-2003 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. unit HTTPBase;
  12. interface
  13. uses Classes, fpAsync;
  14. const
  15. fieldAccept = 'Accept';
  16. fieldAcceptCharset = 'Accept-Charset';
  17. fieldAcceptEncoding = 'Accept-Encoding';
  18. fieldAcceptLanguage = 'Accept-Language';
  19. fieldAuthorization = 'Authorization';
  20. fieldConnection = 'Connection';
  21. fieldContentEncoding = 'Content-Encoding';
  22. fieldContentLanguage = 'Content-Language';
  23. fieldContentLength = 'Content-Length';
  24. fieldContentType = 'Content-Type';
  25. fieldCookie = 'Cookie';
  26. fieldDate = 'Date';
  27. fieldExpires = 'Expires';
  28. fieldFrom = 'From';
  29. fieldIfModifiedSince = 'If-Modified-Since';
  30. fieldLastModified = 'Last-Modified';
  31. fieldLocation = 'Location';
  32. fieldPragma = 'Pragma';
  33. fieldReferer = 'Referer';
  34. fieldRetryAfter = 'Retry-After';
  35. fieldServer = 'Server';
  36. fieldSetCookie = 'Set-Cookie';
  37. fieldUserAgent = 'User-Agent';
  38. fieldWWWAuthenticate = 'WWW-Authenticate';
  39. type
  40. PHttpField = ^THttpField;
  41. THttpField = record
  42. Name, Value: String;
  43. end;
  44. THttpHeader = class
  45. protected
  46. FReader: TAsyncStreamLineReader;
  47. FWriter: TAsyncWriteStream;
  48. FOnCompleted: TNotifyEvent;
  49. FOnEOF: TNotifyEvent;
  50. FFields: TList;
  51. procedure ParseFirstHeaderLine(const line: String); virtual; abstract;
  52. procedure LineReceived(const ALine: String);
  53. procedure ReaderEOF(Sender: TObject);
  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 GetConnection: String;
  73. procedure SetConnection(const AValue: String);
  74. function GetContentEncoding: String;
  75. procedure SetContentEncoding(const AValue: String);
  76. function GetContentLanguage: String;
  77. procedure SetContentLanguage(const AValue: String);
  78. function GetContentLength: Integer;
  79. procedure SetContentLength(AValue: Integer);
  80. function GetContentType: String;
  81. procedure SetContentType(const AValue: String);
  82. function Get_Cookie: String;
  83. procedure Set_Cookie(const AValue: String);
  84. function GetDate: String;
  85. procedure SetDate(const AValue: String);
  86. function GetExpires: String;
  87. procedure SetExpires(const AValue: String);
  88. function GetFrom: String;
  89. procedure SetFrom(const AValue: String);
  90. function GetIfModifiedSince: String;
  91. procedure SetIfModifiedSince(const AValue: String);
  92. function GetLastModified: String;
  93. procedure SetLastModified(const AValue: String);
  94. function GetLocation: String;
  95. procedure SetLocation(const AValue: String);
  96. function GetPragma: String;
  97. procedure SetPragma(const AValue: String);
  98. function GetReferer: String;
  99. procedure SetReferer(const AValue: String);
  100. function GetRetryAfter: String;
  101. procedure SetRetryAfter(const AValue: String);
  102. function GetServer: String;
  103. procedure SetServer(const AValue: String);
  104. function Get_SetCookie: String;
  105. procedure Set_SetCookie(const AValue: String);
  106. function GetUserAgent: String;
  107. procedure SetUserAgent(const AValue: String);
  108. function GetWWWAuthenticate: String;
  109. procedure SetWWWAuthenticate(const AValue: String);
  110. public
  111. DataReceived, CmdReceived: Boolean; // !!!: Only temporarily here
  112. HttpVersion: String;
  113. constructor Create;
  114. destructor Destroy; override;
  115. procedure SetFieldByName(const AName, AValue: String);
  116. function GetFieldByName(const AName: String): String;
  117. procedure AsyncSend(AManager: TEventLoop; AStream: THandleStream);
  118. procedure AsyncReceive(AManager: TEventLoop; AStream: THandleStream);
  119. property Reader: TAsyncStreamLineReader read FReader;
  120. property Writer: TAsyncWriteStream read FWriter;
  121. property FieldCount: Integer read GetFieldCount;
  122. property Fields[AIndex: Integer]: String read GetFields;
  123. property FieldNames[AIndex: Integer]: String read GetFieldNames write SetFieldNames;
  124. property FieldValues[AIndex: Integer]: String read GetFieldValues write SetFieldValues;
  125. property OnCompleted: TNotifyEvent read FOnCompleted write FOnCompleted;
  126. property OnEOF: TNotifyEvent read FOnEOF write FOnEOF;
  127. property Accept: String read GetAccept write SetAccept;
  128. property AcceptCharset: String read GetAcceptCharset write SetAcceptCharset;
  129. property AcceptEncoding: String read GetAcceptEncoding write SetAcceptEncoding;
  130. property AcceptLanguage: String read GetAcceptLanguage write SetAcceptLanguage;
  131. property Authorization: String read GetAuthorization write SetAuthorization;
  132. property Connection: String read GetConnection write SetConnection;
  133. property ContentEncoding: String read GetContentEncoding write SetContentEncoding;
  134. property ContentLanguage: String read GetContentLanguage write SetContentLanguage;
  135. property ContentLength: Integer read GetContentLength write SetContentLength;
  136. property ContentType: String read GetContentType write SetContentType;
  137. property Cookie: String read Get_Cookie write Set_Cookie;
  138. property Date: String read GetDate write SetDate;
  139. property Expires: String read GetExpires write SetExpires;
  140. property From: String read GetFrom write SetFrom;
  141. property IfModifiedSince: String read GetIfModifiedSince write SetIfModifiedSince;
  142. property LastModified: String read GetLastModified write SetLastModified;
  143. property Location: String read GetLocation write SetLocation;
  144. property Pragma: String read GetPragma write SetPragma;
  145. property Referer: String read GetReferer write SetReferer;
  146. property RetryAfter: String read GetRetryAfter write SetRetryAfter;
  147. property Server: String read GetServer write SetServer;
  148. property SetCookie: String read Get_SetCookie write Set_SetCookie;
  149. property UserAgent: String read GetUserAgent write SetUserAgent;
  150. property WWWAuthenticate: String read GetWWWAuthenticate write SetWWWAuthenticate;
  151. end;
  152. THttpRequestHeader = class(THttpHeader)
  153. protected
  154. procedure ParseFirstHeaderLine(const line: String); override;
  155. function GetFirstHeaderLine: String; override;
  156. public
  157. CommandLine: String;
  158. Command: String;
  159. URI: String; // Uniform Resource Identifier
  160. QueryString: String;
  161. end;
  162. THttpResponseHeader = class(THttpHeader)
  163. protected
  164. procedure ParseFirstHeaderLine(const line: String); override;
  165. function GetFirstHeaderLine: String; override;
  166. public
  167. Code: Integer;
  168. CodeText: String;
  169. constructor Create;
  170. end;
  171. implementation
  172. uses SysUtils;
  173. // THttpHeader
  174. procedure THttpHeader.LineReceived(const ALine: String);
  175. var
  176. i: Integer;
  177. begin
  178. if Length(ALine) = 0 then
  179. begin
  180. FReader.OnLine := nil; // Stop receiving
  181. FReader.StopAndFree;
  182. if Assigned(FOnCompleted) then
  183. FOnCompleted(Self);
  184. FReader := nil;
  185. end else
  186. DataReceived := True;
  187. if not CmdReceived then
  188. begin
  189. CmdReceived := True;
  190. ParseFirstHeaderLine(ALine);
  191. end else
  192. begin
  193. i := Pos(':', ALine);
  194. SetFieldByName(Trim(Copy(ALine, 1, i - 1)),
  195. Trim(Copy(ALine, i + 1, Length(ALine))));
  196. end;
  197. end;
  198. procedure THttpHeader.ReaderEOF(Sender: TObject);
  199. begin
  200. if Assigned(OnEOF) then
  201. OnEOF(Self);
  202. end;
  203. procedure THttpHeader.WriterCompleted(ASender: TObject);
  204. begin
  205. if Assigned(FOnCompleted) then
  206. FOnCompleted(Self);
  207. FreeAndNil(FWriter);
  208. end;
  209. function THttpHeader.GetFieldCount: Integer;
  210. begin
  211. Result := FFields.Count;
  212. end;
  213. function THttpHeader.GetFields(AIndex: Integer): String;
  214. begin
  215. Result := FieldNames[AIndex] + ': ' + FieldValues[AIndex];
  216. end;
  217. function THttpHeader.GetFieldNames(AIndex: Integer): String;
  218. begin
  219. Result := PHttpField(FFields.Items[AIndex])^.Name;
  220. end;
  221. procedure THttpHeader.SetFieldNames(AIndex: Integer; const AName: String);
  222. begin
  223. PHttpField(FFields.Items[AIndex])^.Name := AName;
  224. end;
  225. function THttpHeader.GetFieldValues(AIndex: Integer): String;
  226. begin
  227. Result := PHttpField(FFields.Items[AIndex])^.Value;
  228. end;
  229. procedure THttpHeader.SetFieldValues(AIndex: Integer; const AValue: String);
  230. begin
  231. PHttpField(FFields.Items[AIndex])^.Value := AValue;
  232. end;
  233. function THttpHeader.GetAccept: String; begin Result := GetFieldByName(fieldAccept) end;
  234. procedure THttpHeader.SetAccept(const AValue: String); begin SetFieldByName(fieldAccept, AValue) end;
  235. function THttpHeader.GetAcceptCharset: String; begin Result := GetFieldByName(fieldAcceptCharset) end;
  236. procedure THttpHeader.SetAcceptCharset(const AValue: String); begin SetFieldByName(fieldAcceptCharset, AValue) end;
  237. function THttpHeader.GetAcceptEncoding: String; begin Result := GetFieldByName(fieldAcceptEncoding) end;
  238. procedure THttpHeader.SetAcceptEncoding(const AValue: String); begin SetFieldByName(fieldAcceptEncoding, AValue) end;
  239. function THttpHeader.GetAcceptLanguage: String; begin Result := GetFieldByName(fieldAcceptLanguage) end;
  240. procedure THttpHeader.SetAcceptLanguage(const AValue: String); begin SetFieldByName(fieldAcceptLanguage, AValue) end;
  241. function THttpHeader.GetAuthorization: String; begin Result := GetFieldByName(fieldAuthorization) end;
  242. procedure THttpHeader.SetAuthorization(const AValue: String); begin SetFieldByName(fieldAuthorization, AValue) end;
  243. function THttpHeader.GetConnection: String; begin Result := GetFieldByName(fieldConnection) end;
  244. procedure THttpHeader.SetConnection(const AValue: String); begin SetFieldByName(fieldConnection, AValue) end;
  245. function THttpHeader.GetContentEncoding: String; begin Result := GetFieldByName(fieldContentEncoding) end;
  246. procedure THttpHeader.SetContentEncoding(const AValue: String); begin SetFieldByName(fieldContentEncoding, AValue) end;
  247. function THttpHeader.GetContentLanguage: String; begin Result := GetFieldByName(fieldContentLanguage) end;
  248. procedure THttpHeader.SetContentLanguage(const AValue: String); begin SetFieldByName(fieldContentLanguage, AValue) end;
  249. function THttpHeader.GetContentLength: Integer; var s: String; begin s := GetFieldByName(fieldContentLength); if Length(s) = 0 then Result := -1 else Result := StrToInt(s) end;
  250. procedure THttpHeader.SetContentLength(AValue: Integer); begin SetFieldByName(fieldContentLength, IntToStr(AValue)) end;
  251. function THttpHeader.GetContentType: String; begin Result := GetFieldByName(fieldContentType) end;
  252. procedure THttpHeader.SetContentType(const AValue: String); begin SetFieldByName(fieldContentType, AValue) end;
  253. function THttpHeader.Get_Cookie: String; begin Result := GetFieldByName(fieldCookie) end;
  254. procedure THttpHeader.Set_Cookie(const AValue: String); begin SetFieldByName(fieldCookie, AValue) end;
  255. function THttpHeader.GetDate: String; begin Result := GetFieldByName(fieldDate) end;
  256. procedure THttpHeader.SetDate(const AValue: String); begin SetFieldByName(fieldDate, AValue) end;
  257. function THttpHeader.GetExpires: String; begin Result := GetFieldByName(fieldExpires) end;
  258. procedure THttpHeader.SetExpires(const AValue: String); begin SetFieldByName(fieldExpires, AValue) end;
  259. function THttpHeader.GetFrom: String; begin Result := GetFieldByName(fieldFrom) end;
  260. procedure THttpHeader.SetFrom(const AValue: String); begin SetFieldByName(fieldFrom, AValue) end;
  261. function THttpHeader.GetIfModifiedSince: String; begin Result := GetFieldByName(fieldIfModifiedSince) end;
  262. procedure THttpHeader.SetIfModifiedSince(const AValue: String); begin SetFieldByName(fieldIfModifiedSince, AValue) end;
  263. function THttpHeader.GetLastModified: String; begin Result := GetFieldByName(fieldLastModified) end;
  264. procedure THttpHeader.SetLastModified(const AValue: String); begin SetFieldByName(fieldLastModified, AValue) end;
  265. function THttpHeader.GetLocation: String; begin Result := GetFieldByName(fieldLocation) end;
  266. procedure THttpHeader.SetLocation(const AValue: String); begin SetFieldByName(fieldLocation, AValue) end;
  267. function THttpHeader.GetPragma: String; begin Result := GetFieldByName(fieldPragma) end;
  268. procedure THttpHeader.SetPragma(const AValue: String); begin SetFieldByName(fieldPragma, AValue) end;
  269. function THttpHeader.GetReferer: String; begin Result := GetFieldByName(fieldReferer) end;
  270. procedure THttpHeader.SetReferer(const AValue: String); begin SetFieldByName(fieldReferer, AValue) end;
  271. function THttpHeader.GetRetryAfter: String; begin Result := GetFieldByName(fieldRetryAfter) end;
  272. procedure THttpHeader.SetRetryAfter(const AValue: String); begin SetFieldByName(fieldRetryAfter, AValue) end;
  273. function THttpHeader.GetServer: String; begin Result := GetFieldByName(fieldServer) end;
  274. procedure THttpHeader.SetServer(const AValue: String); begin SetFieldByName(fieldServer, AValue) end;
  275. function THttpHeader.Get_SetCookie: String; begin Result := GetFieldByName(fieldSetCookie) end;
  276. procedure THttpHeader.Set_SetCookie(const AValue: String); begin SetFieldByName(fieldSetCookie, AValue) end;
  277. function THttpHeader.GetUserAgent: String; begin Result := GetFieldByName(fieldUserAgent) end;
  278. procedure THttpHeader.SetUserAgent(const AValue: String); begin SetFieldByName(fieldUserAgent, AValue) end;
  279. function THttpHeader.GetWWWAuthenticate: String; begin Result := GetFieldByName(fieldWWWAuthenticate) end;
  280. procedure THttpHeader.SetWWWAuthenticate(const AValue: String); begin SetFieldByName(fieldWWWAuthenticate, AValue) end;
  281. constructor THttpHeader.Create;
  282. begin
  283. inherited Create;
  284. FFields := TList.Create;
  285. HttpVersion := '1.1';
  286. end;
  287. destructor THttpHeader.Destroy;
  288. var
  289. i: Integer;
  290. Field: PHttpField;
  291. begin
  292. if Assigned(FReader) then
  293. FReader.StopAndFree;
  294. if Assigned(FWriter) then
  295. FWriter.StopAndFree;
  296. for i := 0 to FFields.Count - 1 do
  297. begin
  298. Field := PHttpField(FFields.Items[i]);
  299. { SetLength(Field^.Name, 0);
  300. SetLength(Field^.Value, 0);}
  301. Dispose(Field);
  302. end;
  303. FFields.Free;
  304. inherited Destroy;
  305. end;
  306. function THttpHeader.GetFieldByName(const AName: String): String;
  307. var
  308. i: Integer;
  309. Name: String;
  310. begin
  311. Name := UpperCase(AName);
  312. for i := 0 to FFields.Count - 1 do
  313. if UpperCase(FieldNames[i]) = Name then
  314. begin
  315. Result := FieldValues[i];
  316. exit;
  317. end;
  318. SetLength(Result, 0);
  319. end;
  320. procedure THttpHeader.SetFieldByName(const AName, AValue: String);
  321. var
  322. i: Integer;
  323. Name: String;
  324. Field: PHttpField;
  325. begin
  326. Name := UpperCase(AName);
  327. for i := 0 to FFields.Count - 1 do
  328. if UpperCase(FieldNames[i]) = Name then
  329. begin
  330. FieldNames[i] := AName; // preserve case
  331. FieldValues[i] := AValue;
  332. exit;
  333. end;
  334. New(Field);
  335. FillChar(Field^, SizeOf(Field^), 0);
  336. Field^.Name := AName;
  337. Field^.Value := AValue;
  338. FFields.Add(field);
  339. end;
  340. procedure THttpHeader.AsyncSend(AManager: TEventLoop; AStream: THandleStream);
  341. var
  342. i: Integer;
  343. begin
  344. if Assigned(FWriter) then
  345. FWriter.StopAndFree;
  346. FWriter := TAsyncWriteStream.Create(AManager, AStream);
  347. FWriter.OnBufferSent := @WriterCompleted;
  348. FWriter.EndOfLineMarker := #13#10;
  349. FWriter.WriteLine(GetFirstHeaderLine);
  350. for i := 0 to FFields.Count - 1 do
  351. FWriter.WriteLine(Fields[i]);
  352. FWriter.WriteLine('');
  353. end;
  354. procedure THttpHeader.AsyncReceive(AManager: TEventLoop; AStream: THandleStream);
  355. begin
  356. CmdReceived := False;
  357. FReader.Free;
  358. FReader := TAsyncStreamLineReader.Create(AManager, AStream);
  359. FReader.OnLine := @LineReceived;
  360. FReader.OnEOF := @ReaderEOF;
  361. end;
  362. // -------------------------------------------------------------------
  363. // THttpRequestHeader
  364. // -------------------------------------------------------------------
  365. procedure THttpRequestHeader.ParseFirstHeaderLine(const line: String);
  366. var
  367. i: Integer;
  368. begin
  369. CommandLine := line;
  370. i := Pos(' ', line);
  371. Command := UpperCase(Copy(line, 1, i - 1));
  372. URI := Copy(line, i + 1, Length(line));
  373. // Extract HTTP version
  374. i := Pos(' ', URI);
  375. if i > 0 then
  376. begin
  377. HttpVersion := Copy(URI, i + 1, Length(URI));
  378. URI := Copy(URI, 1, i - 1);
  379. HttpVersion := Copy(HttpVersion, Pos('/', HttpVersion) + 1, Length(HttpVersion));
  380. end;
  381. // Extract query string
  382. i := Pos('?', URI);
  383. if i > 0 then
  384. begin
  385. QueryString := Copy(URI, i + 1, Length(URI));
  386. URI := Copy(URI, 1, i - 1);
  387. end;
  388. end;
  389. function THttpRequestHeader.GetFirstHeaderLine: String;
  390. begin
  391. Result := Command + ' ' + URI;
  392. if Length(HttpVersion) > 0 then
  393. Result := Result + ' HTTP/' + HttpVersion;
  394. end;
  395. // -------------------------------------------------------------------
  396. // THttpResponseHeader
  397. // -------------------------------------------------------------------
  398. procedure THttpResponseHeader.ParseFirstHeaderLine(const line: String);
  399. var
  400. i: Integer;
  401. s: String;
  402. begin
  403. i := Pos('/', line);
  404. s := Copy(line, i + 1, Length(line));
  405. i := Pos(' ', s);
  406. HttpVersion := Copy(s, 1, i - 1);
  407. s := Copy(s, i + 1, Length(s));
  408. i := Pos(' ', s);
  409. if i > 0 then begin
  410. CodeText := Copy(s, i + 1, Length(s));
  411. s := Copy(s, 1, i - 1);
  412. end;
  413. Code := StrToInt(s);
  414. end;
  415. function THttpResponseHeader.GetFirstHeaderLine: String;
  416. begin
  417. Result := Format('HTTP/%s %d %s', [HttpVersion, Code, CodeText]);
  418. end;
  419. constructor THttpResponseHeader.Create;
  420. begin
  421. inherited Create;
  422. Code := 200;
  423. CodeText := 'OK';
  424. end;
  425. end.
  426. {
  427. $Log$
  428. Revision 1.2 2004-02-02 17:12:01 sg
  429. * Some small fixes to get the code at least compiling again; the HTTP
  430. client class is not expected to work at the moment, and the XML-RPC
  431. client has been fully disabled for now.
  432. Revision 1.1 2004/01/31 19:13:14 sg
  433. * Splittet old HTTP unit into httpbase and httpclient
  434. * Many improvements in fpSock (e.g. better disconnection detection)
  435. }