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
38 changes: 32 additions & 6 deletions src/Horse.Core.Router.Radix.pas
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ function THorseRadixRouter.MatchStaticRoute(const ABuffer: TBytes; const APathSp
LItem: TStaticRouteItem;
LPathLen: Integer;
LMatch: Boolean;
B1, B2: Byte;
begin
ACallbacks := nil;
if not FStaticRoutesBuilt then
Expand All @@ -581,7 +582,14 @@ function THorseRadixRouter.MatchStaticRoute(const ABuffer: TBytes; const APathSp
LMatch := True;
for J := 0 to LPathLen - 1 do
begin
if ABuffer[APathSpan.Offset + J] <> LItem.PathBytes[J] then
B1 := ABuffer[APathSpan.Offset + J];
B2 := LItem.PathBytes[J];
if not THorseCore.CaseSensitive then
begin
if (B1 >= 65) and (B1 <= 90) then B1 := B1 + 32;
if (B2 >= 65) and (B2 <= 90) then B2 := B2 + 32;
end;
if B1 <> B2 then
begin
LMatch := False;
Break;
Expand Down Expand Up @@ -617,14 +625,32 @@ procedure THorseRadixRouter.InsertRoute(const APath: string; const AHTTPType: TM
if (LSeg = '') and (I > 0) and (I = Length(LSegments) - 1) then
Continue;

if not THorseCore.CaseSensitive then
begin
if (not LSeg.StartsWith(':')) and (not LSeg.StartsWith('(')) then
LSeg := LowerCase(LSeg);
end;

LFound := False;
for LChild in LCurrent.Children do
begin
if SameText(LChild.Part, LSeg) then
if THorseCore.CaseSensitive then
begin
LCurrent := LChild;
LFound := True;
Break;
if LChild.Part = LSeg then
begin
LCurrent := LChild;
LFound := True;
Break;
end;
end
else
begin
if SameText(LChild.Part, LSeg) then
begin
LCurrent := LChild;
LFound := True;
Break;
end;
end;
end;

Expand Down Expand Up @@ -712,7 +738,7 @@ function THorseRadixRouter.FindNode(const ASegments: TArray<THorseBufferSlice>;
// 1. Tenta correspondência exata via SWAR 64-bit
for LChild in ANode.Children do
begin
if (not LChild.IsParam) and (LChild.Part <> '*') and LCurrentSlice.Compare(LChild.Part, True) then
if (not LChild.IsParam) and (LChild.Part <> '*') and LCurrentSlice.Compare(LChild.Part, not THorseCore.CaseSensitive) then
begin
LTempNode := LChild;
LBestMatch := FindNode(ASegments, AIndex + 1, LTempNode, AHTTPType, AMiddlewares, AParams);
Expand Down
14 changes: 9 additions & 5 deletions src/Horse.Core.RouterTree.NextCaller.pas
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ TNextCaller = class
{$ENDIF}
FCallNextPath: TCallNextPath;
FIsGroup: Boolean;
FTag: string;
FTags: TArray<string>;
FIsParamsKey: Boolean;
FPart: string;
FFound: ^Boolean;
Expand All @@ -61,7 +61,7 @@ TNextCaller = class
{$ELSE}
const AMiddleware: TArray<THorseCallback>;
{$ENDIF}
const ATag: string;
const ATags: TArray<string>;
const AIsParamsKey: Boolean;
const ACallNextPath: TCallNextPath;
const APart: string;
Expand Down Expand Up @@ -143,7 +143,7 @@ procedure TNextCaller.Configure(
{$ELSE}
const AMiddleware: TArray<THorseCallback>;
{$ENDIF}
const ATag: string;
const ATags: TArray<string>;
const AIsParamsKey: Boolean;
const ACallNextPath: TCallNextPath;
const APart: string;
Expand All @@ -158,7 +158,7 @@ procedure TNextCaller.Configure(
FResponse := AResponse;
FIsGroup := AIsGroup;
FMiddleware := AMiddleware;
FTag := ATag;
FTags := ATags;
FIsParamsKey := AIsParamsKey;
FCallNextPath := ACallNextPath;
FPart := APart;
Expand All @@ -169,6 +169,7 @@ procedure TNextCaller.Init;
var
LCurrent: THorseBufferSlice;
LCurrentStr: string;
LTag: string;
begin
LCurrentStr := '';
if (not FIsGroup) and (FIndexSegment < Length(FSegments)) then
Expand All @@ -181,7 +182,10 @@ procedure TNextCaller.Init;
FIndexCallback := -1;
if FIsParamsKey then
begin
FRequest.Params.Dictionary.AddOrSetValue(FTag, DecodeParam(LCurrentStr));
for LTag in FTags do
begin
FRequest.Params.Dictionary.AddOrSetValue(LTag, DecodeParam(LCurrentStr));
end;
end;
end;

Expand Down
156 changes: 134 additions & 22 deletions src/Horse.Core.RouterTree.pas
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ THorseRouterTree = class(TInterfacedObject, IHorseRouter)
procedure PopulateQueuePath(AQueue: TQueue<string>; APath: string; const AUsePrefix: Boolean = True);
private
FPart: string;
FTag: string;
FTags: TArray<string>;
FFullPath: string;
FIsParamsKey: Boolean;
FRouterRegex: string;
Expand All @@ -53,6 +53,7 @@ THorseRouterTree = class(TInterfacedObject, IHorseRouter)
{$ENDIF}
FHandlerMethods: TList<TMethodType>;
FRoute: TObjectDictionary<string, THorseRouterTree>;
procedure AddTag(const ATag: string);
procedure RegisterInternal(const AHTTPType: TMethodType; var APath: TQueue<string>; const ACallback: THorseCallback; const AFullPath: string; const AIsMiddleware: Boolean = False);
procedure RegisterMiddlewareInternal(var APath: TQueue<string>; const AMiddleware: THorseCallback);
function ExecuteInternal(const ASegments: TArray<THorseBufferSlice>; AIndex: Integer; const AHTTPType: TMethodType; const ARequest: THorseRequest; const AResponse: THorseResponse; const AIsGroup: Boolean = False): Boolean;
Expand Down Expand Up @@ -289,7 +290,7 @@ function THorseRouterTree.CallNextPath(const ASegments: TArray<THorseBufferSlice

for LPair in FRoute do
begin
if (LPair.Key <> '*') and LCurrent.Compare(LPair.Key) then
if (LPair.Key <> '*') and LCurrent.Compare(LPair.Key, not THorseCore.CaseSensitive) then
begin
LAcceptable := LPair.Value;
LFound := True;
Expand Down Expand Up @@ -485,7 +486,7 @@ function THorseRouterTree.ExecuteInternal(const ASegments: TArray<THorseBufferSl
AResponse,
AIsGroup,
FMiddleware,
FTag,
FTags,
FIsParamsKey,
CallNextPath,
FPart,
Expand All @@ -499,6 +500,19 @@ function THorseRouterTree.ExecuteInternal(const ASegments: TArray<THorseBufferSl
Result := LFound;
end;

procedure THorseRouterTree.AddTag(const ATag: string);
var
LItem: string;
begin
for LItem in FTags do
begin
if LItem = ATag then
Exit;
end;
SetLength(FTags, Length(FTags) + 1);
FTags[High(FTags)] := ATag;
end;

function THorseRouterTree.ForcePath(const APath: string): THorseRouterTree;
begin
if not FRoute.TryGetValue(APath, Result) then
Expand Down Expand Up @@ -596,7 +610,7 @@ function THorseRouterTree.CountLiteralSegments(const AMethod: TMethodType; const
LNextRoute := nil;
for LPair in FRoute do
begin
if LNext.Compare(LPair.Key) or (LPair.Key = '*') then
if LNext.Compare(LPair.Key, not THorseCore.CaseSensitive) or (LPair.Key = '*') then
begin
LNextRoute := LPair.Value;
LFound := True;
Expand Down Expand Up @@ -639,7 +653,7 @@ function THorseRouterTree.HasNext(const AMethod: TMethodType; const APaths: TArr
if Length(APaths) - 1 = AIndex then
Exit(FCallBack.ContainsKey(AMethod) or (AMethod = mtAny));
end
else if (Length(APaths) - 1 = AIndex) and (APaths[AIndex].Compare(FPart) or FIsParamsKey) then
else if (Length(APaths) - 1 = AIndex) and (APaths[AIndex].Compare(FPart, not THorseCore.CaseSensitive) or FIsParamsKey) then
begin
Exit(FCallBack.ContainsKey(AMethod) or (AMethod = mtAny));
end;
Expand All @@ -651,7 +665,7 @@ function THorseRouterTree.HasNext(const AMethod: TMethodType; const APaths: TArr
LNextRoute := nil;
for LPair in FRoute do
begin
if LNext.Compare(LPair.Key) or (LPair.Key = '*') then
if LNext.Compare(LPair.Key, not THorseCore.CaseSensitive) or (LPair.Key = '*') then
begin
LNextRoute := LPair.Value;
LFound := True;
Expand Down Expand Up @@ -686,6 +700,7 @@ procedure THorseRouterTree.RegisterInternal(const AHTTPType: TMethodType; var AP
LRawPart: string;
LOpenParenthesis: Integer;
LCloseParenthesis: Integer;
LTag: string;
begin
if not FIsInitialized then
begin
Expand Down Expand Up @@ -715,14 +730,16 @@ procedure THorseRouterTree.RegisterInternal(const AHTTPType: TMethodType; var AP
if LCloseParenthesis > LOpenParenthesis then
begin
FIsRouterRegex := True;
FTag := LNormalizedNextPart.Substring(0, LOpenParenthesis);
LTag := LNormalizedNextPart.Substring(0, LOpenParenthesis);
FRouterRegex := LNormalizedNextPart.Substring(LOpenParenthesis + 1, LCloseParenthesis - LOpenParenthesis - 1);
FRegexMatcher := THorseRegex.Create(FRouterRegex);
end;
end;

if not FIsRouterRegex then
FTag := LNormalizedNextPart;
LTag := LNormalizedNextPart;

AddTag(LTag);
end
else
begin
Expand All @@ -731,18 +748,39 @@ procedure THorseRouterTree.RegisterInternal(const AHTTPType: TMethodType; var AP
FIsRouterRegex := True;
FRouterRegex := FPart.Substring(1, FPart.Length - 2);
FRegexMatcher := THorseRegex.Create(FRouterRegex);
FTag := '';
end
else
begin
FTag := '';
end;
end;

FIsInitialized := True;
end
else
APath.Dequeue;
begin
LRawPart := APath.Dequeue;
if FIsParamsKey then
begin
LNormalizedNextPart := LRawPart.Substring(1);

if LNormalizedNextPart.EndsWith('?') then
begin
LNormalizedNextPart := LNormalizedNextPart.Substring(0, LNormalizedNextPart.Length - 1);
end;

LOpenParenthesis := LNormalizedNextPart.IndexOf('(');
if LOpenParenthesis >= 0 then
begin
LCloseParenthesis := LNormalizedNextPart.IndexOf(')');
if LCloseParenthesis > LOpenParenthesis then
begin
LTag := LNormalizedNextPart.Substring(0, LOpenParenthesis);
end;
end
else
LTag := LNormalizedNextPart;

if LTag <> '' then
AddTag(LTag);
end;
end;

if APath.Count = 0 then
begin
Expand Down Expand Up @@ -776,6 +814,11 @@ procedure THorseRouterTree.RegisterInternal(const AHTTPType: TMethodType; var AP
begin
LNextPart := APath.Peek;
LNormalizedNextPart := NormalizeParamKey(LNextPart);
if not THorseCore.CaseSensitive then
begin
if (not LNextPart.StartsWith(':')) and (not LNextPart.StartsWith('(')) then
LNormalizedNextPart := LowerCase(LNormalizedNextPart);
end;

LForceRouter := ForcePath(LNormalizedNextPart);

Expand Down Expand Up @@ -811,19 +854,88 @@ procedure THorseRouterTree.RegisterMiddleware(const APath: string; const AMiddle
end;

procedure THorseRouterTree.RegisterMiddlewareInternal(var APath: TQueue<string>; const AMiddleware: THorseCallback);
var
LNextPart: string;
LNormalizedNextPart: string;
LForceRouter: THorseRouterTree;
LRawPart: string;
LNormalizedRawPart: string;
LOpenParenthesis: Integer;
LCloseParenthesis: Integer;
LTag: string;
begin
APath.Dequeue;
if not FIsInitialized then
begin
LRawPart := APath.Dequeue;
FPart := LRawPart;
FIsParamsKey := FPart.StartsWith(':');
if FIsParamsKey then
begin
LNormalizedRawPart := FPart.Substring(1);
if LNormalizedRawPart.EndsWith('?') then
LNormalizedRawPart := LNormalizedRawPart.Substring(0, LNormalizedRawPart.Length - 1);
LOpenParenthesis := LNormalizedRawPart.IndexOf('(');
if LOpenParenthesis >= 0 then
begin
LCloseParenthesis := LNormalizedRawPart.IndexOf(')');
if LCloseParenthesis > LOpenParenthesis then
LTag := LNormalizedRawPart.Substring(0, LOpenParenthesis);
end
else
LTag := LNormalizedRawPart;

if LTag <> '' then
AddTag(LTag);
end;
FIsInitialized := True;
end
else
begin
LRawPart := APath.Dequeue;
if FIsParamsKey then
begin
LNormalizedRawPart := LRawPart.Substring(1);
if LNormalizedRawPart.EndsWith('?') then
LNormalizedRawPart := LNormalizedRawPart.Substring(0, LNormalizedRawPart.Length - 1);
LOpenParenthesis := LNormalizedRawPart.IndexOf('(');
if LOpenParenthesis >= 0 then
begin
LCloseParenthesis := LNormalizedRawPart.IndexOf(')');
if LCloseParenthesis > LOpenParenthesis then
LTag := LNormalizedRawPart.Substring(0, LOpenParenthesis);
end
else
LTag := LNormalizedRawPart;

if LTag <> '' then
AddTag(LTag);
end;
end;

if APath.Count = 0 then
begin
{$IF DEFINED(FPC)}
FMiddleware.Add(AMiddleware)
FMiddleware.Add(AMiddleware);
{$ELSE}
begin
SetLength(FMiddleware, Length(FMiddleware) + 1);
FMiddleware[Length(FMiddleware) - 1] := AMiddleware;
end
SetLength(FMiddleware, Length(FMiddleware) + 1);
FMiddleware[Length(FMiddleware) - 1] := AMiddleware;
{$ENDIF}
else
ForcePath(APath.Peek).RegisterMiddlewareInternal(APath, AMiddleware);
end;

if APath.Count > 0 then
begin
LNextPart := APath.Peek;
LNormalizedNextPart := NormalizeParamKey(LNextPart);
if not THorseCore.CaseSensitive then
begin
if (not LNextPart.StartsWith(':')) and (not LNextPart.StartsWith('(')) then
LNormalizedNextPart := LowerCase(LNormalizedNextPart);
end;

LForceRouter := ForcePath(LNormalizedNextPart);

LForceRouter.RegisterMiddlewareInternal(APath, AMiddleware);
end;
end;

initialization
Expand Down
Loading