Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pistache
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
pistache
Commits
a64024b5
Commit
a64024b5
authored
Jan 07, 2016
by
octal
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implemented splat parameters
parent
9620f79d
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
225 additions
and
131 deletions
+225
-131
main.cc
main.cc
+1
-1
src/router.cc
src/router.cc
+81
-40
src/router.h
src/router.h
+91
-76
tests/router_test.cc
tests/router_test.cc
+52
-14
No files found.
main.cc
View file @
a64024b5
...
@@ -201,7 +201,7 @@ private:
...
@@ -201,7 +201,7 @@ private:
});
});
if
(
it
==
std
::
end
(
metrics
))
{
if
(
it
==
std
::
end
(
metrics
))
{
response
.
send
(
Http
::
Code
::
Not_Found
);
response
.
send
(
Http
::
Code
::
Not_Found
,
"Metric does not exist"
);
}
else
{
}
else
{
const
auto
&
metric
=
*
it
;
const
auto
&
metric
=
*
it
;
response
.
send
(
Http
::
Code
::
Ok
,
std
::
to_string
(
metric
.
value
()));
response
.
send
(
Http
::
Code
::
Ok
,
std
::
to_string
(
metric
.
value
()));
...
...
src/router.cc
View file @
a64024b5
...
@@ -13,9 +13,11 @@ namespace Rest {
...
@@ -13,9 +13,11 @@ namespace Rest {
Request
::
Request
(
Request
::
Request
(
const
Http
::
Request
&
request
,
const
Http
::
Request
&
request
,
std
::
vector
<
TypedParam
>&&
params
)
std
::
vector
<
TypedParam
>&&
params
,
std
::
vector
<
TypedParam
>&&
splats
)
:
Http
::
Request
(
request
)
:
Http
::
Request
(
request
)
,
params_
(
std
::
move
(
params
))
,
params_
(
std
::
move
(
params
))
,
splats_
(
std
::
move
(
splats
))
{
{
}
}
...
@@ -41,7 +43,21 @@ Request::param(std::string name) const {
...
@@ -41,7 +43,21 @@ Request::param(std::string name) const {
return
*
it
;
return
*
it
;
}
}
Router
::
Route
::
Fragment
::
Fragment
(
std
::
string
value
)
TypedParam
Request
::
splatAt
(
size_t
index
)
const
{
if
(
index
>=
splats_
.
size
())
{
throw
std
::
out_of_range
(
"Request splat index out of range"
);
}
return
splats_
[
index
];
}
std
::
vector
<
TypedParam
>
Request
::
splat
()
const
{
return
splats_
;
}
Route
::
Fragment
::
Fragment
(
std
::
string
value
)
{
{
if
(
value
.
empty
())
if
(
value
.
empty
())
throw
std
::
runtime_error
(
"Invalid empty fragment"
);
throw
std
::
runtime_error
(
"Invalid empty fragment"
);
...
@@ -50,13 +66,11 @@ Router::Route::Fragment::Fragment(std::string value)
...
@@ -50,13 +66,11 @@ Router::Route::Fragment::Fragment(std::string value)
}
}
bool
bool
Route
r
::
Route
::
Fragment
::
match
(
const
std
::
string
&
raw
)
const
{
Route
::
Fragment
::
match
(
const
std
::
string
&
raw
)
const
{
if
(
flags
.
hasFlag
(
Flag
::
Fixed
))
{
if
(
flags
.
hasFlag
(
Flag
::
Fixed
))
{
if
(
!
flags
.
hasFlag
(
Flag
::
Optional
))
{
return
raw
==
value_
;
return
raw
==
value_
;
}
}
}
else
if
(
flags
.
hasFlag
(
Flag
::
Parameter
))
{
else
if
(
flags
.
hasFlag
(
Flag
::
Parameter
)
||
flags
.
hasFlag
(
Flag
::
Splat
)
)
{
return
true
;
return
true
;
}
}
...
@@ -64,16 +78,22 @@ Router::Route::Fragment::match(const std::string& raw) const {
...
@@ -64,16 +78,22 @@ Router::Route::Fragment::match(const std::string& raw) const {
}
}
bool
bool
Route
r
::
Route
::
Fragment
::
match
(
const
Fragment
&
other
)
const
{
Route
::
Fragment
::
match
(
const
Fragment
&
other
)
const
{
return
match
(
other
.
value
());
return
match
(
other
.
value
());
}
}
void
void
Route
r
::
Route
::
Fragment
::
init
(
std
::
string
value
)
{
Route
::
Fragment
::
init
(
std
::
string
value
)
{
if
(
value
[
0
]
==
':'
)
if
(
value
[
0
]
==
':'
)
flags
.
setFlag
(
Flag
::
Parameter
);
flags
.
setFlag
(
Flag
::
Parameter
);
else
else
if
(
value
[
0
]
==
'*'
)
{
if
(
value
.
size
()
>
1
)
throw
std
::
runtime_error
(
"Invalid splat parameter"
);
flags
.
setFlag
(
Flag
::
Splat
);
}
else
{
flags
.
setFlag
(
Flag
::
Fixed
);
flags
.
setFlag
(
Flag
::
Fixed
);
}
// Let's search for any '?'
// Let's search for any '?'
auto
pos
=
value
.
find
(
'?'
);
auto
pos
=
value
.
find
(
'?'
);
...
@@ -94,7 +114,7 @@ Router::Route::Fragment::init(std::string value) {
...
@@ -94,7 +114,7 @@ Router::Route::Fragment::init(std::string value) {
}
}
void
void
Route
r
::
Route
::
Fragment
::
checkInvariant
()
const
{
Route
::
Fragment
::
checkInvariant
()
const
{
auto
check
=
[
this
](
std
::
initializer_list
<
Flag
>
exclusiveFlags
)
{
auto
check
=
[
this
](
std
::
initializer_list
<
Flag
>
exclusiveFlags
)
{
for
(
auto
flag
:
exclusiveFlags
)
{
for
(
auto
flag
:
exclusiveFlags
)
{
if
(
!
flags
.
hasFlag
(
flag
))
return
;
if
(
!
flags
.
hasFlag
(
flag
))
return
;
...
@@ -106,11 +126,14 @@ Router::Route::Fragment::checkInvariant() const {
...
@@ -106,11 +126,14 @@ Router::Route::Fragment::checkInvariant() const {
check
({
Flag
::
Fixed
,
Flag
::
Optional
});
check
({
Flag
::
Fixed
,
Flag
::
Optional
});
check
({
Flag
::
Fixed
,
Flag
::
Parameter
});
check
({
Flag
::
Fixed
,
Flag
::
Parameter
});
check
({
Flag
::
Splat
,
Flag
::
Fixed
});
check
({
Flag
::
Splat
,
Flag
::
Optional
});
check
({
Flag
::
Splat
,
Flag
::
Parameter
});
}
}
std
::
vector
<
Route
r
::
Route
::
Fragment
>
std
::
vector
<
Route
::
Fragment
>
Route
r
::
Route
::
Fragment
::
fromUrl
(
const
std
::
string
&
url
)
{
Route
::
Fragment
::
fromUrl
(
const
std
::
string
&
url
)
{
std
::
vector
<
Route
r
::
Route
::
Fragment
>
fragments
;
std
::
vector
<
Route
::
Fragment
>
fragments
;
std
::
istringstream
iss
(
url
);
std
::
istringstream
iss
(
url
);
std
::
string
p
;
std
::
string
p
;
...
@@ -125,29 +148,38 @@ Router::Route::Fragment::fromUrl(const std::string& url) {
...
@@ -125,29 +148,38 @@ Router::Route::Fragment::fromUrl(const std::string& url) {
}
}
bool
bool
Route
r
::
Route
::
Fragment
::
isParameter
()
const
{
Route
::
Fragment
::
isParameter
()
const
{
return
flags
.
hasFlag
(
Flag
::
Parameter
);
return
flags
.
hasFlag
(
Flag
::
Parameter
);
}
}
bool
bool
Route
r
::
Route
::
Fragment
::
isOptional
()
const
{
Route
::
Fragment
::
isOptional
()
const
{
return
isParameter
()
&&
flags
.
hasFlag
(
Flag
::
Optional
);
return
isParameter
()
&&
flags
.
hasFlag
(
Flag
::
Optional
);
}
}
std
::
pair
<
bool
,
std
::
vector
<
TypedParam
>>
bool
Router
::
Route
::
match
(
const
Http
::
Request
&
req
)
const
Route
::
Fragment
::
isSplat
()
const
{
return
flags
.
hasFlag
(
Flag
::
Splat
);
}
std
::
tuple
<
bool
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
Route
::
match
(
const
Http
::
Request
&
req
)
const
{
{
return
match
(
req
.
resource
());
return
match
(
req
.
resource
());
}
}
std
::
pair
<
bool
,
std
::
vector
<
TypedParam
>>
std
::
tuple
<
bool
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
Route
r
::
Route
::
match
(
const
std
::
string
&
req
)
const
Route
::
match
(
const
std
::
string
&
req
)
const
{
{
#define UNMATCH() std::make_tuple(false, std::vector<TypedParam>(), std::vector<TypedParam>())
auto
reqFragments
=
Fragment
::
fromUrl
(
req
);
auto
reqFragments
=
Fragment
::
fromUrl
(
req
);
if
(
reqFragments
.
size
()
>
fragments_
.
size
())
if
(
reqFragments
.
size
()
>
fragments_
.
size
())
return
std
::
make_pair
(
false
,
std
::
vector
<
TypedParam
>
());
return
UNMATCH
();
std
::
vector
<
TypedParam
>
params
;
std
::
vector
<
TypedParam
>
splats
;
std
::
vector
<
TypedParam
>
extractedParams
;
for
(
std
::
vector
<
Fragment
>::
size_type
i
=
0
;
i
<
fragments_
.
size
();
++
i
)
{
for
(
std
::
vector
<
Fragment
>::
size_type
i
=
0
;
i
<
fragments_
.
size
();
++
i
)
{
const
auto
&
fragment
=
fragments_
[
i
];
const
auto
&
fragment
=
fragments_
[
i
];
...
@@ -155,30 +187,36 @@ Router::Route::match(const std::string& req) const
...
@@ -155,30 +187,36 @@ Router::Route::match(const std::string& req) const
if
(
fragment
.
isOptional
())
if
(
fragment
.
isOptional
())
continue
;
continue
;
return
std
::
make_pair
(
false
,
std
::
vector
<
TypedParam
>
()
);
return
UNMATCH
(
);
}
}
const
auto
&
reqFragment
=
reqFragments
[
i
];
const
auto
&
reqFragment
=
reqFragments
[
i
];
if
(
!
fragment
.
match
(
reqFragment
))
if
(
!
fragment
.
match
(
reqFragment
))
return
std
::
make_pair
(
false
,
std
::
vector
<
TypedParam
>
()
);
return
UNMATCH
(
);
if
(
fragment
.
isParameter
())
{
if
(
fragment
.
isParameter
())
{
extractedParams
.
push_back
(
TypedParam
(
fragment
.
value
(),
reqFragment
.
value
()));
params
.
push_back
(
TypedParam
(
fragment
.
value
(),
reqFragment
.
value
()));
}
else
if
(
fragment
.
isSplat
())
{
splats
.
push_back
(
TypedParam
(
reqFragment
.
value
(),
reqFragment
.
value
()));
}
}
}
}
return
make_pair
(
true
,
std
::
move
(
extractedParams
));
#undef UNMATCH
return
make_tuple
(
true
,
std
::
move
(
params
),
std
::
move
(
splats
));
}
}
Router
::
HttpHandler
::
HttpHandler
(
namespace
Private
{
const
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Router
::
Route
>>&
routes
)
HttpHandler
::
HttpHandler
(
const
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>&
routes
)
:
routes
(
routes
)
:
routes
(
routes
)
{
{
}
}
void
void
Router
::
HttpHandler
::
onRequest
(
HttpHandler
::
onRequest
(
const
Http
::
Request
&
req
,
const
Http
::
Request
&
req
,
Http
::
Response
response
,
Http
::
Response
response
,
Http
::
Timeout
timeout
)
Http
::
Timeout
timeout
)
...
@@ -187,9 +225,10 @@ Router::HttpHandler::onRequest(
...
@@ -187,9 +225,10 @@ Router::HttpHandler::onRequest(
for
(
const
auto
&
route
:
r
)
{
for
(
const
auto
&
route
:
r
)
{
bool
match
;
bool
match
;
std
::
vector
<
TypedParam
>
params
;
std
::
vector
<
TypedParam
>
params
;
std
::
tie
(
match
,
params
)
=
route
.
match
(
req
);
std
::
vector
<
TypedParam
>
splats
;
std
::
tie
(
match
,
params
,
splats
)
=
route
.
match
(
req
);
if
(
match
)
{
if
(
match
)
{
route
.
invokeHandler
(
Request
(
req
,
std
::
move
(
params
)),
std
::
move
(
response
));
route
.
invokeHandler
(
Request
(
req
,
std
::
move
(
params
)
,
std
::
move
(
splats
)
),
std
::
move
(
response
));
return
;
return
;
}
}
}
}
...
@@ -197,28 +236,30 @@ Router::HttpHandler::onRequest(
...
@@ -197,28 +236,30 @@ Router::HttpHandler::onRequest(
response
.
send
(
Http
::
Code
::
Not_Found
,
"Could not find a matching route"
);
response
.
send
(
Http
::
Code
::
Not_Found
,
"Could not find a matching route"
);
}
}
}
// namespace Private
void
void
Router
::
get
(
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
Router
::
get
(
std
::
string
resource
,
Route
::
Handler
handler
)
{
addRoute
(
Http
::
Method
::
Get
,
std
::
move
(
resource
),
std
::
move
(
handler
));
addRoute
(
Http
::
Method
::
Get
,
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
void
void
Router
::
post
(
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
Router
::
post
(
std
::
string
resource
,
Route
::
Handler
handler
)
{
addRoute
(
Http
::
Method
::
Post
,
std
::
move
(
resource
),
std
::
move
(
handler
));
addRoute
(
Http
::
Method
::
Post
,
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
void
void
Router
::
put
(
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
Router
::
put
(
std
::
string
resource
,
Route
::
Handler
handler
)
{
addRoute
(
Http
::
Method
::
Put
,
std
::
move
(
resource
),
std
::
move
(
handler
));
addRoute
(
Http
::
Method
::
Put
,
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
void
void
Router
::
del
(
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
Router
::
del
(
std
::
string
resource
,
Route
::
Handler
handler
)
{
addRoute
(
Http
::
Method
::
Delete
,
std
::
move
(
resource
),
std
::
move
(
handler
));
addRoute
(
Http
::
Method
::
Delete
,
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
void
void
Router
::
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Route
r
::
Handler
handler
)
Router
::
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Route
::
Handler
handler
)
{
{
auto
&
r
=
routes
[
method
];
auto
&
r
=
routes
[
method
];
r
.
push_back
(
Route
(
std
::
move
(
resource
),
method
,
std
::
move
(
handler
)));
r
.
push_back
(
Route
(
std
::
move
(
resource
),
method
,
std
::
move
(
handler
)));
...
@@ -227,19 +268,19 @@ Router::addRoute(Http::Method method, std::string resource, Router::Handler hand
...
@@ -227,19 +268,19 @@ Router::addRoute(Http::Method method, std::string resource, Router::Handler hand
namespace
Routes
{
namespace
Routes
{
void
Get
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
void
Get
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
)
{
router
.
get
(
std
::
move
(
resource
),
std
::
move
(
handler
));
router
.
get
(
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
void
Post
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
void
Post
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
)
{
router
.
post
(
std
::
move
(
resource
),
std
::
move
(
handler
));
router
.
post
(
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
void
Put
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
void
Put
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
)
{
router
.
put
(
std
::
move
(
resource
),
std
::
move
(
handler
));
router
.
put
(
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
void
Delete
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
)
{
void
Delete
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
)
{
router
.
del
(
std
::
move
(
resource
),
std
::
move
(
handler
));
router
.
del
(
std
::
move
(
resource
),
std
::
move
(
handler
));
}
}
...
...
src/router.h
View file @
a64024b5
...
@@ -55,88 +55,79 @@ private:
...
@@ -55,88 +55,79 @@ private:
std
::
string
value_
;
std
::
string
value_
;
};
};
class
Request
:
public
Http
::
Request
{
class
Request
;
public:
explicit
Request
(
const
Http
::
Request
&
request
,
std
::
vector
<
TypedParam
>&&
params
);
bool
hasParam
(
std
::
string
name
)
const
;
TypedParam
param
(
std
::
string
name
)
const
;
private:
std
::
vector
<
TypedParam
>
params_
;
};
class
Router
{
struct
Route
{
public:
typedef
std
::
function
<
void
(
const
Request
&
,
Net
::
Http
::
Response
)
>
Handler
;
typedef
std
::
function
<
void
(
const
Request
&
,
Net
::
Http
::
Response
)
>
Handler
;
struct
Route
{
Route
(
std
::
string
resource
,
Http
::
Method
method
,
Handler
handler
)
Route
(
std
::
string
resource
,
Http
::
Method
method
,
Handler
handler
)
:
resource_
(
std
::
move
(
resource
))
:
resource_
(
std
::
move
(
resource
))
,
method_
(
method
)
,
method_
(
method
)
,
handler_
(
std
::
move
(
handler
))
,
handler_
(
std
::
move
(
handler
))
,
fragments_
(
Fragment
::
fromUrl
(
resource_
))
,
fragments_
(
Fragment
::
fromUrl
(
resource_
))
{
{
}
}
std
::
pair
<
bool
,
std
::
vector
<
TypedParam
>>
std
::
tuple
<
bool
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
match
(
const
Http
::
Request
&
req
)
const
;
match
(
const
Http
::
Request
&
req
)
const
;
std
::
pair
<
bool
,
std
::
vector
<
TypedParam
>>
std
::
tuple
<
bool
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
match
(
const
std
::
string
&
req
)
const
;
match
(
const
std
::
string
&
req
)
const
;
template
<
typename
...
Args
>
template
<
typename
...
Args
>
void
invokeHandler
(
Args
&&
...
args
)
const
{
void
invokeHandler
(
Args
&&
...
args
)
const
{
handler_
(
std
::
forward
<
Args
>
(
args
)...);
handler_
(
std
::
forward
<
Args
>
(
args
)...);
}
}
private:
private:
struct
Fragment
{
struct
Fragment
{
explicit
Fragment
(
std
::
string
value
);
explicit
Fragment
(
std
::
string
value
);
bool
match
(
const
std
::
string
&
raw
)
const
;
bool
match
(
const
std
::
string
&
raw
)
const
;
bool
match
(
const
Fragment
&
other
)
const
;
bool
match
(
const
Fragment
&
other
)
const
;
bool
isParameter
()
const
;
bool
isParameter
()
const
;
bool
isOptional
()
const
;
bool
isSplat
()
const
;
bool
isOptional
()
const
;
std
::
string
value
()
const
{
std
::
string
value
()
const
{
return
value_
;
return
value_
;
}
}
static
std
::
vector
<
Fragment
>
fromUrl
(
const
std
::
string
&
url
);
static
std
::
vector
<
Fragment
>
fromUrl
(
const
std
::
string
&
url
);
private:
private:
enum
class
Flag
{
enum
class
Flag
{
None
=
0x0
,
None
=
0x0
,
Fixed
=
0x1
,
Fixed
=
0x1
,
Parameter
=
Fixed
<<
1
,
Parameter
=
Fixed
<<
1
,
Optional
=
Parameter
<<
1
Optional
=
Parameter
<<
1
,
};
Splat
=
Optional
<<
1
};
void
init
(
std
::
string
value
);
void
init
(
std
::
string
value
);
void
checkInvariant
()
const
;
void
checkInvariant
()
const
;
Flags
<
Flag
>
flags
;
Flags
<
Flag
>
flags
;
std
::
string
value_
;
std
::
string
value_
;
};
std
::
string
resource_
;
Net
::
Http
::
Method
method_
;
Handler
handler_
;
/* @Performance: since we know that resource_ will live as long as the vector underneath,
* we would benefit from std::experimental::string_view to store fragments.
*
* We could use string_view instead of allocating strings everytime. However, string_view is
* only available in c++17, so I might have to come with my own lightweight implementation of
* it
*/
std
::
vector
<
Fragment
>
fragments_
;
};
};
std
::
string
resource_
;
Net
::
Http
::
Method
method_
;
Handler
handler_
;
/* @Performance: since we know that resource_ will live as long as the vector underneath,
* we would benefit from std::experimental::string_view to store fragments.
*
* We could use string_view instead of allocating strings everytime. However, string_view is
* only available in c++17, so I might have to come with my own lightweight implementation of
* it
*/
std
::
vector
<
Fragment
>
fragments_
;
};
namespace
Private
{
class
HttpHandler
:
public
Net
::
Http
::
Handler
{
class
HttpHandler
:
public
Net
::
Http
::
Handler
{
public:
public:
HttpHandler
(
const
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>&
routes
);
HttpHandler
(
const
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>&
routes
);
...
@@ -149,28 +140,52 @@ public:
...
@@ -149,28 +140,52 @@ public:
private:
private:
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>
routes
;
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>
routes
;
};
};
}
class
Request
:
public
Http
::
Request
{
public:
friend
class
Private
::
HttpHandler
;
bool
hasParam
(
std
::
string
name
)
const
;
TypedParam
param
(
std
::
string
name
)
const
;
TypedParam
splatAt
(
size_t
index
)
const
;
std
::
vector
<
TypedParam
>
splat
()
const
;
private:
explicit
Request
(
const
Http
::
Request
&
request
,
std
::
vector
<
TypedParam
>&&
params
,
std
::
vector
<
TypedParam
>&&
splats
);
std
::
vector
<
TypedParam
>
params_
;
std
::
vector
<
TypedParam
>
splats_
;
};
class
Router
{
public:
std
::
shared_ptr
<
HttpHandler
>
std
::
shared_ptr
<
Private
::
HttpHandler
>
handler
()
const
{
handler
()
const
{
return
std
::
make_shared
<
HttpHandler
>
(
routes
);
return
std
::
make_shared
<
Private
::
HttpHandler
>
(
routes
);
}
}
void
get
(
std
::
string
resource
,
Handler
handler
);
void
get
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
post
(
std
::
string
resource
,
Handler
handler
);
void
post
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
put
(
std
::
string
resource
,
Handler
handler
);
void
put
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
del
(
std
::
string
resource
,
Handler
handler
);
void
del
(
std
::
string
resource
,
Route
::
Handler
handler
);
private:
private:
void
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Handler
handler
);
void
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Route
::
Handler
handler
);
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>
routes
;
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>
routes
;
};
};
namespace
Routes
{
namespace
Routes
{
void
Get
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
);
void
Get
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Post
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
);
void
Post
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Put
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
);
void
Put
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Delete
(
Router
&
router
,
std
::
string
resource
,
Route
r
::
Handler
handler
);
void
Delete
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
template
<
typename
Handler
,
typename
Obj
>
template
<
typename
Handler
,
typename
Obj
>
void
Get
(
Router
&
router
,
std
::
string
resource
,
Handler
handler
,
Obj
obj
)
{
void
Get
(
Router
&
router
,
std
::
string
resource
,
Handler
handler
,
Obj
obj
)
{
...
...
tests/router_test.cc
View file @
a64024b5
...
@@ -9,24 +9,24 @@
...
@@ -9,24 +9,24 @@
#include "router.h"
#include "router.h"
#include <algorithm>
#include <algorithm>
using
namespace
Net
::
Rest
;
using
namespace
Net
;
bool
match
(
const
R
outer
::
Route
&
route
,
const
std
::
string
&
req
)
{
bool
match
(
const
R
est
::
Route
&
route
,
const
std
::
string
&
req
)
{
return
route
.
match
(
req
).
first
;
return
std
::
get
<
0
>
(
route
.
match
(
req
))
;
}
}
bool
match
(
bool
match
Params
(
const
R
outer
::
Route
&
route
,
const
std
::
string
&
req
,
const
R
est
::
Route
&
route
,
const
std
::
string
&
req
,
std
::
initializer_list
<
std
::
pair
<
std
::
string
,
std
::
string
>>
list
)
std
::
initializer_list
<
std
::
pair
<
std
::
string
,
std
::
string
>>
list
)
{
{
bool
ok
;
bool
ok
;
std
::
vector
<
TypedParam
>
params
;
std
::
vector
<
Rest
::
TypedParam
>
params
;
std
::
tie
(
ok
,
params
)
=
route
.
match
(
req
);
std
::
tie
(
ok
,
params
,
std
::
ignore
)
=
route
.
match
(
req
);
if
(
!
ok
)
return
false
;
if
(
!
ok
)
return
false
;
for
(
const
auto
&
p
:
list
)
{
for
(
const
auto
&
p
:
list
)
{
auto
it
=
std
::
find_if
(
params
.
begin
(),
params
.
end
(),
[
&
](
const
TypedParam
&
param
)
{
auto
it
=
std
::
find_if
(
params
.
begin
(),
params
.
end
(),
[
&
](
const
Rest
::
TypedParam
&
param
)
{
return
param
.
name
()
==
p
.
first
;
return
param
.
name
()
==
p
.
first
;
});
});
if
(
it
==
std
::
end
(
params
))
{
if
(
it
==
std
::
end
(
params
))
{
...
@@ -43,12 +43,40 @@ bool match(
...
@@ -43,12 +43,40 @@ bool match(
return
true
;
return
true
;
}
}
bool
matchSplat
(
const
Rest
::
Route
&
route
,
const
std
::
string
&
req
,
std
::
initializer_list
<
std
::
string
>
list
)
{
bool
ok
;
std
::
vector
<
Rest
::
TypedParam
>
splats
;
std
::
tie
(
ok
,
std
::
ignore
,
splats
)
=
route
.
match
(
req
);
if
(
!
ok
)
return
false
;
if
(
list
.
size
()
!=
splats
.
size
())
{
std
::
cerr
<<
"Size mismatch ("
<<
list
.
size
()
<<
" != "
<<
splats
.
size
()
<<
")"
<<
std
::
endl
;
return
false
;
}
size_t
i
=
0
;
for
(
const
auto
&
s
:
list
)
{
auto
splat
=
splats
[
i
].
as
<
std
::
string
>
();
if
(
splat
!=
s
)
{
std
::
cerr
<<
"Splat number "
<<
i
<<
" did not match ("
<<
splat
<<
" != "
<<
s
<<
")"
<<
std
::
endl
;
return
false
;
}
++
i
;
}
return
true
;
}
R
outer
::
Route
R
est
::
Route
makeRoute
(
std
::
string
value
)
{
makeRoute
(
std
::
string
value
)
{
auto
noop
=
[](
const
Net
::
Http
::
Request
&
,
Net
::
Http
::
Response
)
{
};
auto
noop
=
[](
const
Net
::
Http
::
Request
&
,
Net
::
Http
::
Response
)
{
};
return
R
outer
::
Route
(
value
,
Net
::
Http
::
Method
::
Get
,
noop
);
return
R
est
::
Route
(
value
,
Net
::
Http
::
Method
::
Get
,
noop
);
}
}
TEST
(
router_test
,
test_fixed_routes
)
{
TEST
(
router_test
,
test_fixed_routes
)
{
...
@@ -63,12 +91,12 @@ TEST(router_test, test_fixed_routes) {
...
@@ -63,12 +91,12 @@ TEST(router_test, test_fixed_routes) {
TEST
(
router_test
,
test_parameters
)
{
TEST
(
router_test
,
test_parameters
)
{
auto
r1
=
makeRoute
(
"/v1/hello/:name"
);
auto
r1
=
makeRoute
(
"/v1/hello/:name"
);
ASSERT_TRUE
(
match
(
r1
,
"/v1/hello/joe"
,
{
ASSERT_TRUE
(
match
Params
(
r1
,
"/v1/hello/joe"
,
{
{
":name"
,
"joe"
}
{
":name"
,
"joe"
}
}));
}));
auto
r2
=
makeRoute
(
"/greetings/:from/:to"
);
auto
r2
=
makeRoute
(
"/greetings/:from/:to"
);
ASSERT_TRUE
(
match
(
r2
,
"/greetings/foo/bar"
,
{
ASSERT_TRUE
(
match
Params
(
r2
,
"/greetings/foo/bar"
,
{
{
":from"
,
"foo"
},
{
":from"
,
"foo"
},
{
":to"
,
"bar"
}
{
":to"
,
"bar"
}
}));
}));
...
@@ -78,12 +106,22 @@ TEST(router_test, test_optional) {
...
@@ -78,12 +106,22 @@ TEST(router_test, test_optional) {
auto
r1
=
makeRoute
(
"/get/:key?"
);
auto
r1
=
makeRoute
(
"/get/:key?"
);
ASSERT_TRUE
(
match
(
r1
,
"/get"
));
ASSERT_TRUE
(
match
(
r1
,
"/get"
));
ASSERT_TRUE
(
match
(
r1
,
"/get/"
));
ASSERT_TRUE
(
match
(
r1
,
"/get/"
));
ASSERT_TRUE
(
match
(
r1
,
"/get/foo"
,
{
ASSERT_TRUE
(
match
Params
(
r1
,
"/get/foo"
,
{
{
":key"
,
"foo"
}
{
":key"
,
"foo"
}
}));
}));
ASSERT_TRUE
(
match
(
r1
,
"/get/foo/"
,
{
ASSERT_TRUE
(
match
Params
(
r1
,
"/get/foo/"
,
{
{
":key"
,
"foo"
}
{
":key"
,
"foo"
}
}));
}));
ASSERT_FALSE
(
match
(
r1
,
"/get/foo/bar"
));
ASSERT_FALSE
(
match
(
r1
,
"/get/foo/bar"
));
}
}
TEST
(
router_test
,
test_splat
)
{
auto
r1
=
makeRoute
(
"/say/*/to/*"
);
ASSERT_TRUE
(
match
(
r1
,
"/say/hello/to/user"
));
ASSERT_FALSE
(
match
(
r1
,
"/say/hello/to"
));
ASSERT_FALSE
(
match
(
r1
,
"/say/hello/to/user/please"
));
ASSERT_TRUE
(
matchSplat
(
r1
,
"/say/hello/to/user"
,
{
"hello"
,
"user"
}));
ASSERT_TRUE
(
matchSplat
(
r1
,
"/say/hello/to/user/"
,
{
"hello"
,
"user"
}));
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment