Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Horse.Core.Router.Radix.pas
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ TRadixNode = class
Part: string;
IsParam: Boolean;
ParamName: string;
FullPath: string;
Children: TObjectList<TRadixNode>;
Callbacks: TDictionary<TMethodType, TArray<THorseCallback>>;
Middlewares: TList<THorseCallback>;
Expand Down Expand Up @@ -400,6 +401,7 @@ procedure THorseRadixRouter.InsertRoute(const APath: string; const AHTTPType: TM
end;

LCurrent.AddRouteCallback(AHTTPType, ACallback, AIsMiddleware);
LCurrent.FullPath := '/' + APath.Trim(['/']);
end;

procedure THorseRadixRouter.RegisterRoute(const AHTTPType: TMethodType; const APath: string; const ACallback: THorseCallback);
Expand Down Expand Up @@ -530,6 +532,7 @@ function THorseRadixRouter.Execute(const ARequest: THorseRequest; const ARespons

if LNode <> nil then
begin
ARequest.MatchedRoute := LNode.FullPath;
if LParams <> nil then
begin
LKeys := LParams.Keys.ToArray;
Expand Down
4 changes: 4 additions & 0 deletions src/Horse.Core.RouterTree.pas
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ THorseRouterTree = class(TInterfacedObject, IHorseRouter)
private
FPart: string;
FTag: string;
FFullPath: string;
FIsParamsKey: Boolean;
FRouterRegex: string;
FIsRouterRegex: Boolean;
Expand Down Expand Up @@ -294,6 +295,8 @@ function THorseRouterTree.ExecuteInternal(const ASegments: TArray<THorseBufferSl
LNextCaller: TNextCaller;
LFound: Boolean;
begin
if FFullPath <> '' then
ARequest.MatchedRoute := FFullPath;
LFound := False;
LNextCaller := TNextCaller.Create;
try
Expand Down Expand Up @@ -544,6 +547,7 @@ procedure THorseRouterTree.RegisterInternal(const AHTTPType: TMethodType; var AP

if not AIsMiddleware then
FHandlerMethods.Add(AHTTPType);
FFullPath := '/' + AFullPath.Trim(['/']);
end;

if APath.Count > 0 then
Expand Down
14 changes: 14 additions & 0 deletions src/Horse.Request.pas
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ interface
SysUtils,
fpHTTP,
HTTPDefs,
Generics.Collections,
{$ELSE}
System.SysUtils,
Web.HTTPApp,
System.Generics.Collections,
{$IF CompilerVersion > 32.0}
Web.ReqMulti,
{$ENDIF}
Expand Down Expand Up @@ -73,6 +75,9 @@ THorseRequest = class
See: Horse.Provider.CrossSocket.WebRequestAdapter.pas
=========================================================================== }
FCSRawWebRequest: {$IF DEFINED(FPC)}TRequest{$ELSE}TWebRequest{$ENDIF};
{ =========================================================================== }
FMatchedRoute: string;
FState: TObjectDictionary<string, TObject>;
{ =========================================================================== }
function GetArena: THorseArenaAllocator;
procedure InitializeQuery;
Expand Down Expand Up @@ -205,6 +210,8 @@ THorseRequest = class
{ PATCH-REQ-9 called by TRequestBridge.MapBody to cache the decoded body. }
procedure SetBodyString(const AValue: string);
{ =========================================================================== }
property MatchedRoute: string read FMatchedRoute write FMatchedRoute;
property State: TObjectDictionary<string, TObject> read FState;
destructor Destroy; override;
end;

Expand Down Expand Up @@ -281,6 +288,7 @@ function THorseRequest.Cookie: THorseCoreParam;
constructor THorseRequest.Create(const AWebRequest: {$IF DEFINED(FPC)}TRequest{$ELSE}TWebRequest{$ENDIF});
begin
FWebRequest := AWebRequest;
FState := TObjectDictionary<string, TObject>.Create([doOwnsValues]);
end;

{ ===========================================================================
Expand All @@ -289,6 +297,7 @@ constructor THorseRequest.Create(const AWebRequest: {$IF DEFINED(FPC)}TRequest{$
constructor THorseRequest.Create;
begin
FWebRequest := nil;
FState := TObjectDictionary<string, TObject>.Create([doOwnsValues]);
end;
{ =========================================================================== }

Expand Down Expand Up @@ -367,6 +376,9 @@ procedure THorseRequest.Clear;
if Assigned(FSessions) then
FSessions.Clear;
{ end PATCH-SES-1 }
FMatchedRoute := '';
if Assigned(FState) then
FState.Clear;
end;
{ =========================================================================== }

Expand Down Expand Up @@ -396,6 +408,8 @@ destructor THorseRequest.Destroy;
{ end PATCH-REQ-8 }
if FOwnsArena and Assigned(FArena) then
FreeAndNil(FArena);
if Assigned(FState) then
FreeAndNil(FState);
inherited;
end;

Expand Down
1 change: 1 addition & 0 deletions tests/src/tests/Tests.Horse.Core.Router.Radix.pas
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ procedure TTestHorseCoreRouterRadix.ExecuteRouteGroupWithParams;
begin
LCalled := True;
Assert.AreEqual('123', Req.Params.Items['id']);
Assert.AreEqual('/users/:id', Req.MatchedRoute);
end);

FRequest.Populate('GET', mtGet, '/users/123', '', '');
Expand Down
1 change: 1 addition & 0 deletions tests/src/tests/Tests.Horse.Core.RouterTree.pas
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ procedure TTestHorseCoreRouterTree.ExecuteRouteGroupWithParams;
begin
LCalled := True;
Assert.AreEqual('123', Req.Params.Items['id']);
Assert.AreEqual('/users/:id', Req.MatchedRoute);
end);

FRequest.Populate('GET', mtGet, '/users/123', '', '');
Expand Down
25 changes: 25 additions & 0 deletions tests/src/tests/Tests.Horse.Request.Recycle.pas
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ TTestHorseRequestRecycle = class
procedure TestRequestArenaRecyclingAndObjectLifetime;
[Test]
procedure TestResponseSendBytesOverload;
[Test]
procedure TestRequestStateAndMatchedRouteCycle;
end;

implementation
Expand Down Expand Up @@ -148,6 +150,29 @@ procedure TTestHorseRequestRecycle.TestResponseSendBytesOverload;
end;
end;

procedure TTestHorseRequestRecycle.TestRequestStateAndMatchedRouteCycle;
var
LDestroyed: Boolean;
LTestObj: TTestArenaObject;
begin
LDestroyed := False;
FRequest.MatchedRoute := '/my/route/:id';
LTestObj := TTestArenaObject.Create(@LDestroyed);

FRequest.State.Add('context', LTestObj);

Assert.AreEqual('/my/route/:id', FRequest.MatchedRoute);
Assert.IsNotNull(FRequest.State.Items['context']);
Assert.IsFalse(LDestroyed);

// Limpa o request (reciclagem no pool)
FRequest.Clear;

Assert.AreEqual('', FRequest.MatchedRoute, 'MatchedRoute should be empty after Clear');
Assert.AreEqual<Integer>(0, FRequest.State.Count, 'State dictionary should be empty after Clear');
Assert.IsTrue(LDestroyed, 'State values should be automatically freed after Clear due to doOwnsValues');
end;

initialization
TDUnitX.RegisterTestFixture(TTestHorseRequestRecycle);

Expand Down