PageRenderTime 52ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/ProSnooperFx_src/indy10.0.52_source/Protocols/IdHTTP.pas

http://github.com/lookias/ProSnooper
Pascal | 1967 lines | 1318 code | 178 blank | 471 comment | 155 complexity | e45989a8a835d0494321a8a3994fa814 MD5 | raw file
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 11615: IdHTTP.pas
  11. {
  12. { Rev 1.54 10/26/2004 10:13:24 PM JPMugaas
  13. { Updated refs.
  14. }
  15. {
  16. { Rev 1.53 7/16/04 1:19:20 AM RLebeau
  17. { Fix for compiler error
  18. }
  19. {
  20. { Rev 1.52 7/15/04 8:19:30 PM RLebeau
  21. { Updated TIdHTTPProtocol.ProcessResponse() to treat 302 redirects like 303.
  22. {
  23. { Updated TIdHTTPProtocol.BuildAndSendRequest() to use a try...except block
  24. }
  25. {
  26. Rev 1.51 6/17/2004 8:30:04 AM DSiders
  27. TIdCustomHTTP modified:
  28. - Fixed error in AuthRetries property reading wrong member var.
  29. - Added AuthProxyRetries and MaxAuthRetries properties to public interface.
  30. TIdHTTP modified to publish AuthRetries, AuthProxyRetries, and MaxAuthRetries.
  31. TIdHTTPProtocol.ProcessResponse modified to use public properties
  32. AuthRetries, AuthProxyRetries, and MaxAutrhRetries.
  33. }
  34. {
  35. { Rev 1.50 2004.05.20 11:36:46 AM czhower
  36. { IdStreamVCL
  37. }
  38. {
  39. { Rev 1.49 4/28/04 1:45:26 PM RLebeau
  40. { Updated TIdCustomHTTP.SetRequestParams() to strip off the trailing CRLF
  41. { before encoding rather than afterwards
  42. }
  43. {
  44. { Rev 1.48 2004.04.07 11:18:08 PM czhower
  45. { Bug and naming fix.
  46. }
  47. {
  48. { Rev 1.47 7/4/2004 6:00:02 PM SGrobety
  49. { Reformatted to match project guidelines
  50. }
  51. {
  52. { Rev 1.46 7/4/2004 4:58:24 PM SGrobety
  53. { Reformatted to match project guidelines
  54. }
  55. {
  56. { Rev 1.45 6/4/2004 5:16:40 PM SGrobety
  57. { Added AMaxHeaderCount: integer parameter to TIdHTTPProtocol.RetrieveHeaders
  58. { and MaxHeaderLines property to TIdCustomHTTP (default to 255)
  59. }
  60. {
  61. { Rev 1.44 2004.03.06 10:39:52 PM czhower
  62. { Removed duplicate code
  63. }
  64. {
  65. { Rev 1.43 2004.03.06 8:56:30 PM czhower
  66. { -Change to disconnect
  67. { -Addition of DisconnectNotifyPeer
  68. { -WriteHeader now write bufers
  69. }
  70. {
  71. { Rev 1.42 3/3/2004 5:58:00 AM JPMugaas
  72. { Some IFDEF excluses were removed because the functionality is now in DotNET.
  73. }
  74. {
  75. { Rev 1.41 2004.02.23 9:33:12 PM czhower
  76. { Now can optionally ignore response codes for exceptions.
  77. }
  78. {
  79. { Rev 1.40 2/15/2004 6:34:02 AM JPMugaas
  80. { Fix for where I broke the HTTP client with a parameter change in the GZip
  81. { decompress method.
  82. }
  83. {
  84. { Rev 1.39 2004.02.03 5:43:44 PM czhower
  85. { Name changes
  86. }
  87. {
  88. { Rev 1.38 2004.02.03 2:12:10 PM czhower
  89. { $I path change
  90. }
  91. {
  92. { Rev 1.37 2004.01.27 11:41:18 PM czhower
  93. { Removed const arguments
  94. }
  95. {
  96. { Rev 1.35 24/01/2004 19:22:34 CCostelloe
  97. { Cleaned up warnings
  98. }
  99. {
  100. { Rev 1.34 2004.01.22 5:29:02 PM czhower
  101. { TextIsSame
  102. }
  103. {
  104. { Rev 1.33 2004.01.21 1:04:50 PM czhower
  105. { InitComponenet
  106. }
  107. {
  108. { Rev 1.32 1/2/2004 11:41:48 AM BGooijen
  109. { Enabled IPv6 support
  110. }
  111. {
  112. { Rev 1.31 22/11/2003 12:04:28 AM GGrieve
  113. { Add support for HTTP status code 303
  114. }
  115. {
  116. { Rev 1.30 10/25/2003 06:51:58 AM JPMugaas
  117. { Updated for new API changes and tried to restore some functionality.
  118. }
  119. {
  120. { Rev 1.29 2003.10.24 10:43:08 AM czhower
  121. { TIdSTream to dos
  122. }
  123. {
  124. { Rev 1.28 24/10/2003 10:58:40 AM SGrobety
  125. { Made authentication work even if no OnAnthenticate envent handler present
  126. }
  127. {
  128. { Rev 1.27 10/18/2003 1:53:10 PM BGooijen
  129. { Added include
  130. }
  131. {
  132. Rev 1.26 10/17/2003 12:08:48 AM DSiders
  133. Added localization comments.
  134. }
  135. {
  136. { Rev 1.25 2003.10.14 1:27:52 PM czhower
  137. { DotNet
  138. }
  139. {
  140. { Rev 1.24 10/7/2003 11:33:54 PM GGrieve
  141. { Get works under DotNet
  142. }
  143. {
  144. { Rev 1.23 10/7/2003 10:07:04 PM GGrieve
  145. { Get HTTP compiling for DotNet
  146. }
  147. {
  148. { Rev 1.22 10/4/2003 9:15:58 PM GGrieve
  149. { fix to compile
  150. }
  151. {
  152. { Rev 1.21 9/26/2003 01:41:48 PM JPMugaas
  153. { Fix for problem wihere "identity" was being added more than once to the
  154. { accepted encoding contents.
  155. }
  156. {
  157. { Rev 1.20 9/14/2003 07:54:20 PM JPMugaas
  158. { Published the Compressor property.
  159. }
  160. {
  161. { Rev 1.19 7/30/2003 05:34:22 AM JPMugaas
  162. { Fix for bug where decompression was not done if the Content Length was
  163. { specified. I found that at http://www.news.com.
  164. { Added Identity to the content encoding to be consistant with Opera. Identity
  165. { is the default Accept-Encoding (RFC 2616).
  166. }
  167. {
  168. Rev 1.18 7/13/2003 10:57:28 PM BGooijen
  169. Fixed GZip and Deflate decoding
  170. }
  171. {
  172. { Rev 1.17 7/13/2003 11:29:06 AM JPMugaas
  173. { Made sure some GZIP decompression stub code is in IdHTTP.
  174. }
  175. {
  176. { Rev 1.15 10.7.2003 ã. 21:03:02 DBondzhev
  177. { Fixed NTML proxy authorization
  178. }
  179. {
  180. { Rev 1.14 6/19/2003 02:36:56 PM JPMugaas
  181. { Removed a connected check and it seems to work better that way.
  182. }
  183. {
  184. { Rev 1.13 6/5/2003 04:53:54 AM JPMugaas
  185. { Reworkings and minor changes for new Reply exception framework.
  186. }
  187. {
  188. { Rev 1.12 4/30/2003 01:47:24 PM JPMugaas
  189. { Added TODO concerning a ConnectTimeout.
  190. }
  191. {
  192. Rev 1.11 4/2/2003 3:18:30 PM BGooijen
  193. fixed av when retrieving an url when no iohandler was assigned
  194. }
  195. {
  196. Rev 1.10 3/26/2003 5:13:40 PM BGooijen
  197. TIdSSLIOHandlerSocketBase.URIToCheck is now set
  198. }
  199. {
  200. { Rev 1.9 3/13/2003 11:05:26 AM JPMugaas
  201. { Now should work with 3rd party vendor SSL IOHandlers.
  202. }
  203. {
  204. Rev 1.8 3/11/2003 10:14:52 PM BGooijen
  205. Undid the stripping of the CR
  206. }
  207. {
  208. Rev 1.7 2/27/2003 2:04:26 PM BGooijen
  209. If any call to iohandler.readln returns a CR at the end, it is removed now.
  210. }
  211. {
  212. Rev 1.6 2/26/2003 11:50:08 AM BGooijen
  213. things were messed up in TIdHTTPProtocol.RetrieveHeaders, because the call to
  214. readln doesn't strip the CR at the end (terminator=LF), therefore the end of
  215. the header was not found.
  216. }
  217. {
  218. Rev 1.5 2/26/2003 11:42:46 AM BGooijen
  219. changed ReadLn (IOerror 6) to IOHandler.ReadLn
  220. }
  221. {
  222. Rev 1.4 2/4/2003 6:30:44 PM BGooijen
  223. Re-enabled SSL-support
  224. }
  225. {
  226. { Rev 1.3 1/17/2003 04:14:42 PM JPMugaas
  227. { Fixed warnings.
  228. }
  229. {
  230. { Rev 1.2 12/7/2002 05:32:16 PM JPMugaas
  231. { Now compiles with destination removed.
  232. }
  233. {
  234. { Rev 1.1 12/6/2002 05:29:52 PM JPMugaas
  235. { Now decend from TIdTCPClientCustom instead of TIdTCPClient.
  236. }
  237. {
  238. { Rev 1.0 11/13/2002 07:54:12 AM JPMugaas
  239. }
  240. unit IdHTTP;
  241. {TODO: Figure out what to do with ConnectTimeout. Ideally, that should be in the core
  242. and is not the same as a read Timeout.}
  243. {
  244. Implementation of the HTTP protcol as specified in RFC 2616, 2109, 2965.
  245. (See NOTE below for details of what is exactly implemented)
  246. Author: Hadi Hariri (hadi@urusoft.com)
  247. Copyright: (c) Chad Z. Hower and The Winshoes Working Group.
  248. NOTE:
  249. Initially only GET and POST will be supported. As time goes on more will
  250. be added. For other developers, please add the date and what you have done
  251. below.
  252. Initials: Hadi Hariri - HH
  253. Details of implementation
  254. -------------------------
  255. 2001-Nov Nick Panteleeff
  256. - Authentication and POST parameter extentsions
  257. 2001-Sept Doychin Bondzhev
  258. - New internal design and new Authentication procedures.
  259. - Bug fixes and new features in few other supporting components
  260. 2001-Jul-7 Doychin Bondzhev
  261. - new property AllowCookie
  262. - There is no more ExtraHeders property in Request/Response. Raw headers is used for that purpose.
  263. 2001-Jul-1 Doychin Bondzhev
  264. - SSL support is up again - Thanks to Gregor
  265. 2001-Jun-17 Doychin Bondzhev
  266. - New unit IdHTTPHeaderInfo.pas that contains the
  267. TIdHeaderInfo(TIdEntytiHeaderInfo, TIdRequestHeaderInfo and TIdResponseHeaderInfo)
  268. - Still in development and not verry well tested
  269. By default when there is no authorization object associated with HTTP compoenet and there is user name and password
  270. HTTP component creates and instance of TIdBasicAuthentication class. This behaivor is for both web server and proxy server
  271. authorizations
  272. 2001-Apr-17 Doychin Bondzhev
  273. - Added OnProxyAuthorization event. This event is called on 407 response from the HTTP Proxy.
  274. - Added 2 new properties in TIdHeaderInfo
  275. property AuthenticationScheme: TIdAuthenticationScheme - this property contains information for authentication scheme
  276. requested by the web server
  277. property ProxyAuthenticationScheme: TIdAuthenticationScheme - this property contains information for authentication scheme
  278. requested by the proxy server
  279. - Now the component authomaticly reconginizes the requested authorization scheme and it supports Basic like before and has been
  280. extend to support Digest authorization
  281. 2001-Mar-31 Doychin Bondzhev
  282. - If there is no CookieManager it does not support cookies.
  283. 2001-Feb-18 Doychin Bondzhev
  284. - Added OnAuthorization event. This event is called on 401 response from the HTTP server.
  285. This can be used to ask the user program to supply user name and password in order to acces
  286. the requested resource
  287. 2001-Feb-02 Doychin Bondzhev
  288. - Added Cookie support and relative paths on redirect
  289. 2000-Jul-25 Hadi Hariri
  290. - Overloaded POst and moved clearing to disconect.
  291. 2000-June-22 Hadi Hariri
  292. - Added Proxy support.
  293. 2000-June-10 Hadi Hariri
  294. - Added Chunk-Encoding support and HTTP version number. Some additional
  295. improvements.
  296. 2000-May-23 J. Peter Mugaas
  297. -added redirect capability and supporting properties. Redirect is optional
  298. and is set with HandleRedirects. Redirection is limited to RedirectMaximum
  299. to prevent stack overflow due to recursion and to prevent redirects between
  300. two places which would cause this to go on to infinity.
  301. 2000-May-22 J. Peter Mugaas
  302. -adjusted code for servers which returned LF instead of EOL
  303. -Headers are now retreived before an exception is raised. This
  304. also facilitates server redirection where the server tells the client to
  305. get a document from another location.
  306. 2000-May-01 Hadi Hariri
  307. -Converted to Mercury
  308. 2000-May-01 Hadi Hariri
  309. -Added PostFromStream and some clean up
  310. 2000-Apr-10 Hadi Hariri
  311. -Re-done quite a few things and fixed GET bugs and finished POST method.
  312. 2000-Jan-13 MTL
  313. -Moved to the New Palette Scheme
  314. 2000-Jan-08 MTL
  315. -Cleaned up a few compiler hints during 7.038 build
  316. 1999-Dec-10 Hadi Hariri
  317. -Started.
  318. }
  319. interface
  320. {$I IdCompilerDefines.inc}
  321. uses
  322. Classes,
  323. IdException, IdExceptionCore, IdAssignedNumbers, IdHeaderList, IdHTTPHeaderInfo, IdReplyRFC,
  324. IdSSL, IdZLibCompressorBase,
  325. IdTCPClient, IdURI, IdCookie, IdCookieManager, IdAuthentication , IdAuthenticationManager,
  326. IdMultipartFormData, IdGlobal, IdTStrings;
  327. type
  328. // TO DOCUMENTATION TEAM
  329. // ------------------------
  330. // For internal use. No need of documentation
  331. // hmConnect - Used to connect trought CERN proxy to SSL enabled sites.
  332. TIdHTTPMethod = (hmHead, hmGet, hmPost, hmOptions, hmTrace, hmPut, hmDelete, hmConnect);
  333. TIdHTTPWhatsNext = (wnGoToURL, wnJustExit, wnDontKnow, wnReadAndGo, wnAuthRequest);
  334. TIdHTTPConnectionType = (ctNormal, ctSSL, ctProxy, ctSSLProxy);
  335. // Protocol options
  336. TIdHTTPOption = (hoInProcessAuth, hoKeepOrigProtocol, hoForceEncodeParams);
  337. TIdHTTPOptions = set of TIdHTTPOption;
  338. // Must be documented
  339. TIdHTTPProtocolVersion = (pv1_0, pv1_1);
  340. TIdHTTPOnRedirectEvent = procedure(Sender: TObject; var dest: string; var NumRedirect: Integer; var Handled: boolean; var VMethod: TIdHTTPMethod) of object;
  341. TIdOnSelectAuthorization = procedure(Sender: TObject; var AuthenticationClass: TIdAuthenticationClass; AuthInfo: TIdHeaderList) of object;
  342. TIdOnAuthorization = procedure(Sender: TObject; Authentication: TIdAuthentication; var Handled: boolean) of object;
  343. // TIdProxyOnAuthorization = procedure(Sender: TObject; Authentication: TIdAuthentication; var Handled: boolean) of object;
  344. const
  345. Id_TIdHTTP_ProtocolVersion = pv1_1;
  346. Id_TIdHTTP_RedirectMax = 15;
  347. Id_TIdHTTP_MaxHeaderLines = 255;
  348. Id_TIdHTTP_HandleRedirects = False;
  349. Id_TIdHTTP_MaxAuthRetries = 3;
  350. type
  351. TIdCustomHTTP = class;
  352. // TO DOCUMENTATION TEAM
  353. // ------------------------
  354. // The following classes are used internally and no need of documentation
  355. // Only TIdHTTP must be documented
  356. //
  357. TIdHTTPResponse = class(TIdResponseHeaderInfo)
  358. protected
  359. FHTTP: TIdCustomHTTP;
  360. FResponseCode: Integer;
  361. FResponseText: string;
  362. FKeepAlive: Boolean;
  363. FContentStream: TStream;
  364. FResponseVersion: TIdHTTPProtocolVersion;
  365. //
  366. function GetKeepAlive: Boolean;
  367. function GetResponseCode: Integer;
  368. public
  369. constructor Create(AParent: TIdCustomHTTP); reintroduce; virtual;
  370. property KeepAlive: Boolean read GetKeepAlive write FKeepAlive;
  371. property ResponseText: string read FResponseText write FResponseText;
  372. property ResponseCode: Integer read GetResponseCode write FResponseCode;
  373. property ResponseVersion: TIdHTTPProtocolVersion read FResponseVersion write FResponseVersion;
  374. property ContentStream: TStream read FContentStream write FContentStream;
  375. end;
  376. TIdHTTPRequest = class(TIdRequestHeaderInfo)
  377. protected
  378. FHTTP: TIdCustomHTTP;
  379. FURL: string;
  380. FMethod: TIdHTTPMethod;
  381. FSourceStream: TStream;
  382. FUseProxy: TIdHTTPConnectionType;
  383. FIPVersion: TIdIPVersion;
  384. public
  385. constructor Create(AHTTP: TIdCustomHTTP); reintroduce; virtual;
  386. property URL: string read FURL write FURL;
  387. property Method: TIdHTTPMethod read FMethod write FMethod;
  388. property Source: TStream read FSourceStream write FSourceStream;
  389. property UseProxy: TIdHTTPConnectionType read FUseProxy;
  390. property IPVersion: TIdIPversion read FIPVersion write FIPVersion;
  391. end;
  392. TIdHTTPProtocol = class(TObject)
  393. FHTTP: TIdCustomHTTP;
  394. FResponseCode: Integer;
  395. FRequest: TIdHTTPRequest;
  396. FResponse: TIdHTTPResponse;
  397. public
  398. constructor Create(AConnection: TIdCustomHTTP);
  399. destructor Destroy; override;
  400. function ProcessResponse(AIgnoreReplies: array of SmallInt): TIdHTTPWhatsNext;
  401. procedure BuildAndSendRequest(AURI: TIdURI);
  402. procedure RetrieveHeaders(AMaxHeaderCount: integer);
  403. //
  404. property ResponseCode: Integer read FResponseCode;
  405. property Request: TIdHTTPRequest read FRequest;
  406. property Response: TIdHTTPResponse read FResponse;
  407. end;
  408. TIdCustomHTTP = class(TIdTCPClientCustom)
  409. protected
  410. {Retries counter for WWW authorization}
  411. FAuthRetries: Integer;
  412. {Retries counter for proxy authorization}
  413. FAuthProxyRetries: Integer;
  414. FCookieManager: TIdCookieManager;
  415. FCompressor : TIdZLibCompressorBase;
  416. FFreeOnDestroy: Boolean;
  417. {Max retries for authorization}
  418. FMaxAuthRetries: Integer;
  419. FMaxHeaderLines: integer;
  420. FAllowCookies: Boolean;
  421. FAuthenticationManager: TIdAuthenticationManager;
  422. FProtocolVersion: TIdHTTPProtocolVersion;
  423. {this is an internal counter for redirercts}
  424. FRedirectCount: Integer;
  425. FRedirectMax: Integer;
  426. FHandleRedirects: Boolean;
  427. FOptions: TIdHTTPOptions;
  428. FURI: TIdURI;
  429. FHTTPProto: TIdHTTPProtocol;
  430. FProxyParameters: TIdProxyConnectionInfo;
  431. //
  432. FOnRedirect: TIdHTTPOnRedirectEvent;
  433. FOnSelectAuthorization: TIdOnSelectAuthorization;
  434. FOnSelectProxyAuthorization: TIdOnSelectAuthorization;
  435. FOnAuthorization: TIdOnAuthorization;
  436. FOnProxyAuthorization: TIdOnAuthorization;
  437. //
  438. {
  439. procedure SetHost(const Value: string); override;
  440. procedure SetPort(const Value: integer); override;
  441. }
  442. procedure DoRequest(const AMethod: TIdHTTPMethod; AURL: string;
  443. ASource, AResponseContent: TStream; AIgnoreReplies: array of SmallInt); virtual;
  444. procedure InitComponent; override;
  445. procedure SetAuthenticationManager(Value: TIdAuthenticationManager);
  446. procedure SetCookieManager(ACookieManager: TIdCookieManager);
  447. procedure SetAllowCookies(AValue: Boolean);
  448. function GetResponseCode: Integer;
  449. function GetResponseText: string;
  450. function DoOnAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; virtual;
  451. function DoOnProxyAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; virtual;
  452. function DoOnRedirect(var Location: string; var VMethod: TIdHTTPMethod; RedirectCount: integer): boolean; virtual;
  453. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  454. procedure ProcessCookies(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  455. function SetHostAndPort: TIdHTTPConnectionType;
  456. procedure SetCookies(AURL: TIdURI; ARequest: TIdHTTPRequest);
  457. procedure ReadResult(AResponse: TIdHTTPResponse);
  458. procedure PrepareRequest(ARequest: TIdHTTPRequest);
  459. procedure ConnectToHost(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  460. function GetResponseHeaders: TIdHTTPResponse;
  461. function GetRequestHeaders: TIdHTTPRequest;
  462. procedure SetRequestHeaders(Value: TIdHTTPRequest);
  463. procedure EncodeRequestParams(AStrings: TIdStrings);
  464. function SetRequestParams(AStrings: TIdStrings): string;
  465. procedure CheckAndConnect(AResponse: TIdHTTPResponse);
  466. procedure DoOnDisconnected; override;
  467. public
  468. destructor Destroy; override;
  469. procedure Options(AURL: string); overload;
  470. procedure Get(AURL: string; AResponseContent: TStream); overload;
  471. procedure Get(AURL: string; AResponseContent: TStream; AIgnoreReplies: array of SmallInt);
  472. overload;
  473. function Get(AURL: string): string; overload;
  474. function Get(AURL: string; AIgnoreReplies: array of SmallInt): string; overload;
  475. procedure Trace(AURL: string; AResponseContent: TStream); overload;
  476. function Trace(AURL: string): string; overload;
  477. procedure Head(AURL: string);
  478. function Post(AURL: string; ASource: TIdStrings): string; overload;
  479. function Post(AURL: string; ASource: TStream): string; overload;
  480. function Post(AURL: string; ASource: TIdMultiPartFormDataStream): string; overload;
  481. procedure Post(AURL: string; ASource: TIdMultiPartFormDataStream; AResponseContent: TStream); overload;
  482. procedure Post(AURL: string; ASource: TIdStrings; AResponseContent: TStream); overload;
  483. {Post data provided by a stream, this is for submitting data to a server}
  484. procedure Post(AURL: string; ASource, AResponseContent: TStream); overload;
  485. function Put(AURL: string; ASource: TStream): string; overload;
  486. procedure Put(AURL: string; ASource, AResponseContent: TStream); overload;
  487. {This is an object that can compress and decompress HTTP Deflate encoding}
  488. property Compressor : TIdZLibCompressorBase read FCompressor write FCompressor;
  489. {This is the response code number such as 404 for File not Found}
  490. property ResponseCode: Integer read GetResponseCode;
  491. {This is the text of the message such as "404 File Not Found here Sorry"}
  492. property ResponseText: string read GetResponseText;
  493. property Response: TIdHTTPResponse read GetResponseHeaders;
  494. { This is the last processed URL }
  495. property URL: TIdURI read FURI;
  496. // number of retry attempts for Authentication
  497. property AuthRetries: Integer read FAuthRetries write FAuthRetries;
  498. property AuthProxyRetries: Integer read FAuthProxyRetries write FAuthProxyRetries;
  499. // maximum number of Authentication retries permitted
  500. property MaxAuthRetries: Integer read FMaxAuthRetries write FMaxAuthRetries default Id_TIdHTTP_MaxAuthRetries;
  501. property AllowCookies: Boolean read FAllowCookies write SetAllowCookies;
  502. {Do we handle redirect requests or simply raise an exception and let the
  503. developer deal with it}
  504. property HandleRedirects: Boolean read FHandleRedirects write FHandleRedirects default Id_TIdHTTP_HandleRedirects;
  505. property ProtocolVersion: TIdHTTPProtocolVersion read FProtocolVersion write FProtocolVersion default Id_TIdHTTP_ProtocolVersion;
  506. {This is the maximum number of redirects we wish to handle, we limit this
  507. to prevent stack overflow due to recursion. Recursion is safe ONLY if
  508. prevented for continuing to infinity}
  509. property RedirectMaximum: Integer read FRedirectMax write FRedirectMax default Id_TIdHTTP_RedirectMax;
  510. // S.G. 6/4/2004: This is to prevent the server from responding with too many header lines
  511. property MaxHeaderLines: integer read FMaxHeaderLines write FMaxHeaderLines default Id_TIdHTTP_MaxHeaderLines;
  512. property ProxyParams: TIdProxyConnectionInfo read FProxyParameters write FProxyParameters;
  513. property Request: TIdHTTPRequest read GetRequestHeaders write SetRequestHeaders;
  514. property HTTPOptions: TIdHTTPOptions read FOptions write FOptions;
  515. // Fired when a rediretion is requested.
  516. property OnRedirect: TIdHTTPOnRedirectEvent read FOnRedirect write FOnRedirect;
  517. property OnSelectAuthorization: TIdOnSelectAuthorization read FOnSelectAuthorization write FOnSelectAuthorization;
  518. property OnSelectProxyAuthorization: TIdOnSelectAuthorization read FOnSelectProxyAuthorization write FOnSelectProxyAuthorization;
  519. property OnAuthorization: TIdOnAuthorization read FOnAuthorization write FOnAuthorization;
  520. property OnProxyAuthorization: TIdOnAuthorization read FOnProxyAuthorization write FOnProxyAuthorization;
  521. // Cookie stuff
  522. property CookieManager: TIdCookieManager read FCookieManager write SetCookieManager;
  523. //
  524. property AuthenticationManager: TIdAuthenticationManager read FAuthenticationManager write SetAuthenticationManager;
  525. end;
  526. TIdHTTP = class(TIdCustomHTTP)
  527. published
  528. // number of retries attempted for Authentication
  529. property AuthRetries;
  530. property AuthProxyRetries;
  531. // number of Authentication retries permitted
  532. property MaxAuthRetries;
  533. property AllowCookies;
  534. { Do we handle redirect requests or simply raise an exception and let the
  535. developer deal with it }
  536. property HandleRedirects;
  537. property ProtocolVersion;
  538. { This is the maximum number of redirects we wish to handle, we limit this
  539. to prevent stack overflow due to recursion. Recursion is safe ONLY if
  540. prevented for continuing to infinity }
  541. property RedirectMaximum;
  542. property ProxyParams;
  543. property Request;
  544. property HTTPOptions;
  545. // Fired when a rediretion is requested.
  546. property OnRedirect;
  547. property OnSelectAuthorization;
  548. property OnSelectProxyAuthorization;
  549. property OnAuthorization;
  550. property OnProxyAuthorization;
  551. // property Host;
  552. // property Port default IdPORT_HTTP;
  553. // Cookie stuff
  554. property CookieManager;
  555. // property AuthenticationManager: TIdAuthenticationManager read FAuthenticationManager write SetAuthenticationManager;
  556. // ZLib compression library object for use with deflate and gzip encoding
  557. property Compressor;
  558. end;
  559. EIdUnknownProtocol = class(EIdException);
  560. EIdHTTPProtocolException = class( EIdReplyRFCError )
  561. protected
  562. FErrorMessage: string;
  563. public
  564. constructor CreateError(const anErrCode: Integer; const asReplyMessage: string;
  565. const asErrorMessage: string); reintroduce; virtual;
  566. property ErrorMessage: string read FErrorMessage;
  567. end;
  568. implementation
  569. uses
  570. SysUtils,
  571. IdComponent, IdCoderMIME, IdTCPConnection, IdResourceStringsProtocols,
  572. IdGlobalProtocols, IdIOHandler,IdIOHandlerSocket, IdStreamVCL;
  573. const
  574. ProtocolVersionString: array[TIdHTTPProtocolVersion] of string = ('1.0', '1.1'); {do not localize}
  575. { EIdHTTPProtocolException }
  576. constructor EIdHTTPProtocolException.CreateError(const anErrCode: Integer;
  577. const asReplyMessage: string; const asErrorMessage: string);
  578. begin
  579. inherited CreateError(anErrCode, asReplyMessage);
  580. FErrorMessage := asErrorMessage;
  581. end;
  582. { TIdHTTP }
  583. destructor TIdCustomHTTP.Destroy;
  584. begin
  585. FreeAndNil(FHTTPProto);
  586. FreeAndNil(FURI);
  587. FreeAndNil(FProxyParameters);
  588. inherited Destroy;
  589. end;
  590. procedure TIdCustomHTTP.Options(AURL: string);
  591. begin
  592. DoRequest(hmOptions, AURL, nil, nil, []);
  593. end;
  594. procedure TIdCustomHTTP.Get(AURL: string; AResponseContent: TStream);
  595. begin
  596. Get(AURL, AResponseContent, []);
  597. end;
  598. procedure TIdCustomHTTP.Trace(AURL: string; AResponseContent: TStream);
  599. begin
  600. DoRequest(hmTrace, AURL, nil, AResponseContent, []);
  601. end;
  602. procedure TIdCustomHTTP.Head(AURL: string);
  603. begin
  604. DoRequest(hmHead, AURL, nil, nil, []);
  605. end;
  606. procedure TIdCustomHTTP.Post(AURL: string; ASource, AResponseContent: TStream);
  607. var
  608. OldProtocol: TIdHTTPProtocolVersion;
  609. begin
  610. // PLEASE READ CAREFULLY
  611. // Currently when issuing a POST, IdHTTP will automatically set the protocol
  612. // to version 1.0 independently of the value it had initially. This is because
  613. // there are some servers that don't respect the RFC to the full extent. In
  614. // particular, they don't respect sending/not sending the Expect: 100-Continue
  615. // header. Until we find an optimum solution that does NOT break the RFC, we
  616. // will restrict POSTS to version 1.0.
  617. if Connected then
  618. begin
  619. Disconnect;
  620. end;
  621. OldProtocol := FProtocolVersion;
  622. // If hoKeepOrigProtocol is SET, is possible to assume that the developer
  623. // is sure in operations of the server
  624. if not (hoKeepOrigProtocol in FOptions) then
  625. FProtocolVersion := pv1_0;
  626. DoRequest(hmPost, AURL, ASource, AResponseContent, []);
  627. FProtocolVersion := OldProtocol;
  628. end;
  629. procedure TIdCustomHTTP.EncodeRequestParams(AStrings: TIdStrings);
  630. var
  631. i: Integer;
  632. S: string;
  633. begin
  634. for i := 0 to AStrings.Count - 1 do begin
  635. S := AStrings.Names[i];
  636. if Length(AStrings.Values[S]) > 0 then begin
  637. AStrings.Values[S] := TIdURI.ParamsEncode(AStrings.Values[S]);
  638. end;
  639. end;
  640. end;
  641. function TIdCustomHTTP.SetRequestParams(AStrings: TIdStrings): string;
  642. begin
  643. if Assigned(AStrings) then begin
  644. if hoForceEncodeParams in FOptions then begin
  645. EncodeRequestParams(AStrings);
  646. end;
  647. if AStrings.Count > 1 then begin
  648. // break trailing CR&LF
  649. Result := StringReplace(Trim(AStrings.Text), sLineBreak, '&', [rfReplaceAll])
  650. end else begin
  651. Result := Trim(AStrings.Text);
  652. end;
  653. end else begin
  654. Result := '';
  655. end;
  656. end;
  657. procedure TIdCustomHTTP.Post(AURL: string; ASource: TIdStrings; AResponseContent: TStream);
  658. var
  659. LParams: TStringStream;
  660. begin
  661. // Usual posting request have default ContentType is application/x-www-form-urlencoded
  662. if (Request.ContentType = '') or (TextIsSame(Request.ContentType, 'text/html')) then {do not localize}
  663. Request.ContentType := 'application/x-www-form-urlencoded'; {do not localize}
  664. LParams := TStringStream.Create(SetRequestParams(ASource));
  665. try
  666. Post(AURL, LParams, AResponseContent);
  667. finally
  668. FreeAndNil(LParams);
  669. end;
  670. end;
  671. function TIdCustomHTTP.Post(AURL: string; ASource: TIdStrings): string;
  672. var
  673. LResponse: TStringStream;
  674. begin
  675. LResponse := TStringStream.Create('');
  676. try
  677. Post(AURL, ASource, LResponse);
  678. finally
  679. result := LResponse.DataString;
  680. FreeAndNil(LResponse);
  681. end;
  682. end;
  683. function TIdCustomHTTP.Post(AURL: string; ASource: TStream): string;
  684. var
  685. LResponse: TStringStream;
  686. begin
  687. LResponse := TStringStream.Create('');
  688. try
  689. Post(AURL, ASource, LResponse);
  690. finally
  691. result := LResponse.DataString;
  692. FreeAndNil(LResponse);
  693. end;
  694. end;
  695. procedure TIdCustomHTTP.Put(AURL: string; ASource, AResponseContent: TStream);
  696. begin
  697. DoRequest(hmPut, AURL, ASource, AResponseContent, []);
  698. end;
  699. function TIdCustomHTTP.Put(AURL: string; ASource: TStream): string;
  700. var
  701. LResponse: TStringStream;
  702. begin
  703. LResponse := TStringStream.Create('');
  704. try
  705. Put(AURL, ASource, LResponse);
  706. finally
  707. result := LResponse.DataString;
  708. FreeAndNil(LResponse);
  709. end;
  710. end;
  711. function TIdCustomHTTP.Get(AURL: string): string;
  712. begin
  713. Result := Get(AURL, []);
  714. end;
  715. function TIdCustomHTTP.Trace(AURL: string): string;
  716. var
  717. Stream: TStringStream;
  718. begin
  719. Stream := TStringStream.Create('');
  720. try
  721. Trace(AURL, Stream);
  722. result := Stream.DataString;
  723. finally
  724. FreeAndNil(Stream)
  725. end;
  726. end;
  727. function TIdCustomHTTP.DoOnRedirect(var Location: string; var VMethod: TIdHTTPMethod; RedirectCount: integer): boolean;
  728. begin
  729. result := HandleRedirects;
  730. if assigned(FOnRedirect) then
  731. begin
  732. FOnRedirect(self, Location, RedirectCount, result, VMethod);
  733. end;
  734. end;
  735. procedure TIdCustomHTTP.SetCookies(AURL: TIdURI; ARequest: TIdHTTPRequest);
  736. var
  737. S: string;
  738. begin
  739. if Assigned(FCookieManager) then
  740. begin
  741. // Send secure cookies only if we have Secured connection
  742. S := FCookieManager.GenerateCookieList(AURL, (IOHandler is TIdSSLIOHandlerSocketBase));
  743. if Length(S) > 0 then
  744. begin
  745. ARequest.RawHeaders.Values['Cookie'] := S; {do not localize}
  746. end;
  747. end;
  748. end;
  749. // This function sets the Host and Port and returns a boolean depending on
  750. // whether a PROXY is being used or not.
  751. function TIdCustomHTTP.SetHostAndPort: TIdHTTPConnectionType;
  752. var
  753. LHost:string;
  754. LPort:integer;
  755. begin
  756. FPort := StrToIntDef(URL.Port,80);
  757. FHost := URL.Host;
  758. LPort := FPort;
  759. {
  760. Destination:= URL.Host+':'+URL.Port;
  761. Result := ctNormal;
  762. }
  763. // First check to see if a Proxy has been specified.
  764. if Length(ProxyParams.ProxyServer) > 0 then
  765. begin
  766. if ((not TextIsSame(LHost, ProxyParams.ProxyServer)) or
  767. (LPort <> ProxyParams.ProxyPort)) and (Connected) then
  768. begin
  769. Disconnect;
  770. end;
  771. LHost := ProxyParams.ProxyServer;
  772. LPort := ProxyParams.ProxyPort;
  773. if TextIsSame(URL.Protocol, 'HTTPS') then {do not localize}
  774. begin
  775. Result := ctSSLProxy;
  776. if Assigned(IOHandler) then
  777. begin
  778. if not (IOHandler is TIdSSLIOHandlerSocketBase) then
  779. begin
  780. raise EIdIOHandlerPropInvalid.Create(RSIOHandlerPropInvalid);
  781. end else begin
  782. (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := true;
  783. end;
  784. end;
  785. end
  786. else begin
  787. Result := ctProxy;
  788. if Assigned(IOHandler) and (IOHandler is TIdSSLIOHandlerSocketBase) then
  789. begin
  790. (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := true;
  791. end;
  792. end;
  793. end
  794. else begin
  795. Result := ctNormal;
  796. if IOHandler is TIdIOHandlerSocket then begin
  797. if Assigned(TIdIOHandlerSocket(IOHandler).Binding) then begin
  798. if URL.IPVersion <> TIdIOHandlerSocket(IOHandler).Binding.IPVersion then begin
  799. if Connected then begin
  800. Disconnect; // get rid of current socket handle
  801. end;
  802. end;
  803. end;
  804. end;
  805. if ((not TextIsSame(LHost, URL.Host)) or
  806. (LPort <> StrToInt(URL.Port))) then begin
  807. if Connected then begin
  808. Disconnect;
  809. end;
  810. LHost := URL.Host;
  811. LPort := StrToInt(URL.Port);
  812. end;
  813. if TextIsSame(URL.Protocol, 'HTTPS') then {do not localize}
  814. begin
  815. // Just check can we do SSL
  816. if not Assigned(IOHandler) or (not (IOHandler is TIdSSLIOHandlerSocketBase)) then
  817. raise EIdIOHandlerPropInvalid.Create(RSIOHandlerPropInvalid)
  818. else begin
  819. (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := false;
  820. result := ctSSL;
  821. end;
  822. end
  823. else
  824. begin
  825. if Assigned(IOHandler) then
  826. begin
  827. if (IOHandler is TIdSSLIOHandlerSocketBase) then
  828. begin
  829. (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := true;
  830. end;
  831. end;
  832. end;
  833. end;
  834. Host := LHost;
  835. Port := LPort;
  836. end;
  837. procedure TIdCustomHTTP.ReadResult(AResponse: TIdHTTPResponse);
  838. var
  839. Size: Integer;
  840. LS : TIdStreamVCL;
  841. function ChunkSize: integer;
  842. var
  843. j: Integer;
  844. s: string;
  845. begin
  846. s := IOHandler.ReadLn;
  847. j := IndyPos(' ', s);
  848. if j > 0 then
  849. begin
  850. s := Copy(s, 1, j - 1);
  851. end;
  852. Result := StrToIntDef('$' + s, 0);
  853. end;
  854. begin
  855. if Assigned(AResponse.ContentStream) then // Only for Get and Post
  856. begin
  857. LS := TIdStreamVCL.Create(AResponse.ContentStream);
  858. try
  859. if AResponse.ContentLength > 0 then // If chunked then this is also 0
  860. begin
  861. try
  862. IOHandler.ReadStream(LS, AResponse.ContentLength);
  863. except
  864. on E: EIdConnClosedGracefully do
  865. end;
  866. end
  867. else
  868. begin
  869. if IndyPos('chunked', AResponse.RawHeaders.Values['Transfer-Encoding']) > 0 then {do not localize}
  870. begin // Chunked
  871. DoStatus(hsStatusText, [RSHTTPChunkStarted]);
  872. Size := ChunkSize;
  873. while Size > 0 do
  874. begin
  875. IOHandler.ReadStream(LS, Size);
  876. IOHandler.ReadLn; // blank line
  877. Size := ChunkSize;
  878. end;
  879. IOHandler.ReadLn; // blank line
  880. end else begin
  881. if not AResponse.HasContentLength then
  882. begin
  883. IOHandler.ReadStream(LS, -1, True);
  884. end;
  885. end;
  886. end;
  887. if Assigned(Compressor) and (Response.ContentEncoding = 'deflate') then begin {do not localize}
  888. AResponse.ContentStream.Position := 0;
  889. Compressor.DecompressDeflateStream(AResponse.ContentStream);
  890. end else if Assigned(Compressor) and (Response.ContentEncoding = 'gzip') then begin {do not localize}
  891. AResponse.ContentStream.Position := 0;
  892. Compressor.DecompressGZipStream(AResponse.ContentStream);
  893. end;
  894. finally
  895. FreeAndNil(LS);
  896. end;
  897. end;
  898. end;
  899. procedure TIdCustomHTTP.PrepareRequest(ARequest: TIdHTTPRequest);
  900. var
  901. LURI: TIdURI;
  902. begin
  903. LURI := TIdURI.Create(ARequest.URL);
  904. if Length(LURI.Username) > 0 then
  905. begin
  906. ARequest.Username := LURI.Username;
  907. ARequest.Password := LURI.Password;
  908. end;
  909. FURI.Username := ARequest.Username;
  910. FURI.Password := ARequest.Password;
  911. FURI.Path := ProcessPath(FURI.Path, LURI.Path);
  912. FURI.Document := LURI.Document;
  913. FURI.Params := LURI.Params;
  914. if Length(LURI.Host) > 0 then begin
  915. FURI.Host := LURI.Host;
  916. end;
  917. if Length(LURI.Protocol) > 0 then begin
  918. FURI.Protocol := LURI.Protocol;
  919. end
  920. // non elegant solution - to be recoded, only for pointing the bug / GREGOR
  921. else if TextIsSame(FURI.Protocol, 'https') then begin {do not localize}
  922. FURI.Protocol := 'https'; {do not localize}
  923. end
  924. else begin
  925. FURI.Protocol := 'http'; {do not localize}
  926. end;
  927. if Length(LURI.Port) > 0 then begin
  928. FURI.Port := LURI.Port;
  929. end
  930. else begin
  931. if TextIsSame(LURI.Protocol, 'http') then begin {do not localize}
  932. FURI.Port := IntToStr(IdPORT_HTTP);
  933. end else begin
  934. if TextIsSame(LURI.Protocol, 'https') then begin {do not localize}
  935. FURI.Port := IntToStr(IdPORT_SSL);
  936. end else begin
  937. if Length(FURI.Port) > 0 then begin
  938. { FURI.Port:=FURI.Port; } // do nothing, as the port is already filled in.
  939. end else begin
  940. raise EIdUnknownProtocol.Create('');
  941. end;
  942. end;
  943. end;
  944. end;
  945. // The URL part is not URL encoded at this place
  946. ARequest.URL := URL.Path + URL.Document + URL.Params;
  947. if ARequest.Method = hmOptions then
  948. begin
  949. if TextIsSame(LURI.Document, '*') then
  950. begin
  951. ARequest.URL := LURI.Document;
  952. end;
  953. end;
  954. ARequest.IPVersion := LURI.IPVersion;
  955. FURI.IPVersion := ARequest.IPVersion;
  956. FreeAndNil(LURI); // Free URI Object
  957. // Check for valid HTTP request methods
  958. if ARequest.Method in [hmTrace, hmPut, hmOptions, hmDelete] then
  959. begin
  960. if ProtocolVersion <> pv1_1 then
  961. begin
  962. raise EIdException.Create(RSHTTPMethodRequiresVersion);
  963. end;
  964. end;
  965. if ARequest.Method in [hmPost, hmPut] then begin
  966. ARequest.ContentLength := ARequest.Source.Size;
  967. end else begin
  968. ARequest.ContentLength := -1;
  969. end;
  970. if FURI.Port <> IntToStr(IdPORT_HTTP) then begin
  971. ARequest.Host := FURI.Host + ':' + FURI.Port
  972. end else begin
  973. ARequest.Host := FURI.Host;
  974. end;
  975. end;
  976. procedure TIdCustomHTTP.CheckAndConnect(AResponse: TIdHTTPResponse);
  977. begin
  978. if not AResponse.KeepAlive then begin
  979. Disconnect;
  980. end;
  981. CheckForGracefulDisconnect(false);
  982. if not Connected then try
  983. IPVersion := FURI.IPVersion;
  984. Connect;
  985. except
  986. on E: EIdSSLProtocolReplyError do begin
  987. Disconnect;
  988. raise;
  989. end;
  990. end;
  991. end;
  992. procedure TIdCustomHTTP.ConnectToHost(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  993. var
  994. LLocalHTTP: TIdHTTPProtocol;
  995. LS : TIdStreamVCL;
  996. begin
  997. ARequest.FUseProxy := SetHostAndPort;
  998. if ARequest.UseProxy = ctProxy then
  999. begin
  1000. ARequest.URL := FURI.URI;
  1001. end;
  1002. case ARequest.UseProxy of
  1003. ctNormal:
  1004. if (ProtocolVersion = pv1_0) and (Length(ARequest.Connection) = 0) then
  1005. ARequest.Connection := 'keep-alive'; {do not localize}
  1006. ctSSL, ctSSLProxy: ARequest.Connection := '';
  1007. ctProxy:
  1008. if (ProtocolVersion = pv1_0) and (Length(ARequest.Connection) = 0) then
  1009. begin
  1010. ARequest.ProxyConnection := 'keep-alive'; {do not localize}
  1011. end;
  1012. end;
  1013. if Assigned(FCompressor) then
  1014. begin
  1015. if (IndyPos('deflate',Request.AcceptEncoding)=0) and {do not localize}
  1016. (IndyPos('gizp',Request.AcceptEncoding)=0) then {do not localize}
  1017. begin
  1018. Request.AcceptEncoding := 'deflate, gzip, '; {do not localize}
  1019. end;
  1020. end;
  1021. if IndyPos('identity',Request.AcceptEncoding)=0 then {do not localize}
  1022. begin
  1023. Request.AcceptEncoding := Request.AcceptEncoding + 'identity'; {do not localize}
  1024. end;
  1025. if ARequest.UseProxy = ctSSLProxy then begin
  1026. LLocalHTTP := TIdHTTPProtocol.Create(Self);
  1027. with LLocalHTTP do begin
  1028. Request.UserAgent := ARequest.UserAgent;
  1029. Request.Host := ARequest.Host;
  1030. Request.ContentLength := ARequest.ContentLength;
  1031. Request.Pragma := 'no-cache'; {do not localize}
  1032. Request.URL := URL.Host + ':' + URL.Port;
  1033. Request.Method := hmConnect;
  1034. Request.ProxyConnection := 'keep-alive'; {do not localize}
  1035. Response.ContentStream := TMemoryStream.Create;
  1036. try
  1037. try
  1038. repeat
  1039. CheckAndConnect(Response);
  1040. BuildAndSendRequest(nil);
  1041. Response.ResponseText := IOHandler.ReadLn;
  1042. if Length(Response.ResponseText) = 0 then begin
  1043. // Support for HTTP responses without status line and headers
  1044. Response.ResponseText := 'HTTP/1.0 200 OK'; {do not localize}
  1045. Response.Connection := 'close'; {do not localize}
  1046. end
  1047. else begin
  1048. RetrieveHeaders(MaxHeaderLines);
  1049. ProcessCookies(LLocalHTTP.Request, LLocalHTTP.Response);
  1050. end;
  1051. if Response.ResponseCode = 200 then
  1052. begin
  1053. // Connection established
  1054. (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := false;
  1055. Break;
  1056. end else begin
  1057. ProcessResponse([]);
  1058. end;
  1059. until False;
  1060. except
  1061. raise;
  1062. // TODO: Add property that will contain the error messages.
  1063. end;
  1064. finally
  1065. LLocalHTTP.Response.ContentStream.Free;
  1066. FreeAndNil(LLocalHTTP);
  1067. end;
  1068. end;
  1069. end else begin
  1070. CheckAndConnect(AResponse);
  1071. end;
  1072. FHTTPProto.BuildAndSendRequest(URL);
  1073. if (ARequest.Method in [hmPost, hmPut]) then
  1074. begin
  1075. LS := TIdStreamVCL.Create(ARequest.Source);
  1076. try
  1077. IOHandler.Write(LS, 0, false);
  1078. finally
  1079. FreeAndNil(LS);
  1080. end;
  1081. end;
  1082. end;
  1083. procedure TIdCustomHTTP.SetAllowCookies(AValue: Boolean);
  1084. begin
  1085. FAllowCookies := AValue;
  1086. end;
  1087. procedure TIdCustomHTTP.ProcessCookies(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  1088. var
  1089. Cookies, Cookies2: TIdStringList;
  1090. i: Integer;
  1091. begin
  1092. Cookies := nil;
  1093. Cookies2 := nil;
  1094. try
  1095. if not Assigned(FCookieManager) and AllowCookies then
  1096. begin
  1097. CookieManager := TIdCookieManager.Create(Self);
  1098. FFreeOnDestroy := true;
  1099. end;
  1100. if Assigned(FCookieManager) then
  1101. begin
  1102. Cookies := TIdStringList.Create;
  1103. Cookies2 := TIdStringList.Create;
  1104. AResponse.RawHeaders.Extract('Set-cookie', Cookies); {do not localize}
  1105. AResponse.RawHeaders.Extract('Set-cookie2', Cookies2); {do not localize}
  1106. for i := 0 to Cookies.Count - 1 do
  1107. CookieManager.AddCookie(Cookies[i], FURI.Host);
  1108. for i := 0 to Cookies2.Count - 1 do
  1109. CookieManager.AddCookie2(Cookies2[i], FURI.Host);
  1110. end;
  1111. finally
  1112. FreeAndNil(Cookies);
  1113. FreeAndNil(Cookies2);
  1114. end;
  1115. end;
  1116. procedure TIdCustomHTTP.Notification(AComponent: TComponent; Operation: TOperation);
  1117. begin
  1118. inherited Notification(AComponent, Operation);
  1119. if Operation = opRemove then
  1120. begin
  1121. if (AComponent = FCookieManager) then
  1122. begin
  1123. FCookieManager := nil;
  1124. end;
  1125. if AComponent = FAuthenticationManager then
  1126. begin
  1127. FAuthenticationManager := nil;
  1128. end;
  1129. if AComponent = FCompressor then begin
  1130. FCompressor := nil;
  1131. end;
  1132. end;
  1133. end;
  1134. procedure TIdCustomHTTP.SetCookieManager(ACookieManager: TIdCookieManager);
  1135. begin
  1136. if Assigned(FCookieManager) then
  1137. begin
  1138. if FFreeOnDestroy then begin
  1139. FreeAndNil(FCookieManager);
  1140. end;
  1141. end;
  1142. FCookieManager := ACookieManager;
  1143. FFreeOnDestroy := false;
  1144. if Assigned(FCookieManager) then
  1145. begin
  1146. FCookieManager.FreeNotification(Self);
  1147. end;
  1148. end;
  1149. function TIdCustomHTTP.DoOnAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean;
  1150. var
  1151. i: Integer;
  1152. S: string;
  1153. Auth: TIdAuthenticationClass;
  1154. begin
  1155. Inc(FAuthRetries);
  1156. if not Assigned(ARequest.Authentication) then
  1157. begin
  1158. // Find wich Authentication method is supported from us.
  1159. for i := 0 to AResponse.WWWAuthenticate.Count - 1 do
  1160. begin
  1161. S := AResponse.WWWAuthenticate[i];
  1162. Auth := FindAuthClass(Fetch(S));
  1163. if Auth <> nil then
  1164. break;
  1165. end;
  1166. if Auth = nil then begin
  1167. result := false;
  1168. exit;
  1169. end;
  1170. if Assigned(FOnSelectAuthorization) then
  1171. begin
  1172. OnSelectAuthorization(self, Auth, AResponse.WWWAuthenticate);
  1173. end;
  1174. ARequest.Authentication := Auth.Create;
  1175. end;
  1176. // Clear password and reset autorization if previous failed
  1177. {if (AResponse.FResponseCode = 401) then begin
  1178. ARequest.Password := '';
  1179. ARequest.Authentication.Reset;
  1180. end;}
  1181. // S.G. 20/10/2003: Added part about the password. Not testing user name as some
  1182. // S.G. 20/10/2003: web sites do not require user name, only password.
  1183. result := Assigned(FOnAuthorization) or (trim(ARequest.Password) <> '');
  1184. if Result then
  1185. begin
  1186. with ARequest.Authentication do
  1187. begin
  1188. Username := ARequest.Username;
  1189. Password := ARequest.Password;
  1190. // S.G. 20/10/2003: ToDo: We need to have a marker here to prevent the code to test with the same username/password combo
  1191. // S.G. 20/10/2003: if they are picked up from properties.
  1192. Params.Values['Authorization'] := ARequest.Authentication.Authentication; {do not localize}
  1193. AuthParams := AResponse.WWWAuthenticate;
  1194. end;
  1195. result := false;
  1196. repeat
  1197. case ARequest.Authentication.Next of
  1198. wnAskTheProgram:
  1199. begin // Ask the user porgram to supply us with authorization information
  1200. if Assigned(FOnAuthorization) then
  1201. begin
  1202. ARequest.Authentication.UserName := ARequest.Username;
  1203. ARequest.Authentication.Password := ARequest.Password;
  1204. OnAuthorization(self, ARequest.Authentication, result);
  1205. if result then begin
  1206. ARequest.BasicAuthentication := true;
  1207. ARequest.Username := ARequest.Authentication.UserName;
  1208. ARequest.Password := ARequest.Authentication.Password;
  1209. end
  1210. else begin
  1211. break;
  1212. end;
  1213. end;
  1214. end;
  1215. wnDoRequest:
  1216. begin
  1217. result := true;
  1218. break;
  1219. end;
  1220. wnFail:
  1221. begin
  1222. result := False;
  1223. Break;
  1224. end;
  1225. end;
  1226. until false;
  1227. end;
  1228. end;
  1229. function TIdCustomHTTP.DoOnProxyAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean;
  1230. var
  1231. i: Integer;
  1232. S: string;
  1233. Auth: TIdAuthenticationClass;
  1234. begin
  1235. Inc(FAuthProxyRetries);
  1236. if not Assigned(ProxyParams.Authentication) then
  1237. begin
  1238. // Find which Authentication method is supported from us.
  1239. i := 0;
  1240. while i < AResponse.ProxyAuthenticate.Count do
  1241. begin
  1242. S := AResponse.ProxyAuthenticate[i];
  1243. try
  1244. Auth := FindAuthClass(Fetch(S));
  1245. break;
  1246. except
  1247. end;
  1248. inc(i);
  1249. end;
  1250. if i = AResponse.ProxyAuthenticate.Count then
  1251. begin
  1252. result := false;
  1253. exit;
  1254. end;
  1255. if Assigned(FOnSelectProxyAuthorization) then
  1256. begin
  1257. OnSelectProxyAuthorization(self, Auth, AResponse.ProxyAuthenticate);
  1258. end;
  1259. ProxyParams.Authentication := Auth.Create;
  1260. end;
  1261. result := Assigned(OnProxyAuthorization);
  1262. // Clear password and reset autorization if previous failed
  1263. if (AResponse.FResponseCode = 407) then begin
  1264. ProxyParams.ProxyPassword := '';
  1265. ProxyParams.Authentication.Reset;
  1266. end;
  1267. if Result then
  1268. begin
  1269. with ProxyParams.Authentication do
  1270. begin
  1271. Username := ProxyParams.ProxyUsername;
  1272. Password := ProxyParams.ProxyPassword;
  1273. AuthParams := AResponse.ProxyAuthenticate;
  1274. end;
  1275. result := false;
  1276. repeat
  1277. case ProxyParams.Authentication.Next of
  1278. wnAskTheProgram: // Ask the user porgram to supply us with authorization information
  1279. begin
  1280. if Assigned(OnProxyAuthorization) then
  1281. begin
  1282. ProxyParams.Authentication.Username := ProxyParams.ProxyUsername;
  1283. ProxyParams.Authentication.Password := ProxyParams.ProxyPassword;
  1284. OnProxyAuthorization(self, ProxyParams.Authentication, result);
  1285. if result then begin
  1286. ProxyParams.ProxyUsername := ProxyParams.Authentication.Username;
  1287. ProxyParams.ProxyPassword := ProxyParams.Authentication.Password;
  1288. end
  1289. else begin
  1290. break;
  1291. end;
  1292. end;
  1293. end;
  1294. wnDoRequest:
  1295. begin
  1296. result := true;
  1297. break;
  1298. end;
  1299. wnFail:
  1300. begin
  1301. result := False;
  1302. Break;
  1303. end;
  1304. end;
  1305. until false;
  1306. end;
  1307. end;
  1308. function TIdCustomHTTP.GetResponseCode: Integer;
  1309. begin
  1310. Result := Response.ResponseCode;
  1311. end;
  1312. function TIdCustomHTTP.GetResponseText: string;
  1313. begin
  1314. Result := Response.FResponseText;
  1315. end;
  1316. function TIdCustomHTTP.GetResponseHeaders: TIdHTTPResponse;
  1317. begin
  1318. Result := FHTTPProto.Response;
  1319. end;
  1320. function TIdCustomHTTP.GetRequestHeaders: TIdHTTPRequest;
  1321. begin
  1322. Result := FHTTPProto.Request;
  1323. end;
  1324. procedure TIdCustomHTTP.DoOnDisconnected;
  1325. begin
  1326. inherited DoOnDisconnected;
  1327. if Assigned(Request.Authentication) and
  1328. (Request.Authentication.CurrentStep = Request.Authentication.Steps) then begin
  1329. if Assigned(AuthenticationManager) then begin
  1330. AuthenticationManager.AddAuthentication(Request.Authentication, URL);
  1331. end;
  1332. Request.Authentication.Free;
  1333. Request.Authentication := nil;
  1334. end;
  1335. if Assigned(ProxyParams.Authentication) and
  1336. (ProxyParams.Authentication.CurrentStep = ProxyParams.Authentication.Steps) then begin
  1337. ProxyParams.Authentication.Reset;
  1338. end;
  1339. end;
  1340. procedure TIdCustomHTTP.SetAuthenticationManager(Value: TIdAuthenticationManager);
  1341. begin
  1342. FAuthenticationManager := Value;
  1343. if Assigned(FAuthenticationManager) then
  1344. begin
  1345. FAuthenticationManager.FreeNotification(self);
  1346. end;
  1347. end;
  1348. {
  1349. procedure TIdCustomHTTP.SetHost(const Value: string);
  1350. begin
  1351. inherited SetHost(Value);
  1352. URL.Host := Value;
  1353. end;
  1354. procedure TIdCustomHTTP.SetPort(const Value: integer);
  1355. begin
  1356. inherited SetPort(Value);
  1357. URL.Port := IntToStr(Value);
  1358. end;
  1359. }
  1360. procedure TIdCustomHTTP.SetRequestHEaders(Value: TIdHTTPRequest);
  1361. begin
  1362. FHTTPProto.Request.Assign(Value);
  1363. end;
  1364. procedure TIdCustomHTTP.Post(AURL: string;
  1365. ASource: TIdMultiPartFormDataStream; AResponseContent: TStream);
  1366. begin
  1367. Request.ContentType := ASource.RequestContentType;
  1368. Post(AURL, TStream(ASource), AResponseContent);
  1369. end;
  1370. function TIdCustomHTTP.Post(AURL: string;
  1371. ASource: TIdMultiPartFormDataStream): string;
  1372. begin
  1373. Request.ContentType := ASource.RequestContentType;
  1374. result := Post(AURL, TStream(ASource));
  1375. end;
  1376. { TIdHTTPResponse }
  1377. constructor TIdHTTPResponse.Create(AParent: TIdCustomHTTP);
  1378. begin
  1379. inherited Create;
  1380. FHTTP := AParent;
  1381. end;
  1382. function TIdHTTPResponse.GetKeepAlive: Boolean;
  1383. var
  1384. S: string;
  1385. i: TIdHTTPProtocolVersion;
  1386. begin
  1387. S := Copy(FResponseText, 6, 3);
  1388. for i := Low(TIdHTtpProtocolVersion) to High(TIdHTtpProtocolVersion) do
  1389. if TextIsSame(ProtocolVersionString[i], S) then
  1390. begin
  1391. ResponseVersion := i;
  1392. break;
  1393. end;
  1394. if FHTTP.Connected then begin
  1395. FHTTP.IOHandler.CheckForDisconnect(false);
  1396. end;
  1397. FKeepAlive := FHTTP.Connected;
  1398. if FKeepAlive then
  1399. case FHTTP.ProtocolVersion of
  1400. pv1_1:
  1401. { By default we assume that keep-alive is by default and will close
  1402. the connection only there is "close" }
  1403. begin
  1404. FKeepAlive :=
  1405. not (TextIsSame(Trim(Connection), 'CLOSE') or {do not localize}
  1406. TextIsSame(Trim(ProxyConnection), 'CLOSE')); {do not localize}
  1407. end;
  1408. pv1_0:
  1409. { By default we assume that keep-alive is not by default and will keep
  1410. the connection only if there is "keep-alive" }
  1411. begin
  1412. FKeepAlive := TextIsSame(Trim(Connection), 'KEEP-ALIVE') or {do not localize}
  1413. TextIsSame(Trim(ProxyConnection), 'KEEP-ALIVE') {do not localize}
  1414. { or ((ResponseVersion = pv1_1) and
  1415. (Length(Trim(Connection)) = 0) and
  1416. (Length(Trim(ProxyConnection)) = 0)) };
  1417. end;
  1418. end;
  1419. result := FKeepAlive;
  1420. end;
  1421. function TIdHTTPResponse.GetResponseCode: Integer;
  1422. var
  1423. S: string;
  1424. begin
  1425. S := FResponseText;
  1426. Fetch(S);
  1427. S := Trim(S);
  1428. FResponseCode := StrToIntDef(Fetch(S, ' ', False), -1);
  1429. Result := FResponseCode;
  1430. end;
  1431. { TIdHTTPRequest }
  1432. constructor TIdHTTPRequest.Create(AHTTP: TIdCustomHTTP);
  1433. begin
  1434. inherited Create;
  1435. FHTTP := AHTTP;
  1436. FUseProxy := ctNormal;
  1437. end;
  1438. { TIdHTTPProtocol }
  1439. constructor TIdHTTPProtocol.Create(AConnection: TIdCustomHTTP);
  1440. begin
  1441. inherited Create;
  1442. FHTTP := AConnection;
  1443. // Create the headers
  1444. FRequest := TIdHTTPRequest.Create(FHTTP);
  1445. FResponse := TIdHTTPResponse.Create(FHTTP);
  1446. end;
  1447. destructor TIdHTTPProtocol.Destroy;
  1448. begin
  1449. FreeAndNil(FRequest);
  1450. FreeAndNil(FResponse);
  1451. inherited Destroy;
  1452. end;
  1453. procedure TIdHTTPProtocol.BuildAndSendRequest(AURI: TIdURI);
  1454. var
  1455. i: Integer;
  1456. begin
  1457. Request.SetHeaders;
  1458. FHTTP.ProxyParams.SetHeaders(Request.RawHeaders);
  1459. if Assigned(AURI) then
  1460. begin
  1461. FHTTP.SetCookies(AURI, Request);
  1462. end;
  1463. // This is a wrokaround for some HTTP servers wich does not implement properly the HTTP protocol
  1464. FHTTP.IOHandler.WriteBufferOpen;
  1465. try
  1466. case Request.Method of
  1467. hmHead: FHTTP.IOHandler.WriteLn('HEAD ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1468. hmGet: FHTTP.IOHandler.WriteLn('GET ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1469. hmPost: FHTTP.IOHandler.WriteLn('POST ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1470. // HTTP 1.1 only
  1471. hmOptions: FHTTP.IOHandler.WriteLn('OPTIONS ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1472. hmTrace: FHTTP.IOHandler.WriteLn('TRACE ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1473. hmPut: FHTTP.IOHandler.WriteLn('PUT ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1474. hmConnect: FHTTP.IOHandler.WriteLn('CONNECT ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1475. end;
  1476. // write the headers
  1477. for i := 0 to Request.RawHeaders.Count - 1 do
  1478. if Length(Request.RawHeaders.Strings[i]) > 0 then
  1479. FHTTP.IOHandler.WriteLn(Request.RawHeaders.Strings[i]);
  1480. FHTTP.IOHandler.WriteLn('');
  1481. FHTTP.IOHandler.WriteBufferClose;
  1482. except
  1483. FHTTP.IOHandler.WriteBufferCancel;
  1484. raise;
  1485. end;
  1486. end;
  1487. procedure TIdHTTPProtocol.RetrieveHeaders(AMaxHeaderCount: integer);
  1488. var
  1489. s: string;
  1490. LHeaderCount: Integer;
  1491. begin
  1492. // Set the response headers
  1493. // Clear headers
  1494. // Don't use Capture.
  1495. // S.G. 6/4/2004: Added AmaxHeaderCount parameter to prevent the "header bombing" of the server
  1496. Response.RawHeaders.Clear;
  1497. s := FHTTP.IOHandler.ReadLn;
  1498. try
  1499. LHeaderCount := 0;
  1500. while (s <> '') and ( (AMaxHeaderCount > 0) or (LHeaderCount < AMaxHeaderCount) ) do
  1501. begin
  1502. Response.RawHeaders.Add(S);
  1503. s := FHTTP.IOHandler.ReadLn;
  1504. inc(LHeaderCount);
  1505. end;
  1506. except
  1507. on E: EIdConnClosedGracefully do begin
  1508. FHTTP.Disconnect;
  1509. end else begin
  1510. raise;
  1511. end;
  1512. end;
  1513. Response.ProcessHeaders;
  1514. end;
  1515. function TIdHTTPProtocol.ProcessResponse(AIgnoreReplies: array of SmallInt): TIdHTTPWhatsNext;
  1516. procedure CheckException(AIgnoreReplies: array of SmallInt);
  1517. var
  1518. i: Integer;
  1519. LResponseCode: Integer;
  1520. LRespStream: TStringStream;
  1521. LTempStream: TStream;
  1522. begin
  1523. //Kudzu: Why should we override the user? User can set ReadTimeout. Respect theirs.
  1524. //FHTTP.IOHandler.ReadTimeout := 2000; // Lets wait 2 seconds for any kind of content
  1525. LRespStream := TStringStream.Create('');
  1526. LTempStream := Response.ContentStream;
  1527. Response.ContentStream := LRespStream;
  1528. try
  1529. FHTTP.ReadResult(Response);
  1530. // Cache this as ResponseCode calls GetResponseCode which parses it out
  1531. LResponseCode := Response.ResponseCode;
  1532. if High(AIgnoreReplies) > -1 then begin
  1533. for i := Low(AIgnoreReplies) to High(AIgnoreReplies) do begin
  1534. if LResponseCode = AIgnoreReplies[i] then begin
  1535. Exit;
  1536. end;
  1537. end;
  1538. end;
  1539. raise EIdHTTPProtocolException.CreateError(LResponseCode, FHTTP.ResponseText
  1540. , LRespStream.DataString);
  1541. finally
  1542. Response.ContentStream := LTempStream;
  1543. FreeAndNil(LRespStream);
  1544. end;
  1545. end;
  1546. procedure ReadContent;
  1547. var
  1548. LTempResponse: TStringStream;
  1549. LTempStream: TStream;
  1550. begin
  1551. LTempResponse := TStringStream.Create('');
  1552. LTempStream := Response.ContentStream;
  1553. Response.ContentStream := LTempResponse;
  1554. try
  1555. FHTTP.ReadResult(Response);
  1556. finally
  1557. FreeAndNil(LTempResponse);
  1558. Response.ContentStream := LTempStream;
  1559. end;
  1560. end;
  1561. var
  1562. LLocation: string;
  1563. LMethod: TIdHTTPMethod;
  1564. LNeedAuth: Boolean;
  1565. LResponseDigit: Integer;
  1566. LTemp: Integer;
  1567. begin
  1568. LNeedAuth := False;
  1569. LResponseDigit := Response.ResponseCode div 100;
  1570. // Handle Redirects
  1571. if ((LResponseDigit = 3) and (Response.ResponseCode <> 304)) or (Response.Location <> '') then
  1572. begin
  1573. // LLocation := TIdURI.URLDecode(Response.Location);
  1574. LLocation := Response.Location;
  1575. if (FHTTP.FHandleRedirects) and (FHTTP.FRedirectCount < FHTTP.FRedirectMax) then begin
  1576. LMethod := Request.Method;
  1577. if FHTTP.DoOnRedirect(LLocation, LMethod, FHTTP.FRedirectCount) then begin
  1578. Result := wnGoToURL;
  1579. Request.URL := LLocation;
  1580. // GDG 21/11/2003. If it's a 303, we should do a get this time
  1581. // RLebeau 7/15/2004 - do a GET on 302 as well, as mentioned in RFC 2616
  1582. if (Response.ResponseCode = 302) or (Response.ResponseCode = 303) then begin
  1583. Request.Source := nil;
  1584. Request.Method := hmGet;
  1585. end else begin
  1586. Request.Method := LMethod;
  1587. end;
  1588. end else begin
  1589. CheckException(AIgnoreReplies);
  1590. Result := wnJustExit;
  1591. Exit;
  1592. end;
  1593. // Just fire the event
  1594. end else begin
  1595. LMethod := Request.Method;
  1596. result := wnJustExit;
  1597. // If not Handled
  1598. if not FHTTP.DoOnRedirect(LLocation, LMethod, FHTTP.FRedirectCount) then begin
  1599. CheckException(AIgnoreReplies);
  1600. Result := wnJustExit;
  1601. Exit;
  1602. end else begin
  1603. Response.Location := LLocation;
  1604. end;
  1605. end;
  1606. if FHTTP.Connected then begin
  1607. // This is a workaround for buggy HTTP 1.1 servers which
  1608. // does not return any body with 302 response code
  1609. //
  1610. // Kudzu: Does this need to be duplicated elsewhere? If so it needs to be put in a reuable
  1611. // proc. I removed it in other places as it was somtimes trashing user values and also
  1612. // had no comments.
  1613. LTemp := FHTTP.IOHandler.ReadTimeout; try
  1614. FHTTP.IOHandler.ReadTimeout := 4000; // Lets wait 4 seconds for any kind of content
  1615. try
  1616. ReadContent;
  1617. except end;
  1618. finally FHTTP.IOHandler.ReadTimeout := LTemp; end;
  1619. end;
  1620. end else begin
  1621. // GREGOR Workaround
  1622. // if we get an error we disconnect if we use SSLIOHandler
  1623. if Assigned(FHTTP.IOHandler) then
  1624. begin
  1625. Response.KeepAlive := not (FHTTP.Connected and (FHTTP.IOHandler is TIdSSLIOHandlerSocketBase) and Response.KeepAlive);
  1626. end;
  1627. if LResponseDigit <> 2 then begin
  1628. case Response.ResponseCode of
  1629. 401:
  1630. begin // HTTP Server authorization requered
  1631. if (FHTTP.AuthRetries >= FHTTP.MaxAuthRetries) or
  1632. (not FHTTP.DoOnAuthorization(Request, Response)) then
  1633. begin
  1634. if Assigned(Request.Authentication) then begin
  1635. Request.Authentication.Reset;
  1636. end;
  1637. CheckException(AIgnoreReplies);
  1638. Result := wnJustExit;
  1639. Exit;
  1640. end else begin
  1641. LNeedAuth := hoInProcessAuth in FHTTP.HTTPOptions;
  1642. end;
  1643. end;
  1644. 407:
  1645. begin // Proxy Server authorization requered
  1646. if (FHTTP.AuthProxyRetries >= FHTTP.MaxAuthRetries) or
  1647. (not FHTTP.DoOnProxyAuthorization(Request, Response)) then
  1648. begin
  1649. if Assigned(FHTTP.ProxyParams.Authentication) then begin
  1650. FHTTP.ProxyParams.Authentication.Reset;
  1651. end;
  1652. CheckException(AIgnoreReplies);
  1653. Result := wnJustExit;
  1654. Exit;
  1655. end else begin
  1656. LNeedAuth := hoInProcessAuth in FHTTP.HTTPOptions;
  1657. end;
  1658. end;
  1659. else begin
  1660. CheckException(AIgnoreReplies);
  1661. Result := wnJustExit;
  1662. Exit;
  1663. end;
  1664. end;
  1665. end;
  1666. if LNeedAuth then begin
  1667. // Read the content of Error message in temporary stream
  1668. ReadContent;
  1669. Result := wnAuthRequest
  1670. end else if (Response.ResponseCode <> 204) then begin
  1671. FHTTP.ReadResult(Response);
  1672. Result := wnJustExit;
  1673. end else begin
  1674. Result := wnJustExit;
  1675. end;
  1676. end;
  1677. end;
  1678. procedure TIdCustomHTTP.InitComponent;
  1679. begin
  1680. inherited;
  1681. FURI := TIdURI.Create('');
  1682. FAuthRetries := 0;
  1683. FAuthProxyRetries := 0;
  1684. AllowCookies := true;
  1685. FFreeOnDestroy := false;
  1686. FOptions := [hoForceEncodeParams];
  1687. FRedirectMax := Id_TIdHTTP_RedirectMax;
  1688. FHandleRedirects := Id_TIdHTTP_HandleRedirects;
  1689. //
  1690. FProtocolVersion := Id_TIdHTTP_ProtocolVersion;
  1691. FHTTPProto := TIdHTTPProtocol.Create(Self);
  1692. FProxyParameters := TIdProxyConnectionInfo.Create;
  1693. FProxyParameters.Clear;
  1694. FMaxAuthRetries := Id_TIdHTTP_MaxAuthRetries;
  1695. FMaxHeaderLines := Id_TIdHTTP_MaxHeaderLines;
  1696. end;
  1697. function TIdCustomHTTP.Get(
  1698. AURL: string;
  1699. AIgnoreReplies: array of SmallInt
  1700. ): string;
  1701. var
  1702. LStream: TMemoryStream;
  1703. begin
  1704. LStream := TMemoryStream.Create; try
  1705. Get(AURL, LStream, AIgnoreReplies);
  1706. LStream.Position := 0;
  1707. // This is here instead of a TStringSream for .net conversions?
  1708. Result := ReadStringFromStream(LStream, LStream.Size);
  1709. finally FreeAndNil(LStream); end;
  1710. end;
  1711. procedure TIdCustomHTTP.Get(AURL: string; AResponseContent: TStream;
  1712. AIgnoreReplies: array of SmallInt);
  1713. begin
  1714. DoRequest(hmGet, AURL, nil, AResponseContent, AIgnoreReplies);
  1715. end;
  1716. procedure TIdCustomHTTP.DoRequest(const AMethod: TIdHTTPMethod;
  1717. AURL: string; ASource, AResponseContent: TStream;
  1718. AIgnoreReplies: array of SmallInt);
  1719. var
  1720. LResponseLocation: Integer;
  1721. begin
  1722. if Assigned(AResponseContent) then begin
  1723. LResponseLocation := AResponseContent.Position;
  1724. end else begin
  1725. LResponseLocation := 0; // Just to avoid the waringing message
  1726. end;
  1727. FAuthRetries := 0;
  1728. FAuthProxyRetries := 0;
  1729. Request.URL := AURL;
  1730. Request.Method := AMethod;
  1731. Request.Source := ASource;
  1732. Response.ContentStream := AResponseContent;
  1733. try
  1734. repeat
  1735. Inc(FRedirectCount);
  1736. PrepareRequest(Request);
  1737. if IOHandler is TIdSSLIOHandlerSocketBase then begin
  1738. TIdSSLIOHandlerSocketBase(IOHandler).URIToCheck := FURI.URI;
  1739. end;
  1740. ConnectToHost(Request, Response);
  1741. // Workaround for servers wich respond with 100 Continue on GET and HEAD
  1742. // This workaround is just for temporary use until we have final HTTP 1.1
  1743. // realisation. HTTP 1.1 is ongoing because of all the buggy and conflicting servers.
  1744. repeat
  1745. Response.ResponseText := IOHandler.ReadLn;
  1746. FHTTPProto.RetrieveHeaders(MaxHeaderLines);
  1747. ProcessCookies(Request, Response);
  1748. until Response.ResponseCode <> 100;
  1749. case FHTTPProto.ProcessResponse(AIgnoreReplies) of
  1750. wnAuthRequest: begin
  1751. Dec(FRedirectCount);
  1752. Request.URL := AURL;
  1753. end;
  1754. wnReadAndGo: begin
  1755. ReadResult(Response);
  1756. if Assigned(AResponseContent) then begin
  1757. AResponseContent.Position := LResponseLocation;
  1758. AResponseContent.Size := LResponseLocation;
  1759. end;
  1760. FAuthRetries := 0;
  1761. FAuthProxyRetries := 0;
  1762. end;
  1763. wnGoToURL: begin
  1764. if Assigned(AResponseContent) then begin
  1765. AResponseContent.Position := LResponseLocation;
  1766. AResponseContent.Size := LResponseLocation;
  1767. end;
  1768. FAuthRetries := 0;
  1769. FAuthProxyRetries := 0;
  1770. end;
  1771. wnJustExit: Break;
  1772. wnDontKnow: raise EIdException.Create(RSHTTPNotAcceptable);
  1773. end;
  1774. until False;
  1775. finally
  1776. if not Response.KeepAlive then begin
  1777. Disconnect;
  1778. end;
  1779. end;
  1780. FRedirectCount := 0;
  1781. end;
  1782. end.