IdHTTP.pas 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672
  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: 10191: IdHTTP.pas
  11. {
  12. { Rev 1.8 7/22/04 3:59:44 PM RLebeau
  13. { FindAuthClass() bug fix for TIdCustomHTTP.DoOnProxyAuthorization()
  14. }
  15. {
  16. { Rev 1.7 4/28/04 1:45:54 PM RLebeau
  17. { Updated TIdCustomHTTP.SetRequestParams() to strip off the trailing CRLF
  18. { before encoding rather than afterwards
  19. }
  20. {
  21. { Rev 1.6 29/11/2003 7:37:02 AM GGrieve
  22. { make TIdHTTPHeaderInfo.authentication a property and destroy it
  23. }
  24. {
  25. { Rev 1.5 17.7.2003 ã. 22:27:48 DBondzhev
  26. { Added domain name for authorizing against MS Proxy
  27. }
  28. {
  29. { Rev 1.4 10.7.2003 ã. 20:57:00 DBondzhev
  30. { NTLM AUthentication is working wiht Proxy servers now
  31. }
  32. {
  33. { Rev 1.3 4/30/2003 01:21:30 PM JPMugaas
  34. { Added ConnectTimeout property because ReadTimeout is problematic in HTTP with
  35. { Connect. Discussed that with Kudzu this morning.
  36. }
  37. {
  38. { Rev 1.2 06.3.2003 ã. 20:07:02 DBondzhev
  39. }
  40. {
  41. { Rev 1.1 01.2.2003 ã. 11:54:28 DBondzhev
  42. }
  43. {
  44. { Rev 1.0 2002.11.12 10:41:00 PM czhower
  45. }
  46. unit IdHTTP;
  47. {
  48. Implementation of the HTTP protcol as specified in RFC 2616, 2109, 2965.
  49. (See NOTE below for details of what is exactly implemented)
  50. Author: Hadi Hariri ([email protected])
  51. Copyright: (c) Chad Z. Hower and The Winshoes Working Group.
  52. NOTE:
  53. Initially only GET and POST will be supported. As time goes on more will
  54. be added. For other developers, please add the date and what you have done
  55. below.
  56. Initials: Hadi Hariri - HH
  57. Details of implementation
  58. -------------------------
  59. 2001-Nov Nick Panteleeff
  60. - Authentication and POST parameter extentsions
  61. 2001-Sept Doychin Bondzhev
  62. - New internal design and new Authentication procedures.
  63. - Bug fixes and new features in few other supporting components
  64. 2001-Jul-7 Doychin Bondzhev
  65. - new property AllowCookie
  66. - There is no more ExtraHeders property in Request/Response. Raw headers is used for that purpose.
  67. 2001-Jul-1 Doychin Bondzhev
  68. - SSL support is up again - Thanks to Gregor
  69. 2001-Jun-17 Doychin Bondzhev
  70. - New unit IdHTTPHeaderInfo.pas that contains the
  71. TIdHeaderInfo(TIdEntytiHeaderInfo, TIdRequestHeaderInfo and TIdResponseHeaderInfo)
  72. - Still in development and not verry well tested
  73. By default when there is no authorization object associated with HTTP compoenet and there is user name and password
  74. HTTP component creates and instance of TIdBasicAuthentication class. This behaivor is for both web server and proxy server
  75. authorizations
  76. 2001-Apr-17 Doychin Bondzhev
  77. - Added OnProxyAuthorization event. This event is called on 407 response from the HTTP Proxy.
  78. - Added 2 new properties in TIdHeaderInfo
  79. property AuthenticationScheme: TIdAuthenticationScheme - this property contains information for authentication scheme
  80. requested by the web server
  81. property ProxyAuthenticationScheme: TIdAuthenticationScheme - this property contains information for authentication scheme
  82. requested by the proxy server
  83. - Now the component authomaticly reconginizes the requested authorization scheme and it supports Basic like before and has been
  84. extend to support Digest authorization
  85. 2001-Mar-31 Doychin Bondzhev
  86. - If there is no CookieManager it does not support cookies.
  87. 2001-Feb-18 Doychin Bondzhev
  88. - Added OnAuthorization event. This event is called on 401 response from the HTTP server.
  89. This can be used to ask the user program to supply user name and password in order to acces
  90. the requested resource
  91. 2001-Feb-02 Doychin Bondzhev
  92. - Added Cookie support and relative paths on redirect
  93. 2000-Jul-25 Hadi Hariri
  94. - Overloaded POst and moved clearing to disconect.
  95. 2000-June-22 Hadi Hariri
  96. - Added Proxy support.
  97. 2000-June-10 Hadi Hariri
  98. - Added Chunk-Encoding support and HTTP version number. Some additional
  99. improvements.
  100. 2000-May-23 J. Peter Mugaas
  101. -added redirect capability and supporting properties. Redirect is optional
  102. and is set with HandleRedirects. Redirection is limited to RedirectMaximum
  103. to prevent stack overflow due to recursion and to prevent redirects between
  104. two places which would cause this to go on to infinity.
  105. 2000-May-22 J. Peter Mugaas
  106. -adjusted code for servers which returned LF instead of EOL
  107. -Headers are now retreived before an exception is raised. This
  108. also facilitates server redirection where the server tells the client to
  109. get a document from another location.
  110. 2000-May-01 Hadi Hariri
  111. -Converted to Mercury
  112. 2000-May-01 Hadi Hariri
  113. -Added PostFromStream and some clean up
  114. 2000-Apr-10 Hadi Hariri
  115. -Re-done quite a few things and fixed GET bugs and finished POST method.
  116. 2000-Jan-13 MTL
  117. -Moved to the New Palette Scheme
  118. 2000-Jan-08 MTL
  119. -Cleaned up a few compiler hints during 7.038 build
  120. 1999-Dec-10 Hadi Hariri
  121. -Started.
  122. }
  123. interface
  124. uses
  125. Classes,
  126. IdException, IdAssignedNumbers, IdHeaderList, IdHTTPHeaderInfo, IdSSLOpenSSL,
  127. IdTCPConnection,
  128. IdTCPClient, IdURI, IdCookie, IdCookieManager, IdAuthentication , IdAuthenticationManager,
  129. IdMultipartFormData;
  130. type
  131. // TO DOCUMENTATION TEAM
  132. // ------------------------
  133. // For internal use. No need of documentation
  134. // hmConnect - Used to connect trought CERN proxy to SSL enabled sites.
  135. TIdHTTPMethod = (hmHead, hmGet, hmPost, hmOptions, hmTrace, hmPut, hmDelete, hmConnect);
  136. TIdHTTPWhatsNext = (wnGoToURL, wnJustExit, wnDontKnow, wnReadAndGo, wnAuthRequest);
  137. TIdHTTPConnectionType = (ctNormal, ctSSL, ctProxy, ctSSLProxy);
  138. // Protocol options
  139. TIdHTTPOption = (hoInProcessAuth, hoKeepOrigProtocol, hoForceEncodeParams);
  140. TIdHTTPOptions = set of TIdHTTPOption;
  141. // Must be documented
  142. TIdHTTPProtocolVersion = (pv1_0, pv1_1);
  143. TIdHTTPOnHeadersAvailable = procedure(Sender: TObject; AHeaders: TIdHeaderList; var VContinue: Boolean) of object;
  144. TIdHTTPOnRedirectEvent = procedure(Sender: TObject; var dest: string; var NumRedirect: Integer; var Handled: boolean; var VMethod: TIdHTTPMethod) of object;
  145. TIdOnSelectAuthorization = procedure(Sender: TObject; var AuthenticationClass: TIdAuthenticationClass; AuthInfo: TIdHeaderList) of object;
  146. TIdOnAuthorization = procedure(Sender: TObject; Authentication: TIdAuthentication; var Handled: boolean) of object;
  147. // TIdProxyOnAuthorization = procedure(Sender: TObject; Authentication: TIdAuthentication; var Handled: boolean) of object;
  148. const
  149. Id_TIdHTTP_ProtocolVersion = pv1_1;
  150. Id_TIdHTTP_RedirectMax = 15;
  151. Id_TIdHTTP_HandleRedirects = False;
  152. type
  153. TIdCustomHTTP = class;
  154. // TO DOCUMENTATION TEAM
  155. // ------------------------
  156. // The following classes are used internally and no need of documentation
  157. // Only TIdHTTP must be documented
  158. //
  159. TIdHTTPResponse = class(TIdResponseHeaderInfo)
  160. protected
  161. FHTTP: TIdCustomHTTP;
  162. FResponseCode: Integer;
  163. FResponseText: string;
  164. FKeepAlive: Boolean;
  165. FContentStream: TStream;
  166. FResponseVersion: TIdHTTPProtocolVersion;
  167. //
  168. function GetKeepAlive: Boolean;
  169. function GetResponseCode: Integer;
  170. public
  171. constructor Create(AParent: TIdCustomHTTP); reintroduce; virtual;
  172. property KeepAlive: Boolean read GetKeepAlive write FKeepAlive;
  173. property ResponseText: string read FResponseText write FResponseText;
  174. property ResponseCode: Integer read GetResponseCode write FResponseCode;
  175. property ResponseVersion: TIdHTTPProtocolVersion read FResponseVersion write FResponseVersion;
  176. property ContentStream: TStream read FContentStream write FContentStream;
  177. end;
  178. TIdHTTPRequest = class(TIdRequestHeaderInfo)
  179. protected
  180. FHTTP: TIdCustomHTTP;
  181. FURL: string;
  182. FMethod: TIdHTTPMethod;
  183. FSourceStream: TStream;
  184. FUseProxy: TIdHTTPConnectionType;
  185. public
  186. constructor Create(AHTTP: TIdCustomHTTP); reintroduce; virtual;
  187. property URL: string read FURL write FURL;
  188. property Method: TIdHTTPMethod read FMethod write FMethod;
  189. property Source: TStream read FSourceStream write FSourceStream;
  190. property UseProxy: TIdHTTPConnectionType read FUseProxy;
  191. end;
  192. TIdHTTPProtocol = class(TObject)
  193. FHTTP: TIdCustomHTTP;
  194. FRequest: TIdHTTPRequest;
  195. FResponse: TIdHTTPResponse;
  196. public
  197. constructor Create(AConnection: TIdCustomHTTP);
  198. destructor Destroy; override;
  199. function ProcessResponse: TIdHTTPWhatsNext;
  200. procedure BuildAndSendRequest(AURI: TIdURI);
  201. procedure RetrieveHeaders;
  202. property Request: TIdHTTPRequest read FRequest;
  203. property Response: TIdHTTPResponse read FResponse;
  204. end;
  205. TIdCustomHTTP = class(TIdTCPClient)
  206. protected
  207. FCookieManager: TIdCookieManager;
  208. FFreeOnDestroy: Boolean;
  209. {Max retries for authorization}
  210. FMaxAuthRetries: Integer;
  211. FAllowCookies: Boolean;
  212. FAuthenticationManager: TIdAuthenticationManager;
  213. FProtocolVersion: TIdHTTPProtocolVersion;
  214. {this is an internal counter for redirercts}
  215. FRedirectCount: Integer;
  216. FRedirectMax: Integer;
  217. FHandleRedirects: Boolean;
  218. FOptions: TIdHTTPOptions;
  219. FURI: TIdURI;
  220. FHTTPProto: TIdHTTPProtocol;
  221. FProxyParameters: TIdProxyConnectionInfo;
  222. //
  223. FOnHeadersAvailable: TIdHTTPOnHeadersAvailable;
  224. FOnRedirect: TIdHTTPOnRedirectEvent;
  225. FOnSelectAuthorization: TIdOnSelectAuthorization;
  226. FOnSelectProxyAuthorization: TIdOnSelectAuthorization;
  227. FOnAuthorization: TIdOnAuthorization;
  228. FOnProxyAuthorization: TIdOnAuthorization;
  229. FConnectTimeout : Integer;
  230. //
  231. procedure SetHost(const Value: string); override;
  232. procedure SetPort(const Value: integer); override;
  233. procedure SetAuthenticationManager(const Value: TIdAuthenticationManager);
  234. procedure SetCookieManager(ACookieManager: TIdCookieManager);
  235. procedure SetAllowCookies(AValue: Boolean);
  236. function GetResponseCode: Integer;
  237. function GetResponseText: string;
  238. function DoOnAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; virtual;
  239. function DoOnProxyAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean; virtual;
  240. function DoOnRedirect(var Location: string; var VMethod: TIdHTTPMethod; RedirectCount: integer): boolean; virtual;
  241. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  242. procedure ProcessCookies(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  243. function SetHostAndPort: TIdHTTPConnectionType;
  244. procedure SetCookies(AURL: TIdURI; ARequest: TIdHTTPRequest);
  245. procedure ReadResult(AResponse: TIdHTTPResponse);
  246. procedure PrepareRequest(ARequest: TIdHTTPRequest);
  247. procedure ConnectToHost(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  248. function GetResponseHeaders: TIdHTTPResponse;
  249. function GetRequestHeaders: TIdHTTPRequest;
  250. procedure SetRequestHeaders(const Value: TIdHTTPRequest);
  251. procedure EncodeRequestParams(const AStrings: TStrings);
  252. function SetRequestParams(const AStrings: TStrings): string;
  253. procedure CheckAndConnect(AResponse: TIdHTTPResponse);
  254. procedure DoOnDisconnected; override;
  255. function GetAuthRetries: Integer;
  256. function GetProxyAuthRetries: Integer;
  257. property InternalAuthRetries: Integer read GetAuthRetries;
  258. property InternalProxyAuthRetries: Integer read GetProxyAuthRetries;
  259. public
  260. constructor Create(AOwner: TComponent); override;
  261. destructor Destroy; override;
  262. procedure DoRequest(const AMethod: TIdHTTPMethod; AURL: string;
  263. const ASource, AResponseContent: TStream); virtual;
  264. procedure Options(AURL: string); overload;
  265. procedure Get(AURL: string; const AResponseContent: TStream); overload;
  266. function Get(AURL: string): string; overload;
  267. procedure Trace(AURL: string; const AResponseContent: TStream); overload;
  268. function Trace(AURL: string): string; overload;
  269. procedure Head(AURL: string);
  270. function Post(AURL: string; const ASource: TStrings): string; overload;
  271. function Post(AURL: string; const ASource: TStream): string; overload;
  272. function Post(AURL: string; const ASource: TIdMultiPartFormDataStream): string; overload;
  273. procedure Post(AURL: string; const ASource: TStrings; const AResponseContent: TStream);
  274. overload;
  275. {Post data provided by a stream, this is for submitting data to a server}
  276. procedure Post(AURL: string; const ASource, AResponseContent: TStream);
  277. overload;
  278. procedure Post(AURL: string; const ASource: TIdMultiPartFormDataStream; AResponseContent: TStream);
  279. overload;
  280. //
  281. function Put(AURL: string; const ASource: TStream): string; overload;
  282. procedure Put(AURL: string; const ASource, AResponseContent: TStream);
  283. overload;
  284. {This is the response code number such as 404 for File not Found}
  285. property ResponseCode: Integer read GetResponseCode;
  286. {This is the text of the message such as "404 File Not Found here Sorry"}
  287. property ResponseText: string read GetResponseText;
  288. property Response: TIdHTTPResponse read GetResponseHeaders;
  289. { This is the last processed URL }
  290. property URL: TIdURI read FURI;
  291. // Num retries for Authentication
  292. property AuthRetries: Integer read FMaxAuthRetries write FMaxAuthRetries default 3;
  293. property AllowCookies: Boolean read FAllowCookies write SetAllowCookies;
  294. {Do we handle redirect requests or simply raise an exception and let the
  295. developer deal with it}
  296. property HandleRedirects: Boolean read FHandleRedirects write FHandleRedirects default Id_TIdHTTP_HandleRedirects;
  297. property ProtocolVersion: TIdHTTPProtocolVersion read FProtocolVersion write FProtocolVersion default Id_TIdHTTP_ProtocolVersion;
  298. {This is the maximum number of redirects we wish to handle, we limit this
  299. to prevent stack overflow due to recursion. Recursion is safe ONLY if
  300. prevented for continuing to infinity}
  301. property RedirectMaximum: Integer read FRedirectMax write FRedirectMax default Id_TIdHTTP_RedirectMax;
  302. property ProxyParams: TIdProxyConnectionInfo read FProxyParameters write FProxyParameters;
  303. property Request: TIdHTTPRequest read GetRequestHeaders write SetRequestHeaders;
  304. property HTTPOptions: TIdHTTPOptions read FOptions write FOptions;
  305. property OnHeadersAvailable: TIdHTTPOnHeadersAvailable read FOnHeadersAvailable write FOnHeadersAvailable;
  306. // Fired when a rediretion is requested.
  307. property OnRedirect: TIdHTTPOnRedirectEvent read FOnRedirect write FOnRedirect;
  308. property OnSelectAuthorization: TIdOnSelectAuthorization read FOnSelectAuthorization write FOnSelectAuthorization;
  309. property OnSelectProxyAuthorization: TIdOnSelectAuthorization read FOnSelectProxyAuthorization write FOnSelectProxyAuthorization;
  310. property OnAuthorization: TIdOnAuthorization read FOnAuthorization write FOnAuthorization;
  311. property OnProxyAuthorization: TIdOnAuthorization read FOnProxyAuthorization write FOnProxyAuthorization;
  312. // Cookie stuff
  313. property CookieManager: TIdCookieManager read FCookieManager write SetCookieManager;
  314. //
  315. property AuthenticationManager: TIdAuthenticationManager read FAuthenticationManager write SetAuthenticationManager;
  316. property ConnectTimeout : Integer read FConnectTimeout write FConnectTimeout default IdDefTimeout;
  317. end;
  318. TIdHTTP = class(TIdCustomHTTP)
  319. published
  320. // Num retries for Authentication
  321. property AuthRetries;
  322. property AllowCookies;
  323. {Do we handle redirect requests or simply raise an exception and let the
  324. developer deal with it}
  325. property HandleRedirects;
  326. property ProtocolVersion;
  327. {This is the maximum number of redirects we wish to handle, we limit this
  328. to prevent stack overflow due to recursion. Recursion is safe ONLY if
  329. prevented for continuing to infinity}
  330. property RedirectMaximum;
  331. property ProxyParams;
  332. property Request;
  333. property HTTPOptions;
  334. property OnHeadersAvailable;
  335. // Fired when a rediretion is requested.
  336. property OnRedirect;
  337. property OnSelectAuthorization;
  338. property OnSelectProxyAuthorization;
  339. property OnAuthorization;
  340. property OnProxyAuthorization;
  341. property Host;
  342. property Port default IdPORT_HTTP;
  343. // Cookie stuff
  344. property CookieManager;
  345. //
  346. // property AuthenticationManager: TIdAuthenticationManager read FAuthenticationManager write SetAuthenticationManager;
  347. property ConnectTimeout;
  348. end;
  349. EIdUnknownProtocol = class(EIdException);
  350. EIdHTTPProtocolException = class(EIdProtocolReplyError)
  351. protected
  352. FErrorMessage: string;
  353. public
  354. constructor CreateError(const anErrCode: Integer; const asReplyMessage: string;
  355. const asErrorMessage: string); reintroduce; virtual;
  356. property ErrorMessage: string read FErrorMessage;
  357. end;
  358. implementation
  359. uses
  360. SysUtils,
  361. IdGlobal, IdComponent, IdCoderMIME, IdResourceStrings;
  362. const
  363. ProtocolVersionString: array[TIdHTTPProtocolVersion] of string = ('1.0', '1.1'); {do not localize}
  364. MethodString: array[TIdHTTPMethod] of String = ('HEAD', 'GET', 'POST', 'OPTIONS', 'TRACE', 'PUT', 'DELETE', 'CONNECT'); {do not localize}
  365. { EIdHTTPProtocolException }
  366. constructor EIdHTTPProtocolException.CreateError(const anErrCode: Integer;
  367. const asReplyMessage: string; const asErrorMessage: string);
  368. begin
  369. inherited CreateError(anErrCode, asReplyMessage);
  370. FErrorMessage := asErrorMessage;
  371. end;
  372. { TIdHTTP }
  373. constructor TIdCustomHTTP.Create(AOwner: TComponent);
  374. begin
  375. FURI := TIdURI.Create('');
  376. inherited Create(AOwner);
  377. Port := IdPORT_HTTP;
  378. FMaxAuthRetries := 3;
  379. AllowCookies := true;
  380. FFreeOnDestroy := false;
  381. FOptions := [hoForceEncodeParams];
  382. FRedirectMax := Id_TIdHTTP_RedirectMax;
  383. FHandleRedirects := Id_TIdHTTP_HandleRedirects;
  384. //
  385. FProtocolVersion := Id_TIdHTTP_ProtocolVersion;
  386. FHTTPProto := TIdHTTPProtocol.Create(self);
  387. FProxyParameters := TIdProxyConnectionInfo.Create;
  388. FProxyParameters.Clear;
  389. FConnectTimeout := IdDefTimeout;
  390. end;
  391. destructor TIdCustomHTTP.Destroy;
  392. begin
  393. FreeAndNil(FHTTPProto);
  394. FreeAndNil(FURI);
  395. FreeAndNil(FProxyParameters);
  396. {if FFreeOnDestroy then
  397. begin
  398. FreeAndNil(FCookieManager);
  399. end;}
  400. inherited Destroy;
  401. end;
  402. procedure TIdCustomHTTP.Options(AURL: string);
  403. begin
  404. DoRequest(hmOptions, AURL, nil, nil);
  405. end;
  406. procedure TIdCustomHTTP.Get(AURL: string; const AResponseContent: TStream);
  407. begin
  408. DoRequest(hmGet, AURL, nil, AResponseContent);
  409. end;
  410. procedure TIdCustomHTTP.Trace(AURL: string; const AResponseContent: TStream);
  411. begin
  412. DoRequest(hmTrace, AURL, nil, AResponseContent);
  413. end;
  414. procedure TIdCustomHTTP.Head(AURL: string);
  415. begin
  416. DoRequest(hmHead, AURL, nil, nil);
  417. end;
  418. procedure TIdCustomHTTP.Post(AURL: string; const ASource, AResponseContent: TStream);
  419. var
  420. OldProtocol: TIdHTTPProtocolVersion;
  421. begin
  422. // PLEASE READ CAREFULLY
  423. // Currently when issuing a POST, IdHTTP will automatically set the protocol
  424. // to version 1.0 independently of the value it had initially. This is because
  425. // there are some servers that don't respect the RFC to the full extent. In
  426. // particular, they don't respect sending/not sending the Expect: 100-Continue
  427. // header. Until we find an optimum solution that does NOT break the RFC, we
  428. // will restrict POSTS to version 1.0.
  429. if Connected then
  430. begin
  431. Disconnect;
  432. end;
  433. OldProtocol := FProtocolVersion;
  434. // If hoKeepOrigProtocol is SET, is possible to assume that the developer
  435. // is sure in operations of the server
  436. if not (hoKeepOrigProtocol in FOptions) then
  437. FProtocolVersion := pv1_0;
  438. DoRequest(hmPost, AURL, ASource, AResponseContent);
  439. FProtocolVersion := OldProtocol;
  440. end;
  441. procedure TIdCustomHTTP.EncodeRequestParams(const AStrings: TStrings);
  442. var
  443. i: Integer;
  444. S: string;
  445. begin
  446. for i := 0 to AStrings.Count - 1 do begin
  447. S := AStrings.Names[i];
  448. if Length(AStrings.Values[S]) > 0 then begin
  449. AStrings.Values[S] := TIdURI.ParamsEncode(AStrings.Values[S]);
  450. end;
  451. end;
  452. end;
  453. function TIdCustomHTTP.SetRequestParams(const AStrings: TStrings): string;
  454. begin
  455. if Assigned(AStrings) then begin
  456. if hoForceEncodeParams in FOptions then begin
  457. EncodeRequestParams(AStrings);
  458. end;
  459. if AStrings.Count > 1 then begin
  460. // break trailing CR&LF
  461. Result := StringReplace(Trim(AStrings.Text), sLineBreak, '&', [rfReplaceAll])
  462. end else begin
  463. Result := Trim(AStrings.Text);
  464. end;
  465. end else begin
  466. Result := '';
  467. end;
  468. end;
  469. procedure TIdCustomHTTP.Post(AURL: string; const ASource: TStrings; const AResponseContent: TStream);
  470. var
  471. LParams: TStringStream;
  472. begin
  473. // Usual posting request have default ContentType is application/x-www-form-urlencoded
  474. if (Request.ContentType = '') or (AnsiSameText(Request.ContentType, 'text/html')) then
  475. Request.ContentType := 'application/x-www-form-urlencoded';
  476. LParams := TStringStream.Create(SetRequestParams(ASource));
  477. try
  478. Post(AURL, LParams, AResponseContent);
  479. finally
  480. LParams.Free;
  481. end;
  482. end;
  483. function TIdCustomHTTP.Post(AURL: string; const ASource: TStrings): string;
  484. var
  485. LResponse: TStringStream;
  486. begin
  487. LResponse := TStringStream.Create('');
  488. try
  489. Post(AURL, ASource, LResponse);
  490. finally
  491. Result := LResponse.DataString;
  492. LResponse.Free;
  493. end;
  494. end;
  495. function TIdCustomHTTP.Post(AURL: string; const ASource: TStream): string;
  496. var
  497. LResponse: TStringStream;
  498. begin
  499. LResponse := TStringStream.Create('');
  500. try
  501. Post(AURL, ASource, LResponse);
  502. finally
  503. result := LResponse.DataString;
  504. LResponse.Free;
  505. end;
  506. end;
  507. procedure TIdCustomHTTP.Put(AURL: string; const ASource, AResponseContent: TStream);
  508. begin
  509. DoRequest(hmPut, AURL, ASource, AResponseContent);
  510. end;
  511. function TIdCustomHTTP.Put(AURL: string; const ASource: TStream): string;
  512. var
  513. LResponse: TStringStream;
  514. begin
  515. LResponse := TStringStream.Create('');
  516. try
  517. Put(AURL, ASource, LResponse);
  518. finally
  519. result := LResponse.DataString;
  520. LResponse.Free;
  521. end;
  522. end;
  523. function TIdCustomHTTP.Get(AURL: string): string;
  524. var
  525. Stream: TMemoryStream;
  526. begin
  527. Stream := TMemoryStream.Create;
  528. try
  529. Get(AURL, Stream);
  530. finally
  531. if Stream.Size > 0 then // DO we have result?
  532. begin
  533. SetLength(result, Stream.Size);
  534. Move(PChar(Stream.Memory)^, result[1], Stream.Size);
  535. end;
  536. Stream.Free;
  537. end;
  538. end;
  539. function TIdCustomHTTP.Trace(AURL: string): string;
  540. var
  541. Stream: TStringStream;
  542. begin
  543. Stream := TStringStream.Create(''); try
  544. Trace(AURL, Stream);
  545. result := Stream.DataString;
  546. finally Stream.Free; end;
  547. end;
  548. function TIdCustomHTTP.DoOnRedirect(var Location: string; var VMethod: TIdHTTPMethod; RedirectCount: integer): boolean;
  549. begin
  550. result := HandleRedirects;
  551. if assigned(FOnRedirect) then
  552. begin
  553. FOnRedirect(self, Location, RedirectCount, result, VMethod);
  554. end;
  555. end;
  556. procedure TIdCustomHTTP.SetCookies(AURL: TIdURI; ARequest: TIdHTTPRequest);
  557. var
  558. S: string;
  559. begin
  560. if Assigned(FCookieManager) then
  561. begin
  562. // Send secure cookies only if we have Secured connection
  563. S := FCookieManager.GenerateCookieList(AURL, (IOHandler is TIdSSLIOHandlerSocket));
  564. if Length(S) > 0 then
  565. begin
  566. ARequest.RawHeaders.Values['Cookie'] := S;
  567. end;
  568. end;
  569. end;
  570. // This function sets the Host and Port and returns a boolean depending on
  571. // whether a PROXY is being used or not.
  572. function TIdCustomHTTP.SetHostAndPort: TIdHTTPConnectionType;
  573. begin
  574. // First check to see if a Proxy has been specified.
  575. if Length(ProxyParams.ProxyServer) > 0 then
  576. begin
  577. if ((not AnsiSameText(Host, ProxyParams.ProxyServer)) or
  578. (Port <> ProxyParams.ProxyPort)) and (Connected) then
  579. begin
  580. Disconnect;
  581. end;
  582. FHost := ProxyParams.ProxyServer;
  583. FPort := ProxyParams.ProxyPort;
  584. if AnsiSameText(URL.Protocol, 'HTTPS') then
  585. begin
  586. Result := ctSSLProxy;
  587. if Assigned(IOHandler) then
  588. begin
  589. if not (IOHandler is TIdSSLIOHandlerSocket) then
  590. begin
  591. raise EIdIOHandlerPropInvalid.Create(RSIOHandlerPropInvalid);
  592. end else begin
  593. (IOHandler as TIdSSLIOHandlerSocket).PassThrough := true;
  594. end;
  595. end;
  596. end
  597. else begin
  598. Result := ctProxy;
  599. if Assigned(IOHandler) and (IOHandler is TIdSSLIOHandlerSocket) then
  600. begin
  601. (IOHandler as TIdSSLIOHandlerSocket).PassThrough := true;
  602. end;
  603. end;
  604. end
  605. else begin
  606. Result := ctNormal;
  607. if ((not AnsiSameText(Host, URL.Host)) or (Port <> StrToInt(URL.Port))) then begin
  608. if Connected then begin
  609. Disconnect;
  610. end;
  611. Host := URL.Host;
  612. Port := StrToInt(URL.Port);
  613. end;
  614. if AnsiSameText(URL.Protocol, 'HTTPS') then
  615. begin
  616. // Just check can we do SSL
  617. if not Assigned(IOHandler) or (not (IOHandler is TIdSSLIOHandlerSocket)) then
  618. raise EIdIOHandlerPropInvalid.Create(RSIOHandlerPropInvalid)
  619. else begin
  620. (IOHandler as TIdSSLIOHandlerSocket).PassThrough := false;
  621. result := ctSSL;
  622. end;
  623. end
  624. else
  625. begin
  626. if Assigned(IOHandler) then
  627. begin
  628. if (IOHandler is TIdSSLIOHandlerSocket) then
  629. begin
  630. (IOHandler as TIdSSLIOHandlerSocket).PassThrough := true;
  631. end;
  632. end;
  633. end;
  634. end;
  635. end;
  636. procedure TIdCustomHTTP.ReadResult(AResponse: TIdHTTPResponse);
  637. var
  638. Size: Integer;
  639. function ChunkSize: integer;
  640. var
  641. j: Integer;
  642. s: string;
  643. begin
  644. s := ReadLn;
  645. j := AnsiPos(' ', s);
  646. if j > 0 then
  647. begin
  648. s := Copy(s, 1, j - 1);
  649. end;
  650. Result := StrToIntDef('$' + s, 0);
  651. end;
  652. begin
  653. if Assigned(AResponse.ContentStream) then // Only for Get and Post
  654. begin
  655. if AResponse.ContentLength > 0 then // If chunked then this is also 0
  656. begin
  657. try
  658. ReadStream(AResponse.ContentStream, AResponse.ContentLength);
  659. except
  660. on E: EIdConnClosedGracefully do
  661. end;
  662. end
  663. else
  664. begin
  665. if AnsiPos('chunked', LowerCase(AResponse.RawHeaders.Values['Transfer-Encoding'])) > 0 then {do not localize}
  666. begin // Chunked
  667. DoStatus(hsStatusText, [RSHTTPChunkStarted]);
  668. Size := ChunkSize;
  669. while Size > 0 do
  670. begin
  671. ReadStream(AResponse.ContentStream, Size);
  672. ReadLn; // blank line
  673. Size := ChunkSize;
  674. end;
  675. ReadLn; // blank line
  676. end
  677. else begin
  678. if not AResponse.HasContentLength then
  679. ReadStream(AResponse.ContentStream, -1, True);
  680. end;
  681. end;
  682. end;
  683. end;
  684. procedure TIdCustomHTTP.PrepareRequest(ARequest: TIdHTTPRequest);
  685. var
  686. LURI: TIdURI;
  687. begin
  688. LURI := TIdURI.Create(ARequest.URL);
  689. if Length(LURI.Username) > 0 then
  690. begin
  691. ARequest.Username := LURI.Username;
  692. ARequest.Password := LURI.Password;
  693. end;
  694. FURI.Username := ARequest.Username;
  695. FURI.Password := ARequest.Password;
  696. FURI.Path := ProcessPath(FURI.Path, LURI.Path);
  697. FURI.Document := LURI.Document;
  698. FURI.Params := LURI.Params;
  699. if Length(LURI.Host) > 0 then begin
  700. FURI.Host := LURI.Host;
  701. end;
  702. if Length(LURI.Protocol) > 0 then begin
  703. FURI.Protocol := LURI.Protocol;
  704. end else begin
  705. FURI.Protocol := 'http';
  706. end;
  707. if Length(LURI.Port) > 0 then begin
  708. FURI.Port := LURI.Port;
  709. end
  710. else begin
  711. if AnsiSameText(LURI.Protocol, 'http') then begin
  712. FURI.Port := IntToStr(IdPORT_HTTP);
  713. end else begin
  714. if AnsiSameText(LURI.Protocol, 'https') then begin
  715. FURI.Port := IntToStr(IdPORT_SSL);
  716. end else begin
  717. if Length(FURI.Port) > 0 then begin
  718. { FURI.Port:=FURI.Port; } // do nothing, as the port is already filled in.
  719. end else begin
  720. raise EIdUnknownProtocol.Create('');
  721. end;
  722. end;
  723. end;
  724. end;
  725. // The URL part is not URL encoded at this place
  726. ARequest.URL := URL.Path + URL.Document + URL.Params;
  727. if ARequest.Method = hmOptions then
  728. begin
  729. if AnsiSameText(LURI.Document, '*') then
  730. begin
  731. ARequest.URL := LURI.Document;
  732. end;
  733. end;
  734. LURI.Free; // Free URI Object;
  735. // Check for valid HTTP request methods
  736. if ARequest.Method in [hmTrace, hmPut, hmOptions, hmDelete] then
  737. begin
  738. if ProtocolVersion <> pv1_1 then
  739. begin
  740. raise EIdException.Create('This request method is supported in HTTP 1.1');
  741. end;
  742. end;
  743. if ARequest.Method in [hmPost, hmPut] then
  744. begin
  745. ARequest.ContentLength := ARequest.Source.Size;
  746. end
  747. else ARequest.ContentLength := -1;
  748. if FURI.Port <> IntToStr(IdPORT_HTTP) then
  749. ARequest.Host := FURI.Host + ':' + FURI.Port
  750. else
  751. ARequest.Host := FURI.Host;
  752. end;
  753. procedure TIdCustomHTTP.CheckAndConnect(AResponse: TIdHTTPResponse);
  754. begin
  755. if not AResponse.KeepAlive then begin
  756. Disconnect;
  757. end;
  758. CheckForGracefulDisconnect(false);
  759. if not Connected then try
  760. Connect(FConnectTimeout);
  761. except
  762. on E: EIdSSLProtocolReplyError do
  763. begin
  764. Disconnect;
  765. raise;
  766. end;
  767. end;
  768. end;
  769. procedure TIdCustomHTTP.ConnectToHost(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  770. var
  771. LLocalHTTP: TIdHTTPProtocol;
  772. begin
  773. ARequest.FUseProxy := SetHostAndPort;
  774. if ARequest.UseProxy = ctProxy then
  775. begin
  776. ARequest.URL := FURI.URI;
  777. end;
  778. case ARequest.UseProxy of
  779. ctNormal:
  780. if (ProtocolVersion = pv1_0) and (ARequest.Connection = '') then
  781. ARequest.Connection := 'keep-alive';
  782. ctSSL, ctSSLProxy: ARequest.Connection := '';
  783. ctProxy:
  784. if (ProtocolVersion = pv1_0) and (ARequest.Connection = '') then
  785. ARequest.ProxyConnection := 'keep-alive';
  786. end;
  787. if ARequest.UseProxy = ctSSLProxy then begin
  788. LLocalHTTP := TIdHTTPProtocol.Create(Self);
  789. with LLocalHTTP do begin
  790. Request.UserAgent := ARequest.UserAgent;
  791. Request.Host := ARequest.Host;
  792. Request.ContentLength := ARequest.ContentLength;
  793. Request.Pragma := 'no-cache';
  794. Request.URL := URL.Host + ':' + URL.Port;
  795. Request.Method := hmConnect;
  796. Request.ProxyConnection := 'keep-alive';
  797. Response.ContentStream := TMemoryStream.Create;
  798. try
  799. try
  800. repeat
  801. CheckAndConnect(Response);
  802. BuildAndSendRequest(nil);
  803. Response.ResponseText := ReadLn;
  804. if Length(Response.ResponseText) = 0 then begin
  805. Response.ResponseText := 'HTTP/1.0 200 OK'; // Support for HTTP responses whithout Status line and headers
  806. Response.Connection := 'close';
  807. end
  808. else begin
  809. RetrieveHeaders;
  810. ProcessCookies(LLocalHTTP.Request, LLocalHTTP.Response);
  811. end;
  812. if Response.ResponseCode = 200 then
  813. begin
  814. // Connection established
  815. (IOHandler as TIdSSLIOHandlerSocket).PassThrough := False;
  816. Break;
  817. end
  818. else begin
  819. ProcessResponse;
  820. end;
  821. until false;
  822. except
  823. raise;
  824. // TODO: Add property that will contain the error messages.
  825. end;
  826. finally
  827. LLocalHTTP.Response.ContentStream.Free;
  828. LLocalHTTP.Free;
  829. end;
  830. end;
  831. end
  832. else begin
  833. CheckAndConnect(AResponse);
  834. end;
  835. FHTTPProto.BuildAndSendRequest(URL);
  836. if (ARequest.Method in [hmPost, hmPut]) then
  837. begin
  838. WriteStream(ARequest.Source, True, false);
  839. end;
  840. end;
  841. procedure TIdCustomHTTP.DoRequest(const AMethod: TIdHTTPMethod; AURL: string;
  842. const ASource, AResponseContent: TStream);
  843. var
  844. LResponseLocation: Integer;
  845. begin
  846. if Assigned(AResponseContent) then
  847. begin
  848. LResponseLocation := AResponseContent.Position;
  849. end
  850. else
  851. LResponseLocation := 0; // Just to avoid the waringing message
  852. Request.URL := AURL;
  853. Request.Method := AMethod;
  854. Request.Source := ASource;
  855. Response.ContentStream := AResponseContent;
  856. try
  857. repeat
  858. Inc(FRedirectCount);
  859. PrepareRequest(Request);
  860. ConnectToHost(Request, Response);
  861. // Workaround for servers wich respond with 100 Continue on GET and HEAD
  862. // This workaround is just for temporary use until we have final HTTP 1.1
  863. // realisation
  864. repeat
  865. Response.ResponseText := ReadLn;
  866. FHTTPProto.RetrieveHeaders;
  867. ProcessCookies(Request, Response);
  868. until Response.ResponseCode <> 100;
  869. case FHTTPProto.ProcessResponse of
  870. wnAuthRequest: begin
  871. Dec(FRedirectCount);
  872. Request.URL := AURL;
  873. end;
  874. wnReadAndGo: begin
  875. ReadResult(Response);
  876. if Assigned(AResponseContent) then
  877. begin
  878. AResponseContent.Position := LResponseLocation;
  879. AResponseContent.Size := LResponseLocation;
  880. end;
  881. end;
  882. wnGoToURL: begin
  883. if Assigned(AResponseContent) then
  884. begin
  885. AResponseContent.Position := LResponseLocation;
  886. AResponseContent.Size := LResponseLocation;
  887. end;
  888. end;
  889. wnJustExit: begin
  890. break;
  891. end;
  892. wnDontKnow:
  893. // TODO: This is for temporary use. Will remove it for final release
  894. raise EIdException.Create('Undefined situation');
  895. end;
  896. until false;
  897. finally
  898. if not Response.KeepAlive then begin
  899. Disconnect;
  900. end;
  901. end;
  902. FRedirectCount := 0;
  903. end;
  904. procedure TIdCustomHTTP.SetAllowCookies(AValue: Boolean);
  905. begin
  906. FAllowCookies := AValue;
  907. end;
  908. procedure TIdCustomHTTP.ProcessCookies(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse);
  909. var
  910. Cookies, Cookies2: TStringList;
  911. i: Integer;
  912. begin
  913. Cookies := nil;
  914. Cookies2 := nil;
  915. try
  916. if not Assigned(FCookieManager) and AllowCookies then
  917. begin
  918. CookieManager := TIdCookieManager.Create(Self);
  919. FFreeOnDestroy := true;
  920. end;
  921. if Assigned(FCookieManager) then
  922. begin
  923. Cookies := TStringList.Create;
  924. Cookies2 := TStringList.Create;
  925. AResponse.RawHeaders.Extract('Set-cookie', Cookies);
  926. AResponse.RawHeaders.Extract('Set-cookie2', Cookies2);
  927. for i := 0 to Cookies.Count - 1 do
  928. CookieManager.AddCookie(Cookies[i], FURI.Host);
  929. for i := 0 to Cookies2.Count - 1 do
  930. CookieManager.AddCookie2(Cookies2[i], FURI.Host);
  931. end;
  932. finally
  933. FreeAndNil(Cookies);
  934. FreeAndNil(Cookies2);
  935. end;
  936. end;
  937. procedure TIdCustomHTTP.Notification(AComponent: TComponent; Operation: TOperation);
  938. begin
  939. inherited Notification(AComponent, Operation);
  940. if Operation = opRemove then
  941. begin
  942. if (AComponent = FCookieManager) then
  943. begin
  944. FCookieManager := nil;
  945. end;
  946. if AComponent = FAuthenticationManager then
  947. begin
  948. FAuthenticationManager := nil;
  949. end;
  950. end;
  951. end;
  952. procedure TIdCustomHTTP.SetCookieManager(ACookieManager: TIdCookieManager);
  953. begin
  954. if Assigned(FCookieManager) then
  955. begin
  956. if FFreeOnDestroy then begin
  957. FCookieManager.Free;
  958. end;
  959. end;
  960. FCookieManager := ACookieManager;
  961. FFreeOnDestroy := false;
  962. if Assigned(FCookieManager) then
  963. begin
  964. FCookieManager.FreeNotification(Self);
  965. end;
  966. end;
  967. function TIdCustomHTTP.DoOnAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean;
  968. var
  969. i: Integer;
  970. S: string;
  971. Auth: TIdAuthenticationClass;
  972. begin
  973. if not Assigned(ARequest.Authentication) then
  974. begin
  975. // Find wich Authentication method is supported from us.
  976. Auth := nil;
  977. for i := 0 to AResponse.WWWAuthenticate.Count - 1 do
  978. begin
  979. S := AResponse.WWWAuthenticate[i];
  980. Auth := FindAuthClass(Fetch(S));
  981. if Auth <> nil then begin
  982. Break;
  983. end;
  984. end;
  985. if Auth = nil then begin
  986. Result := False;
  987. Exit;
  988. end;
  989. if Assigned(FOnSelectAuthorization) then begin
  990. OnSelectAuthorization(Self, Auth, AResponse.WWWAuthenticate);
  991. end;
  992. ARequest.Authentication := Auth.Create;
  993. end;
  994. // Clear password and reset autorization if previous failed
  995. {if (AResponse.FResponseCode = 401) then begin
  996. ARequest.Password := '';
  997. ARequest.Authentication.Reset;
  998. end;}
  999. Result := Assigned(FOnAuthorization) or (hoInProcessAuth in HTTPOptions);
  1000. if Result then
  1001. begin
  1002. with ARequest.Authentication do
  1003. begin
  1004. Username := ARequest.Username;
  1005. Password := ARequest.Password;
  1006. Params.Values['Authorization'] := Authentication;
  1007. AuthParams := AResponse.WWWAuthenticate;
  1008. end;
  1009. Result := False;
  1010. repeat
  1011. case ARequest.Authentication.Next of
  1012. wnAskTheProgram:
  1013. begin // Ask the user porgram to supply us with authorization information
  1014. if Assigned(FOnAuthorization) then
  1015. begin
  1016. ARequest.Authentication.UserName := ARequest.Username;
  1017. ARequest.Authentication.Password := ARequest.Password;
  1018. OnAuthorization(self, ARequest.Authentication, Result);
  1019. if Result then begin
  1020. ARequest.BasicAuthentication := True;
  1021. ARequest.Username := ARequest.Authentication.UserName;
  1022. ARequest.Password := ARequest.Authentication.Password;
  1023. end
  1024. else begin
  1025. Break;
  1026. end;
  1027. end else begin
  1028. Result := False;
  1029. Break;
  1030. end;
  1031. end;
  1032. wnDoRequest:
  1033. begin
  1034. Result := True;
  1035. Break;
  1036. end;
  1037. wnFail:
  1038. begin
  1039. Result := False;
  1040. Break;
  1041. end;
  1042. end;
  1043. until False;
  1044. end;
  1045. end;
  1046. function TIdCustomHTTP.DoOnProxyAuthorization(ARequest: TIdHTTPRequest; AResponse: TIdHTTPResponse): Boolean;
  1047. var
  1048. i: Integer;
  1049. S: string;
  1050. Auth: TIdAuthenticationClass;
  1051. begin
  1052. if not Assigned(ProxyParams.Authentication) then
  1053. begin
  1054. // Find which Authentication method is supported from us.
  1055. for i := 0 to AResponse.ProxyAuthenticate.Count - 1 do
  1056. begin
  1057. S := AResponse.ProxyAuthenticate[i];
  1058. Auth := FindAuthClass(Fetch(S));
  1059. if Auth <> nil then begin
  1060. Break;
  1061. end;
  1062. end;
  1063. if Auth = nil then begin
  1064. Result := False;
  1065. Exit;
  1066. end;
  1067. if Assigned(FOnSelectProxyAuthorization) then begin
  1068. OnSelectProxyAuthorization(self, Auth, AResponse.ProxyAuthenticate);
  1069. end;
  1070. ProxyParams.Authentication := Auth.Create;
  1071. end;
  1072. Result := Assigned(OnProxyAuthorization) or (hoInProcessAuth in HTTPOptions);
  1073. // Clear password and reset autorization if previous failed
  1074. {if (AResponse.FResponseCode = 407) then begin
  1075. ProxyParams.ProxyPassword := '';
  1076. ProxyParams.Authentication.Reset;
  1077. end;}
  1078. if Result then
  1079. begin
  1080. with ProxyParams.Authentication do
  1081. begin
  1082. Username := ProxyParams.ProxyUsername;
  1083. Password := ProxyParams.ProxyPassword;
  1084. Params.Values['Authorization'] := Authentication;
  1085. AuthParams := AResponse.ProxyAuthenticate;
  1086. end;
  1087. Result := false;
  1088. repeat
  1089. case ProxyParams.Authentication.Next of
  1090. wnAskTheProgram: // Ask the user porgram to supply us with authorization information
  1091. begin
  1092. if Assigned(OnProxyAuthorization) then
  1093. begin
  1094. ProxyParams.Authentication.Username := ProxyParams.ProxyUsername;
  1095. ProxyParams.Authentication.Password := ProxyParams.ProxyPassword;
  1096. OnProxyAuthorization(self, ProxyParams.Authentication, result);
  1097. if Result then begin
  1098. ProxyParams.BasicAuthentication := true;
  1099. ProxyParams.ProxyUsername := ProxyParams.Authentication.Username;
  1100. ProxyParams.ProxyPassword := ProxyParams.Authentication.Password;
  1101. end else begin
  1102. Break;
  1103. end;
  1104. end else begin
  1105. Result := False;
  1106. Break;
  1107. end;
  1108. end;
  1109. wnDoRequest:
  1110. begin
  1111. Result := True;
  1112. Break;
  1113. end;
  1114. wnFail:
  1115. begin
  1116. Result := False;
  1117. Break;
  1118. end;
  1119. end;
  1120. until False;
  1121. end;
  1122. end;
  1123. function TIdCustomHTTP.GetResponseCode: Integer;
  1124. begin
  1125. result := Response.ResponseCode;
  1126. end;
  1127. function TIdCustomHTTP.GetResponseText: string;
  1128. begin
  1129. result := Response.FResponseText;
  1130. end;
  1131. function TIdCustomHTTP.GetResponseHeaders: TIdHTTPResponse;
  1132. begin
  1133. result := FHTTPProto.Response;
  1134. end;
  1135. function TIdCustomHTTP.GetRequestHeaders: TIdHTTPRequest;
  1136. begin
  1137. result := FHTTPProto.Request;
  1138. end;
  1139. procedure TIdCustomHTTP.DoOnDisconnected;
  1140. begin
  1141. inherited DoOnDisconnected;
  1142. if Assigned(Request.Authentication) and
  1143. (Request.Authentication.CurrentStep = Request.Authentication.Steps) then begin
  1144. if Assigned(AuthenticationManager) then begin
  1145. AuthenticationManager.AddAuthentication(Request.Authentication, URL);
  1146. end;
  1147. Request.Authentication.Free;
  1148. Request.Authentication := nil;
  1149. end;
  1150. if Assigned(ProxyParams.Authentication) and
  1151. (ProxyParams.Authentication.CurrentStep = ProxyParams.Authentication.Steps) then begin
  1152. ProxyParams.ProxyUsername := '';
  1153. ProxyParams.ProxyPassword := '';
  1154. ProxyParams.Authentication.Reset;
  1155. end;
  1156. end;
  1157. procedure TIdCustomHTTP.SetAuthenticationManager(const Value: TIdAuthenticationManager);
  1158. begin
  1159. FAuthenticationManager := Value;
  1160. if Assigned(FAuthenticationManager) then
  1161. begin
  1162. FAuthenticationManager.FreeNotification(self);
  1163. end;
  1164. end;
  1165. procedure TIdCustomHTTP.SetHost(const Value: string);
  1166. begin
  1167. inherited SetHost(Value);
  1168. URL.Host := Value;
  1169. end;
  1170. procedure TIdCustomHTTP.SetPort(const Value: integer);
  1171. begin
  1172. inherited SetPort(Value);
  1173. URL.Port := IntToStr(Value);
  1174. end;
  1175. procedure TIdCustomHTTP.SetRequestHEaders(const Value: TIdHTTPRequest);
  1176. begin
  1177. FHTTPProto.Request.Assign(Value);
  1178. end;
  1179. procedure TIdCustomHTTP.Post(AURL: string;
  1180. const ASource: TIdMultiPartFormDataStream; AResponseContent: TStream);
  1181. begin
  1182. Request.ContentType := ASource.RequestContentType;
  1183. Post(AURL, TStream(ASource), AResponseContent);
  1184. end;
  1185. function TIdCustomHTTP.Post(AURL: string;
  1186. const ASource: TIdMultiPartFormDataStream): string;
  1187. begin
  1188. Request.ContentType := ASource.RequestContentType;
  1189. result := Post(AURL, TStream(ASource));
  1190. end;
  1191. { TIdHTTPResponse }
  1192. constructor TIdHTTPResponse.Create(AParent: TIdCustomHTTP);
  1193. begin
  1194. inherited Create;
  1195. FHTTP := AParent;
  1196. end;
  1197. function TIdHTTPResponse.GetKeepAlive: Boolean;
  1198. var
  1199. S: string;
  1200. i: TIdHTTPProtocolVersion;
  1201. begin
  1202. S := Copy(FResponseText, 6, 3);
  1203. for i := Low(TIdHTtpProtocolVersion) to High(TIdHTtpProtocolVersion) do
  1204. if AnsiSameText(ProtocolVersionString[i], S) then
  1205. begin
  1206. ResponseVersion := i;
  1207. break;
  1208. end;
  1209. FHTTP.CheckForDisconnect(false);
  1210. FKeepAlive := FHTTP.Connected;
  1211. if FKeepAlive then
  1212. case FHTTP.ProtocolVersion of
  1213. pv1_1: // By default we assume that keep-alive is by default and will close the connection only there is "close"
  1214. begin
  1215. FKeepAlive :=
  1216. not (AnsiSameText(Trim(Connection), 'CLOSE') or
  1217. AnsiSameText(Trim(ProxyConnection), 'CLOSE'));
  1218. end;
  1219. pv1_0: // By default we assume that keep-alive is not by default and will keep the connection only if there is "keep-alive"
  1220. begin
  1221. FKeepAlive := AnsiSameText(Trim(Connection), 'KEEP-ALIVE') or
  1222. AnsiSameText(Trim(ProxyConnection), 'KEEP-ALIVE') {or
  1223. ((ResponseVersion = pv1_1) and (Length(Trim(Connection)) = 0) and
  1224. (Length(Trim(ProxyConnection)) = 0))};
  1225. end;
  1226. end;
  1227. result := FKeepAlive;
  1228. end;
  1229. function TIdHTTPResponse.GetResponseCode: Integer;
  1230. var
  1231. S: string;
  1232. begin
  1233. S := FResponseText;
  1234. Fetch(S);
  1235. S := Trim(S);
  1236. FResponseCode := StrToIntDef(Fetch(S, ' ', False), -1);
  1237. Result := FResponseCode;
  1238. end;
  1239. { TIdHTTPRequest }
  1240. constructor TIdHTTPRequest.Create(AHTTP: TIdCustomHTTP);
  1241. begin
  1242. inherited Create;
  1243. FHTTP := AHTTP;
  1244. FUseProxy := ctNormal;
  1245. end;
  1246. { TIdHTTPProtocol }
  1247. constructor TIdHTTPProtocol.Create(AConnection: TIdCustomHTTP);
  1248. begin
  1249. inherited Create;
  1250. FHTTP := AConnection;
  1251. // Create the headers
  1252. FRequest := TIdHTTPRequest.Create(FHTTP);
  1253. FResponse := TIdHTTPResponse.Create(FHTTP);
  1254. end;
  1255. destructor TIdHTTPProtocol.Destroy;
  1256. begin
  1257. FreeAndNil(FRequest);
  1258. FreeAndNil(FResponse);
  1259. inherited Destroy;
  1260. end;
  1261. procedure TIdHTTPProtocol.BuildAndSendRequest(AURI: TIdURI);
  1262. var
  1263. i: Integer;
  1264. begin
  1265. Request.SetHeaders;
  1266. FHTTP.ProxyParams.SetHeaders(Request.RawHeaders);
  1267. if Assigned(AURI) then begin
  1268. FHTTP.SetCookies(AURI, Request);
  1269. end;
  1270. // This is a wrokaround for some HTTP servers wich does not implement properly the HTTP protocol
  1271. FHTTP.OpenWriteBuffer;
  1272. try
  1273. FHTTP.WriteLn(MethodString[Request.Method] + ' ' + Request.URL + ' HTTP/' + ProtocolVersionString[FHTTP.ProtocolVersion]); {do not localize}
  1274. // write the headers
  1275. for i := 0 to Request.RawHeaders.Count - 1 do
  1276. if Length(Request.RawHeaders.Strings[i]) > 0 then
  1277. FHTTP.WriteLn(Request.RawHeaders.Strings[i]);
  1278. FHTTP.WriteLn('');
  1279. FHTTP.CloseWriteBuffer;
  1280. except
  1281. FHTTP.CancelWriteBuffer;
  1282. raise;
  1283. end;
  1284. end;
  1285. procedure TIdHTTPProtocol.RetrieveHeaders;
  1286. var
  1287. S: string;
  1288. begin
  1289. // Set the response headers
  1290. // Clear headers
  1291. // Don't use Capture.
  1292. Response.RawHeaders.Clear;
  1293. s := FHTTP.ReadLn;
  1294. try
  1295. while Length(s) > 0 do
  1296. begin
  1297. Response.RawHeaders.Add(S);
  1298. s := FHTTP.ReadLn;
  1299. end;
  1300. except
  1301. on E: EIdConnClosedGracefully do begin
  1302. FHTTP.Disconnect;
  1303. end;
  1304. end;
  1305. Response.ProcessHeaders;
  1306. end;
  1307. function TIdHTTPProtocol.ProcessResponse: TIdHTTPWhatsNext;
  1308. procedure RaiseException;
  1309. var
  1310. LRespStream: TStringStream;
  1311. LTempStream: TStream;
  1312. LTemp: Integer;
  1313. begin
  1314. LTemp := FHTTP.ReadTimeout;
  1315. FHTTP.ReadTimeout := 2000; // Lets wait 2 seconds for any kind of content
  1316. LRespStream := TStringStream.Create('');
  1317. LTempStream := Response.ContentStream;
  1318. Response.ContentStream := LRespStream;
  1319. try
  1320. FHTTP.ReadResult(Response);
  1321. raise EIdHTTPProtocolException.CreateError(Response.ResponseCode, FHTTP.ResponseText, LRespStream.DataString);
  1322. finally
  1323. Response.ContentStream := LTempStream;
  1324. LRespStream.Free;
  1325. FHTTP.ReadTimeout := LTemp;
  1326. end;
  1327. end;
  1328. procedure ReadContent;
  1329. Var
  1330. LTempResponse: TStringStream;
  1331. LTempStream: TStream;
  1332. begin
  1333. LTempResponse := TStringStream.Create('');
  1334. LTempStream := Response.ContentStream;
  1335. Response.ContentStream := LTempResponse;
  1336. try
  1337. FHTTP.ReadResult(Response);
  1338. finally
  1339. LTempResponse.Free;
  1340. Response.ContentStream := LTempStream;
  1341. end;
  1342. end;
  1343. function HeadersCanContinue: Boolean;
  1344. begin
  1345. Result := True;
  1346. if Assigned(FHTTP.OnHeadersAvailable) then begin
  1347. FHTTP.OnHeadersAvailable(FHTTP, Response.RawHeaders, Result);
  1348. end;
  1349. end;
  1350. var
  1351. LTemp: Integer;
  1352. LLocation: string;
  1353. LMethod: TIdHTTPMethod;
  1354. LResponseDigit: Integer;
  1355. LNeedAutorization: Boolean;
  1356. begin
  1357. // provide the user with the headers and let the user decide
  1358. // whether the response processing should continue...
  1359. if not HeadersCanContinue then begin
  1360. Response.KeepAlive := False; // force DoRequest() to disconnect the connection
  1361. Result := wnJustExit;
  1362. Exit;
  1363. end;
  1364. Result := wnDontKnow;
  1365. LNeedAutorization := False;
  1366. LResponseDigit := Response.ResponseCode div 100;
  1367. // Handle Redirects
  1368. if ((LResponseDigit = 3) and (Response.ResponseCode <> 304)) or (Length(Response.Location) > 0) then
  1369. begin
  1370. // LLocation := TIdURI.URLDecode(Response.Location);
  1371. LLocation := Response.Location;
  1372. if (FHTTP.FHandleRedirects) and (FHTTP.FRedirectCount < FHTTP.FRedirectMax) then
  1373. begin
  1374. LMethod := Request.Method;
  1375. if FHTTP.DoOnRedirect(LLocation, LMethod, FHTTP.FRedirectCount) then
  1376. begin
  1377. Result := wnGoToURL;
  1378. Request.URL := LLocation;
  1379. Request.Method := LMethod;
  1380. end
  1381. else
  1382. RaiseException;
  1383. end
  1384. else // Just fire the event
  1385. begin
  1386. LMethod := Request.Method;
  1387. Result := wnJustExit;
  1388. if not FHTTP.DoOnRedirect(LLocation, LMethod, FHTTP.FRedirectCount) then begin // If not Handled
  1389. RaiseException;
  1390. end else begin
  1391. Response.Location := LLocation;
  1392. end;
  1393. end;
  1394. if FHTTP.Connected then
  1395. begin
  1396. // This is a workaround for buggy HTTP 1.1 servers which
  1397. // does not return any body with 302 response code
  1398. LTemp := FHTTP.ReadTimeout;
  1399. FHTTP.ReadTimeout := 4000; // Lets wait 4 seconds for any kind of content
  1400. try
  1401. ReadContent;
  1402. except end;
  1403. FHTTP.ReadTimeout := LTemp;
  1404. end;
  1405. end
  1406. else
  1407. begin
  1408. // GREGOR Workaround
  1409. // if we get an error we disconnect if we use SSLIOHandler
  1410. if Assigned(FHTTP.IOHandler) then
  1411. begin
  1412. Response.KeepAlive := not (FHTTP.Connected and (FHTTP.IOHandler is TIdSSLIOHandlerSocket) and Response.KeepAlive);
  1413. end;
  1414. if LResponseDigit <> 2 then
  1415. begin
  1416. result := wnGoToURL;
  1417. case Response.ResponseCode of
  1418. 401:
  1419. begin // HTTP Server authorization requered
  1420. if (FHTTP.InternalAuthRetries >= FHTTP.AuthRetries) or not FHTTP.DoOnAuthorization(Request, Response) then
  1421. begin
  1422. if Assigned(Request.Authentication) then
  1423. Request.Authentication.Reset;
  1424. RaiseException;
  1425. end
  1426. else if hoInProcessAuth in FHTTP.HTTPOptions then begin
  1427. LNeedAutorization := True;
  1428. end;
  1429. end;
  1430. 407:
  1431. begin // Proxy Server authorization requered
  1432. if (FHTTP.InternalProxyAuthRetries >= FHTTP.AuthRetries) or not FHTTP.DoOnProxyAuthorization(Request, Response) then
  1433. begin
  1434. if Assigned(FHTTP.ProxyParams.Authentication) then
  1435. with FHTTP.ProxyParams do begin
  1436. Authentication.Reset;
  1437. ProxyUsername := '';
  1438. ProxyPassword := '';
  1439. end;
  1440. RaiseException;
  1441. end else begin
  1442. if hoInProcessAuth in FHTTP.HTTPOptions then
  1443. LNeedAutorization := True;
  1444. end;
  1445. end;
  1446. else begin
  1447. RaiseException;
  1448. end;
  1449. end;
  1450. end;
  1451. if FHTTP.Connected then begin
  1452. if LNeedAutorization then begin
  1453. // Read the content of Error message in temporary stream
  1454. LTemp := FHTTP.ReadTimeout;
  1455. FHTTP.ReadTimeout := 4000; // Lets wait 4 seconds for any kind of content
  1456. try
  1457. ReadContent;
  1458. except end;
  1459. FHTTP.ReadTimeout := LTemp;
  1460. Result := wnAuthRequest
  1461. end
  1462. else if (Response.ResponseCode <> 204) then
  1463. begin
  1464. FHTTP.ReadResult(Response);
  1465. Result := wnJustExit;
  1466. end
  1467. else begin
  1468. Result := wnJustExit;
  1469. end;
  1470. end;
  1471. end;
  1472. end;
  1473. function TIdCustomHTTP.GetAuthRetries: Integer;
  1474. begin
  1475. if Assigned(Request.Authentication) then begin
  1476. Result := Request.Authentication.AuthRetries;
  1477. end else begin
  1478. Result := 0;
  1479. end;
  1480. end;
  1481. function TIdCustomHTTP.GetProxyAuthRetries: Integer;
  1482. begin
  1483. if Assigned(ProxyParams.Authentication) then begin
  1484. Result := ProxyParams.Authentication.AuthRetries;
  1485. end else begin
  1486. Result := 0;
  1487. end;
  1488. end;
  1489. end.