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
37c872c6
Commit
37c872c6
authored
Feb 28, 2016
by
octal
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Swagger implementation
parent
dfd57229
Changes
15
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
582 additions
and
75 deletions
+582
-75
.gitmodules
.gitmodules
+3
-0
CMakeLists.txt
CMakeLists.txt
+3
-0
examples/rest_description.cc
examples/rest_description.cc
+8
-0
examples/rest_server.cc
examples/rest_server.cc
+0
-17
include/description.h
include/description.h
+201
-4
include/http_header.h
include/http_header.h
+17
-0
include/iterator_adapter.h
include/iterator_adapter.h
+44
-0
include/router.h
include/router.h
+44
-27
src/common/description.cc
src/common/description.cc
+196
-7
src/common/http_header.cc
src/common/http_header.cc
+14
-0
src/common/http_headers.cc
src/common/http_headers.cc
+1
-0
src/server/router.cc
src/server/router.cc
+48
-19
tests/router_test.cc
tests/router_test.cc
+1
-1
thirdparty/CMakeLists.txt
thirdparty/CMakeLists.txt
+1
-0
thirdparty/rapidjson
thirdparty/rapidjson
+1
-0
No files found.
.gitmodules
0 → 100644
View file @
37c872c6
[submodule "thirdparty/rapidjson"]
path = thirdparty/rapidjson
url = https://github.com/miloyip/rapidjson.git
CMakeLists.txt
View file @
37c872c6
...
...
@@ -16,6 +16,9 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
file
(
GLOB HEADER_FILES
"include/*.h"
)
install
(
FILES
${
HEADER_FILES
}
DESTINATION include/rest
)
add_subdirectory
(
thirdparty
)
include_directories
(
${
RapidJSON_SOURCE_DIR
}
/include
)
add_subdirectory
(
src
)
include_directories
(
src
)
...
...
examples/rest_description.cc
View file @
37c872c6
...
...
@@ -37,6 +37,13 @@ public:
void
start
()
{
router
.
initFromDescription
(
desc
);
Rest
::
Swagger
swagger
(
desc
);
swagger
.
uiPath
(
"/doc"
)
.
uiDirectory
(
"/home/octal/code/web/swagger-ui-2.1.4/dist/"
)
.
apiPath
(
"/banker-api.json"
)
.
install
(
router
);
httpEndpoint
->
setHandler
(
router
.
handler
());
httpEndpoint
->
serve
();
}
...
...
@@ -97,6 +104,7 @@ private:
.
produces
(
MIME
(
Application
,
Json
))
.
response
(
Http
::
Code
::
Ok
,
"Budget has been added to the account"
)
.
response
(
backendErrorResponse
);
}
void
retrieveAllAccounts
(
const
Rest
::
Request
&
req
,
Http
::
ResponseWriter
response
)
{
...
...
examples/rest_server.cc
View file @
37c872c6
...
...
@@ -57,23 +57,6 @@ private:
void
setupRoutes
()
{
using
namespace
Net
::
Rest
;
#if 0
Rest::Description apiDescription("my-api");
auto versionPath = desc.path("/v1");
versionPath
.route(desc.get("/record/:name/:value?"))
.bind(&StatsEndpoint::doRecordMetric, this)
.produces(MIME(Text, Plain))
.consumes(MIME(Star, Star))
.parameter<Rest::Type::String>("name", "The name of the metric to record", Rest::Flag::Required)
.parameter<Rest::Type::String>("value", "The value to record", Rest::Flag::Optional)
.response(Http::Code::Created, "The metric has been created")
.response(Http::Code::Not_Found, "The creation of the metric failed");
#endif
Routes
::
Post
(
router
,
"/record/:name/:value?"
,
Routes
::
bind
(
&
StatsEndpoint
::
doRecordMetric
,
this
));
Routes
::
Get
(
router
,
"/value/:name"
,
Routes
::
bind
(
&
StatsEndpoint
::
doGetMetric
,
this
));
Routes
::
Get
(
router
,
"/ready"
,
Routes
::
bind
(
&
Generic
::
handleReady
));
...
...
include/description.h
View file @
37c872c6
...
...
@@ -15,6 +15,7 @@
#include "mime.h"
#include "optional.h"
#include "router.h"
#include "iterator_adapter.h"
namespace
Net
{
...
...
@@ -115,6 +116,27 @@ struct Info {
Optional
<
Contact
>
contact
;
Optional
<
License
>
license
;
template
<
typename
Writer
>
void
serialize
(
Writer
&
writer
)
const
{
writer
.
String
(
"info"
);
writer
.
StartObject
();
{
writer
.
String
(
"title"
);
writer
.
String
(
title
.
c_str
());
writer
.
String
(
"version"
);
writer
.
String
(
version
.
c_str
());
if
(
!
description
.
empty
())
{
writer
.
String
(
"description"
);
writer
.
String
(
description
.
c_str
());
}
if
(
!
termsOfService
.
empty
())
{
writer
.
String
(
"termsOfService"
);
writer
.
String
(
termsOfService
.
c_str
());
}
}
writer
.
EndObject
();
}
};
struct
InfoBuilder
{
...
...
@@ -161,10 +183,27 @@ struct Parameter {
return
p
;
}
template
<
typename
Writer
>
void
serialize
(
Writer
&
writer
)
const
{
writer
.
StartObject
();
{
writer
.
String
(
"name"
);
writer
.
String
(
name
.
c_str
());
writer
.
String
(
"description"
);
writer
.
String
(
description
.
c_str
());
writer
.
String
(
"required"
);
writer
.
Bool
(
required
);
writer
.
String
(
"type"
);
writer
.
String
(
type
->
typeName
());
}
writer
.
EndObject
();
}
std
::
string
name
;
std
::
string
description
;
bool
required
;
std
::
shared_ptr
<
DataType
>
type
;
};
struct
Response
{
...
...
@@ -172,6 +211,18 @@ struct Response {
Http
::
Code
statusCode
;
std
::
string
description
;
template
<
typename
Writer
>
void
serialize
(
Writer
&
writer
)
const
{
auto
code
=
std
::
to_string
(
static_cast
<
uint32_t
>
(
statusCode
));
writer
.
String
(
code
.
c_str
());
writer
.
StartObject
();
{
writer
.
String
(
"description"
);
writer
.
String
(
description
.
c_str
());
}
writer
.
EndObject
();
}
};
struct
ResponseBuilder
{
...
...
@@ -203,6 +254,104 @@ struct Path {
std
::
vector
<
Response
>
responses
;
Route
::
Handler
handler
;
template
<
typename
Writer
>
void
serialize
(
Writer
&
writer
)
const
{
auto
serializeMimes
=
[
&
](
const
char
*
name
,
const
std
::
vector
<
Http
::
Mime
::
MediaType
>&
mimes
)
{
if
(
!
mimes
.
empty
())
{
writer
.
String
(
name
);
writer
.
StartArray
();
{
for
(
const
auto
&
mime
:
mimes
)
{
auto
str
=
mime
.
toString
();
writer
.
String
(
str
.
c_str
());
}
}
writer
.
EndArray
();
}
};
writer
.
String
(
methodString
(
method
));
writer
.
StartObject
();
{
writer
.
String
(
"description"
);
writer
.
String
(
description
.
c_str
());
serializeMimes
(
"consumes"
,
consumeMimes
);
serializeMimes
(
"produces"
,
produceMimes
);
// @Todo: create a template to serialize vectors in a generic way
if
(
!
parameters
.
empty
())
{
writer
.
String
(
"parameters"
);
writer
.
StartArray
();
{
for
(
const
auto
&
parameter
:
parameters
)
{
parameter
.
serialize
(
writer
);
}
}
writer
.
EndArray
();
}
if
(
!
responses
.
empty
())
{
writer
.
String
(
"responses"
);
writer
.
StartArray
();
for
(
const
auto
&
response
:
responses
)
{
response
.
serialize
(
writer
);
}
writer
.
EndArray
();
}
}
writer
.
EndObject
();
}
};
class
PathGroup
{
public:
typedef
std
::
unordered_map
<
std
::
string
,
std
::
vector
<
Path
>>
Map
;
typedef
Map
::
iterator
iterator
;
typedef
Map
::
const_iterator
const_iterator
;
typedef
std
::
vector
<
Path
>::
iterator
group_iterator
;
typedef
FlatMapIteratorAdapter
<
Map
>
flat_iterator
;
bool
hasPath
(
const
std
::
string
&
name
,
Http
::
Method
method
)
const
;
bool
hasPath
(
const
Path
&
path
)
const
;
std
::
vector
<
Path
>
paths
(
const
std
::
string
&
name
)
const
;
Optional
<
Path
>
path
(
const
std
::
string
&
name
,
Http
::
Method
method
)
const
;
group_iterator
add
(
Path
path
);
template
<
typename
...
Args
>
group_iterator
emplace
(
Args
&&
...
args
)
{
return
add
(
Path
(
std
::
forward
<
Args
>
(
args
)...));
}
const_iterator
begin
()
const
;
const_iterator
end
()
const
;
flat_iterator
flatBegin
()
const
;
flat_iterator
flatEnd
()
const
;
template
<
typename
Writer
>
void
serialize
(
Writer
&
writer
)
const
{
writer
.
String
(
"paths"
);
writer
.
StartArray
();
{
for
(
const
auto
&
group
:
groups
)
{
writer
.
String
(
group
.
first
.
c_str
());
writer
.
StartObject
();
{
for
(
const
auto
&
path
:
group
.
second
)
{
path
.
serialize
(
writer
);
}
}
writer
.
EndObject
();
}
}
writer
.
EndArray
();
}
private:
Map
groups
;
};
struct
PathBuilder
{
...
...
@@ -246,6 +395,8 @@ struct PathBuilder {
path_
->
handler
=
[
=
](
const
Rest
::
Request
&
request
,
Http
::
ResponseWriter
response
)
{
CALL_MEMBER_FN
(
obj
,
func
)(
request
,
std
::
move
(
response
));
return
Route
::
Result
::
Ok
;
};
#undef CALL_MEMBER_FN
...
...
@@ -258,6 +409,8 @@ struct PathBuilder {
path_
->
handler
=
[
=
](
const
Rest
::
Request
&
request
,
Http
::
ResponseWriter
response
)
{
func
(
request
,
std
::
move
(
response
));
return
Route
::
Result
::
Ok
;
};
return
*
this
;
...
...
@@ -269,7 +422,7 @@ private:
};
struct
SubPath
{
SubPath
(
std
::
string
prefix
,
std
::
vector
<
Path
>
*
paths
);
SubPath
(
std
::
string
prefix
,
PathGroup
*
paths
);
PathBuilder
route
(
std
::
string
path
,
Http
::
Method
method
,
std
::
string
description
=
""
);
PathBuilder
route
(
PathFragment
fragment
,
std
::
string
description
=
""
);
...
...
@@ -283,7 +436,7 @@ struct SubPath {
std
::
string
prefix
;
std
::
vector
<
Parameter
>
parameters
;
std
::
vector
<
Path
>
*
paths
;
PathGroup
*
paths
;
};
}
// namespace Schema
...
...
@@ -316,14 +469,58 @@ public:
Schema
::
ResponseBuilder
response
(
Http
::
Code
statusCode
,
std
::
string
description
);
std
::
vector
<
Schema
::
Path
>
paths
()
const
{
return
paths_
;
}
Schema
::
PathGroup
paths
()
const
{
return
paths_
;
}
template
<
typename
Writer
>
void
serialize
(
Writer
&
writer
)
const
{
writer
.
StartObject
();
{
info_
.
serialize
(
writer
);
if
(
!
host_
.
empty
())
{
writer
.
String
(
"host"
);
writer
.
String
(
host_
.
c_str
());
}
if
(
!
schemes_
.
empty
())
{
writer
.
String
(
"schemes"
);
writer
.
StartArray
();
{
for
(
const
auto
&
scheme
:
schemes_
)
{
writer
.
String
(
scheme
.
c_str
());
}
}
writer
.
EndArray
();
}
paths_
.
serialize
(
writer
);
}
writer
.
EndObject
();
}
private:
Schema
::
Info
info_
;
std
::
string
host_
;
std
::
vector
<
std
::
string
>
schemes_
;
std
::
vector
<
Schema
::
Path
>
paths_
;
Schema
::
PathGroup
paths_
;
};
class
Swagger
{
public:
Swagger
(
const
Description
&
description
)
:
description_
(
description
)
{
}
Swagger
&
uiPath
(
std
::
string
path
);
Swagger
&
uiDirectory
(
std
::
string
dir
);
Swagger
&
apiPath
(
std
::
string
path
);
void
install
(
Rest
::
Router
&
router
);
private:
Description
description_
;
std
::
string
uiPath_
;
std
::
string
uiDirectory_
;
std
::
string
apiPath_
;
};
}
// namespace Rest
...
...
include/http_header.h
View file @
37c872c6
...
...
@@ -352,6 +352,23 @@ private:
Net
::
Port
port_
;
};
class
Location
:
public
Header
{
public:
NAME
(
"Location"
)
Location
()
{
}
explicit
Location
(
const
std
::
string
&
location
);
void
parse
(
const
std
::
string
&
data
);
void
write
(
std
::
ostream
&
os
)
const
;
std
::
string
location
()
const
{
return
location_
;
}
private:
std
::
string
location_
;
};
class
Server
:
public
Header
{
public:
NAME
(
"Server"
)
...
...
include/iterator_adapter.h
0 → 100644
View file @
37c872c6
/*
Mathieu Stefani, 28 février 2016
A collection of sample iterator adapters
*/
#pragma once
template
<
typename
Map
>
struct
FlatMapIteratorAdapter
{
typedef
typename
Map
::
key_type
Key
;
typedef
typename
Map
::
mapped_type
Value
;
typedef
typename
Map
::
const_iterator
const_iterator
;
FlatMapIteratorAdapter
(
const_iterator
it
)
:
it
(
it
)
{
}
FlatMapIteratorAdapter
operator
++
()
{
++
it
;
return
FlatMapIteratorAdapter
(
it
);
}
const
Value
&
operator
*
()
{
return
it
->
second
;
}
bool
operator
==
(
FlatMapIteratorAdapter
other
)
{
return
other
.
it
==
it
;
}
bool
operator
!=
(
FlatMapIteratorAdapter
other
)
{
return
!
(
*
this
==
other
);
}
private:
const_iterator
it
;
};
template
<
typename
Map
>
FlatMapIteratorAdapter
<
Map
>
makeFlatMapIterator
(
const
Map
&
,
typename
Map
::
const_iterator
it
)
{
return
FlatMapIteratorAdapter
<
Map
>
(
it
);
}
include/router.h
View file @
37c872c6
...
...
@@ -61,7 +61,9 @@ private:
class
Request
;
struct
Route
{
typedef
std
::
function
<
void
(
const
Request
&
,
Net
::
Http
::
ResponseWriter
)
>
Handler
;
enum
class
Result
{
Ok
,
Failure
};
typedef
std
::
function
<
Result
(
const
Request
&
,
Net
::
Http
::
ResponseWriter
)
>
Handler
;
Route
(
std
::
string
resource
,
Http
::
Method
method
,
Handler
handler
)
:
resource_
(
std
::
move
(
resource
))
...
...
@@ -130,10 +132,42 @@ private:
};
namespace
Private
{
class
RouterHandler
;
}
class
Router
{
public:
enum
class
Status
{
Match
,
NotFound
};
static
Router
fromDescription
(
const
Rest
::
Description
&
desc
);
std
::
shared_ptr
<
Private
::
RouterHandler
>
handler
()
const
;
void
initFromDescription
(
const
Rest
::
Description
&
desc
);
void
get
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
post
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
put
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
del
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
addCustomHandler
(
Route
::
Handler
handler
);
Status
route
(
const
Http
::
Request
&
request
,
Http
::
ResponseWriter
response
);
private:
void
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Route
::
Handler
handler
);
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>
routes
;
std
::
vector
<
Route
::
Handler
>
customHandlers
;
};
class
HttpHandler
:
public
Net
::
Http
::
Handler
{
namespace
Private
{
class
RouterHandler
:
public
Net
::
Http
::
Handler
{
public:
HttpHandler
(
const
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>&
routes
);
RouterHandler
(
const
Rest
::
Router
&
router
);
void
onRequest
(
const
Http
::
Request
&
req
,
...
...
@@ -141,16 +175,16 @@ namespace Private {
private:
std
::
shared_ptr
<
Net
::
Tcp
::
Handler
>
clone
()
const
{
return
std
::
make_shared
<
HttpHandler
>
(
routes
);
return
std
::
make_shared
<
RouterHandler
>
(
router
);
}
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>
routes
;
Rest
::
Router
router
;
};
}
class
Request
:
public
Http
::
Request
{
public:
friend
class
Private
::
HttpHandl
er
;
friend
class
Rout
er
;
bool
hasParam
(
std
::
string
name
)
const
;
TypedParam
param
(
std
::
string
name
)
const
;
...
...
@@ -168,27 +202,6 @@ private:
std
::
vector
<
TypedParam
>
splats_
;
};
class
Router
{
public:
static
Router
fromDescription
(
const
Rest
::
Description
&
desc
);
std
::
shared_ptr
<
Private
::
HttpHandler
>
handler
()
const
{
return
std
::
make_shared
<
Private
::
HttpHandler
>
(
routes
);
}
void
initFromDescription
(
const
Rest
::
Description
&
desc
);
void
get
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
post
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
put
(
std
::
string
resource
,
Route
::
Handler
handler
);
void
del
(
std
::
string
resource
,
Route
::
Handler
handler
);
private:
void
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Route
::
Handler
handler
);
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>
routes
;
};
namespace
Routes
{
...
...
@@ -231,6 +244,8 @@ namespace Routes {
return
[
=
](
const
Rest
::
Request
&
request
,
Http
::
ResponseWriter
response
)
{
CALL_MEMBER_FN
(
obj
,
func
)(
request
,
std
::
move
(
response
));
return
Route
::
Result
::
Ok
;
};
#undef CALL_MEMBER_FN
...
...
@@ -242,6 +257,8 @@ namespace Routes {
return
[
=
](
const
Rest
::
Request
&
request
,
Http
::
ResponseWriter
response
)
{
func
(
request
,
std
::
move
(
response
));
return
Route
::
Result
::
Ok
;
};
}
...
...
src/common/description.cc
View file @
37c872c6
...
...
@@ -5,7 +5,10 @@
*/
#include "description.h"
#include "http_header.h"
#include <sstream>
#include <algorithm>
#include "rapidjson/prettywriter.h"
namespace
Net
{
...
...
@@ -27,10 +30,10 @@ License::License(
{
}
Info
::
Info
(
std
::
string
title
,
std
::
string
description
,
std
::
string
vers
ion
)
std
::
string
title
,
std
::
string
version
,
std
::
string
descript
ion
)
:
title
(
std
::
move
(
title
))
,
description
(
std
::
move
(
description
))
,
version
(
std
::
move
(
version
))
,
description
(
std
::
move
(
description
))
{
}
PathFragment
::
PathFragment
(
...
...
@@ -46,12 +49,77 @@ Path::Path(
,
description
(
std
::
move
(
description
))
{
}
bool
PathGroup
::
hasPath
(
const
std
::
string
&
name
,
Http
::
Method
method
)
const
{
auto
group
=
paths
(
name
);
auto
it
=
std
::
find_if
(
std
::
begin
(
group
),
std
::
end
(
group
),
[
&
](
const
Path
&
p
)
{
return
p
.
method
==
method
;
});
return
it
!=
std
::
end
(
group
);
}
bool
PathGroup
::
hasPath
(
const
Path
&
path
)
const
{
return
hasPath
(
path
.
value
,
path
.
method
);
}
std
::
vector
<
Path
>
PathGroup
::
paths
(
const
std
::
string
&
name
)
const
{
auto
it
=
groups
.
find
(
name
);
if
(
it
==
std
::
end
(
groups
))
return
std
::
vector
<
Path
>
{
};
return
it
->
second
;
}
Optional
<
Path
>
PathGroup
::
path
(
const
std
::
string
&
name
,
Http
::
Method
method
)
const
{
auto
group
=
paths
(
name
);
auto
it
=
std
::
find_if
(
std
::
begin
(
group
),
std
::
end
(
group
),
[
&
](
const
Path
&
p
)
{
return
p
.
method
==
method
;
});
if
(
it
!=
std
::
end
(
group
))
{
return
Some
(
*
it
);
}
return
None
();
}
PathGroup
::
group_iterator
PathGroup
::
add
(
Path
path
)
{
if
(
hasPath
(
path
))
return
PathGroup
::
group_iterator
{
};
auto
&
group
=
groups
[
path
.
value
];
return
group
.
insert
(
group
.
end
(),
std
::
move
(
path
));
}
PathGroup
::
const_iterator
PathGroup
::
begin
()
const
{
return
groups
.
begin
();
}
PathGroup
::
const_iterator
PathGroup
::
end
()
const
{
return
groups
.
end
();
}
PathGroup
::
flat_iterator
PathGroup
::
flatBegin
()
const
{
return
makeFlatMapIterator
(
groups
,
begin
());
}
PathGroup
::
flat_iterator
PathGroup
::
flatEnd
()
const
{
return
makeFlatMapIterator
(
groups
,
end
());
}
PathBuilder
::
PathBuilder
(
Path
*
path
)
:
path_
(
path
)
{
}
SubPath
::
SubPath
(
std
::
string
prefix
,
std
::
vector
<
Path
>
*
paths
)
std
::
string
prefix
,
PathGroup
*
paths
)
:
prefix
(
std
::
move
(
prefix
))
,
paths
(
paths
)
{
}
...
...
@@ -62,9 +130,9 @@ SubPath::route(std::string name, Http::Method method, std::string description) {
Path
path
(
std
::
move
(
fullPath
),
method
,
std
::
move
(
description
));
std
::
copy
(
std
::
begin
(
parameters
),
std
::
end
(
parameters
),
std
::
back_inserter
(
path
.
parameters
));
paths
->
push_back
(
std
::
move
(
path
));
auto
it
=
paths
->
add
(
std
::
move
(
path
));
return
PathBuilder
(
&
paths
->
back
()
);
return
PathBuilder
(
&
*
it
);
}
PathBuilder
...
...
@@ -164,8 +232,8 @@ Description::path(std::string name) {
Schema
::
PathBuilder
Description
::
route
(
std
::
string
name
,
Http
::
Method
method
,
std
::
string
description
)
{
paths_
.
emplace_back
(
std
::
move
(
name
),
method
,
std
::
move
(
description
));
return
Schema
::
PathBuilder
(
&
paths_
.
back
()
);
auto
it
=
paths_
.
emplace
(
std
::
move
(
name
),
method
,
std
::
move
(
description
));
return
Schema
::
PathBuilder
(
&
*
it
);
}
Schema
::
PathBuilder
...
...
@@ -179,6 +247,127 @@ Description::response(Http::Code statusCode, std::string description) {
return
builder
;
}
Swagger
&
Swagger
::
uiPath
(
std
::
string
path
)
{
uiPath_
=
std
::
move
(
path
);
return
*
this
;
}
Swagger
&
Swagger
::
uiDirectory
(
std
::
string
dir
)
{
uiDirectory_
=
std
::
move
(
dir
);
return
*
this
;
}
Swagger
&
Swagger
::
apiPath
(
std
::
string
path
)
{
apiPath_
=
std
::
move
(
path
);
return
*
this
;
}
void
Swagger
::
install
(
Rest
::
Router
&
router
)
{
Route
::
Handler
uiHandler
=
[
=
](
const
Rest
::
Request
&
req
,
Http
::
ResponseWriter
response
)
{
auto
res
=
req
.
resource
();
/*
* @Export might be useful for routing also. Make it public
*/
struct
Path
{
Path
(
const
std
::
string
&
value
)
:
value
(
value
)
,
trailingSlashValue
(
value
)
{
if
(
trailingSlashValue
.
back
()
!=
'/'
)
trailingSlashValue
+=
'/'
;
}
static
bool
hasTrailingSlash
(
const
Rest
::
Request
&
req
)
{
auto
res
=
req
.
resource
();
return
res
.
back
()
==
'/'
;
}
std
::
string
stripPrefix
(
const
Rest
::
Request
&
req
)
{
auto
res
=
req
.
resource
();
if
(
!
res
.
compare
(
0
,
value
.
size
(),
value
))
{
return
res
.
substr
(
value
.
size
());
}
return
res
;
}
bool
matches
(
const
Rest
::
Request
&
req
)
{
auto
res
=
req
.
resource
();
if
(
value
==
res
)
return
true
;
if
(
res
==
trailingSlashValue
)
return
true
;
return
false
;
}
bool
isPrefix
(
const
Rest
::
Request
&
req
)
{
auto
res
=
req
.
resource
();
return
!
res
.
compare
(
0
,
value
.
size
(),
value
);
}
const
std
::
string
&
withTrailingSlash
()
const
{
return
trailingSlashValue
;
}
std
::
string
join
(
const
std
::
string
&
value
)
const
{
std
::
string
val
;
if
(
value
[
0
]
==
'/'
)
val
=
value
.
substr
(
1
);
else
val
=
value
;
return
trailingSlashValue
+
val
;
}
private:
std
::
string
value
;
std
::
string
trailingSlashValue
;
};
Path
ui
(
uiPath_
);
Path
uiDir
(
uiDirectory_
);
if
(
ui
.
matches
(
req
))
{
if
(
!
Path
::
hasTrailingSlash
(
req
))
{
response
.
headers
()
.
add
<
Http
::
Header
::
Location
>
(
uiPath_
+
'/'
);
response
.
send
(
Http
::
Code
::
Moved_Permanently
);
}
else
{
auto
index
=
uiDir
.
join
(
"index.html"
);
Http
::
serveFile
(
response
,
index
.
c_str
());
}
return
Route
::
Result
::
Ok
;
}
else
if
(
ui
.
isPrefix
(
req
))
{
auto
file
=
ui
.
stripPrefix
(
req
);
auto
path
=
uiDir
.
join
(
file
);
Http
::
serveFile
(
response
,
path
.
c_str
());
return
Route
::
Result
::
Ok
;
}
else
if
(
res
==
apiPath_
)
{
rapidjson
::
StringBuffer
sb
;
rapidjson
::
PrettyWriter
<
rapidjson
::
StringBuffer
>
writer
(
sb
);
description_
.
serialize
(
writer
);
response
.
send
(
Http
::
Code
::
Ok
,
sb
.
GetString
(),
MIME
(
Application
,
Json
));
return
Route
::
Result
::
Ok
;
}
return
Route
::
Result
::
Failure
;
};
router
.
addCustomHandler
(
uiHandler
);
}
}
// namespace Rest
...
...
src/common/http_header.cc
View file @
37c872c6
...
...
@@ -359,6 +359,20 @@ Host::write(std::ostream& os) const {
}
}
Location
::
Location
(
const
std
::
string
&
location
)
:
location_
(
location
)
{
}
void
Location
::
parse
(
const
std
::
string
&
data
)
{
location_
=
data
;
}
void
Location
::
write
(
std
::
ostream
&
os
)
const
{
os
<<
location_
;
}
void
UserAgent
::
parse
(
const
std
::
string
&
data
)
{
ua_
=
data
;
...
...
src/common/http_headers.cc
View file @
37c872c6
...
...
@@ -31,6 +31,7 @@ RegisterHeader(ContentType);
RegisterHeader
(
Date
);
RegisterHeader
(
Expect
);
RegisterHeader
(
Host
);
RegisterHeader
(
Location
);
RegisterHeader
(
Server
);
RegisterHeader
(
UserAgent
);
...
...
src/server/router.cc
View file @
37c872c6
...
...
@@ -210,30 +210,17 @@ Route::match(const std::string& req) const
namespace
Private
{
HttpHandler
::
HttpHandler
(
const
std
::
unordered_map
<
Http
::
Method
,
std
::
vector
<
Route
>>&
routes
)
:
routes
(
routes
)
RouterHandler
::
RouterHandler
(
const
Rest
::
Router
&
router
)
:
router
(
router
)
{
}
void
Http
Handler
::
onRequest
(
Router
Handler
::
onRequest
(
const
Http
::
Request
&
req
,
Http
::
ResponseWriter
response
)
{
auto
&
r
=
routes
[
req
.
method
()];
for
(
const
auto
&
route
:
r
)
{
bool
match
;
std
::
vector
<
TypedParam
>
params
;
std
::
vector
<
TypedParam
>
splats
;
std
::
tie
(
match
,
params
,
splats
)
=
route
.
match
(
req
);
if
(
match
)
{
route
.
invokeHandler
(
Request
(
req
,
std
::
move
(
params
),
std
::
move
(
splats
)),
std
::
move
(
response
));
return
;
}
}
response
.
send
(
Http
::
Code
::
Not_Found
,
"Could not find a matching route"
);
router
.
route
(
req
,
std
::
move
(
response
));
}
}
// namespace Private
...
...
@@ -245,12 +232,20 @@ Router::fromDescription(const Rest::Description& desc) {
return
router
;
}
std
::
shared_ptr
<
Private
::
RouterHandler
>
Router
::
handler
()
const
{
return
std
::
make_shared
<
Private
::
RouterHandler
>
(
*
this
);
}
void
Router
::
initFromDescription
(
const
Rest
::
Description
&
desc
)
{
auto
paths
=
desc
.
paths
();
for
(
auto
it
=
paths
.
flatBegin
(),
end
=
paths
.
flatEnd
();
it
!=
end
;
++
it
)
{
const
auto
&
paths
=
*
it
;
for
(
const
auto
&
path
:
paths
)
{
addRoute
(
path
.
method
,
std
::
move
(
path
.
value
),
std
::
move
(
path
.
handler
));
}
}
}
void
...
...
@@ -273,6 +268,40 @@ Router::del(std::string resource, Route::Handler handler) {
addRoute
(
Http
::
Method
::
Delete
,
std
::
move
(
resource
),
std
::
move
(
handler
));
}
void
Router
::
addCustomHandler
(
Route
::
Handler
handler
)
{
customHandlers
.
push_back
(
std
::
move
(
handler
));
}
Router
::
Status
Router
::
route
(
const
Http
::
Request
&
req
,
Http
::
ResponseWriter
response
)
{
auto
&
r
=
routes
[
req
.
method
()];
for
(
const
auto
&
route
:
r
)
{
bool
match
;
std
::
vector
<
TypedParam
>
params
;
std
::
vector
<
TypedParam
>
splats
;
std
::
tie
(
match
,
params
,
splats
)
=
route
.
match
(
req
);
if
(
match
)
{
route
.
invokeHandler
(
Request
(
req
,
std
::
move
(
params
),
std
::
move
(
splats
)),
std
::
move
(
response
));
return
Router
::
Status
::
Match
;
}
}
/* @Major @FixMe:
* original response object should not be moved here. Instead, we should provide some sort of clone() method
* to explicit request a copy and then move that clone
*/
for
(
const
auto
&
handler
:
customHandlers
)
{
auto
result
=
handler
(
Request
(
req
,
std
::
vector
<
TypedParam
>
(),
std
::
vector
<
TypedParam
>
()),
std
::
move
(
response
));
if
(
result
==
Route
::
Result
::
Ok
)
return
Router
::
Status
::
Match
;
}
throw
std
::
runtime_error
(
"Fix me"
);
response
.
send
(
Http
::
Code
::
Not_Found
,
"Could not find a matching route"
);
return
Router
::
Status
::
NotFound
;
}
void
Router
::
addRoute
(
Http
::
Method
method
,
std
::
string
resource
,
Route
::
Handler
handler
)
{
...
...
tests/router_test.cc
View file @
37c872c6
...
...
@@ -75,7 +75,7 @@ bool matchSplat(
Rest
::
Route
makeRoute
(
std
::
string
value
)
{
auto
noop
=
[](
const
Net
::
Http
::
Request
&
,
Net
::
Http
::
Response
)
{
};
auto
noop
=
[](
const
Net
::
Http
::
Request
&
,
Net
::
Http
::
Response
)
{
return
Rest
::
Route
::
Result
::
Ok
;
};
return
Rest
::
Route
(
value
,
Net
::
Http
::
Method
::
Get
,
noop
);
}
...
...
thirdparty/CMakeLists.txt
0 → 100644
View file @
37c872c6
add_subdirectory
(
rapidjson
)
rapidjson
@
ef5e74a2
Subproject commit ef5e74a200f5feab8714619dc1136ca9e96de004
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