|
@@ -37,6 +37,8 @@ Type
|
|
// Some common HTTP methods.
|
|
// Some common HTTP methods.
|
|
|
|
|
|
TRouteMethod = (rmUnknown,rmAll,rmGet,rmPost,rmPut,rmDelete,rmOptions,rmHead, rmTrace);
|
|
TRouteMethod = (rmUnknown,rmAll,rmGet,rmPost,rmPut,rmDelete,rmOptions,rmHead, rmTrace);
|
|
|
|
+ TRouteOption = (roCaseSensitive,roEmptyMatchesAll);
|
|
|
|
+ TRouteOptions = Set of TRouteOption;
|
|
|
|
|
|
{ THTTPRoute }
|
|
{ THTTPRoute }
|
|
|
|
|
|
@@ -51,11 +53,13 @@ Type
|
|
Public
|
|
Public
|
|
Destructor Destroy; override;
|
|
Destructor Destroy; override;
|
|
Procedure HandleRequest(ARequest : TRequest; AResponse : TResponse);
|
|
Procedure HandleRequest(ARequest : TRequest; AResponse : TResponse);
|
|
- Function Matches(Const APattern : String; AMethod : TRouteMethod) : Boolean;
|
|
|
|
- Function MatchPattern(Const Path : String; L : TStrings) : Boolean;
|
|
|
|
|
|
+ Function Matches(Const APattern : String; AMethod : TRouteMethod; Options : TRouteOptions) : Boolean;
|
|
|
|
+ Function MatchPattern(Const Path : String; L : TStrings; Options : TRouteOptions) : Boolean;
|
|
Function MatchMethod(Const AMethod : TRouteMethod) : Boolean;
|
|
Function MatchMethod(Const AMethod : TRouteMethod) : Boolean;
|
|
Published
|
|
Published
|
|
|
|
+ // Default route is per method. This means you can register
|
|
Property Default : Boolean Read FDefault Write FDefault;
|
|
Property Default : Boolean Read FDefault Write FDefault;
|
|
|
|
+ // Depending on options, an empty URLPattern matches all, and acts as default.
|
|
Property URLPattern : String Read FURLPattern Write SetURLPattern;
|
|
Property URLPattern : String Read FURLPattern Write SetURLPattern;
|
|
Property Method : TRouteMethod Read FMethod Write FMethod;
|
|
Property Method : TRouteMethod Read FMethod Write FMethod;
|
|
end;
|
|
end;
|
|
@@ -154,6 +158,7 @@ Type
|
|
private
|
|
private
|
|
FAfterRequest: THTTPRouteRequestEvent;
|
|
FAfterRequest: THTTPRouteRequestEvent;
|
|
FBeforeRequest: THTTPRouteRequestEvent;
|
|
FBeforeRequest: THTTPRouteRequestEvent;
|
|
|
|
+ FRouteOptions: TRouteOptions;
|
|
FRoutes : THTTPRouteList;
|
|
FRoutes : THTTPRouteList;
|
|
function GetR(AIndex : Integer): THTTPRoute;
|
|
function GetR(AIndex : Integer): THTTPRoute;
|
|
Class Procedure DoneService;
|
|
Class Procedure DoneService;
|
|
@@ -219,6 +224,8 @@ Type
|
|
Property BeforeRequest : THTTPRouteRequestEvent Read FBeforeRequest Write FBeforeRequest;
|
|
Property BeforeRequest : THTTPRouteRequestEvent Read FBeforeRequest Write FBeforeRequest;
|
|
// Called after the request is routed, if no exception was raised during or before the request.
|
|
// Called after the request is routed, if no exception was raised during or before the request.
|
|
Property AfterRequest : THTTPRouteRequestEvent Read FAfterRequest Write FAfterRequest;
|
|
Property AfterRequest : THTTPRouteRequestEvent Read FAfterRequest Write FAfterRequest;
|
|
|
|
+ // Global options used when routing a request.
|
|
|
|
+ Property RouteOptions : TRouteOptions Read FRouteOptions Write FRouteOptions;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function RouteMethodToString (R : TRouteMethod) : String;
|
|
Function RouteMethodToString (R : TRouteMethod) : String;
|
|
@@ -318,7 +325,7 @@ begin
|
|
R:=FRoutes[I];
|
|
R:=FRoutes[I];
|
|
if R.Default then
|
|
if R.Default then
|
|
DI:=I;
|
|
DI:=I;
|
|
- if R.Matches(APattern,AMethod) then
|
|
|
|
|
|
+ if R.Matches(APattern,AMethod,FRouteOptions) then
|
|
Raise EHTTPRoute.CreateFmt(EDuplicateRoute,[APattern,RouteMethodToString(AMethod)]);
|
|
Raise EHTTPRoute.CreateFmt(EDuplicateRoute,[APattern,RouteMethodToString(AMethod)]);
|
|
end;
|
|
end;
|
|
if isDefault and (DI<>-1) then
|
|
if isDefault and (DI<>-1) then
|
|
@@ -526,7 +533,7 @@ begin
|
|
While (Result=Nil) and (I<FRoutes.Count) do
|
|
While (Result=Nil) and (I<FRoutes.Count) do
|
|
begin
|
|
begin
|
|
Result:=FRoutes[i];
|
|
Result:=FRoutes[i];
|
|
- If Not Result.MatchPattern(APathInfo,Params) then
|
|
|
|
|
|
+ If Not Result.MatchPattern(APathInfo,Params,FRouteOptions) then
|
|
Result:=Nil
|
|
Result:=Nil
|
|
else if Not Result.MatchMethod(AMethod) then
|
|
else if Not Result.MatchMethod(AMethod) then
|
|
begin
|
|
begin
|
|
@@ -536,6 +543,18 @@ begin
|
|
end;
|
|
end;
|
|
Inc(I);
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
|
|
+ // Find default route.
|
|
|
|
+ if (Result=Nil) then
|
|
|
|
+ begin
|
|
|
|
+ I:=0;
|
|
|
|
+ While (Result=Nil) and (I<FRoutes.Count) do
|
|
|
|
+ begin
|
|
|
|
+ Result:=FRoutes[i];
|
|
|
|
+ if Not (Result.Default and Result.MatchMethod(AMethod)) then
|
|
|
|
+ Result:=Nil;
|
|
|
|
+ Inc(I);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function THTTPRouter.GetHTTPRoute(const Path: String; AMethod: TRouteMethod; Params : TStrings): THTTPRoute;
|
|
function THTTPRouter.GetHTTPRoute(const Path: String; AMethod: TRouteMethod; Params : TStrings): THTTPRoute;
|
|
@@ -605,7 +624,7 @@ Var
|
|
|
|
|
|
begin
|
|
begin
|
|
V:=IncludeHTTPPathDelimiter(AValue);
|
|
V:=IncludeHTTPPathDelimiter(AValue);
|
|
- if (V<>'/') and (V[1]='/') then
|
|
|
|
|
|
+ if (V<>'') and (V<>'/') and (V[1]='/') then
|
|
Delete(V,1,1);
|
|
Delete(V,1,1);
|
|
if FURLPattern=V then Exit;
|
|
if FURLPattern=V then Exit;
|
|
FURLPattern:=V;
|
|
FURLPattern:=V;
|
|
@@ -627,22 +646,24 @@ begin
|
|
DoHandleRequest(ARequest,AResponse);
|
|
DoHandleRequest(ARequest,AResponse);
|
|
end;
|
|
end;
|
|
|
|
|
|
-function THTTPRoute.Matches(const APattern: String; AMethod: TRouteMethod
|
|
|
|
- ): Boolean;
|
|
|
|
|
|
+function THTTPRoute.Matches(const APattern: String; AMethod: TRouteMethod; Options: TRouteOptions): Boolean;
|
|
begin
|
|
begin
|
|
- Result:=(CompareText(URLPattern,APattern)=0)
|
|
|
|
- and ((Method=rmAll) or (AMethod=Method))
|
|
|
|
|
|
+ Result:=((Method=rmAll) or (AMethod=Method));
|
|
|
|
+ if Result then
|
|
|
|
+ Result:=SameText(URLPattern,APattern) or ((URLPattern='') and (roEmptyMatchesAll in Options))
|
|
end;
|
|
end;
|
|
|
|
|
|
-Function THTTPRoute.MatchPattern(Const Path : String; L : TStrings) : Boolean;
|
|
|
|
|
|
+Function THTTPRoute.MatchPattern(Const Path : String; L : TStrings; Options: TRouteOptions) : Boolean;
|
|
|
|
|
|
|
|
+ // This is used only with special chars, so we do not check case sensitivity
|
|
Function StartsWith(C : Char; S : String): Boolean;
|
|
Function StartsWith(C : Char; S : String): Boolean;
|
|
|
|
|
|
begin
|
|
begin
|
|
Result:=(Length(S)>0) and (S[1]=C);
|
|
Result:=(Length(S)>0) and (S[1]=C);
|
|
end;
|
|
end;
|
|
|
|
|
|
- Function EndsWith(C : Char; S : String): Boolean;
|
|
|
|
|
|
+ // This is used only with special chars, so we do not check case sensitivity
|
|
|
|
+ Function EndsWith(C : Char; S : String): Boolean;
|
|
|
|
|
|
Var
|
|
Var
|
|
L : Integer;
|
|
L : Integer;
|
|
@@ -651,6 +672,15 @@ Function THTTPRoute.MatchPattern(Const Path : String; L : TStrings) : Boolean;
|
|
L:=Length(S);
|
|
L:=Length(S);
|
|
Result:=(L>0) and (S[L]=C);
|
|
Result:=(L>0) and (S[L]=C);
|
|
end;
|
|
end;
|
|
|
|
+
|
|
|
|
+ Function SameString(A,B : String) : Boolean;
|
|
|
|
+
|
|
|
|
+ begin
|
|
|
|
+ if roCaseSensitive in Options then
|
|
|
|
+ Result:=(A=B)
|
|
|
|
+ else
|
|
|
|
+ Result:=SameText(A,B);
|
|
|
|
+ end;
|
|
|
|
|
|
|
|
|
|
procedure ExtractNextPathLevel(var ALeft: string;
|
|
procedure ExtractNextPathLevel(var ALeft: string;
|
|
@@ -700,9 +730,9 @@ var
|
|
VLeftPat, VRightPat, VLeftVal, VRightVal, VVal, VPat, VName: string;
|
|
VLeftPat, VRightPat, VLeftVal, VRightVal, VVal, VPat, VName: string;
|
|
|
|
|
|
begin
|
|
begin
|
|
- Result:= False;
|
|
|
|
|
|
+ Result:=False;
|
|
if (URLPattern='') then
|
|
if (URLPattern='') then
|
|
- Exit; // Maybe empty pattern should match any path?
|
|
|
|
|
|
+ Exit(roEmptyMatchesAll in Options);
|
|
APathInfo:=Path;
|
|
APathInfo:=Path;
|
|
APattern:=URLPattern;
|
|
APattern:=URLPattern;
|
|
Delete(APattern, Pos('?', APattern), MaxInt);
|
|
Delete(APattern, Pos('?', APattern), MaxInt);
|
|
@@ -750,7 +780,7 @@ begin
|
|
end
|
|
end
|
|
else
|
|
else
|
|
// *path/const
|
|
// *path/const
|
|
- if not ((VPat='') and (VLeftPat='')) and Not SameText(VPat,VVal) then
|
|
|
|
|
|
+ if not ((VPat='') and (VLeftPat='')) and Not SameString(VPat,VVal) then
|
|
Exit;
|
|
Exit;
|
|
// Check if we already done
|
|
// Check if we already done
|
|
if (VLeftPat='') or (VLeftVal='') then
|
|
if (VLeftPat='') or (VLeftVal='') then
|
|
@@ -767,7 +797,7 @@ begin
|
|
end
|
|
end
|
|
else
|
|
else
|
|
// const
|
|
// const
|
|
- if Not SameText(VPat,VVal) then
|
|
|
|
|
|
+ if Not SameString(VPat,VVal) then
|
|
Exit;
|
|
Exit;
|
|
// Check if we already done
|
|
// Check if we already done
|
|
if (VRightPat='') or (VRightVal='') then
|
|
if (VRightPat='') or (VRightVal='') then
|