brookrouter.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. (*
  2. Brook for Free Pascal
  3. Copyright (C) 2014-2019 Silvio Clecio
  4. See the file LICENSE.txt, included in this distribution,
  5. for details about the copyright.
  6. This library is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. *)
  10. { Router classes. }
  11. unit BrookRouter;
  12. {$i brook.inc}
  13. interface
  14. uses
  15. BrookClasses, BrookHttpDefs, BrookException, BrookAction, BrookUtils,
  16. BrookConsts, BrookMessages, BrookHTTPConsts, HTTPDefs, Classes, SysUtils,
  17. StrUtils;
  18. type
  19. { Handles exceptions for @link(TBrookRoutes). }
  20. EBrookRoutes = class(EBrook);
  21. { Handles exceptions for @link(TBrookRouter). }
  22. EBrookRouter = class(EBrook);
  23. { Is a metaclass for @link(TBrookRoutes) class. }
  24. TBrookRoutesClass = class of TBrookRoutes;
  25. { Is a metaclass for @link(TBrookRouter) class. }
  26. TBrookRouterClass = class of TBrookRouter;
  27. { Defines a route item. }
  28. TBrookRoute = record
  29. { Specifies the class of the action to be called. }
  30. ActionClass: TBrookActionClass;
  31. { Checks if the action is default. }
  32. Default: Boolean;
  33. { Specifies a HTTP request method of the action to be called. }
  34. Method: TBrookRequestMethod;
  35. { Specifies the patter of the action to be called. }
  36. Pattern: string;
  37. end;
  38. { Defines a pointer to the route item.}
  39. PBrookRoute = ^TBrookRoute;
  40. { Is a type to @code(*MatchPattern) event. }
  41. TBrookMatchPatternEvent = function(ASender: TObject;
  42. APattern, APathInfo: string; out ARedirect: Boolean;
  43. out ANames, AValues: TBrookArrayOfString;
  44. var AHandled: Boolean): Boolean of object;
  45. { Defines a pointer to the match pattern event.}
  46. PBrookMatchPatternEvent = ^TBrookMatchPatternEvent;
  47. { Is a type to @code(*Route) event. }
  48. TBrookRouteEvent = procedure(ASender: TObject; ARequest: TBrookRequest;
  49. AResponse: TBrookResponse; var AHandled: Boolean) of object;
  50. { Defines a pointer to the route event.}
  51. PBrookRouteEvent = ^TBrookRouteEvent;
  52. { Is a type to @code(*ExecuteAction) event. }
  53. TBrookExecuteActionEvent = procedure(ASender: TObject; AAction: TBrookAction;
  54. ARequest: TBrookRequest; AResponse: TBrookResponse; const ANames,
  55. AValues: TBrookArrayOfString; ARoute: TBrookRoute;
  56. var AHandled: Boolean) of object;
  57. { Defines a pointer to the execute action event.}
  58. PBrookExecuteActionEvent = ^TBrookExecuteActionEvent;
  59. { Defines a list of routes. }
  60. TBrookRoutes = class(TBrookPersistent)
  61. private
  62. FList: TFPList;
  63. function GetItems(const AIndex: Integer): PBrookRoute;
  64. procedure SetItems(const AIndex: Integer; const AValue: PBrookRoute);
  65. protected
  66. procedure FreeRoutes; virtual;
  67. property List: TFPList read FList;
  68. public
  69. { Creates an instance of a @link(TBrookRoutes) class. }
  70. constructor Create; virtual;
  71. { Frees an instance of @link(TBrookRoutes) class. }
  72. destructor Destroy; override;
  73. { Clears all routes. }
  74. procedure Clear;
  75. { Returns the number of registered routes. }
  76. function Count: Integer;
  77. { Adds a route item. }
  78. function Add(AActionClass: TBrookActionClass; const APattern: string;
  79. const AMethod: TBrookRequestMethod; const ADefault: Boolean): Integer;
  80. { Get the default action class. }
  81. procedure GetDefaultActionClass(out AClass: TBrookActionClass;
  82. out AIndex: Integer);
  83. { Get the action class with empty pattern. }
  84. procedure GetEmptyPatternActionClass(out AClass: TBrookActionClass;
  85. out AIndex: Integer);
  86. { Get the registered pattern of a class. }
  87. function PatternByActionClass(AClass: TBrookActionClass): string;
  88. { Get the action class from a patter. }
  89. function ActionClassByPattern(const APattern: string): TBrookActionClass;
  90. { Get an action class from its class name. }
  91. function ActionClassByClassName(const AName: string): TBrookActionClass;
  92. { The list of routes. }
  93. property Items[const AIndex: Integer]: PBrookRoute read GetItems
  94. write SetItems; default;
  95. end;
  96. { Provides features for the route handling. }
  97. TBrookRouter = class(TBrookComponent)
  98. private
  99. FAfterExecuteAction: TBrookExecuteActionEvent;
  100. FAfterMatchPattern: TBrookMatchPatternEvent;
  101. FAfterRoute: TBrookRouteEvent;
  102. FBeforeExecuteAction: TBrookExecuteActionEvent;
  103. FBeforeMatchPattern: TBrookMatchPatternEvent;
  104. FBeforeRoute: TBrookRouteEvent;
  105. FRoutes: TBrookRoutes;
  106. protected
  107. function CreateRoutes: TBrookRoutes; virtual;
  108. procedure FreeRoutes(ARoutes: TBrookRoutes); virtual;
  109. function CreateAction(out AActionClass: TBrookActionClass;
  110. ARequest: TBrookRequest; AResponse: TBrookResponse): TBrookAction; virtual;
  111. procedure FreeAction(AAction: TBrookAction); virtual;
  112. procedure ExecuteAction(AAction: TBrookAction; ARequest: TBrookRequest;
  113. AResponse: TBrookResponse; const ANames, AValues: TBrookArrayOfString;
  114. ARoute: TBrookRoute); overload; virtual;
  115. public
  116. { Creates an instance of a @link(TBrookRouter) class. }
  117. constructor Create(AOwner: TComponent); override;
  118. { Frees an instance of @link(TBrookRouter) class. }
  119. destructor Destroy; override;
  120. { Return the service class provided by this class. }
  121. class function GetServiceClass: TBrookRouterClass;
  122. { Registers the service provided by this class. }
  123. class procedure RegisterService;
  124. { Unregisters the service provided by this class. }
  125. class procedure UnregisterService;
  126. { Return an instance of this class. }
  127. class function Service: TBrookRouter;
  128. { Return the root URL. }
  129. class function RootUrl: string;
  130. { Return the root URL passing @code(TBrookRequest) as param. }
  131. class function RootUrl(ARequest: TBrookRequest): string;
  132. { Sends the HTTP "NotAllowed" status code to the response. }
  133. class procedure MethodNotAllowed(AResponse: TBrookResponse);
  134. { Creates an URL for an action informing an array of parameters. Exemple:
  135. @longCode(
  136. procedure TMyAction.Get;
  137. begin
  138. // When calling with http://localhost/cgi-bin/cgi1/foo/myvalue
  139. // the output will be /cgi-bin/cgi1/foo/myvalue
  140. Write(UrlFor(TMyAction, ['myvalue']));
  141. end;
  142. initialization
  143. TMyAction.Register('/foo/:myvar');) }
  144. function UrlFor(AActionClass: TBrookActionClass;
  145. const AParams: array of string): string; overload;
  146. { Creates an URL for an action passing an array of parameters however
  147. informing the class name as string }
  148. function UrlFor(AClassName: string;
  149. const AParams: array of string): string; overload;
  150. { Adds an slash to the end of the URL if does not exist. }
  151. function Canonicalize(ARequest: TBrookRequest;
  152. AResponse: TBrookResponse): Boolean;
  153. { Checks if the given parameters match with a registered route. }
  154. function MatchPattern(APattern, APathInfo: string; out ARedirect: Boolean;
  155. out ANames, AValues: TBrookArrayOfString): Boolean; virtual;
  156. { Runs the route processing. }
  157. procedure Route(ARequest: TBrookRequest; AResponse: TBrookResponse); virtual;
  158. { List of available routes. }
  159. property Routes: TBrookRoutes read FRoutes write FRoutes;
  160. { Is triggered after the router executes a action. }
  161. property AfterExecuteAction: TBrookExecuteActionEvent
  162. read FAfterExecuteAction write FAfterExecuteAction;
  163. { Is triggered after the router matches a pattern. }
  164. property AfterMatchPattern: TBrookMatchPatternEvent
  165. read FAfterMatchPattern write FAfterMatchPattern;
  166. { Is triggered after the router is routing. }
  167. property AfterRoute: TBrookRouteEvent read FAfterRoute
  168. write FAfterRoute;
  169. { Is triggered before the router executes a action. }
  170. property BeforeExecuteAction: TBrookExecuteActionEvent
  171. read FBeforeExecuteAction write FBeforeExecuteAction;
  172. { Is triggered before the router matches a pattern. }
  173. property BeforeMatchPattern: TBrookMatchPatternEvent
  174. read FBeforeMatchPattern write FBeforeMatchPattern;
  175. { Is triggered before the router is routing. }
  176. property BeforeRoute: TBrookRouteEvent read FBeforeRoute
  177. write FBeforeRoute;
  178. end;
  179. implementation
  180. var
  181. _BrookRouterService: TBrookRouter = nil;
  182. _BrookRouterServiceClass: TBrookRouterClass = nil;
  183. { TBrookRoutes }
  184. constructor TBrookRoutes.Create;
  185. begin
  186. inherited Create;
  187. FList := TFPList.Create;
  188. end;
  189. destructor TBrookRoutes.Destroy;
  190. begin
  191. FreeRoutes;
  192. FList.Free;
  193. inherited Destroy;
  194. end;
  195. procedure TBrookRoutes.Clear;
  196. begin
  197. FreeRoutes;
  198. FList.Clear;
  199. end;
  200. function TBrookRoutes.Count: Integer;
  201. begin
  202. Result := FList.Count;
  203. end;
  204. function TBrookRoutes.GetItems(const AIndex: Integer): PBrookRoute;
  205. begin
  206. Result := FList.Items[AIndex];
  207. end;
  208. procedure TBrookRoutes.SetItems(const AIndex: Integer; const AValue: PBrookRoute);
  209. begin
  210. FList.Items[AIndex] := AValue;
  211. end;
  212. procedure TBrookRoutes.FreeRoutes;
  213. var
  214. P: PBrookRoute;
  215. begin
  216. for P in FList do
  217. Dispose(P);
  218. end;
  219. function TBrookRoutes.Add(AActionClass: TBrookActionClass;
  220. const APattern: string; const AMethod: TBrookRequestMethod;
  221. const ADefault: Boolean): Integer;
  222. var
  223. PRoute: PBrookRoute;
  224. begin
  225. for PRoute in FList do
  226. begin
  227. if BrookSettings.Mapped then
  228. begin
  229. if (PRoute^.ActionClass = AActionClass) and
  230. (PRoute^.Pattern = APattern) and (PRoute^.Method = AMethod) then
  231. raise EBrookRoutes.CreateFmt(Self, SBrookActionAlreadyRegisteredError,
  232. [AActionClass.ClassName]);
  233. if (PRoute^.Pattern = APattern) and (PRoute^.Method = AMethod) then
  234. raise EBrookRoutes.CreateFmt(Self, SBrookPatternAlreadyRegisteredError,
  235. [APattern]);
  236. end
  237. else
  238. begin
  239. if (PRoute^.ActionClass = AActionClass) and
  240. (PRoute^.Pattern = APattern) then
  241. raise EBrookRoutes.CreateFmt(Self, SBrookActionAlreadyRegisteredError,
  242. [AActionClass.ClassName]);
  243. if PRoute^.Pattern = APattern then
  244. raise EBrookRoutes.CreateFmt(Self, SBrookPatternAlreadyRegisteredError,
  245. [APattern]);
  246. end;
  247. if ADefault and PRoute^.Default and (PRoute^.ActionClass <> AActionClass) then
  248. raise EBrookRoutes.Create(Self, SBrookDefaultActionAlreadyRegisteredError);
  249. end;
  250. New(PRoute);
  251. PRoute^.ActionClass := AActionClass;
  252. PRoute^.Default := ADefault;
  253. PRoute^.Method := AMethod;
  254. PRoute^.Pattern := APattern;
  255. Result := FList.Add(PRoute);
  256. end;
  257. procedure TBrookRoutes.GetDefaultActionClass(out AClass: TBrookActionClass;
  258. out AIndex: Integer);
  259. var
  260. I: Integer;
  261. PRoute: PBrookRoute;
  262. begin
  263. for I := 0 to Pred(FList.Count) do
  264. begin
  265. PRoute := FList[I];
  266. if PRoute^.Default then
  267. begin
  268. AIndex := I;
  269. AClass := PRoute^.ActionClass;
  270. Exit;
  271. end;
  272. end;
  273. AIndex := -1;
  274. AClass := nil;
  275. end;
  276. procedure TBrookRoutes.GetEmptyPatternActionClass(out
  277. AClass: TBrookActionClass; out AIndex: Integer);
  278. var
  279. I: Integer;
  280. PRoute: PBrookRoute;
  281. begin
  282. for I := 0 to Pred(FList.Count) do
  283. begin
  284. PRoute := FList[I];
  285. if PRoute^.Pattern = ES then
  286. begin
  287. AIndex := I;
  288. AClass := PRoute^.ActionClass;
  289. Exit;
  290. end;
  291. end;
  292. AIndex := -1;
  293. AClass := nil;
  294. end;
  295. function TBrookRoutes.PatternByActionClass(AClass: TBrookActionClass): string;
  296. var
  297. PRoute: PBrookRoute;
  298. begin
  299. for PRoute in FList do
  300. if PRoute^.ActionClass = AClass then
  301. begin
  302. Result := PRoute^.Pattern;
  303. Exit;
  304. end;
  305. Result := ES;
  306. end;
  307. function TBrookRoutes.ActionClassByPattern(
  308. const APattern: string): TBrookActionClass;
  309. var
  310. PRoute: PBrookRoute;
  311. begin
  312. for PRoute in FList do
  313. if PRoute^.Pattern = APattern then
  314. begin
  315. Result := PRoute^.ActionClass;
  316. Exit;
  317. end;
  318. Result := nil;
  319. end;
  320. function TBrookRoutes.ActionClassByClassName(
  321. const AName: string): TBrookActionClass;
  322. var
  323. PRoute: PBrookRoute;
  324. begin
  325. for PRoute in FList do
  326. if SameText(PRoute^.ActionClass.ClassName, AName) then
  327. begin
  328. Result := PRoute^.ActionClass;
  329. Exit;
  330. end;
  331. Result := nil;
  332. end;
  333. { TBrookRouter }
  334. constructor TBrookRouter.Create(AOwner: TComponent);
  335. begin
  336. inherited Create(AOwner);
  337. FRoutes := CreateRoutes;
  338. end;
  339. destructor TBrookRouter.Destroy;
  340. begin
  341. FreeRoutes(FRoutes);
  342. inherited Destroy;
  343. end;
  344. class function TBrookRouter.GetServiceClass: TBrookRouterClass;
  345. begin
  346. Result := _BrookRouterServiceClass;
  347. end;
  348. function TBrookRouter.CreateRoutes: TBrookRoutes;
  349. begin
  350. Result := TBrookRoutes.Create;
  351. end;
  352. procedure TBrookRouter.FreeRoutes(ARoutes: TBrookRoutes);
  353. begin
  354. FreeAndNil(ARoutes);
  355. end;
  356. function TBrookRouter.CreateAction(out AActionClass: TBrookActionClass;
  357. ARequest: TBrookRequest; AResponse: TBrookResponse): TBrookAction;
  358. begin
  359. Result := AActionClass.Create(ARequest, AResponse);
  360. end;
  361. procedure TBrookRouter.FreeAction(AAction: TBrookAction);
  362. begin
  363. AAction.Free;
  364. end;
  365. procedure TBrookRouter.ExecuteAction(AAction: TBrookAction;
  366. ARequest: TBrookRequest; AResponse: TBrookResponse; const ANames,
  367. AValues: TBrookArrayOfString; ARoute: TBrookRoute);
  368. var
  369. VHandled: Boolean = False;
  370. begin
  371. try
  372. if Assigned(FBeforeExecuteAction) then
  373. FBeforeExecuteAction(Self, AAction, ARequest, AResponse, ANames, AValues,
  374. ARoute, VHandled);
  375. if not VHandled then
  376. begin
  377. AAction.DoFillVariables(ANames, AValues);
  378. AAction.DoRequest(ARequest, AResponse);
  379. end;
  380. finally
  381. if Assigned(FAfterExecuteAction) then
  382. FAfterExecuteAction(Self, AAction, ARequest, AResponse, ANames, AValues,
  383. ARoute, VHandled);
  384. end;
  385. end;
  386. class procedure TBrookRouter.RegisterService;
  387. begin
  388. if Assigned(_BrookRouterServiceClass) then
  389. raise EBrookRouter.Create(Self, SBrookRouterServiceAlreadyRegisteredError);
  390. _BrookRouterServiceClass := Self;
  391. end;
  392. class procedure TBrookRouter.UnregisterService;
  393. begin
  394. FreeAndNil(_BrookRouterService);
  395. _BrookRouterServiceClass := nil;
  396. end;
  397. class function TBrookRouter.Service: TBrookRouter;
  398. begin
  399. if not Assigned(_BrookRouterService) then
  400. begin
  401. if not Assigned(_BrookRouterServiceClass) then
  402. raise EBrookRouter.Create(Self, SBrookNoRouterServiceRegisteredError);
  403. _BrookRouterService := _BrookRouterServiceClass.Create(nil);
  404. end;
  405. Result := _BrookRouterService;
  406. end;
  407. class function TBrookRouter.RootUrl: string;
  408. begin
  409. if BrookSettings.RootUrl = ES then
  410. Result := GetEnvironmentVariable(BROOK_SRV_ENV_SCRIPT_NAME)
  411. else
  412. Result := BrookSettings.RootUrl;
  413. end;
  414. class function TBrookRouter.RootUrl(ARequest: TBrookRequest): string;
  415. begin
  416. if BrookSettings.RootUrl = ES then
  417. Result := ARequest.ScriptName
  418. else
  419. Result := BrookSettings.RootUrl;
  420. end;
  421. class procedure TBrookRouter.MethodNotAllowed(AResponse: TBrookResponse);
  422. begin
  423. AResponse.Code := BROOK_HTTP_STATUS_CODE_METHOD_NOT_ALLOWED;
  424. AResponse.CodeText := BROOK_HTTP_REASON_PHRASE_METHOD_NOT_ALLOWED;
  425. AResponse.Contents.Add(SBrookMethodNotAllowedError);
  426. end;
  427. function TBrookRouter.UrlFor(AActionClass: TBrookActionClass;
  428. const AParams: array of string): string;
  429. var
  430. S, VVal: string;
  431. I, B, E: Integer;
  432. begin
  433. Result := ES;
  434. S := FRoutes.PatternByActionClass(AActionClass);
  435. if Length(S) = 0 then
  436. Exit;
  437. if S[1] = AK then
  438. Delete(S, 1, 1);
  439. for I := 0 to High(AParams) do
  440. begin
  441. VVal := HTTPEncode(AParams[I]);
  442. B := Pos(CO, S);
  443. if B = 0 then
  444. B := Pos(AK,S);
  445. if B <> 0 then
  446. begin
  447. E := PosEx(US, S, B);
  448. if E <> 0 then
  449. begin
  450. Delete(S, B, E - B);
  451. Insert(VVal, S, B);
  452. end
  453. else
  454. begin
  455. Delete(S, B, MaxInt);
  456. Insert(VVal, S, MaxInt);
  457. end;
  458. end;
  459. end;
  460. Result := BrookExcludeTrailingUrlDelimiter(TBrookRouter.RootUrl) + S;
  461. end;
  462. function TBrookRouter.UrlFor(AClassName: string;
  463. const AParams: array of string): string;
  464. begin
  465. Result := UrlFor(FRoutes.ActionClassByClassName(AClassName), AParams);
  466. end;
  467. function TBrookRouter.Canonicalize(ARequest: TBrookRequest;
  468. AResponse: TBrookResponse): Boolean;
  469. var
  470. L: LongInt;
  471. VURL, VQueryStr, VPathInfo: string;
  472. begin
  473. VQueryStr := ARequest.QueryString;
  474. if VQueryStr <> ES then
  475. VQueryStr := QU + VQueryStr;
  476. VPathInfo := Copy(ARequest.PathInfo, 1, Pos(QU, ARequest.PathInfo) - 1);
  477. if VPathInfo = ES then
  478. VPathInfo := ARequest.PathInfo;
  479. VURL := TBrookRouter.RootUrl(ARequest) + VPathInfo;
  480. L := Length(VURL);
  481. Result := ((L > 0) and (VURL[L] <> US)) or (VURL = ES);
  482. if Result then
  483. AResponse.SendRedirect(LowerCase(VURL) + US + VQueryStr);
  484. end;
  485. function TBrookRouter.MatchPattern(APattern, APathInfo: string;
  486. out ARedirect: Boolean; out ANames, AValues: TBrookArrayOfString): Boolean;
  487. procedure ExtractNextPathLevel(var ALeftPart: string;
  488. var ALvl: string; var ARightPart: string; const ADelimiter: Char = US);
  489. var
  490. P: Integer;
  491. begin
  492. if ALvl <> ADelimiter then
  493. begin
  494. ALeftPart := ALeftPart + ALvl;
  495. if BrookStartsChar(ADelimiter, ARightPart) then
  496. begin
  497. ALeftPart := ALeftPart + ADelimiter;
  498. Delete(ARightPart, 1, 1);
  499. end;
  500. end;
  501. P := Pos(ADelimiter, ARightPart);
  502. if P = 0 then
  503. P := Length(ARightPart) + 1;
  504. ALvl := Copy(ARightPart, 1, P - 1);
  505. ARightPart := Copy(ARightPart, P, MaxInt);
  506. end;
  507. procedure ExtractPrevPathLevel(var ALeftPart: string;
  508. var ALvl: string; var ARightPart: string; const ADelimiter: Char = US);
  509. var
  510. P: Integer;
  511. begin
  512. if ALvl <> ADelimiter then
  513. begin
  514. ARightPart := ALvl + ARightPart;
  515. if BrookEndsChar(ADelimiter, ALeftPart) then
  516. begin
  517. ARightPart := ADelimiter + ARightPart;
  518. Delete(ALeftPart, Length(ALeftPart), 1);
  519. end;
  520. end;
  521. P := RPos(ADelimiter, ALeftPart);
  522. ALvl := Copy(ALeftPart, P + 1, MaxInt);
  523. ALeftPart := Copy(ALeftPart, 1, P);
  524. end;
  525. var
  526. VCount: Integer;
  527. VResult: Boolean;
  528. VHandled: Boolean = False;
  529. VLeftPat, VRightPat, VLeftVal, VRightVal, VVal, VPat, VName: string;
  530. begin
  531. try
  532. if Assigned(FBeforeMatchPattern) then
  533. VResult := FBeforeMatchPattern(Self, APattern, APathInfo, ARedirect,
  534. ANames, AValues, VHandled);
  535. if VHandled then
  536. Exit(VResult);
  537. Result := False;
  538. ARedirect := False;
  539. if APattern = ES then
  540. Exit;
  541. if (APattern = US) and (APathInfo = ES) then
  542. begin
  543. ARedirect := True;
  544. Exit(True);
  545. end;
  546. Delete(APattern, Pos(QU, APattern), MaxInt);
  547. Delete(APathInfo, Pos(QU, APathInfo), MaxInt);
  548. if BrookStartsChar(US, APattern) then
  549. Delete(APattern, 1, 1);
  550. if BrookStartsChar(US, APathInfo) then
  551. Delete(APathInfo, 1, 1);
  552. VLeftPat := ES;
  553. VLeftVal := ES;
  554. VPat := US; // init value is '/', not ''
  555. VVal := US; // init value is '/', not ''
  556. VRightPat := APattern;
  557. VRightVal := APathInfo;
  558. VCount := 1;
  559. repeat
  560. // Extract next part
  561. ExtractNextPathLevel(VLeftPat, VPat, VRightPat);
  562. ExtractNextPathLevel(VLeftVal, VVal, VRightVal);
  563. if BrookStartsChar(CO, VPat) then
  564. begin
  565. // :field
  566. SetLength(ANames, VCount);
  567. SetLength(AValues, VCount);
  568. ANames[VCount - 1] := Copy(VPat, 2, MaxInt);
  569. AValues[VCount - 1] := VVal;
  570. Inc(VCount);
  571. end
  572. else
  573. if BrookStartsChar(AK, VPat) then
  574. begin
  575. // *path
  576. VName := Copy(VPat, 2, MaxInt);
  577. VLeftPat := VRightPat;
  578. VLeftVal := VVal + VRightVal;
  579. VPat := US; // init value is '/', not ''
  580. VVal := US; // init value is '/', not ''
  581. VRightPat := ES;
  582. VRightVal := ES;
  583. // if AutoAddSlash ...
  584. if BrookEndsChar(US, VLeftPat) and not BrookEndsChar(US, VLeftVal) then
  585. begin
  586. Delete(VLeftPat, Length(VLeftPat), 1);
  587. ARedirect := True; // Will be Redirect if match
  588. end;
  589. repeat
  590. // Extract backwards
  591. ExtractPrevPathLevel(VLeftPat, VPat, VRightPat);
  592. ExtractPrevPathLevel(VLeftVal, VVal, VRightVal);
  593. if BrookStartsChar(CO, VPat) then
  594. begin
  595. // *path/:field
  596. SetLength(ANames, VCount);
  597. SetLength(AValues, VCount);
  598. ANames[VCount - 1] := Copy(VPat, 2, MaxInt);
  599. AValues[VCount - 1] := VVal;
  600. Inc(VCount);
  601. end
  602. else
  603. // *path/const
  604. if not ((VPat = ES) and (VLeftPat = ES)) and (VPat <> VVal) then
  605. Exit(False);
  606. // Check if we already done
  607. if (VLeftPat = ES) or (VLeftVal = ES) then
  608. begin
  609. if VLeftPat = ES then
  610. begin
  611. SetLength(ANames, VCount);
  612. SetLength(AValues, VCount);
  613. ANames[VCount - 1] := VName;
  614. AValues[VCount - 1] := VLeftVal + VVal;
  615. Inc(VCount);
  616. Exit(True);
  617. end;
  618. Exit(False);
  619. end;
  620. until False;
  621. end
  622. else
  623. // const
  624. if VPat <> VVal then
  625. Exit(False);
  626. // Check if we already done
  627. if (VRightPat = ES) or (VRightVal = ES) then
  628. begin
  629. if (VRightPat = ES) and (VRightVal = ES) then
  630. Exit(True)
  631. else
  632. // if AutoAddSlash ...
  633. if VRightPat = US then
  634. begin
  635. ARedirect := True;
  636. Exit(True);
  637. end;
  638. Exit(False);
  639. end;
  640. until False;
  641. finally
  642. if Assigned(FAfterMatchPattern) then
  643. FAfterMatchPattern(Self, APattern, APathInfo, ARedirect, ANames, AValues,
  644. VHandled);
  645. end;
  646. end;
  647. procedure TBrookRouter.Route(ARequest: TBrookRequest; AResponse: TBrookResponse);
  648. var
  649. I, C: Integer;
  650. PRoute: PBrookRoute;
  651. VAct: TBrookAction;
  652. VHandled: Boolean = False;
  653. VActClass: TBrookActionClass = nil;
  654. VNames, VValues: TBrookArrayOfString;
  655. VTempActClass: TBrookActionClass = nil;
  656. VRedirect, VMatchMethod, VMatchPattern: Boolean;
  657. begin
  658. try
  659. if ARequest.PathInfo = ES then
  660. ARequest.PathInfo := US;
  661. if Assigned(FBeforeRoute) then
  662. FBeforeRoute(Self, ARequest, AResponse, VHandled);
  663. if VHandled then
  664. Exit;
  665. C := FRoutes.List.Count;
  666. if C = 0 then
  667. raise EBrookRouter.Create(Self, SBrookNoRouteRegisteredError);
  668. if ARequest.PathInfo = ES then
  669. begin
  670. FRoutes.GetEmptyPatternActionClass(VTempActClass, I);
  671. if I > -1 then
  672. FRoutes.List.Move(I, C - 1);
  673. end;
  674. if not Assigned(VTempActClass) then
  675. begin
  676. FRoutes.GetDefaultActionClass(VTempActClass, I);
  677. if I > -1 then
  678. FRoutes.List.Move(I, C - 1);
  679. end;
  680. if BrookSettings.Mapped then
  681. begin
  682. VMatchMethod := False;
  683. VMatchPattern := False;
  684. for PRoute in FRoutes.List do
  685. if MatchPattern(PRoute^.Pattern, ARequest.PathInfo, VRedirect,
  686. VNames, VValues) then
  687. begin
  688. if VRedirect and Canonicalize(ARequest, AResponse) then
  689. Exit;
  690. VMatchPattern := True;
  691. if not BrookMatchMethod(PRoute^.Method, ARequest.Method) then
  692. Continue;
  693. VMatchMethod := True;
  694. VActClass := PRoute^.ActionClass;
  695. // if PRoute^.Method <> rmAll then Please see issue #64
  696. Break;
  697. end;
  698. if VMatchPattern then
  699. begin
  700. if VMatchMethod then
  701. begin
  702. if not Assigned(VActClass) then
  703. if Assigned(VTempActClass) then
  704. VActClass := VTempActClass;
  705. end
  706. else
  707. begin
  708. TBrookRouter.MethodNotAllowed(AResponse);
  709. Exit;
  710. end;
  711. end
  712. else
  713. raise EBrookHTTP404.Create(ARequest.PathInfo);
  714. end
  715. else
  716. begin
  717. for PRoute in FRoutes.List do
  718. if MatchPattern(PRoute^.Pattern, ARequest.PathInfo, VRedirect,
  719. VNames, VValues) then
  720. begin
  721. if VRedirect and Canonicalize(ARequest, AResponse) then
  722. Exit;
  723. VActClass := PRoute^.ActionClass;
  724. Break;
  725. end;
  726. if not Assigned(VActClass) then
  727. if Assigned(VTempActClass) then
  728. VActClass := VTempActClass
  729. else
  730. raise EBrookHTTP404.Create(ARequest.PathInfo);
  731. end;
  732. finally
  733. if Assigned(FAfterRoute) then
  734. FAfterRoute(Self, ARequest, AResponse, VHandled);
  735. end;
  736. VAct := CreateAction(VActClass, ARequest, AResponse);
  737. try
  738. ExecuteAction(VAct, ARequest, AResponse, VNames, VValues, PRoute^);
  739. finally
  740. FreeAction(VAct);
  741. end;
  742. end;
  743. initialization
  744. TBrookRouter.RegisterService;
  745. finalization
  746. TBrookRouter.UnregisterService;
  747. end.