fpapache.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. {
  2. $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$H+}
  13. unit fpapache;
  14. interface
  15. uses SysUtils,Classes,CustApp,httpDefs,fpHTTP,httpd, apr;
  16. Type
  17. TCustomApacheApplication = Class;
  18. { TApacheRequest }
  19. TApacheRequest = Class(TRequest)
  20. Private
  21. FApache : TCustomApacheApplication;
  22. FRequest : PRequest_rec;
  23. FContent : String;
  24. FContentRead : Boolean;
  25. procedure ReadContent;
  26. Protected
  27. Function GetFieldValue(Index : Integer) : String; override;
  28. Procedure InitFromRequest;
  29. Public
  30. Constructor CreateReq(App : TCustomApacheApplication; ARequest : PRequest_rec);
  31. Property ApacheRequest : Prequest_rec Read FRequest;
  32. Property ApacheApp : TCustomApacheApplication Read FApache;
  33. end;
  34. { TCGIResponse }
  35. { TApacheResponse }
  36. TApacheResponse = Class(TResponse)
  37. private
  38. FApache : TCustomApacheApplication;
  39. FRequest : PRequest_rec;
  40. procedure SendStream(S: TStream);
  41. Protected
  42. Procedure DoSendHeaders(Headers : TStrings); override;
  43. Procedure DoSendContent; override;
  44. Public
  45. Constructor CreateApache(Req : TApacheRequest);
  46. Property ApacheRequest : Prequest_rec Read FRequest;
  47. Property ApacheApp : TCustomApacheApplication Read FApache;
  48. end;
  49. { TCustomApacheApplication }
  50. THandlerPriority = (hpFirst,hpMiddle,hpLast);
  51. TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
  52. Var ModuleClass : TCustomHTTPModuleClass) of object;
  53. TBeforeRequestEvent = Procedure(Sender : TObject; Const AHandler : String;
  54. Var AllowRequest : Boolean) of object;
  55. TCustomApacheApplication = Class(TCustomApplication)
  56. private
  57. FAdministrator: String;
  58. FBaseLocation: String;
  59. FBeforeRequest: TBeforeRequestEvent;
  60. FEmail: String;
  61. FHandlerName: String;
  62. FModuleName: String;
  63. FOnGetModule: TGetModuleEvent;
  64. FAllowDefaultModule: Boolean;
  65. FModules : Array[0..1] of TStrings;
  66. FPriority: THandlerPriority;
  67. FModuleRecord : PModule;
  68. function GetModules(Index: integer): TStrings;
  69. procedure SetModules(Index: integer; const AValue: TStrings);
  70. procedure ShowRequestException(R: TResponse; E: Exception);
  71. Protected
  72. Function ProcessRequest(P : PRequest_Rec) : Integer; virtual;
  73. Function GetModuleName(ARequest : TRequest) : string;
  74. function FindModule(ModuleClass : TCustomHTTPModuleClass): TCustomHTTPModule;
  75. Procedure DoRun; override;
  76. Function AllowRequest(P : PRequest_Rec) : Boolean; virtual;
  77. Public
  78. Constructor Create(AOwner : TComponent); override;
  79. Procedure SetModuleRecord(Var ModuleRecord : Module);
  80. Procedure Initialize; override;
  81. Procedure ShowException(E : Exception); override;
  82. Procedure CreateForm(AClass : TComponentClass; Var Reference : TComponent);
  83. Procedure handleRequest(ARequest : TRequest; AResponse : TResponse); virtual;
  84. Property OnGetModule : TGetModuleEvent Read FOnGetModule Write FOnGetModule;
  85. Property AllowDefaultModule : Boolean Read FAllowDefaultModule Write FAllowDefaultModule;
  86. Property HandlerPriority : THandlerPriority Read FPriority Write FPriority default hpMiddle;
  87. Property BeforeModules : TStrings Index 0 Read GetModules Write SetModules;
  88. Property AfterModules : TStrings Index 1 Read GetModules Write SetModules;
  89. Property BaseLocation : String Read FBaseLocation Write FBaseLocation;
  90. Property ModuleName : String Read FModuleName Write FModuleName;
  91. Property HandlerName : String Read FHandlerName Write FHandlerName;
  92. Property BeforeRequest : TBeforeRequestEvent Read FBeforeRequest Write FBeforeRequest;
  93. Property Email : String Read FEmail Write FEmail;
  94. Property Administrator : String Read FAdministrator Write FAdministrator;
  95. end;
  96. TApacheApplication = Class(TCustomApacheApplication)
  97. Public
  98. Property HandlerPriority;
  99. Property BeforeModules;
  100. Property AfterModules;
  101. Property AllowDefaultModule;
  102. Property OnGetModule;
  103. Property BaseLocation;
  104. Property ModuleName;
  105. end;
  106. EFPApacheError = Class(Exception);
  107. Var
  108. Application : TCustomApacheApplication = Nil;
  109. ShowCleanUpErrors : Boolean = False;
  110. AlternateHandler : ap_hook_handler_t = Nil;
  111. Implementation
  112. resourcestring
  113. SErrNoModuleNameForRequest = 'Could not determine HTTP module name for request';
  114. SErrNoModuleForRequest = 'Could not determine HTTP module for request "%s"';
  115. SErrNoModuleRecord = 'No module record location set.';
  116. SErrNoModuleName = 'No module name set';
  117. SModuleError = 'Module Error';
  118. SAppEncounteredError = 'The application encountered the following error:';
  119. SError = 'Error: ';
  120. SNotify = 'Notify: ';
  121. const
  122. HPRIO : Array[THandlerPriority] of Integer
  123. = (APR_HOOK_FIRST,APR_HOOK_MIDDLE,APR_HOOK_LAST);
  124. Procedure InitApache;
  125. begin
  126. Application:=TCustomApacheApplication.Create(Nil);
  127. end;
  128. Procedure DoneApache;
  129. begin
  130. Try
  131. FreeAndNil(Application);
  132. except
  133. if ShowCleanUpErrors then
  134. Raise;
  135. end;
  136. end;
  137. Function DefaultApacheHandler(P : PRequest_Rec) : integer;cdecl;
  138. begin
  139. If (AlternateHandler<>Nil) then
  140. Result:=AlterNateHandler(P)
  141. else
  142. If Application.AllowRequest(P) then
  143. Result:=Application.ProcessRequest(P)
  144. else
  145. Result:=DECLINED;
  146. end;
  147. Procedure RegisterApacheHooks(P: PApr_pool_t);cdecl;
  148. Var
  149. H : ap_hook_handler_t;
  150. PP1,PP2 : PPChar;
  151. begin
  152. H:=AlternateHandler;
  153. If (H=Nil) then
  154. H:=@DefaultApacheHandler;
  155. PP1:=Nil;
  156. PP2:=Nil;
  157. ap_hook_handler(H,PP1,PP2,HPRIO[Application.HandlerPriority]);
  158. end;
  159. { TCustomApacheApplication }
  160. function TCustomApacheApplication.GetModules(Index: integer): TStrings;
  161. begin
  162. If (FModules[Index]=Nil) then
  163. FModules[Index]:=TStringList.Create;
  164. Result:=FModules[Index];
  165. end;
  166. procedure TCustomApacheApplication.SetModules(Index: integer;
  167. const AValue: TStrings);
  168. begin
  169. If (FModules[Index]=Nil) then
  170. FModules[Index]:=TStringList.Create;
  171. FModules[Index].Assign(AValue);
  172. end;
  173. Function TCustomApacheApplication.ProcessRequest(P: PRequest_Rec) : Integer;
  174. Var
  175. Req : TApacheRequest;
  176. Resp : TApacheResponse;
  177. begin
  178. Req:=TApacheRequest.CreateReq(Self,P);
  179. Try
  180. Resp:=TApacheResponse.CreateApache(Req);
  181. Try
  182. HandleRequest(Req,Resp);
  183. Finally
  184. Result:=OK;
  185. Resp.Free;
  186. end;
  187. Finally
  188. Req.Free;
  189. end;
  190. end;
  191. function TCustomApacheApplication.GetModuleName(Arequest: TRequest): string;
  192. begin
  193. Result:=ARequest.GetNextPathInfo;
  194. end;
  195. function TCustomApacheApplication.FindModule(ModuleClass : TCustomHTTPModuleClass): TCustomHTTPModule;
  196. Var
  197. I : Integer;
  198. begin
  199. I:=ComponentCount-1;
  200. While (I>=0) and (Not (Components[i] is ModuleClass)) do
  201. Dec(i);
  202. if (I>=0) then
  203. Result:=Components[i] as TCustomHTTPModule
  204. else
  205. Result:=Nil;
  206. end;
  207. procedure TCustomApacheApplication.DoRun;
  208. begin
  209. inherited DoRun;
  210. end;
  211. function TCustomApacheApplication.AllowRequest(P: PRequest_Rec): Boolean;
  212. Var
  213. Hn : String;
  214. begin
  215. HN:=StrPas(p^.Handler);
  216. Result:=CompareText(HN,FHandlerName)=0;
  217. If Assigned(FBeforeRequest) then
  218. FBeforeRequest(Self,HN,Result);
  219. end;
  220. constructor TCustomApacheApplication.Create(AOwner: TComponent);
  221. begin
  222. inherited Create(AOwner);
  223. FAllowDefaultModule:=True;
  224. FPriority:=hpMiddle;
  225. end;
  226. procedure TCustomApacheApplication.SetModuleRecord(var ModuleRecord: Module);
  227. begin
  228. FModuleRecord:=@ModuleRecord;
  229. FillChar(ModuleRecord,SizeOf(ModuleRecord),0);
  230. end;
  231. procedure TCustomApacheApplication.Initialize;
  232. begin
  233. If (FModuleRecord=nil) then
  234. Raise EFPApacheError.Create(SErrNoModuleRecord);
  235. if (FModuleName='') and (FModuleRecord^.Name=Nil) then
  236. Raise EFPApacheError.Create(SErrNoModuleName);
  237. STANDARD20_MODULE_STUFF(FModuleRecord^);
  238. If (StrPas(FModuleRecord^.name)<>FModuleName) then
  239. FModuleRecord^.Name:=PChar(FModuleName);
  240. FModuleRecord^.register_hooks:=@RegisterApacheHooks;
  241. end;
  242. procedure TCustomApacheApplication.ShowRequestException(R : TResponse; E: Exception);
  243. Var
  244. TheEmail : String;
  245. FrameCount: integer;
  246. Frames: PPointer;
  247. FrameNumber:Integer;
  248. S : TStrings;
  249. begin
  250. If not R.HeadersSent then
  251. begin
  252. R.ContentType:='text/html';
  253. R.SendHeaders;
  254. end;
  255. If (R.ContentType='text/html') then
  256. begin
  257. S:=TStringList.Create;
  258. Try
  259. With S do
  260. begin
  261. Add('<html><head><title>'+Title+': '+SModuleError+'</title></head>'+LineEnding);
  262. Add('<body>');
  263. Add('<center><hr><h1>'+Title+': ERROR</h1><hr></center><br><br>');
  264. Add(SAppEncounteredError+'<br>');
  265. Add('<ul>');
  266. Add('<li>'+SError+' <b>'+E.Message+'</b>');
  267. Add('<li> Stack trace:<br>');
  268. Add(BackTraceStrFunc(ExceptAddr)+'<br>');
  269. FrameCount:=ExceptFrameCount;
  270. Frames:=ExceptFrames;
  271. for FrameNumber := 0 to FrameCount-1 do
  272. Add(BackTraceStrFunc(Frames[FrameNumber])+'<br>');
  273. Add('</ul><hr>');
  274. TheEmail:=Email;
  275. If (TheEmail<>'') then
  276. Add('<h5><p><i>'+SNotify+Administrator+': <a href="mailto:'+TheEmail+'">'+TheEmail+'</a></i></p></h5>');
  277. Add('</body></html>');
  278. end;
  279. R.Content:=S.Text;
  280. R.SendContent;
  281. Finally
  282. FreeAndNil(S);
  283. end;
  284. end;
  285. end;
  286. procedure TCustomApacheApplication.ShowException(E: Exception);
  287. begin
  288. ap_log_error(pchar(FModuleName),0,APLOG_ERR,0,Nil,'module: %s',[Pchar(E.Message)]);
  289. end;
  290. procedure TCustomApacheApplication.CreateForm(AClass: TComponentClass;
  291. var Reference: TComponent);
  292. begin
  293. Reference:=AClass.Create(Self);
  294. end;
  295. procedure TCustomApacheApplication.HandleRequest(ARequest: TRequest; AResponse: TResponse);
  296. Var
  297. MC : TCustomHTTPModuleClass;
  298. M : TCustomHTTPModule;
  299. MN : String;
  300. MI : TModuleItem;
  301. begin
  302. try
  303. MC:=Nil;
  304. If (OnGetModule<>Nil) then
  305. OnGetModule(Self,ARequest,MC);
  306. If (MC=Nil) then
  307. begin
  308. MN:=GetModuleName(ARequest);
  309. If (MN='') and Not AllowDefaultModule then
  310. Raise EFPApacheError.Create(SErrNoModuleNameForRequest);
  311. MI:=ModuleFactory.FindModule(MN);
  312. If (MI=Nil) and (ModuleFactory.Count=1) then
  313. MI:=ModuleFactory[0];
  314. if (MI=Nil) then
  315. begin
  316. Raise EFPApacheError.CreateFmt(SErrNoModuleForRequest,[MN]);
  317. end;
  318. MC:=MI.ModuleClass;
  319. M:=FindModule(MC); // Check if a module exists already
  320. end;
  321. If (M=Nil) then
  322. begin
  323. M:=MC.Create(Self);
  324. end;
  325. M.HandleRequest(ARequest,AResponse);
  326. except
  327. On E : Exception do
  328. ShowRequestException(AResponse,E);
  329. end;
  330. end;
  331. { TApacheRequest }
  332. function TApacheRequest.GetFieldValue(Index: Integer): String;
  333. var
  334. P : Pchar;
  335. FN : String;
  336. I : Integer;
  337. begin
  338. Result:='';
  339. If (Index in [1..NoHTTPFields]) then
  340. begin
  341. FN:=HTTPFieldNames[Index];
  342. P:=apr_table_get(FRequest^.headers_in,pchar(FN));
  343. If (P<>Nil) then
  344. Result:=StrPas(P);
  345. end;
  346. if (Result='') then
  347. case Index of
  348. 0 : Result:=strpas(FRequest^.protocol); // ProtocolVersion
  349. 7 : Result:=Strpas(FRequest^.content_encoding); //ContentEncoding
  350. 25 : Result:=StrPas(FRequest^.path_info); // PathInfo
  351. 26 : Result:=StrPas(FRequest^.filename); // PathTranslated
  352. 27 : // RemoteAddr
  353. If (FRequest^.Connection<>Nil) then
  354. Result:=StrPas(FRequest^.Connection^.remote_ip);
  355. 28 : // RemoteHost
  356. ap_get_remote_host(FRequest^.Connection,
  357. FRequest^.Per_Dir_Config,
  358. REMOTE_HOST,Nil);
  359. 29 : begin // ScriptName
  360. Result:=StrPas(FRequest^.unparsed_uri);
  361. I:=Pos('?',Result)-1;
  362. If (I=-1) then
  363. I:=Length(Result);
  364. Result:=Copy(Result,1,I-Length(PathInfo));
  365. end;
  366. 30 : Result:=IntToStr(ap_get_server_port(FRequest)); // ServerPort
  367. 31 : Result:=StrPas(FRequest^.method); // Method
  368. 32 : Result:=StrPas(FRequest^.unparsed_uri); // URL
  369. 33 : Result:=StrPas(FRequest^.args); // Query
  370. 34 : Result:=StrPas(FRequest^.HostName); // Host
  371. 35 : begin // Content
  372. If Not FContentRead then
  373. ReadContent;
  374. Result:=FContent;
  375. end;
  376. else
  377. Result:=inherited GetFieldValue(Index);
  378. end;
  379. end;
  380. procedure TApacheRequest.ReadContent;
  381. Function MinS(A,B : Integer) : Integer;
  382. begin
  383. If A<B then
  384. Result:=A
  385. else
  386. Result:=B;
  387. end;
  388. Var
  389. Left,Len,Count,Bytes : Integer;
  390. P : Pchar;
  391. begin
  392. ap_setup_client_block(Frequest,REQUEST_CHUNKED_DECHUNK);
  393. If (ap_should_client_block(Frequest)=1) then
  394. begin
  395. Len:=ContentLength;
  396. If (Len>0) then
  397. begin
  398. SetLength(FContent,Len);
  399. P:=PChar(FContent);
  400. Left:=Len;
  401. Count:=0;
  402. Repeat
  403. Bytes:=ap_get_client_block(FRequest,P,MinS(10*1024,Left));
  404. Dec(Left,Bytes);
  405. Inc(P,Bytes);
  406. Inc(Count,Bytes);
  407. Until (Count>=Len) or (Bytes=0);
  408. SetLength(FContent,Count);
  409. end;
  410. end;
  411. FContentRead:=True;
  412. end;
  413. procedure TApacheRequest.InitFromRequest;
  414. Var
  415. I : Integer;
  416. S : String;
  417. begin
  418. // This fills the internal table. We should try
  419. // to get rid of it.
  420. For I:=0 to NoHTTPFields do
  421. begin
  422. S:=GetFieldValue(i);
  423. If (S<>'') then
  424. SetFieldValue(I,S);
  425. end;
  426. end;
  427. Constructor TApacheRequest.CreateReq(App : TCustomApacheApplication; ARequest : PRequest_rec);
  428. begin
  429. FApache:=App;
  430. FRequest:=Arequest;
  431. ReturnedPathInfo:=App.BaseLocation;
  432. InitFromRequest;
  433. Inherited Create;
  434. end;
  435. { TApacheResponse }
  436. procedure TApacheResponse.DoSendHeaders(Headers: TStrings);
  437. Var
  438. I,P : Integer;
  439. N,V : String;
  440. begin
  441. For I:=0 to Headers.Count-1 do
  442. begin
  443. V:=Headers[i];
  444. P:=Pos(':',V);
  445. If (P<>0) and (P<Length(V)) then
  446. begin
  447. N:=Copy(V,1,P-1);
  448. System.Delete(V,1,P);
  449. apr_table_set(FRequest^.headers_out,Pchar(N),Pchar(V));
  450. end;
  451. end;
  452. end;
  453. procedure TApacheResponse.DoSendContent;
  454. Var
  455. S : String;
  456. I : Integer;
  457. begin
  458. S:=ContentType;
  459. If (S<>'') then
  460. FRequest^.content_type:=apr_pstrdup(FRequest^.pool,Pchar(S));
  461. If (ContentStream<>Nil) then
  462. SendStream(Contentstream)
  463. else
  464. for I:=0 to Contents.Count-1 do
  465. begin
  466. S:=Contents[i]+LineEnding;
  467. // If there is a null, it's written also with ap_rwrite
  468. ap_rwrite(PChar(S),Length(S),FRequest);
  469. end;
  470. end;
  471. Procedure TApacheResponse.SendStream(S : TStream);
  472. Var
  473. Buf : Array[0..(10*1024)-1] of Byte;
  474. Count : Integer;
  475. begin
  476. Repeat
  477. Count:=S.Read(Buf,SizeOf(Buf));
  478. If Count>0 then
  479. ap_rwrite(@Buf,Count,FRequest);
  480. Until (Count=0);
  481. end;
  482. Constructor TApacheResponse.CreateApache(Req : TApacheRequest);
  483. begin
  484. FApache:=Req.ApacheApp;
  485. Frequest:=Req.ApacheRequest;
  486. Inherited Create(Req);
  487. end;
  488. Initialization
  489. InitApache;
  490. Finalization
  491. DoneApache;
  492. end.