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
496a2d11
Unverified
Commit
496a2d11
authored
Aug 19, 2018
by
Dennis Jenkins
Committed by
GitHub
Aug 19, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #236 from Fylax/master
Tree Structure and Removal
parents
c613852c
b9f91e6f
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
633 additions
and
244 deletions
+633
-244
CMakeLists.txt
CMakeLists.txt
+12
-2
include/pistache/router.h
include/pistache/router.h
+65
-68
include/pistache/string_view.h
include/pistache/string_view.h
+231
-0
src/CMakeLists.txt
src/CMakeLists.txt
+1
-1
src/server/router.cc
src/server/router.cc
+212
-132
tests/CMakeLists.txt
tests/CMakeLists.txt
+1
-0
tests/router_test.cc
tests/router_test.cc
+45
-41
tests/string_view_test.cc
tests/string_view_test.cc
+66
-0
No files found.
CMakeLists.txt
View file @
496a2d11
cmake_minimum_required
(
VERSION 3.0.2
)
cmake_minimum_required
(
VERSION 3.0.2
)
project
(
pistache
)
project
(
pistache
)
include
(
CheckCXXCompilerFlag
)
include
(
CheckCXXCompilerFlag
)
option
(
PISTACHE_BUILD_TESTS
"build tests alongside the project"
OFF
)
option
(
PISTACHE_BUILD_TESTS
"build tests alongside the project"
OFF
)
option
(
PISTACHE_BUILD_EXAMPLES
"build examples alongside the project"
OFF
)
option
(
PISTACHE_BUILD_EXAMPLES
"build examples alongside the project"
OFF
)
option
(
PISTACHE_INSTALL
"add pistache as install target (recommended)"
ON
)
option
(
PISTACHE_INSTALL
"add pistache as install target (recommended)"
ON
)
CHECK_CXX_COMPILER_FLAG
(
"-std=c++17"
COMPILER_SUPPORTS_CXX17
)
CHECK_CXX_COMPILER_FLAG
(
"-std=c++14"
COMPILER_SUPPORTS_CXX14
)
CHECK_CXX_COMPILER_FLAG
(
"-std=c++11"
COMPILER_SUPPORTS_CXX11
)
CHECK_CXX_COMPILER_FLAG
(
"-std=c++11"
COMPILER_SUPPORTS_CXX11
)
CHECK_CXX_COMPILER_FLAG
(
"-std=c++0x"
COMPILER_SUPPORTS_CXX0X
)
CHECK_CXX_COMPILER_FLAG
(
"-std=c++0x"
COMPILER_SUPPORTS_CXX0X
)
if
(
COMPILER_SUPPORTS_CXX11
)
# Clang exports C++17 in std::experimental namespace (tested on Clang 5 and 6).
# This gives an error on date.h external library.
# Following workaround forces Clang to compile at best with C++14
if
(
COMPILER_SUPPORTS_CXX17 AND NOT CMAKE_CXX_COMPILER_ID STREQUAL
"Clang"
)
set
(
CMAKE_CXX_FLAGS
"
${
CMAKE_CXX_FLAGS
}
-std=c++17"
)
elseif
(
COMPILER_SUPPORTS_CXX14
)
set
(
CMAKE_CXX_FLAGS
"
${
CMAKE_CXX_FLAGS
}
-std=c++14"
)
elseif
(
COMPILER_SUPPORTS_CXX11
)
set
(
CMAKE_CXX_FLAGS
"
${
CMAKE_CXX_FLAGS
}
-std=c++11"
)
set
(
CMAKE_CXX_FLAGS
"
${
CMAKE_CXX_FLAGS
}
-std=c++11"
)
elseif
(
COMPILER_SUPPORTS_CXX0X
)
elseif
(
COMPILER_SUPPORTS_CXX0X
)
set
(
CMAKE_CXX_FLAGS
"
${
CMAKE_CXX_FLAGS
}
-std=c++0x"
)
set
(
CMAKE_CXX_FLAGS
"
${
CMAKE_CXX_FLAGS
}
-std=c++0x"
)
...
@@ -82,4 +93,3 @@ endif()
...
@@ -82,4 +93,3 @@ endif()
DESTINATION
DESTINATION
lib/pkgconfig/
lib/pkgconfig/
)
)
include/pistache/router.h
View file @
496a2d11
...
@@ -8,11 +8,15 @@
...
@@ -8,11 +8,15 @@
#include <string>
#include <string>
#include <tuple>
#include <tuple>
#include <unordered_map>
#include <memory>
#include <pistache/http.h>
#include <pistache/http.h>
#include <pistache/http_defs.h>
#include <pistache/http_defs.h>
#include <pistache/flags.h>
#include <pistache/flags.h>
#include "pistache/string_view.h"
namespace
Pistache
{
namespace
Pistache
{
namespace
Rest
{
namespace
Rest
{
...
@@ -20,8 +24,8 @@ class Description;
...
@@ -20,8 +24,8 @@ class Description;
namespace
details
{
namespace
details
{
template
<
typename
T
>
struct
LexicalCast
{
template
<
typename
T
>
struct
LexicalCast
{
static
T
cast
(
const
std
::
string
&
value
)
{
static
T
cast
(
const
std
::
string
_view
&
value
)
{
std
::
istringstream
iss
(
value
);
std
::
istringstream
iss
(
std
::
string
(
value
.
data
(),
value
.
length
())
);
T
out
;
T
out
;
if
(
!
(
iss
>>
out
))
if
(
!
(
iss
>>
out
))
throw
std
::
runtime_error
(
"Bad lexical cast"
);
throw
std
::
runtime_error
(
"Bad lexical cast"
);
...
@@ -30,8 +34,8 @@ namespace details {
...
@@ -30,8 +34,8 @@ namespace details {
};
};
template
<
>
template
<
>
struct
LexicalCast
<
std
::
string
>
{
struct
LexicalCast
<
std
::
string
_view
>
{
static
std
::
string
cast
(
const
std
::
string
&
value
)
{
static
std
::
string
_view
cast
(
const
std
::
string_view
&
value
)
{
return
value
;
return
value
;
}
}
};
};
...
@@ -39,7 +43,7 @@ namespace details {
...
@@ -39,7 +43,7 @@ namespace details {
class
TypedParam
{
class
TypedParam
{
public:
public:
TypedParam
(
const
std
::
string
&
name
,
const
std
::
string
&
value
)
TypedParam
(
const
std
::
string
_view
&
name
,
const
std
::
string_view
&
value
)
:
name_
(
name
)
:
name_
(
name
)
,
value_
(
value
)
,
value_
(
value
)
{
}
{
}
...
@@ -49,97 +53,88 @@ public:
...
@@ -49,97 +53,88 @@ public:
return
details
::
LexicalCast
<
T
>::
cast
(
value_
);
return
details
::
LexicalCast
<
T
>::
cast
(
value_
);
}
}
std
::
string
name
()
const
{
std
::
string
_view
name
()
const
{
return
name_
;
return
name_
;
}
}
private:
private:
std
::
string
name_
;
std
::
string
_view
name_
;
std
::
string
value_
;
std
::
string
_view
value_
;
};
};
class
Request
;
class
Request
;
struct
Route
{
struct
Route
{
enum
class
Result
{
Ok
,
Failure
};
typedef
std
::
function
<
Result
(
const
Request
&
,
Http
::
ResponseWriter
)
>
Handler
;
enum
class
Status
{
Match
,
NotFound
}
;
Route
(
std
::
string
resource
,
Http
::
Method
method
,
Handler
handler
)
enum
class
Result
{
Ok
,
Failure
};
:
resource_
(
std
::
move
(
resource
))
,
method_
(
method
)
,
handler_
(
std
::
move
(
handler
))
,
fragments_
(
Fragment
::
fromUrl
(
resource_
))
{
}
std
::
tuple
<
bool
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
typedef
std
::
function
<
Result
(
const
Request
,
Http
::
ResponseWriter
)
>
Handler
;
match
(
const
Http
::
Request
&
req
)
const
;
std
::
tuple
<
bool
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
explicit
Route
(
Route
::
Handler
handler
)
:
handler_
(
std
::
move
(
handler
))
{
}
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:
Handler
handler_
;
struct
Fragment
{
};
explicit
Fragment
(
std
::
string
value
);
bool
match
(
const
std
::
string
&
raw
)
const
;
namespace
Private
{
bool
match
(
const
Fragment
&
other
)
const
;
class
RouterHandler
;
}
bool
isParameter
()
const
;
class
FragmentTreeNode
{
bool
isSplat
()
const
;
private:
bool
isOptional
()
const
;
enum
class
FragmentType
{
Fixed
,
Param
,
Optional
,
Splat
};
std
::
string
value
()
const
{
/**
return
value_
;
* Resource path are allocated on stack. To make them survive it is required to allocate them on heap.
}
* Since the main idea between string_view is to have constant substring and thus a reduced number of
* char* allocated on heap, this shared_ptr is used to know if a string_view can be destroyed (after a
* route removal).
*/
std
::
shared_ptr
<
char
>
resourceRef_
;
static
std
::
vector
<
Fragment
>
fromUrl
(
const
std
::
string
&
url
);
std
::
unordered_map
<
std
::
string_view
,
std
::
shared_ptr
<
FragmentTreeNode
>>
fixed_
;
std
::
unordered_map
<
std
::
string_view
,
std
::
shared_ptr
<
FragmentTreeNode
>>
param_
;
std
::
unordered_map
<
std
::
string_view
,
std
::
shared_ptr
<
FragmentTreeNode
>>
optional_
;
std
::
shared_ptr
<
FragmentTreeNode
>
splat_
;
std
::
shared_ptr
<
Route
>
route_
;
private:
static
FragmentType
getFragmentType
(
const
std
::
string_view
&
fragment
);
enum
class
Flag
{
None
=
0x0
,
Fixed
=
0x1
,
Parameter
=
Fixed
<<
1
,
Optional
=
Parameter
<<
1
,
Splat
=
Optional
<<
1
};
void
init
(
std
::
string
value
);
std
::
tuple
<
std
::
shared_ptr
<
Route
>
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
findRoute
(
const
std
::
string_view
&
path
,
std
::
vector
<
TypedParam
>
&
params
,
std
::
vector
<
TypedParam
>
&
splats
)
const
;
void
checkInvariant
()
const
;
public:
FragmentTreeNode
();
explicit
FragmentTreeNode
(
const
std
::
shared_ptr
<
char
>
&
resourceReference
);
Flags
<
Flag
>
flags
;
void
addRoute
(
const
std
::
string_view
&
path
,
const
Route
::
Handler
&
handler
,
std
::
string
value_
;
const
std
::
shared_ptr
<
char
>
&
resourceReference
);
};
std
::
string
resource_
;
bool
removeRoute
(
const
std
::
string_view
&
path
);
Http
::
Method
method_
;
Handler
handler_
;
/**
/* @Performance: since we know that resource_ will live as long as the vector underneath,
* Finds the correct route for the given path.
* we would benefit from std::experimental::string_view to store fragments.
* \param[in] path Requested resource path (usually derived from request)
*
* \throws std::runtime_error An empty path was given
* We could use string_view instead of allocating strings everytime. However, string_view is
* \return Found route with its resolved parameters and splats (if no route is found, first element of the tuple
* only available in c++17, so I might have to come with my own lightweight implementation of
* is a null pointer).
* it
*/
*/
std
::
vector
<
Fragment
>
fragments_
;
std
::
tuple
<
std
::
shared_ptr
<
Route
>
,
std
::
vector
<
TypedParam
>
,
std
::
vector
<
TypedParam
>>
findRoute
(
const
std
::
string_view
&
path
)
const
;
};
};
namespace
Private
{
class
RouterHandler
;
}
class
Router
{
class
Router
{
public:
public:
enum
class
Status
{
Match
,
NotFound
};
static
Router
fromDescription
(
const
Rest
::
Description
&
desc
);
static
Router
fromDescription
(
const
Rest
::
Description
&
desc
);
std
::
shared_ptr
<
Private
::
RouterHandler
>
std
::
shared_ptr
<
Private
::
RouterHandler
>
...
@@ -153,18 +148,18 @@ public:
...
@@ -153,18 +148,18 @@ public:
void
patch
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
patch
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
del
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
del
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
options
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
options
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
removeRoute
(
Http
::
Method
method
,
std
::
string
resource
);
void
addCustomHandler
(
Route
::
Handler
handler
);
void
addCustomHandler
(
Route
::
Handler
handler
);
void
addNotFoundHandler
(
Route
::
Handler
handler
);
void
addNotFoundHandler
(
Route
::
Handler
handler
);
inline
bool
hasNotFoundHandler
()
{
return
notFoundHandler
!=
nullptr
;
}
inline
bool
hasNotFoundHandler
()
{
return
notFoundHandler
!=
nullptr
;
}
void
invokeNotFoundHandler
(
const
Http
::
Request
&
req
,
Http
::
ResponseWriter
resp
)
const
;
void
invokeNotFoundHandler
(
const
Http
::
Request
&
req
,
Http
::
ResponseWriter
resp
)
const
;
Status
route
(
const
Http
::
Request
&
request
,
Http
::
ResponseWriter
response
);
Route
::
Status
route
(
const
Http
::
Request
&
request
,
Http
::
ResponseWriter
response
);
private:
private:
void
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Route
::
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
,
FragmentTreeNode
>
routes
;
std
::
vector
<
Route
::
Handler
>
customHandlers
;
std
::
vector
<
Route
::
Handler
>
customHandlers
;
...
@@ -192,6 +187,7 @@ namespace Private {
...
@@ -192,6 +187,7 @@ namespace Private {
class
Request
:
public
Http
::
Request
{
class
Request
:
public
Http
::
Request
{
public:
public:
friend
class
FragmentTreeNode
;
friend
class
Router
;
friend
class
Router
;
bool
hasParam
(
std
::
string
name
)
const
;
bool
hasParam
(
std
::
string
name
)
const
;
...
@@ -202,7 +198,7 @@ public:
...
@@ -202,7 +198,7 @@ public:
private:
private:
explicit
Request
(
explicit
Request
(
const
Http
::
Request
&
request
,
const
Http
::
Request
&
request
,
std
::
vector
<
TypedParam
>&&
params
,
std
::
vector
<
TypedParam
>&&
params
,
std
::
vector
<
TypedParam
>&&
splats
);
std
::
vector
<
TypedParam
>&&
splats
);
...
@@ -219,6 +215,7 @@ namespace Routes {
...
@@ -219,6 +215,7 @@ namespace Routes {
void
Patch
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Patch
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Delete
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Delete
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Options
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Options
(
Router
&
router
,
std
::
string
resource
,
Route
::
Handler
handler
);
void
Remove
(
Router
&
router
,
Http
::
Method
method
,
std
::
string
resource
);
void
NotFound
(
Router
&
router
,
Route
::
Handler
handler
);
void
NotFound
(
Router
&
router
,
Route
::
Handler
handler
);
...
...
include/pistache/string_view.h
0 → 100644
View file @
496a2d11
#pragma once
#define CUSTOM_STRING_VIEW 1
#if __cplusplus >= 201703L
# if defined(__has_include)
# if __has_include(<string_view>)
# undef CUSTOM_STRING_VIEW
# include <string_view>
# elif __has_include(<experimental/string_view>)
# undef CUSTOM_STRING_VIEW
# include <experimental/string_view>
namespace
std
{
typedef
experimental
::
string_view
string_view
;
}
# endif
# endif
#endif
#ifdef CUSTOM_STRING_VIEW
#include <endian.h>
#include <string>
#include <stdexcept>
#include <cstring>
typedef
std
::
size_t
size_type
;
namespace
std
{
class
string_view
{
private:
const
char
*
data_
;
size_type
size_
;
public:
static
constexpr
size_type
npos
=
size_type
(
-
1
);
constexpr
string_view
()
noexcept
:
data_
(
nullptr
),
size_
(
0
)
{
}
constexpr
string_view
(
const
string_view
&
other
)
noexcept
=
default
;
constexpr
string_view
(
const
char
*
s
,
size_type
count
)
:
data_
(
s
),
size_
(
count
)
{
}
string_view
(
const
char
*
s
)
:
data_
(
s
),
size_
(
strlen
(
s
))
{
}
string_view
substr
(
size_type
pos
,
size_type
count
=
npos
)
const
{
if
(
pos
>
size_
)
{
throw
std
::
out_of_range
(
"Out of range."
);
}
size_type
rcount
=
std
::
min
(
count
,
size_
-
pos
);
return
{
data_
+
pos
,
rcount
};
}
constexpr
size_type
size
()
const
noexcept
{
return
size_
;
}
constexpr
size_type
length
()
const
noexcept
{
return
size_
;
}
constexpr
const
char
operator
[](
size_type
pos
)
const
{
return
data_
[
pos
];
}
constexpr
const
char
*
data
()
const
noexcept
{
return
data_
;
}
bool
operator
==
(
const
string_view
&
rhs
)
const
{
return
(
!
std
::
lexicographical_compare
(
data_
,
data_
+
size_
,
rhs
.
data_
,
rhs
.
data_
+
rhs
.
size_
)
&&
!
std
::
lexicographical_compare
(
rhs
.
data_
,
rhs
.
data_
+
rhs
.
size_
,
data_
,
data_
+
size_
));
}
string_view
&
operator
=
(
const
string_view
&
view
)
noexcept
=
default
;
size_type
find
(
string_view
v
,
size_type
pos
=
0
)
const
noexcept
{
for
(;
size_
-
pos
>=
v
.
size_
;
pos
++
)
{
string_view
compare
=
substr
(
pos
,
v
.
size_
);
if
(
v
==
compare
)
{
return
pos
;
}
}
return
npos
;
}
size_type
find
(
char
ch
,
size_type
pos
=
0
)
const
noexcept
{
return
find
(
string_view
(
&
ch
,
1
),
pos
);
}
size_type
find
(
const
char
*
s
,
size_type
pos
,
size_type
count
)
const
{
return
find
(
string_view
(
s
,
count
),
pos
);
}
size_type
find
(
const
char
*
s
,
size_type
pos
=
0
)
const
{
return
find
(
string_view
(
s
),
pos
);
}
size_type
rfind
(
string_view
v
,
size_type
pos
=
npos
)
const
noexcept
{
if
(
pos
>=
size_
)
{
pos
=
size_
-
v
.
size_
;
}
for
(;
pos
>=
0
&&
pos
!=
npos
;
pos
--
)
{
string_view
compare
=
substr
(
pos
,
v
.
size_
);
if
(
v
==
compare
)
{
return
pos
;
}
}
return
npos
;
}
size_type
rfind
(
char
c
,
size_type
pos
=
npos
)
const
noexcept
{
return
rfind
(
string_view
(
&
c
,
1
),
pos
);
}
size_type
rfind
(
const
char
*
s
,
size_type
pos
,
size_type
count
)
const
{
return
rfind
(
string_view
(
s
,
count
),
pos
);
}
size_type
rfind
(
const
char
*
s
,
size_type
pos
=
npos
)
const
{
return
rfind
(
string_view
(
s
),
pos
);
}
constexpr
bool
empty
()
const
noexcept
{
return
size_
==
0
;
}
};
template
<
>
struct
hash
<
string_view
>
{
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
private:
#ifdef __GNUC__
#define FORCE_INLINE __attribute__((always_inline)) inline
#else
#define FORCE_INLINE inline
#endif
static
FORCE_INLINE
std
::
uint32_t
Rotl32
(
const
std
::
uint32_t
x
,
const
std
::
int8_t
r
)
{
return
(
x
<<
r
)
|
(
x
>>
(
32
-
r
));
}
static
FORCE_INLINE
std
::
uint32_t
Getblock
(
const
std
::
uint32_t
*
p
,
const
int
i
)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return
p
[
i
];
#else
std
::
uint32_t
temp_number
=
p
[
i
];
uint8_t
(
&
number
)[
4
]
=
*
reinterpret_cast
<
uint8_t
(
*
)[
4
]
>
(
&
temp_number
);
std
::
swap
(
number
[
0
],
number
[
3
]);
std
::
swap
(
number
[
1
],
number
[
2
]);
return
temp_number
;
#endif
}
static
FORCE_INLINE
uint32_t
fmix32
(
uint32_t
h
)
{
h
^=
h
>>
16
;
h
*=
0x85ebca6b
;
h
^=
h
>>
13
;
h
*=
0xc2b2ae35
;
h
^=
h
>>
16
;
return
h
;
}
public:
size_type
operator
()(
const
string_view
&
str
)
const
{
const
size_t
len
=
str
.
length
();
const
uint8_t
*
data
=
reinterpret_cast
<
const
uint8_t
*>
(
str
.
data
());
const
int
nblocks
=
static_cast
<
int
>
(
len
/
4
);
uint32_t
h1
=
0
;
// seed
const
uint32_t
c1
=
0xcc9e2d51U
;
const
uint32_t
c2
=
0x1b873593U
;
const
uint32_t
*
blocks
=
reinterpret_cast
<
const
uint32_t
*>
(
data
+
nblocks
*
4
);
for
(
auto
i
=
-
nblocks
;
i
;
++
i
)
{
uint32_t
k1
=
Getblock
(
blocks
,
i
);
k1
*=
c1
;
k1
=
Rotl32
(
k1
,
15
);
k1
*=
c2
;
h1
^=
k1
;
h1
=
Rotl32
(
h1
,
13
);
h1
=
h1
*
5
+
0xe6546b64
;
}
const
uint8_t
*
tail
=
data
+
nblocks
*
4
;
uint32_t
k1
=
0
;
switch
(
len
&
3
)
{
case
3
:
k1
^=
tail
[
2
]
<<
16
;
case
2
:
k1
^=
tail
[
1
]
<<
8
;
case
1
:
k1
^=
tail
[
0
];
k1
*=
c1
;
k1
=
Rotl32
(
k1
,
15
);
k1
*=
c2
;
h1
^=
k1
;
default:
break
;
}
h1
^=
len
;
h1
=
fmix32
(
h1
);
return
hash
<
int
>
()(
h1
);
}
#undef FORCE_INLINE
};
}
#endif
src/CMakeLists.txt
View file @
496a2d11
...
@@ -9,7 +9,7 @@ set(SOURCE_FILES
...
@@ -9,7 +9,7 @@ set(SOURCE_FILES
${
SERVER_SOURCE_FILES
}
${
SERVER_SOURCE_FILES
}
${
CLIENT_SOURCE_FILES
}
${
CLIENT_SOURCE_FILES
}
${
INCLUDE_FILES
}
${
INCLUDE_FILES
}
)
../include/pistache/string_view.h
)
add_library
(
pistache
${
SOURCE_FILES
}
)
add_library
(
pistache
${
SOURCE_FILES
}
)
set_target_properties
(
pistache PROPERTIES
set_target_properties
(
pistache PROPERTIES
...
...
src/server/router.cc
View file @
496a2d11
This diff is collapsed.
Click to expand it.
tests/CMakeLists.txt
View file @
496a2d11
...
@@ -16,3 +16,4 @@ pistache_test(router_test)
...
@@ -16,3 +16,4 @@ pistache_test(router_test)
pistache_test
(
cookie_test
)
pistache_test
(
cookie_test
)
pistache_test
(
view_test
)
pistache_test
(
view_test
)
pistache_test
(
http_parsing_test
)
pistache_test
(
http_parsing_test
)
pistache_test
(
string_view_test
)
tests/router_test.cc
View file @
496a2d11
...
@@ -12,23 +12,25 @@
...
@@ -12,23 +12,25 @@
using
namespace
Pistache
;
using
namespace
Pistache
;
bool
match
(
const
Rest
::
Route
&
route
,
const
std
::
string
&
req
)
{
bool
match
(
const
Rest
::
FragmentTreeNode
&
routes
,
const
std
::
string
&
req
)
{
return
std
::
get
<
0
>
(
route
.
match
(
req
));
std
::
shared_ptr
<
Rest
::
Route
>
route
;
std
::
tie
(
route
,
std
::
ignore
,
std
::
ignore
)
=
routes
.
findRoute
({
req
.
data
(),
req
.
size
()});
return
route
!=
nullptr
;
}
}
bool
matchParams
(
bool
matchParams
(
const
Rest
::
Route
&
route
,
const
std
::
string
&
req
,
const
Rest
::
FragmentTreeNode
&
routes
,
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
;
std
::
shared_ptr
<
Rest
::
Route
>
route
;
std
::
vector
<
Rest
::
TypedParam
>
params
;
std
::
vector
<
Rest
::
TypedParam
>
params
;
std
::
tie
(
ok
,
params
,
std
::
ignore
)
=
route
.
match
(
req
);
std
::
tie
(
route
,
params
,
std
::
ignore
)
=
routes
.
findRoute
({
req
.
data
(),
req
.
size
()}
);
if
(
!
ok
)
return
false
;
if
(
route
==
nullptr
)
return
false
;
for
(
const
auto
&
p
:
list
)
{
for
(
const
auto
&
p
:
list
)
{
auto
it
=
std
::
find_if
(
params
.
begin
(),
params
.
end
(),
[
&
](
const
Rest
::
TypedParam
&
param
)
{
auto
it
=
std
::
find_if
(
params
.
begin
(),
params
.
end
(),
[
&
](
const
Rest
::
TypedParam
&
param
)
{
return
param
.
name
()
==
p
.
first
;
return
param
.
name
()
==
std
::
string_view
(
p
.
first
.
data
(),
p
.
first
.
size
())
;
});
});
if
(
it
==
std
::
end
(
params
))
{
if
(
it
==
std
::
end
(
params
))
{
std
::
cerr
<<
"Did not find param '"
<<
p
.
first
<<
"'"
<<
std
::
endl
;
std
::
cerr
<<
"Did not find param '"
<<
p
.
first
<<
"'"
<<
std
::
endl
;
...
@@ -45,14 +47,14 @@ bool matchParams(
...
@@ -45,14 +47,14 @@ bool matchParams(
}
}
bool
matchSplat
(
bool
matchSplat
(
const
Rest
::
Route
&
route
,
const
std
::
string
&
req
,
const
Rest
::
FragmentTreeNode
&
routes
,
const
std
::
string
&
req
,
std
::
initializer_list
<
std
::
string
>
list
)
std
::
initializer_list
<
std
::
string
>
list
)
{
{
bool
ok
;
std
::
shared_ptr
<
Rest
::
Route
>
route
;
std
::
vector
<
Rest
::
TypedParam
>
splats
;
std
::
vector
<
Rest
::
TypedParam
>
splats
;
std
::
tie
(
ok
,
std
::
ignore
,
splats
)
=
route
.
match
(
req
);
std
::
tie
(
route
,
std
::
ignore
,
splats
)
=
routes
.
findRoute
({
req
.
data
(),
req
.
size
()}
);
if
(
!
ok
)
return
false
;
if
(
route
==
nullptr
)
return
false
;
if
(
list
.
size
()
!=
splats
.
size
())
{
if
(
list
.
size
()
!=
splats
.
size
())
{
std
::
cerr
<<
"Size mismatch ("
<<
list
.
size
()
<<
" != "
<<
splats
.
size
()
<<
")"
std
::
cerr
<<
"Size mismatch ("
<<
list
.
size
()
<<
" != "
<<
splats
.
size
()
<<
")"
...
@@ -74,55 +76,57 @@ bool matchSplat(
...
@@ -74,55 +76,57 @@ bool matchSplat(
return
true
;
return
true
;
}
}
Rest
::
Route
makeRoute
(
std
::
string
value
)
{
auto
noop
=
[](
const
Http
::
Request
&
,
Http
::
Response
)
{
return
Rest
::
Route
::
Result
::
Ok
;
};
return
Rest
::
Route
(
value
,
Http
::
Method
::
Get
,
noop
);
}
TEST
(
router_test
,
test_fixed_routes
)
{
TEST
(
router_test
,
test_fixed_routes
)
{
auto
r1
=
makeRoute
(
"/v1/hello"
);
Rest
::
FragmentTreeNode
routes
;
ASSERT_TRUE
(
match
(
r1
,
"/v1/hello"
));
routes
.
addRoute
(
std
::
string_view
(
"/v1/hello"
),
nullptr
,
nullptr
);
ASSERT_FALSE
(
match
(
r1
,
"/v2/hello"
));
ASSERT_FALSE
(
match
(
r1
,
"/v1/hell0"
));
ASSERT_TRUE
(
match
(
routes
,
"/v1/hello"
));
ASSERT_FALSE
(
match
(
routes
,
"/v2/hello"
));
ASSERT_FALSE
(
match
(
routes
,
"/v1/hell0"
));
auto
r2
=
makeRoute
(
"/a/b/c"
);
routes
.
addRoute
(
std
::
string_view
(
"/a/b/c"
),
nullptr
,
nullptr
);
ASSERT_TRUE
(
match
(
r
2
,
"/a/b/c"
));
ASSERT_TRUE
(
match
(
r
outes
,
"/a/b/c"
));
}
}
TEST
(
router_test
,
test_parameters
)
{
TEST
(
router_test
,
test_parameters
)
{
auto
r1
=
makeRoute
(
"/v1/hello/:name"
);
Rest
::
FragmentTreeNode
routes
;
ASSERT_TRUE
(
matchParams
(
r1
,
"/v1/hello/joe"
,
{
routes
.
addRoute
(
std
::
string_view
(
"/v1/hello/:name"
),
nullptr
,
nullptr
);
ASSERT_TRUE
(
matchParams
(
routes
,
"/v1/hello/joe"
,
{
{
":name"
,
"joe"
}
{
":name"
,
"joe"
}
}));
}));
auto
r2
=
makeRoute
(
"/greetings/:from/:to"
);
routes
.
addRoute
(
std
::
string_view
(
"/greetings/:from/:to"
),
nullptr
,
nullptr
);
ASSERT_TRUE
(
matchParams
(
r
2
,
"/greetings/foo/bar"
,
{
ASSERT_TRUE
(
matchParams
(
r
outes
,
"/greetings/foo/bar"
,
{
{
":from"
,
"foo"
},
{
":from"
,
"foo"
},
{
":to"
,
"bar"
}
{
":to"
,
"bar"
}
}));
}));
}
}
TEST
(
router_test
,
test_optional
)
{
TEST
(
router_test
,
test_optional
)
{
auto
r1
=
makeRoute
(
"/get/:key?"
);
Rest
::
FragmentTreeNode
routes
;
ASSERT_TRUE
(
match
(
r1
,
"/get"
));
routes
.
addRoute
(
std
::
string_view
(
"/get/:key?"
),
nullptr
,
nullptr
);
ASSERT_TRUE
(
match
(
r1
,
"/get/"
));
ASSERT_TRUE
(
matchParams
(
r1
,
"/get/foo"
,
{
ASSERT_TRUE
(
match
(
routes
,
"/get"
));
ASSERT_TRUE
(
match
(
routes
,
"/get/"
));
ASSERT_TRUE
(
matchParams
(
routes
,
"/get/foo"
,
{
{
":key"
,
"foo"
}
{
":key"
,
"foo"
}
}));
}));
ASSERT_TRUE
(
matchParams
(
r
1
,
"/get/foo/"
,
{
ASSERT_TRUE
(
matchParams
(
r
outes
,
"/get/foo/"
,
{
{
":key"
,
"foo"
}
{
":key"
,
"foo"
}
}));
}));
ASSERT_FALSE
(
match
(
r
1
,
"/get/foo/bar"
));
ASSERT_FALSE
(
match
(
r
outes
,
"/get/foo/bar"
));
}
}
TEST
(
router_test
,
test_splat
)
{
TEST
(
router_test
,
test_splat
)
{
auto
r1
=
makeRoute
(
"/say/*/to/*"
);
Rest
::
FragmentTreeNode
routes
;
ASSERT_TRUE
(
match
(
r1
,
"/say/hello/to/user"
));
routes
.
addRoute
(
std
::
string_view
(
"/say/*/to/*"
),
nullptr
,
nullptr
);
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
(
match
(
routes
,
"/say/hello/to/user"
));
ASSERT_TRUE
(
matchSplat
(
r1
,
"/say/hello/to/user/"
,
{
"hello"
,
"user"
}));
ASSERT_FALSE
(
match
(
routes
,
"/say/hello/to"
));
}
ASSERT_FALSE
(
match
(
routes
,
"/say/hello/to/user/please"
));
ASSERT_TRUE
(
matchSplat
(
routes
,
"/say/hello/to/user"
,
{
"hello"
,
"user"
}));
ASSERT_TRUE
(
matchSplat
(
routes
,
"/say/hello/to/user/"
,
{
"hello"
,
"user"
}));
}
\ No newline at end of file
tests/string_view_test.cc
0 → 100644
View file @
496a2d11
#include "gtest/gtest.h"
#include <pistache/string_view.h>
TEST
(
string_view_test
,
substr_test
)
{
std
::
string_view
orig
(
"test"
);
std
::
string_view
targ
(
"est"
);
std
::
string_view
sub
=
orig
.
substr
(
1
);
ASSERT_EQ
(
sub
,
targ
);
sub
=
orig
.
substr
(
1
,
10
);
ASSERT_EQ
(
sub
,
targ
);
ASSERT_THROW
(
orig
.
substr
(
6
),
std
::
out_of_range
);
}
TEST
(
string_view_test
,
find_test
)
{
std
::
string_view
orig
(
"test"
);
std
::
string_view
find
(
"est"
);
ASSERT_EQ
(
orig
.
find
(
find
),
1
);
ASSERT_EQ
(
orig
.
find
(
find
,
1
),
1
);
ASSERT_EQ
(
orig
.
find
(
find
,
2
),
std
::
size_t
(
-
1
));
ASSERT_EQ
(
orig
.
find
(
'e'
),
1
);
ASSERT_EQ
(
orig
.
find
(
'e'
,
1
),
1
);
ASSERT_EQ
(
orig
.
find
(
'e'
,
2
),
std
::
size_t
(
-
1
));
ASSERT_EQ
(
orig
.
find
(
'1'
),
std
::
size_t
(
-
1
));
ASSERT_EQ
(
orig
.
find
(
"est"
),
1
);
ASSERT_EQ
(
orig
.
find
(
"est"
,
1
),
1
);
ASSERT_EQ
(
orig
.
find
(
"est"
,
1
,
2
),
1
);
ASSERT_EQ
(
orig
.
find
(
"set"
),
std
::
size_t
(
-
1
));
ASSERT_EQ
(
orig
.
find
(
"est"
,
2
),
std
::
size_t
(
-
1
));
ASSERT_EQ
(
orig
.
find
(
"est"
,
2
,
2
),
std
::
size_t
(
-
1
));
}
TEST
(
string_view_test
,
rfind_test
)
{
std
::
string_view
orig
(
"test"
);
std
::
string_view
find
(
"est"
);
ASSERT_EQ
(
orig
.
rfind
(
find
),
1
);
ASSERT_EQ
(
orig
.
rfind
(
find
,
1
),
1
);
ASSERT_EQ
(
orig
.
rfind
(
'e'
),
1
);
ASSERT_EQ
(
orig
.
rfind
(
'e'
,
1
),
1
);
ASSERT_EQ
(
orig
.
rfind
(
'q'
),
std
::
size_t
(
-
1
));
ASSERT_EQ
(
orig
.
rfind
(
"est"
),
1
);
ASSERT_EQ
(
orig
.
rfind
(
"est"
,
1
),
1
);
ASSERT_EQ
(
orig
.
rfind
(
"est"
,
1
,
2
),
1
);
ASSERT_EQ
(
orig
.
rfind
(
"set"
),
std
::
size_t
(
-
1
));
}
TEST
(
string_view_test
,
emptiness
)
{
std
::
string_view
e1
;
std
::
string_view
e2
(
""
);
std
::
string_view
e3
(
"test"
,
0
);
std
::
string_view
ne
(
"test"
);
ASSERT_TRUE
(
e1
.
empty
());
ASSERT_TRUE
(
e2
.
empty
());
ASSERT_TRUE
(
e3
.
empty
());
ASSERT_FALSE
(
ne
.
empty
());
}
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