IdHTTPWebBrokerBridge.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  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: 10197: IdHTTPWebBrokerBridge.pas
  11. {
  12. { Rev 1.1 05.6.2003 ã. 11:27:16 DBondzhev
  13. { Header vars are not the same like for ISAPI in webBroker
  14. }
  15. {
  16. { Rev 1.0 2002.11.12 10:41:30 PM czhower
  17. }
  18. unit IdHTTPWebBrokerBridge;
  19. {$I IdCompilerDefines.inc}
  20. (*
  21. Original Author: Dave Nottage.
  22. Modified by: Grahame Grieve
  23. Modified by: Chad Z. Hower (Kudzu)
  24. *)
  25. interface
  26. uses
  27. Classes,
  28. HTTPApp,
  29. IdCustomHTTPServer, IdTCPServer, IdIOHandlerSocket,
  30. WebBroker;
  31. type
  32. TIdHTTPAppRequest = class(TWebRequest)
  33. protected
  34. FRequestInfo : TIdHTTPRequestInfo;
  35. FResponseInfo : TIdHTTPResponseInfo;
  36. FThread : TIdPeerThread;
  37. FClientCursor : Integer;
  38. //
  39. function GetDateVariable(Index: Integer): TDateTime; override;
  40. function GetIntegerVariable(Index: Integer): Integer; override;
  41. function GetStringVariable(Index: Integer): string; override;
  42. public
  43. constructor Create(AThread: TIdPeerThread; ARequestInfo: TIdHTTPRequestInfo;
  44. AResponseInfo: TIdHTTPResponseInfo);
  45. function GetFieldByName(const Name: string): string; override;
  46. function ReadClient(var Buffer; Count: Integer): Integer; override;
  47. function ReadString(Count: Integer): string; override;
  48. function TranslateURI(const URI: string): string; override;
  49. function WriteClient(var ABuffer; ACount: Integer): Integer; override;
  50. {$IFDEF VCL6ORABOVE}
  51. function WriteHeaders(StatusCode: Integer; const ReasonString, Headers: string): Boolean; override;
  52. {$ENDIF}
  53. function WriteString(const AString: string): Boolean; override;
  54. end;
  55. TIdHTTPAppResponse = class(TWebResponse)
  56. protected
  57. FContent: string;
  58. FRequestInfo: TIdHTTPRequestInfo;
  59. FResponseInfo: TIdHTTPResponseInfo;
  60. FSent: Boolean;
  61. FThread: TIdPeerThread;
  62. //
  63. function GetContent: string; override;
  64. function GetDateVariable(Index: Integer): TDateTime; override;
  65. function GetStatusCode: Integer; override;
  66. function GetIntegerVariable(Index: Integer): Integer; override;
  67. function GetLogMessage: string; override;
  68. function GetStringVariable(Index: Integer): string; override;
  69. procedure SetContent(const AValue: string); override;
  70. procedure SetContentStream(AValue: TStream); override;
  71. procedure SetStatusCode(AValue: Integer); override;
  72. procedure SetStringVariable(Index: Integer; const Value: string); override;
  73. procedure SetDateVariable(Index: Integer; const Value: TDateTime); override;
  74. procedure SetIntegerVariable(Index: Integer; Value: Integer); override;
  75. procedure SetLogMessage(const Value: string); override;
  76. procedure MoveCookiesAndCustomHeaders;
  77. public
  78. constructor Create(AHTTPRequest: TWebRequest; AThread: TIdPeerThread;
  79. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  80. procedure SendRedirect(const URI: string); override;
  81. procedure SendResponse; override;
  82. procedure SendStream(AStream: TStream); override;
  83. function Sent: Boolean; override;
  84. end;
  85. TIdHTTPWebBrokerBridge = class(TIdCustomHTTPServer)
  86. protected
  87. FWebModuleClass: TComponentClass;
  88. //
  89. procedure DoCommandGet(AThread: TIdPeerThread; ARequestInfo: TIdHTTPRequestInfo;
  90. AResponseInfo: TIdHTTPResponseInfo); override;
  91. public
  92. constructor Create(AOwner: TComponent); override;
  93. procedure RegisterWebModuleClass(AClass: TComponentClass);
  94. end;
  95. implementation
  96. uses
  97. IdException, IdHTTPHeaderInfo, IdGlobal, IdCookie,
  98. SysUtils, Math;
  99. type
  100. // Make HandleRequest accessible
  101. TWebDispatcherAccess = class(TCustomWebDispatcher);
  102. const
  103. INDEX_RESP_Version = 0;
  104. INDEX_RESP_ReasonString = 1;
  105. INDEX_RESP_Server = 2;
  106. INDEX_RESP_WWWAuthenticate = 3;
  107. INDEX_RESP_Realm = 4;
  108. INDEX_RESP_Allow = 5;
  109. INDEX_RESP_Location = 6;
  110. INDEX_RESP_ContentEncoding = 7;
  111. INDEX_RESP_ContentType = 8;
  112. INDEX_RESP_ContentVersion = 9;
  113. INDEX_RESP_DerivedFrom = 10;
  114. INDEX_RESP_Title = 11;
  115. //
  116. INDEX_RESP_ContentLength = 0;
  117. //
  118. INDEX_RESP_Date = 0;
  119. INDEX_RESP_Expires = 1;
  120. INDEX_RESP_LastModified = 2;
  121. //
  122. //Borland coder didn't define constants in HTTPApp
  123. INDEX_Method = 0;
  124. INDEX_ProtocolVersion = 1;
  125. INDEX_URL = 2;
  126. INDEX_Query = 3;
  127. INDEX_PathInfo = 4;
  128. INDEX_PathTranslated = 5;
  129. INDEX_CacheControl = 6;
  130. INDEX_Date = 7;
  131. INDEX_Accept = 8;
  132. INDEX_From = 9;
  133. INDEX_Host = 10;
  134. INDEX_IfModifiedSince = 11;
  135. INDEX_Referer = 12;
  136. INDEX_UserAgent = 13;
  137. INDEX_ContentEncoding = 14;
  138. INDEX_ContentType = 15;
  139. INDEX_ContentLength = 16;
  140. INDEX_ContentVersion = 17;
  141. INDEX_DerivedFrom = 18;
  142. INDEX_Expires = 19;
  143. INDEX_Title = 20;
  144. INDEX_RemoteAddr = 21;
  145. INDEX_RemoteHost = 22;
  146. INDEX_ScriptName = 23;
  147. INDEX_ServerPort = 24;
  148. INDEX_Content = 25;
  149. INDEX_Connection = 26;
  150. INDEX_Cookie = 27;
  151. INDEX_Authorization = 28;
  152. { TIdHTTPAppRequest }
  153. constructor TIdHTTPAppRequest.Create(AThread: TIdPeerThread; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  154. Var
  155. i: Integer;
  156. begin
  157. FThread := AThread;
  158. FRequestInfo := ARequestInfo;
  159. FResponseInfo := AResponseInfo;
  160. inherited Create;
  161. FClientCursor := 0;
  162. for i := 0 to ARequestInfo.Cookies.Count - 1 do begin
  163. CookieFields.Add(ARequestInfo.Cookies[i].ClientCookie);
  164. end;
  165. end;
  166. function TIdHTTPAppRequest.GetDateVariable(Index: Integer): TDateTime;
  167. var
  168. LValue: string;
  169. begin
  170. LValue := GetStringVariable(Index);
  171. if Length(LValue) > 0 then begin
  172. Result := ParseDate(LValue)
  173. end else begin
  174. Result := -1;
  175. end;
  176. end;
  177. function TIdHTTPAppRequest.GetIntegerVariable(Index: Integer): Integer;
  178. begin
  179. Result := StrToIntDef(GetStringVariable(Index), -1)
  180. end;
  181. function TIdHTTPAppRequest.GetStringVariable(Index: Integer): string;
  182. var
  183. s: string;
  184. begin
  185. case Index of
  186. INDEX_Method : Result := FRequestInfo.Command;
  187. INDEX_ProtocolVersion : Result := FRequestInfo.Version;
  188. INDEX_URL : Result := FRequestInfo.Document;
  189. INDEX_Query : Result := FRequestInfo.UnparsedParams;
  190. INDEX_PathInfo : Result := FRequestInfo.Document;
  191. INDEX_PathTranslated : Result := FRequestInfo.Document; // it's not clear quite what should be done here - we can't translate to a path
  192. INDEX_CacheControl : Result := GetFieldByName('CACHE_CONTROL'); {do not localize}
  193. INDEX_Date : Result := GetFieldByName('DATE'); {do not localize}
  194. INDEX_Accept : Result := FRequestInfo.Accept;
  195. INDEX_From : Result := FRequestInfo.From;
  196. INDEX_Host: begin
  197. s := FRequestInfo.Host;
  198. Result := Fetch(s, ':');
  199. end;
  200. INDEX_IfModifiedSince : Result := GetFieldByName('If-Modified-Since'); {do not localize}
  201. INDEX_Referer : Result := FRequestInfo.Referer;
  202. INDEX_UserAgent : Result := FRequestInfo.UserAgent;
  203. INDEX_ContentEncoding : Result := FRequestInfo.ContentEncoding;
  204. INDEX_ContentType : Result := FRequestInfo.ContentType;
  205. INDEX_ContentLength : Result := IntToStr(Length(FRequestInfo.UnparsedParams));
  206. INDEX_ContentVersion : Result := GetFieldByName('CONTENT_VERSION'); {do not localize}
  207. INDEX_DerivedFrom : Result := GetFieldByName('DERIVED_FROM'); {do not localize}
  208. INDEX_Expires : Result := GetFieldByName('Expires'); {do not localize}
  209. INDEX_Title : Result := GetFieldByName('TITLE'); {do not localize}
  210. INDEX_RemoteAddr : Result := FRequestInfo.RemoteIP;
  211. INDEX_RemoteHost : Result := GetFieldByName('REMOTE_HOST'); {do not localize}
  212. INDEX_ScriptName : Result := '';
  213. INDEX_ServerPort: begin
  214. Result := FRequestInfo.Host;
  215. Fetch(Result, ':');
  216. if Length(Result) = 0 then begin
  217. Result := IntToStr(TIdIOHandlerSocket(FThread.Connection.IOHandler).Binding.Port);
  218. // Result := '80';
  219. end;
  220. end;
  221. INDEX_Content : Result := FRequestInfo.UnparsedParams;
  222. INDEX_Connection : Result := GetFieldByName('CONNECTION'); {do not localize}
  223. INDEX_Cookie : Result := ''; // not available at present. FRequestInfo.Cookies....;
  224. INDEX_Authorization : Result := GetFieldByName('AUTHORIZATION'); {do not localize}
  225. else
  226. Result := '';
  227. end;
  228. end;
  229. function TIdHTTPAppRequest.GetFieldByName(const Name: string): string;
  230. begin
  231. Result := FRequestInfo.RawHeaders.Values[Name];
  232. end;
  233. function TIdHTTPAppRequest.ReadClient(var Buffer; Count: Integer): Integer;
  234. begin
  235. Result := Min(Count, length(FRequestInfo.UnparsedParams)) - FClientCursor;
  236. if Result > 0 then begin
  237. Move(FRequestInfo.UnparsedParams[FClientCursor + 1], Buffer, Result);
  238. Inc(FClientCursor, Result);
  239. end else begin
  240. // well, it shouldn't be less than 0. but let's not take chances
  241. Result := 0;
  242. end;
  243. end;
  244. function TIdHTTPAppRequest.ReadString(Count: Integer): string;
  245. var
  246. LLength: Integer;
  247. begin
  248. LLength := Min(Count, length(FRequestInfo.UnparsedParams)) - FClientCursor;
  249. if LLength > 0 then
  250. begin
  251. Result := copy(FRequestInfo.UnparsedParams, FClientCursor, LLength);
  252. inc(FClientCursor, LLength);
  253. end
  254. else
  255. Result := '';
  256. end;
  257. function TIdHTTPAppRequest.TranslateURI(const URI: string): string;
  258. begin
  259. // we don't have the concept of a path translation. It's not quite clear
  260. // what to do about this. Comments welcome ([email protected])
  261. Result := URI;
  262. end;
  263. {$IFDEF VCL6ORABOVE}
  264. function TIdHTTPAppRequest.WriteHeaders(StatusCode: Integer; const ReasonString, Headers: string): Boolean;
  265. begin
  266. FResponseInfo.ResponseNo := StatusCode;
  267. FResponseInfo.ResponseText := ReasonString;
  268. FResponseInfo.CustomHeaders.Add(Headers);
  269. FResponseInfo.WriteHeader;
  270. Result := True;
  271. end;
  272. {$ENDIF}
  273. function TIdHTTPAppRequest.WriteString(const AString: string): Boolean;
  274. begin
  275. WriteClient(PChar(AString)^, Length(AString));
  276. Result := True;
  277. end;
  278. function TIdHTTPAppRequest.WriteClient(var ABuffer; ACount: Integer): Integer;
  279. begin
  280. FThread.Connection.WriteBuffer(ABuffer, ACount);
  281. Result := ACount;
  282. end;
  283. { TIdHTTPAppResponse }
  284. constructor TIdHTTPAppResponse.Create(AHTTPRequest: TWebRequest; AThread: TIdPeerThread; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  285. begin
  286. FThread := AThread;
  287. FRequestInfo := ARequestInfo;
  288. FResponseInfo := AResponseInfo;
  289. inherited Create(AHTTPRequest);
  290. if Length(FHTTPRequest.ProtocolVersion) = 0 then begin
  291. Version := '1.0';
  292. end;
  293. StatusCode := 200;
  294. LastModified := -1;
  295. Expires := -1;
  296. Date := -1;
  297. ContentType := 'text/html';
  298. end;
  299. function TIdHTTPAppResponse.GetContent: string;
  300. begin
  301. Result := FResponseInfo.ContentText;
  302. end;
  303. function TIdHTTPAppResponse.GetLogMessage: string;
  304. begin
  305. Result := '';
  306. end;
  307. function TIdHTTPAppResponse.GetStatusCode: Integer;
  308. begin
  309. Result := FResponseInfo.ResponseNo;
  310. end;
  311. function TIdHTTPAppResponse.GetDateVariable(Index: Integer): TDateTime;
  312. begin
  313. //TODO: resource string these
  314. case Index of
  315. INDEX_RESP_Date : Result := FResponseInfo.Date;
  316. INDEX_RESP_Expires : Result := FResponseInfo.Expires;
  317. INDEX_RESP_LastModified : Result := FResponseInfo.LastModified;
  318. else
  319. raise EIdException.Create('Invalid Index '+inttostr(Index)+' in TIdHTTPAppResponse.GetDateVariable');
  320. end;
  321. end;
  322. procedure TIdHTTPAppResponse.SetDateVariable(Index: Integer; const Value: TDateTime);
  323. begin
  324. //TODO: resource string these
  325. case Index of
  326. INDEX_RESP_Date : FResponseInfo.Date := Value;
  327. INDEX_RESP_Expires : FResponseInfo.Expires := Value;
  328. INDEX_RESP_LastModified : FResponseInfo.LastModified := Value;
  329. else
  330. raise EIdException.Create('Invalid Index '+inttostr(Index)+' in TIdHTTPAppResponse.SetDateVariable');
  331. end;
  332. end;
  333. function TIdHTTPAppResponse.GetIntegerVariable(Index: Integer): Integer;
  334. begin
  335. //TODO: resource string these
  336. case Index of
  337. INDEX_RESP_ContentLength: Result := FResponseInfo.ContentLength;
  338. else
  339. raise EIdException.Create('Invalid Index '+inttostr(Index)+' in TIdHTTPAppResponse.GetIntegerVariable');
  340. end;
  341. end;
  342. procedure TIdHTTPAppResponse.SetIntegerVariable(Index, Value: Integer);
  343. begin
  344. //TODO: resource string these
  345. case Index of
  346. INDEX_RESP_ContentLength: FResponseInfo.ContentLength := Value;
  347. else
  348. raise EIdException.Create('Invalid Index '+inttostr(Index)+' in TIdHTTPAppResponse.SetIntegerVariable');
  349. end;
  350. end;
  351. function TIdHTTPAppResponse.GetStringVariable(Index: Integer): string;
  352. begin
  353. //TODO: resource string these
  354. case Index of
  355. INDEX_RESP_Version :Result := FRequestInfo.Version;
  356. INDEX_RESP_ReasonString :Result := FResponseInfo.ResponseText;
  357. INDEX_RESP_Server :Result := FResponseInfo.Server;
  358. INDEX_RESP_WWWAuthenticate :Result := FResponseInfo.WWWAuthenticate.Text;
  359. INDEX_RESP_Realm :Result := FResponseInfo.AuthRealm;
  360. INDEX_RESP_Allow :Result := FResponseInfo.CustomHeaders.Values['Allow'];
  361. INDEX_RESP_Location :Result := FResponseInfo.Location;
  362. INDEX_RESP_ContentEncoding :Result := FResponseInfo.ContentEncoding;
  363. INDEX_RESP_ContentType :Result := FResponseInfo.ContentType;
  364. INDEX_RESP_ContentVersion :Result := FResponseInfo.ContentVersion;
  365. INDEX_RESP_DerivedFrom :Result := FResponseInfo.CustomHeaders.Values['Derived-From'];
  366. INDEX_RESP_Title :Result := FResponseInfo.CustomHeaders.Values['Title'];
  367. else
  368. raise EIdException.Create('Invalid Index ' + IntToStr(Index)
  369. + ' in TIdHTTPAppResponse.GetStringVariable');
  370. end;
  371. end;
  372. procedure TIdHTTPAppResponse.SetStringVariable(Index: Integer; const Value: string);
  373. begin
  374. //TODO: resource string these
  375. case Index of
  376. INDEX_RESP_Version :EIdException.Create('TIdHTTPAppResponse.SetStringVariable: Cannot set the version');
  377. INDEX_RESP_ReasonString :FResponseInfo.ResponseText := Value;
  378. INDEX_RESP_Server :FResponseInfo.Server := Value;
  379. INDEX_RESP_WWWAuthenticate :FResponseInfo.WWWAuthenticate.Text := Value;
  380. INDEX_RESP_Realm :FResponseInfo.AuthRealm := Value;
  381. INDEX_RESP_Allow :FResponseInfo.CustomHeaders.Values['Allow'] := Value;
  382. INDEX_RESP_Location :FResponseInfo.Location := Value;
  383. INDEX_RESP_ContentEncoding :FResponseInfo.ContentEncoding := Value;
  384. INDEX_RESP_ContentType :FResponseInfo.ContentType := Value;
  385. INDEX_RESP_ContentVersion :FResponseInfo.ContentVersion := Value;
  386. INDEX_RESP_DerivedFrom :FResponseInfo.CustomHeaders.Values['Derived-From'] := Value;
  387. INDEX_RESP_Title :FResponseInfo.CustomHeaders.Values['Title'] := Value;
  388. else
  389. raise EIdException.Create('Invalid Index ' + IntToStr(Index)
  390. + ' in TIdHTTPAppResponse.SetStringVariable');
  391. end;
  392. end;
  393. procedure TIdHTTPAppResponse.SendRedirect(const URI: string);
  394. begin
  395. FSent := True;
  396. MoveCookiesAndCustomHeaders;
  397. FResponseInfo.Redirect(URI);
  398. end;
  399. procedure TIdHTTPAppResponse.SendResponse;
  400. begin
  401. FSent := True;
  402. // Reset to -1 so Indy will auto set it
  403. FResponseInfo.ContentLength := -1;
  404. MoveCookiesAndCustomHeaders;
  405. FResponseInfo.WriteContent;
  406. end;
  407. procedure TIdHTTPAppResponse.SendStream(AStream: TStream);
  408. begin
  409. FThread.Connection.WriteStream(AStream);
  410. end;
  411. function TIdHTTPAppResponse.Sent: Boolean;
  412. begin
  413. Result := FSent;
  414. end;
  415. procedure TIdHTTPAppResponse.SetContent(const AValue: string);
  416. begin
  417. FResponseInfo.ContentText := AValue;
  418. FResponseInfo.ContentLength := Length(AValue);
  419. end;
  420. procedure TIdHTTPAppResponse.SetLogMessage(const Value: string);
  421. begin
  422. // logging not supported
  423. end;
  424. procedure TIdHTTPAppResponse.SetStatusCode(AValue: Integer);
  425. begin
  426. FResponseInfo.ResponseNo := AValue;
  427. end;
  428. procedure TIdHTTPAppResponse.SetContentStream(AValue: TStream);
  429. begin
  430. inherited;
  431. FResponseInfo.ContentStream := AValue;
  432. end;
  433. procedure TIdHTTPAppResponse.MoveCookiesAndCustomHeaders;
  434. Var
  435. i: Integer;
  436. begin
  437. for i := 0 to Cookies.Count - 1 do begin
  438. with FResponseInfo.Cookies.Add do begin
  439. CookieText := Cookies[i].HeaderValue
  440. end;
  441. end;
  442. FResponseInfo.CustomHeaders.Clear;
  443. for i := 0 to CustomHeaders.Count - 1 do begin
  444. FResponseInfo.CustomHeaders.Values[CustomHeaders.Names[i]] :=
  445. CustomHeaders.Values[CustomHeaders.Names[i]];
  446. end;
  447. end;
  448. { TIdHTTPWebBrokerBridge }
  449. constructor TIdHTTPWebBrokerBridge.Create;
  450. begin
  451. inherited;
  452. FOkToProcessCommand := True;
  453. end;
  454. procedure TIdHTTPWebBrokerBridge.DoCommandGet(AThread: TIdPeerThread;
  455. ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  456. var
  457. LRequest: TIdHTTPAppRequest;
  458. LResponse: TIdHTTPAppResponse;
  459. LWebModule: TCustomWebDispatcher;
  460. begin
  461. LRequest := TIdHTTPAppRequest.Create(AThread, ARequestInfo, AResponseInfo); try
  462. LResponse := TIdHTTPAppResponse.Create(LRequest, AThread, ARequestInfo, AResponseInfo); try
  463. // WebBroker will free it and we cannot change this behaviour
  464. AResponseInfo.FreeContentStream := False;
  465. // There are better ways in D6, but this works in D5
  466. LWebModule := FWebModuleClass.Create(nil) as TCustomWebDispatcher; try
  467. if TWebDispatcherAccess(LWebModule).DispatchAction(LRequest, LResponse) then begin
  468. if not LResponse.Sent then begin
  469. LResponse.SendResponse;
  470. end;
  471. end;
  472. finally FreeAndNil(LWebModule); end;
  473. finally FreeAndNil(LResponse); end;
  474. finally FreeAndNil(LRequest); end;
  475. end;
  476. procedure TIdHTTPWebBrokerBridge.RegisterWebModuleClass(AClass: TComponentClass);
  477. begin
  478. FWebModuleClass := AClass;
  479. end;
  480. end.