mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
created new rules, used bot to enforce rules in PRs
This commit is contained in:
parent
e87ec70179
commit
7f4b7c8ff9
@ -1,8 +1,77 @@
|
||||
---
|
||||
BasedOnStyle: Google
|
||||
BreakBeforeBraces: Mozilla
|
||||
IndentWidth: '2'
|
||||
TabWidth: '2'
|
||||
BasedOnStyle: Mozilla
|
||||
AccessModifierOffset: '-4'
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: 'false'
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: AlignAfterOperator
|
||||
AlignTrailingComments: 'true'
|
||||
AllowShortBlocksOnASingleLine: 'true'
|
||||
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: 'true'
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: 'false'
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: 'false'
|
||||
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
BinPackArguments: 'true'
|
||||
BinPackParameters: 'true'
|
||||
BreakBeforeBraces: Allman
|
||||
BreakBeforeTernaryOperators: 'false'
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterColon
|
||||
ColumnLimit: '0'
|
||||
CompactNamespaces: 'false'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
|
||||
ContinuationIndentWidth: '2'
|
||||
Cpp11BracedListStyle: 'true'
|
||||
FixNamespaceComments: 'true'
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 2
|
||||
CaseSensitive: true
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '<[[:alnum:].]+>'
|
||||
Priority: 4
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
IndentCaseLabels: 'true'
|
||||
IndentWidth: '4'
|
||||
IndentWrappedFunctionNames: 'true'
|
||||
Language: Cpp
|
||||
MaxEmptyLinesToKeep: '3'
|
||||
NamespaceIndentation: All
|
||||
PointerAlignment: Left
|
||||
ReflowComments: 'false'
|
||||
SortIncludes: 'false'
|
||||
SortUsingDeclarations: 'false'
|
||||
SpaceAfterCStyleCast: 'false'
|
||||
SpaceAfterLogicalNot: 'false'
|
||||
SpaceAfterTemplateKeyword: 'false'
|
||||
SpaceBeforeAssignmentOperators: 'true'
|
||||
SpaceBeforeCpp11BracedList: 'false'
|
||||
SpaceBeforeCtorInitializerColon: 'false'
|
||||
SpaceBeforeInheritanceColon: 'true'
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: 'true'
|
||||
SpaceInEmptyParentheses: 'false'
|
||||
SpacesBeforeTrailingComments: '1'
|
||||
SpacesInAngles: 'false'
|
||||
SpacesInCStyleCastParentheses: 'false'
|
||||
SpacesInContainerLiterals: 'false'
|
||||
SpacesInParentheses: 'false'
|
||||
SpacesInSquareBrackets: 'false'
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
Standard: Cpp11
|
||||
TabWidth: '4'
|
||||
UseTab: Never
|
||||
|
||||
...
|
||||
|
12
.drone.yml
12
.drone.yml
@ -12,6 +12,12 @@ steps:
|
||||
environment:
|
||||
COVERALLS_REPO_TOKEN:
|
||||
from_secret: coveralls_token
|
||||
APP_ID:
|
||||
from_secret: appid
|
||||
APP_INSTALLATION:
|
||||
from_secret: appins
|
||||
PK:
|
||||
from_secret: pk
|
||||
|
||||
commands:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
@ -19,7 +25,7 @@ steps:
|
||||
- export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER
|
||||
- export COVERALLS_PULL_REQUEST=$DRONE_PULL_REQUEST
|
||||
- apt-get -y update
|
||||
- apt-get -y install libboost-all-dev doxygen mkdocs graphviz zlib1g-dev gcc clang make cmake python3 python3-pip git openssl libssl-dev
|
||||
- apt-get -y install libboost-all-dev doxygen mkdocs graphviz zlib1g-dev gcc clang clang-format make cmake python3 python3-pip git openssl libssl-dev jq
|
||||
- git clone https://github.com/CrowCpp/cpp-coveralls.git
|
||||
- cd cpp-coveralls
|
||||
- pip3 install . --no-input
|
||||
@ -33,6 +39,10 @@ steps:
|
||||
- ctest -V -j4
|
||||
- cd ..
|
||||
- coveralls --verbose --exclude-pattern .*/http_parser_merged.h --exclude-pattern .*/TinySHA1.hpp
|
||||
- clone https://github.com/CrowCpp/crow-clang-format.git
|
||||
- cd crow-clang-format
|
||||
- chmod +x crow-clang-format.sh get-access-token.sh make-jwt.sh
|
||||
- ./crow-clang-format.sh
|
||||
- export CC=/usr/bin/clang
|
||||
- export CXX=/usr/bin/clang++
|
||||
- mkdir build-clang
|
||||
|
@ -2,22 +2,25 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
class ExampleLogHandler : public crow::ILogHandler {
|
||||
public:
|
||||
void log(std::string /*message*/, crow::LogLevel /*level*/) override {
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
class ExampleLogHandler : public crow::ILogHandler
|
||||
{
|
||||
public:
|
||||
void log(std::string /*message*/, crow::LogLevel /*level*/) override
|
||||
{
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
};
|
||||
|
||||
struct ExampleMiddleware
|
||||
{
|
||||
std::string message;
|
||||
|
||||
ExampleMiddleware() : message("foo")
|
||||
ExampleMiddleware():
|
||||
message("foo")
|
||||
{
|
||||
}
|
||||
|
||||
void setMessage(const std::string &newMsg)
|
||||
void setMessage(const std::string& newMsg)
|
||||
{
|
||||
message = newMsg;
|
||||
}
|
||||
@ -44,88 +47,83 @@ int main()
|
||||
app.get_middleware<ExampleMiddleware>().setMessage("hello");
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
.name("hello")
|
||||
([]{
|
||||
return "Hello World!";
|
||||
});
|
||||
.name("hello")([]
|
||||
{ return "Hello World!"; });
|
||||
|
||||
CROW_ROUTE(app, "/about")
|
||||
([](){
|
||||
return "About Crow example.";
|
||||
});
|
||||
([]()
|
||||
{ return "About Crow example."; });
|
||||
|
||||
// a request to /path should be forwarded to /path/
|
||||
CROW_ROUTE(app, "/path/")
|
||||
([](){
|
||||
return "Trailing slash test case..";
|
||||
});
|
||||
([]()
|
||||
{ return "Trailing slash test case.."; });
|
||||
|
||||
|
||||
// simple json response
|
||||
CROW_ROUTE(app, "/json")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
crow::json::wvalue x({{"message", "Hello, World!"}});
|
||||
x["message2"] = "Hello, World.. Again!";
|
||||
return x;
|
||||
});
|
||||
return x; });
|
||||
|
||||
CROW_ROUTE(app, "/json-initializer-list-constructor")
|
||||
([] {
|
||||
return crow::json::wvalue({
|
||||
{"first", "Hello world!"}, /* stores a char const* hence a json::type::String */
|
||||
{"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */
|
||||
{"third", std::int64_t(54)}, /* stores a 64-bit int hence a std::int64_t. */
|
||||
{"fourth", std::uint64_t(54)}, /* stores a 64-bit unsigned int hence a std::uint64_t. */
|
||||
{"fifth", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */
|
||||
{"sixth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */
|
||||
{"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */
|
||||
{"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */
|
||||
{"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */
|
||||
{"tenth", true} /* stores a bool hence json::type::True . */
|
||||
});
|
||||
});
|
||||
([]
|
||||
{ return crow::json::wvalue({
|
||||
{"first", "Hello world!"}, /* stores a char const* hence a json::type::String */
|
||||
{"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */
|
||||
{"third", std::int64_t(54)}, /* stores a 64-bit int hence a std::int64_t. */
|
||||
{"fourth", std::uint64_t(54)}, /* stores a 64-bit unsigned int hence a std::uint64_t. */
|
||||
{"fifth", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */
|
||||
{"sixth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */
|
||||
{"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */
|
||||
{"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */
|
||||
{"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */
|
||||
{"tenth", true} /* stores a bool hence json::type::True . */
|
||||
}); });
|
||||
|
||||
// json list response
|
||||
CROW_ROUTE(app, "/json_list")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
crow::json::wvalue x(crow::json::wvalue::list({1,2,3}));
|
||||
return x;
|
||||
});
|
||||
return x; });
|
||||
|
||||
// To see it in action enter {ip}:18080/hello/{integer_between -2^32 and 100} and you should receive
|
||||
// {integer_between -2^31 and 100} bottles of beer!
|
||||
CROW_ROUTE(app,"/hello/<int>")
|
||||
([](int count){
|
||||
CROW_ROUTE(app, "/hello/<int>")
|
||||
([](int count)
|
||||
{
|
||||
if (count > 100)
|
||||
return crow::response(400);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
return crow::response(os.str()); });
|
||||
|
||||
// Same as above, but using crow::status
|
||||
CROW_ROUTE(app,"/hello_status/<int>")
|
||||
([](int count){
|
||||
CROW_ROUTE(app, "/hello_status/<int>")
|
||||
([](int count)
|
||||
{
|
||||
if (count > 100)
|
||||
return crow::response(crow::status::BAD_REQUEST);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
return crow::response(os.str()); });
|
||||
|
||||
// To see it in action submit {ip}:18080/add/1/2 and you should receive 3 (exciting, isn't it)
|
||||
CROW_ROUTE(app,"/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b){
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << a+b;
|
||||
res.write(os.str());
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
|
||||
// Compile error with message "Handler type is mismatched with URL paramters"
|
||||
//CROW_ROUTE(app,"/another/<int>")
|
||||
//([](int a, int b){
|
||||
//return crow::response(500);
|
||||
//return crow::response(500);
|
||||
//});
|
||||
|
||||
// more json example
|
||||
@ -140,22 +138,22 @@ int main()
|
||||
// A simpler way for json example:
|
||||
// * curl -d '{"a":1,"b":2}' {ip}:18080/add_json
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods("POST"_method)
|
||||
([](const crow::request& req){
|
||||
.methods("POST"_method)([](const crow::request& req)
|
||||
{
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
int sum = x["a"].i()+x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
return crow::response{os.str()}; });
|
||||
|
||||
// Example of a request taking URL parameters
|
||||
// If you want to activate all the functions just query
|
||||
// {ip}:18080/params?foo='blabla'&pew=32&count[]=a&count[]=b
|
||||
CROW_ROUTE(app, "/params")
|
||||
([](const crow::request& req){
|
||||
([](const crow::request& req)
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
// To get a simple string from the url params
|
||||
@ -186,27 +184,25 @@ int main()
|
||||
os << " - " << mydictVal.first << " -> " << mydictVal.second << '\n';
|
||||
}
|
||||
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
return crow::response{os.str()}; });
|
||||
|
||||
CROW_ROUTE(app, "/large")
|
||||
([]{
|
||||
return std::string(512*1024, ' ');
|
||||
});
|
||||
([]
|
||||
{ return std::string(512 * 1024, ' '); });
|
||||
|
||||
// Take a multipart/form-data request and print out its body
|
||||
CROW_ROUTE(app,"/multipart")
|
||||
([](const crow::request& req){
|
||||
CROW_ROUTE(app, "/multipart")
|
||||
([](const crow::request& req)
|
||||
{
|
||||
crow::multipart::message msg(req);
|
||||
CROW_LOG_INFO << "body of the first part " << msg.parts[0].body;
|
||||
return "it works!";
|
||||
});
|
||||
return "it works!"; });
|
||||
|
||||
// enables all log
|
||||
app.loglevel(crow::LogLevel::Debug);
|
||||
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
|
||||
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
@ -10,11 +10,10 @@ int main()
|
||||
crow::Blueprint sub_bp("bp2", "csstat", "cstemplate");
|
||||
|
||||
CROW_BP_ROUTE(sub_bp, "/")
|
||||
([]() {
|
||||
return "Hello world!";
|
||||
});
|
||||
([]()
|
||||
{ return "Hello world!"; });
|
||||
|
||||
/* CROW_BP_ROUTE(bp, "/templatt")
|
||||
/* CROW_BP_ROUTE(bp, "/templatt")
|
||||
([]() {
|
||||
crow::mustache::context ctxdat;
|
||||
ctxdat["messg"] = "fifty five!!";
|
||||
@ -24,7 +23,9 @@ int main()
|
||||
return page.render(ctxdat);
|
||||
});
|
||||
*/
|
||||
CROW_BP_CATCHALL_ROUTE(sub_bp)([](){return "WRONG!!";});
|
||||
CROW_BP_CATCHALL_ROUTE(sub_bp)
|
||||
([]()
|
||||
{ return "WRONG!!"; });
|
||||
|
||||
|
||||
bp.register_blueprint(sub_bp);
|
||||
|
@ -6,11 +6,14 @@ int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")([](){return "Hello";});
|
||||
CROW_ROUTE(app, "/")
|
||||
([]()
|
||||
{ return "Hello"; });
|
||||
|
||||
//Setting a custom route for any URL that isn't defined, instead of a simple 404.
|
||||
CROW_CATCHALL_ROUTE(app)
|
||||
([](crow::response& res) {
|
||||
([](crow::response& res)
|
||||
{
|
||||
if (res.code == 404)
|
||||
{
|
||||
res.body = "The URL does not seem to be correct.";
|
||||
@ -19,8 +22,7 @@ int main()
|
||||
{
|
||||
res.body = "The HTTP method does not seem to be correct.";
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
|
||||
app.port(18080).run();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ void broadcast(const string& msg)
|
||||
x["msgs"][0] = msgs.back();
|
||||
x["last"] = msgs.size();
|
||||
string body = x.dump();
|
||||
for(auto p : ress)
|
||||
for (auto p : ress)
|
||||
{
|
||||
auto* res = p.first;
|
||||
CROW_LOG_DEBUG << res << " replied: " << body;
|
||||
@ -30,13 +30,14 @@ int main()
|
||||
crow::mustache::set_base(".");
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
crow::mustache::context ctx;
|
||||
return crow::mustache::load("example_chat.html").render();
|
||||
});
|
||||
return crow::mustache::load("example_chat.html").render(); });
|
||||
|
||||
CROW_ROUTE(app, "/logs")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
CROW_LOG_INFO << "logs requested";
|
||||
crow::json::wvalue x;
|
||||
int start = max(0, (int)msgs.size()-100);
|
||||
@ -44,11 +45,11 @@ int main()
|
||||
x["msgs"][i-start] = msgs[i];
|
||||
x["last"] = msgs.size();
|
||||
CROW_LOG_INFO << "logs completed";
|
||||
return x;
|
||||
});
|
||||
return x; });
|
||||
|
||||
CROW_ROUTE(app, "/logs/<int>")
|
||||
([](const crow::request& /*req*/, crow::response& res, int after){
|
||||
([](const crow::request& /*req*/, crow::response& res, int after)
|
||||
{
|
||||
CROW_LOG_INFO << "logs with last " << after;
|
||||
if (after < (int)msgs.size())
|
||||
{
|
||||
@ -73,19 +74,16 @@ int main()
|
||||
ress.swap(filtered);
|
||||
ress.push_back({&res, chrono::steady_clock::now()});
|
||||
CROW_LOG_DEBUG << &res << " stored " << ress.size();
|
||||
}
|
||||
});
|
||||
} });
|
||||
|
||||
CROW_ROUTE(app, "/send")
|
||||
.methods("GET"_method, "POST"_method)
|
||||
([](const crow::request& req)
|
||||
{
|
||||
.methods("GET"_method, "POST"_method)([](const crow::request& req)
|
||||
{
|
||||
CROW_LOG_INFO << "msg from client: " << req.body;
|
||||
broadcast(req.body);
|
||||
return "";
|
||||
});
|
||||
return ""; });
|
||||
|
||||
app.port(40080)
|
||||
//.multithreaded()
|
||||
.run();
|
||||
//.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
@ -7,23 +7,22 @@ int main()
|
||||
//crow::App<crow::CompressionGzip> app;
|
||||
|
||||
CROW_ROUTE(app, "/hello")
|
||||
([&](const crow::request&, crow::response& res){
|
||||
([&](const crow::request&, crow::response& res)
|
||||
{
|
||||
res.compressed = false;
|
||||
|
||||
res.body = "Hello World! This is uncompressed!";
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
|
||||
CROW_ROUTE(app, "/hello_compressed")
|
||||
([](){
|
||||
return "Hello World! This is compressed by default!";
|
||||
});
|
||||
([]()
|
||||
{ return "Hello World! This is compressed by default!"; });
|
||||
|
||||
|
||||
app.port(18080)
|
||||
.use_compression(crow::compression::algorithm::DEFLATE)
|
||||
.use_compression(crow::compression::algorithm::DEFLATE)
|
||||
//.use_compression(crow::compression::algorithm::GZIP)
|
||||
.loglevel(crow::LogLevel::Debug)
|
||||
.multithreaded()
|
||||
.run();
|
||||
.loglevel(crow::LogLevel::Debug)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
@ -5,17 +5,17 @@ int main()
|
||||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
// simple json response using a map
|
||||
// To see it in action enter {ip}:18080/json
|
||||
// it shoud show amessage before zmessage despite adding zmessage first.
|
||||
CROW_ROUTE(app, "/json")
|
||||
([]{
|
||||
// simple json response using a map
|
||||
// To see it in action enter {ip}:18080/json
|
||||
// it shoud show amessage before zmessage despite adding zmessage first.
|
||||
CROW_ROUTE(app, "/json")
|
||||
([]
|
||||
{
|
||||
crow::json::wvalue x({{"zmessage", "Hello, World!"},
|
||||
{"amessage", "Hello, World2!"}});
|
||||
return x;
|
||||
});
|
||||
return x; });
|
||||
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
@ -6,22 +6,22 @@
|
||||
int main()
|
||||
{
|
||||
|
||||
//Crow app initialization
|
||||
crow::SimpleApp app;
|
||||
//Crow app initialization
|
||||
crow::SimpleApp app;
|
||||
|
||||
//
|
||||
CROW_ROUTE(app, "/")
|
||||
([](const crow::request&, crow::response& res) {
|
||||
CROW_ROUTE(app, "/")
|
||||
([](const crow::request&, crow::response& res)
|
||||
{
|
||||
//replace cat.jpg with your file path
|
||||
res.set_static_file_info("cat.jpg");
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
|
||||
|
||||
app.port(18080).run();
|
||||
app.port(18080).run();
|
||||
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// You can also use the `/static` directory and endpoint (the directory needs to have the same path as your executable).
|
||||
|
@ -2,22 +2,25 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
class ExampleLogHandler : public crow::ILogHandler {
|
||||
public:
|
||||
void log(std::string message, crow::LogLevel level) override {
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
class ExampleLogHandler : public crow::ILogHandler
|
||||
{
|
||||
public:
|
||||
void log(std::string message, crow::LogLevel level) override
|
||||
{
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
};
|
||||
|
||||
struct ExampleMiddleware
|
||||
{
|
||||
std::string message;
|
||||
|
||||
ExampleMiddleware() : message("foo")
|
||||
ExampleMiddleware():
|
||||
message("foo")
|
||||
{
|
||||
}
|
||||
|
||||
void setMessage(const std::string &newMsg)
|
||||
void setMessage(const std::string& newMsg)
|
||||
{
|
||||
message = newMsg;
|
||||
}
|
||||
@ -44,68 +47,63 @@ int main()
|
||||
app.get_middleware<ExampleMiddleware>().setMessage("hello");
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
.name("hello")
|
||||
([]{
|
||||
return "Hello World!";
|
||||
});
|
||||
.name("hello")([]
|
||||
{ return "Hello World!"; });
|
||||
|
||||
CROW_ROUTE(app, "/about")
|
||||
([](){
|
||||
return "About Crow example.";
|
||||
});
|
||||
([]()
|
||||
{ return "About Crow example."; });
|
||||
|
||||
// a request to /path should be forwarded to /path/
|
||||
CROW_ROUTE(app, "/path/")
|
||||
([](){
|
||||
return "Trailing slash test case..";
|
||||
});
|
||||
([]()
|
||||
{ return "Trailing slash test case.."; });
|
||||
|
||||
// simple json response
|
||||
CROW_ROUTE(app, "/json")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
crow::json::wvalue x;
|
||||
x["message"] = "Hello, World!";
|
||||
return x;
|
||||
});
|
||||
return x; });
|
||||
|
||||
CROW_ROUTE(app, "/hello/<int>")
|
||||
([](int count){
|
||||
([](int count)
|
||||
{
|
||||
if (count > 100)
|
||||
return crow::response(400);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
return crow::response(os.str()); });
|
||||
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b){
|
||||
([](crow::response& res, int a, int b)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << a+b;
|
||||
res.write(os.str());
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
|
||||
// Compile error with message "Handler type is mismatched with URL paramters"
|
||||
//CROW_ROUTE(app,"/another/<int>")
|
||||
//([](int a, int b){
|
||||
//return crow::response(500);
|
||||
//return crow::response(500);
|
||||
//});
|
||||
|
||||
// more json example
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods(crow::HTTPMethod::Post)
|
||||
([](const crow::request& req){
|
||||
.methods(crow::HTTPMethod::Post)([](const crow::request& req)
|
||||
{
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
auto sum = x["a"].i()+x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
return crow::response{os.str()}; });
|
||||
|
||||
app.route_dynamic("/params")
|
||||
([](const crow::request& req){
|
||||
app.route_dynamic("/params")([](const crow::request& req)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Params: " << req.url_params << "\n\n";
|
||||
os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n";
|
||||
@ -118,14 +116,13 @@ int main()
|
||||
for(const auto& countVal : count) {
|
||||
os << " - " << countVal << '\n';
|
||||
}
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
return crow::response{os.str()}; });
|
||||
|
||||
// ignore all log
|
||||
crow::logger::setLogLevel(crow::LogLevel::Debug);
|
||||
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
|
||||
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
class ExampleLogHandler : public crow::ILogHandler {
|
||||
public:
|
||||
void log(std::string /*message*/, crow::LogLevel /*level*/) override {
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
class ExampleLogHandler : public crow::ILogHandler
|
||||
{
|
||||
public:
|
||||
void log(std::string /*message*/, crow::LogLevel /*level*/) override
|
||||
{
|
||||
// cerr << "ExampleLogHandler -> " << message;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
@ -14,86 +16,83 @@ int main()
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
.name("hello")
|
||||
([]{
|
||||
return "Hello World!";
|
||||
});
|
||||
.name("hello")([]
|
||||
{ return "Hello World!"; });
|
||||
|
||||
CROW_ROUTE(app, "/about")
|
||||
([](){
|
||||
return "About Crow example.";
|
||||
});
|
||||
([]()
|
||||
{ return "About Crow example."; });
|
||||
|
||||
// simple json response
|
||||
CROW_ROUTE(app, "/json")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
crow::json::wvalue x({{"message", "Hello, World!"}});
|
||||
x["message2"] = "Hello, World.. Again!";
|
||||
return x;
|
||||
});
|
||||
return x; });
|
||||
|
||||
CROW_ROUTE(app, "/json-initializer-list-constructor")
|
||||
([] {
|
||||
return crow::json::wvalue({
|
||||
{"first", "Hello world!"}, /* stores a char const* hence a json::type::String */
|
||||
{"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */
|
||||
{"third", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */
|
||||
{"fourth", 54l}, /* stores a long (as 54l is a long literal) hence a std::int64_t. */
|
||||
{"fifth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */
|
||||
{"sixth", 54ul}, /* stores an unsigned long (as 54ul is an unsigned long literal) hence a std::uint64_t. */
|
||||
{"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */
|
||||
{"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */
|
||||
{"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */
|
||||
{"tenth", true} /* stores a bool hence json::type::True . */
|
||||
});
|
||||
});
|
||||
([]
|
||||
{ return crow::json::wvalue({
|
||||
{"first", "Hello world!"}, /* stores a char const* hence a json::type::String */
|
||||
{"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */
|
||||
{"third", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */
|
||||
{"fourth", 54l}, /* stores a long (as 54l is a long literal) hence a std::int64_t. */
|
||||
{"fifth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */
|
||||
{"sixth", 54ul}, /* stores an unsigned long (as 54ul is an unsigned long literal) hence a std::uint64_t. */
|
||||
{"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */
|
||||
{"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */
|
||||
{"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */
|
||||
{"tenth", true} /* stores a bool hence json::type::True . */
|
||||
}); });
|
||||
|
||||
// json list response
|
||||
CROW_ROUTE(app, "/json_list")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
crow::json::wvalue x(crow::json::wvalue::list({1,2,3}));
|
||||
return x;
|
||||
});
|
||||
return x; });
|
||||
|
||||
CROW_ROUTE(app,"/hello/<int>")
|
||||
([](int count){
|
||||
CROW_ROUTE(app, "/hello/<int>")
|
||||
([](int count)
|
||||
{
|
||||
if (count > 100)
|
||||
return crow::response(400);
|
||||
std::ostringstream os;
|
||||
os << count << " bottles of beer!";
|
||||
return crow::response(os.str());
|
||||
});
|
||||
return crow::response(os.str()); });
|
||||
|
||||
// example which uses only response as a paramter without
|
||||
// request being a parameter.
|
||||
CROW_ROUTE(app,"/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b){
|
||||
CROW_ROUTE(app, "/add/<int>/<int>")
|
||||
([](crow::response& res, int a, int b)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << a+b;
|
||||
res.write(os.str());
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
|
||||
// Compile error with message "Handler type is mismatched with URL paramters"
|
||||
//CROW_ROUTE(app,"/another/<int>")
|
||||
//([](int a, int b){
|
||||
//return crow::response(500);
|
||||
//return crow::response(500);
|
||||
//});
|
||||
|
||||
// more json example
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
([](const crow::request& req){
|
||||
([](const crow::request& req)
|
||||
{
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
int sum = x["a"].i()+x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
return crow::response{os.str()}; });
|
||||
|
||||
CROW_ROUTE(app, "/params")
|
||||
([](const crow::request& req){
|
||||
([](const crow::request& req)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Params: " << req.url_params << "\n\n";
|
||||
os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n";
|
||||
@ -106,15 +105,14 @@ int main()
|
||||
for(const auto& countVal : count) {
|
||||
os << " - " << countVal << '\n';
|
||||
}
|
||||
return crow::response{os.str()};
|
||||
});
|
||||
return crow::response{os.str()}; });
|
||||
|
||||
// ignore all log
|
||||
crow::logger::setLogLevel(crow::LogLevel::Debug);
|
||||
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
|
||||
|
||||
app.port(18080)
|
||||
.server_name("CrowCpp")
|
||||
.multithreaded()
|
||||
.run();
|
||||
.server_name("CrowCpp")
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
@ -5,9 +5,8 @@ int main()
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Hello, world!";
|
||||
});
|
||||
([]()
|
||||
{ return "Hello, world!"; });
|
||||
|
||||
app.port(18080).run();
|
||||
}
|
||||
|
@ -5,9 +5,8 @@ int main()
|
||||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Hello world!";
|
||||
});
|
||||
([]()
|
||||
{ return "Hello world!"; });
|
||||
|
||||
app.port(18080).ssl_file("test.crt", "test.key").run();
|
||||
|
||||
|
@ -11,38 +11,38 @@ int main()
|
||||
std::unordered_set<crow::websocket::connection*> users;
|
||||
|
||||
CROW_ROUTE(app, "/ws")
|
||||
.websocket()
|
||||
.onopen([&](crow::websocket::connection& conn){
|
||||
.websocket()
|
||||
.onopen([&](crow::websocket::connection& conn)
|
||||
{
|
||||
CROW_LOG_INFO << "new websocket connection from " << conn.get_remote_ip();
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
users.insert(&conn);
|
||||
})
|
||||
.onclose([&](crow::websocket::connection& conn, const std::string& reason){
|
||||
users.insert(&conn); })
|
||||
.onclose([&](crow::websocket::connection& conn, const std::string& reason)
|
||||
{
|
||||
CROW_LOG_INFO << "websocket connection closed: " << reason;
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
users.erase(&conn);
|
||||
})
|
||||
.onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary){
|
||||
users.erase(&conn); })
|
||||
.onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
for(auto u:users)
|
||||
if (is_binary)
|
||||
u->send_binary(data);
|
||||
else
|
||||
u->send_text(data);
|
||||
});
|
||||
u->send_text(data); });
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]{
|
||||
([]
|
||||
{
|
||||
char name[256];
|
||||
gethostname(name, 256);
|
||||
crow::mustache::context x;
|
||||
x["servername"] = name;
|
||||
|
||||
auto page = crow::mustache::load("ws.html");
|
||||
return page.render(x);
|
||||
});
|
||||
return page.render(x); });
|
||||
|
||||
app.port(40080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
@ -38,63 +38,59 @@ namespace crow
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
using ssl_context_t = boost::asio::ssl::context;
|
||||
#endif
|
||||
///The main server application
|
||||
|
||||
/// The main server application
|
||||
///
|
||||
/// Use `SimpleApp` or `App<Middleware1, Middleware2, etc...>`
|
||||
template <typename ... Middlewares>
|
||||
template<typename... Middlewares>
|
||||
class Crow
|
||||
{
|
||||
public:
|
||||
///This crow application
|
||||
/// This crow application
|
||||
using self_t = Crow;
|
||||
///The HTTP server
|
||||
/// The HTTP server
|
||||
using server_t = Server<Crow, SocketAdaptor, Middlewares...>;
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
///An HTTP server that runs on SSL with an SSLAdaptor
|
||||
/// An HTTP server that runs on SSL with an SSLAdaptor
|
||||
using ssl_server_t = Server<Crow, SSLAdaptor, Middlewares...>;
|
||||
#endif
|
||||
Crow()
|
||||
{
|
||||
}
|
||||
|
||||
///Process an Upgrade request
|
||||
|
||||
/// Process an Upgrade request
|
||||
///
|
||||
///Currently used to upgrrade an HTTP connection to a WebSocket connection
|
||||
template <typename Adaptor>
|
||||
/// Currently used to upgrrade an HTTP connection to a WebSocket connection
|
||||
template<typename Adaptor>
|
||||
void handle_upgrade(const request& req, response& res, Adaptor&& adaptor)
|
||||
{
|
||||
router_.handle_upgrade(req, res, adaptor);
|
||||
}
|
||||
|
||||
///Process the request and generate a response for it
|
||||
/// Process the request and generate a response for it
|
||||
void handle(const request& req, response& res)
|
||||
{
|
||||
router_.handle(req, res);
|
||||
}
|
||||
|
||||
///Create a dynamic route using a rule (**Use CROW_ROUTE instead**)
|
||||
/// Create a dynamic route using a rule (**Use CROW_ROUTE instead**)
|
||||
DynamicRule& route_dynamic(std::string&& rule)
|
||||
{
|
||||
return router_.new_rule_dynamic(std::move(rule));
|
||||
}
|
||||
|
||||
///Create a route using a rule (**Use CROW_ROUTE instead**)
|
||||
template <uint64_t Tag>
|
||||
/// Create a route using a rule (**Use CROW_ROUTE instead**)
|
||||
template<uint64_t Tag>
|
||||
auto route(std::string&& rule)
|
||||
#ifdef CROW_CAN_USE_CPP17
|
||||
-> typename std::invoke_result<decltype(&Router::new_rule_tagged<Tag>),
|
||||
Router, std::string&&>::type
|
||||
-> typename std::invoke_result<decltype(&Router::new_rule_tagged<Tag>), Router, std::string&&>::type
|
||||
#else
|
||||
-> typename std::result_of<decltype (&Router::new_rule_tagged<Tag>)(
|
||||
Router, std::string&&)>::type
|
||||
-> typename std::result_of<decltype (&Router::new_rule_tagged<Tag>)(Router, std::string&&)>::type
|
||||
#endif
|
||||
{
|
||||
return router_.new_rule_tagged<Tag>(std::move(rule));
|
||||
}
|
||||
|
||||
///Create a route for any requests without a proper route (**Use CROW_CATCHALL_ROUTE instead**)
|
||||
/// Create a route for any requests without a proper route (**Use CROW_CATCHALL_ROUTE instead**)
|
||||
CatchallRule& catchall_route()
|
||||
{
|
||||
return router_.catchall_rule();
|
||||
@ -112,7 +108,7 @@ namespace crow
|
||||
return *this;
|
||||
}
|
||||
|
||||
///Set the port that Crow will handle requests on
|
||||
/// Set the port that Crow will handle requests on
|
||||
self_t& port(std::uint16_t port)
|
||||
{
|
||||
port_ = port;
|
||||
@ -124,34 +120,35 @@ namespace crow
|
||||
return port_;
|
||||
}
|
||||
|
||||
///Set the connection timeout in seconds (default is 5)
|
||||
|
||||
/// Set the connection timeout in seconds (default is 5)
|
||||
self_t& timeout(std::uint8_t timeout)
|
||||
{
|
||||
timeout_ = timeout;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///Set the server name
|
||||
/// Set the server name
|
||||
self_t& server_name(std::string server_name)
|
||||
{
|
||||
server_name_ = server_name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///The IP address that Crow will handle requests on (default is 0.0.0.0)
|
||||
/// The IP address that Crow will handle requests on (default is 0.0.0.0)
|
||||
self_t& bindaddr(std::string bindaddr)
|
||||
{
|
||||
bindaddr_ = bindaddr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///Run the server on multiple threads using all available threads
|
||||
/// Run the server on multiple threads using all available threads
|
||||
self_t& multithreaded()
|
||||
{
|
||||
return concurrency(std::thread::hardware_concurrency());
|
||||
}
|
||||
|
||||
///Run the server on multiple threads using a specific number
|
||||
/// Run the server on multiple threads using a specific number
|
||||
self_t& concurrency(std::uint16_t concurrency)
|
||||
{
|
||||
if (concurrency < 1)
|
||||
@ -160,8 +157,7 @@ namespace crow
|
||||
return *this;
|
||||
}
|
||||
|
||||
///Set the server's log level
|
||||
|
||||
/// Set the server's log level
|
||||
///
|
||||
/// Possible values are:<br>
|
||||
/// crow::LogLevel::Debug (0)<br>
|
||||
@ -176,7 +172,6 @@ namespace crow
|
||||
}
|
||||
|
||||
/// Set a response body size (in bytes) beyond which Crow automatically streams responses (Default is 1MiB)
|
||||
|
||||
///
|
||||
/// Any streamed response is unaffected by Crow's timer, and therefore won't timeout before a response is fully sent.
|
||||
self_t& stream_threshold(size_t threshold)
|
||||
@ -196,9 +191,10 @@ namespace crow
|
||||
return *this;
|
||||
}
|
||||
|
||||
///Set a custom duration and function to run on every tick
|
||||
template <typename Duration, typename Func>
|
||||
self_t& tick(Duration d, Func f) {
|
||||
/// Set a custom duration and function to run on every tick
|
||||
template<typename Duration, typename Func>
|
||||
self_t& tick(Duration d, Func f)
|
||||
{
|
||||
tick_interval_ = std::chrono::duration_cast<std::chrono::milliseconds>(d);
|
||||
tick_function_ = f;
|
||||
return *this;
|
||||
@ -222,22 +218,19 @@ namespace crow
|
||||
return compression_used_;
|
||||
}
|
||||
#endif
|
||||
///A wrapper for `validate()` in the router
|
||||
|
||||
/// A wrapper for `validate()` in the router
|
||||
///
|
||||
///Go through the rules, upgrade them if possible, and add them to the list of rules
|
||||
/// Go through the rules, upgrade them if possible, and add them to the list of rules
|
||||
void validate()
|
||||
{
|
||||
if (!validated_)
|
||||
{
|
||||
|
||||
#ifndef CROW_DISABLE_STATIC_DIR
|
||||
route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)
|
||||
([](crow::response& res, std::string file_path_partial)
|
||||
{
|
||||
route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([](crow::response& res, std::string file_path_partial)
|
||||
{
|
||||
res.set_static_file_info(CROW_STATIC_DIRECTORY + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
if (!router_.blueprints().empty())
|
||||
@ -247,12 +240,10 @@ namespace crow
|
||||
{
|
||||
if (!bp->static_dir().empty())
|
||||
{
|
||||
bp->new_rule_tagged<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)
|
||||
([bp](crow::response& res, std::string file_path_partial)
|
||||
{
|
||||
bp->new_rule_tagged<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([bp](crow::response& res, std::string file_path_partial)
|
||||
{
|
||||
res.set_static_file_info(bp->static_dir() + '/' + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
res.end(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -263,7 +254,7 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
///Notify anything using `wait_for_server_start()` to proceed
|
||||
/// Notify anything using `wait_for_server_start()` to proceed
|
||||
void notify_server_start()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(start_mutex_);
|
||||
@ -271,7 +262,7 @@ namespace crow
|
||||
cv_started_.notify_all();
|
||||
}
|
||||
|
||||
///Run the server
|
||||
/// Run the server
|
||||
void run()
|
||||
{
|
||||
|
||||
@ -300,22 +291,18 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
///Stop the server
|
||||
/// Stop the server
|
||||
void stop()
|
||||
{
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
if (ssl_used_)
|
||||
{
|
||||
if (ssl_server_) {
|
||||
ssl_server_->stop();
|
||||
}
|
||||
if (ssl_server_) { ssl_server_->stop(); }
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (server_) {
|
||||
server_->stop();
|
||||
}
|
||||
if (server_) { server_->stop(); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +315,7 @@ namespace crow
|
||||
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
|
||||
///use certificate and key files for SSL
|
||||
/// use certificate and key files for SSL
|
||||
self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
@ -337,14 +324,11 @@ namespace crow
|
||||
ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem);
|
||||
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
|
||||
ssl_context_.set_options(
|
||||
boost::asio::ssl::context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::no_sslv3
|
||||
);
|
||||
boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///use .pem file for SSL
|
||||
/// use .pem file for SSL
|
||||
self_t& ssl_file(const std::string& pem_filename)
|
||||
{
|
||||
ssl_used_ = true;
|
||||
@ -352,10 +336,7 @@ namespace crow
|
||||
ssl_context_.set_verify_mode(boost::asio::ssl::verify_client_once);
|
||||
ssl_context_.load_verify_file(pem_filename);
|
||||
ssl_context_.set_options(
|
||||
boost::asio::ssl::context::default_workarounds
|
||||
| boost::asio::ssl::context::no_sslv2
|
||||
| boost::asio::ssl::context::no_sslv3
|
||||
);
|
||||
boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -371,25 +352,25 @@ namespace crow
|
||||
return ssl_used_;
|
||||
}
|
||||
#else
|
||||
template <typename T, typename ... Remain>
|
||||
template<typename T, typename... Remain>
|
||||
self_t& ssl_file(T&&, Remain&&...)
|
||||
{
|
||||
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
|
||||
static_assert(
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
self_t& ssl(T&&)
|
||||
{
|
||||
// We can't call .ssl() member function unless CROW_ENABLE_SSL is defined.
|
||||
static_assert(
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
// make static_assert dependent to T; always false
|
||||
std::is_base_of<T, void>::value,
|
||||
"Define CROW_ENABLE_SSL to enable ssl support.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -401,7 +382,7 @@ namespace crow
|
||||
|
||||
// middleware
|
||||
using context_t = detail::context<Middlewares...>;
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
typename T::context& get_context(const request& req)
|
||||
{
|
||||
static_assert(black_magic::contains<T, Middlewares...>::value, "App doesn't have the specified middleware type.");
|
||||
@ -409,13 +390,13 @@ namespace crow
|
||||
return ctx.template get<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
T& get_middleware()
|
||||
{
|
||||
return utility::get_element_by_type<T, Middlewares...>(middlewares_);
|
||||
}
|
||||
|
||||
///Wait until the server has properly started
|
||||
/// Wait until the server has properly started
|
||||
void wait_for_server_start()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(start_mutex_);
|
||||
@ -458,7 +439,7 @@ namespace crow
|
||||
std::condition_variable cv_started_;
|
||||
std::mutex start_mutex_;
|
||||
};
|
||||
template <typename ... Middlewares>
|
||||
template<typename... Middlewares>
|
||||
using App = Crow<Middlewares...>;
|
||||
using SimpleApp = Crow<>;
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -14,7 +14,7 @@ namespace crow
|
||||
std::size_t seed = 0;
|
||||
std::locale locale;
|
||||
|
||||
for(auto c : key)
|
||||
for (auto c : key)
|
||||
{
|
||||
boost::hash_combine(seed, std::toupper(c, locale));
|
||||
}
|
||||
@ -33,4 +33,4 @@ namespace crow
|
||||
};
|
||||
|
||||
using ci_map = std::unordered_multimap<std::string, std::string, ci_hash, ci_key_eq>;
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -39,6 +39,8 @@ namespace crow
|
||||
// should not add an item below this line: used for array count
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
enum status
|
||||
{
|
||||
CONTINUE = 100,
|
||||
@ -87,32 +89,23 @@ namespace crow
|
||||
{
|
||||
switch(method)
|
||||
{
|
||||
case HTTPMethod::Delete:
|
||||
return "DELETE";
|
||||
case HTTPMethod::Get:
|
||||
return "GET";
|
||||
case HTTPMethod::Head:
|
||||
return "HEAD";
|
||||
case HTTPMethod::Post:
|
||||
return "POST";
|
||||
case HTTPMethod::Put:
|
||||
return "PUT";
|
||||
case HTTPMethod::Connect:
|
||||
return "CONNECT";
|
||||
case HTTPMethod::Options:
|
||||
return "OPTIONS";
|
||||
case HTTPMethod::Trace:
|
||||
return "TRACE";
|
||||
case HTTPMethod::Patch:
|
||||
return "PATCH";
|
||||
case HTTPMethod::Purge:
|
||||
return "PURGE";
|
||||
default:
|
||||
return "invalid";
|
||||
case HTTPMethod::Delete: return "DELETE";
|
||||
case HTTPMethod::Get: return "GET";
|
||||
case HTTPMethod::Head: return "HEAD";
|
||||
case HTTPMethod::Post: return "POST";
|
||||
case HTTPMethod::Put: return "PUT";
|
||||
case HTTPMethod::Connect: return "CONNECT";
|
||||
case HTTPMethod::Options: return "OPTIONS";
|
||||
case HTTPMethod::Trace: return "TRACE";
|
||||
case HTTPMethod::Patch: return "PATCH";
|
||||
case HTTPMethod::Purge: return "PURGE";
|
||||
default: return "invalid";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
enum class ParamType : char
|
||||
{
|
||||
INT,
|
||||
@ -134,23 +127,22 @@ namespace crow
|
||||
void debug_print() const
|
||||
{
|
||||
std::cerr << "routing_params" << std::endl;
|
||||
for(auto i:int_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for(auto i:uint_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for(auto i:double_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for(auto& i:string_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for (auto i : int_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto i : uint_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto i : double_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto& i : string_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
T get(unsigned) const;
|
||||
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -176,22 +168,21 @@ namespace crow
|
||||
{
|
||||
return string_params[index];
|
||||
}
|
||||
}
|
||||
} // namespace crow
|
||||
|
||||
#ifndef CROW_MSVC_WORKAROUND
|
||||
constexpr crow::HTTPMethod operator "" _method(const char* str, size_t /*len*/)
|
||||
constexpr crow::HTTPMethod operator"" _method(const char* str, size_t /*len*/)
|
||||
{
|
||||
return
|
||||
crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get :
|
||||
crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete :
|
||||
crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head :
|
||||
crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post :
|
||||
crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put :
|
||||
crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options :
|
||||
crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect :
|
||||
crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace :
|
||||
crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch :
|
||||
crow::black_magic::is_equ_p(str, "PURGE", 5) ? crow::HTTPMethod::Purge :
|
||||
throw std::runtime_error("invalid http method");
|
||||
return crow::black_magic::is_equ_p(str, "GET", 3) ? crow::HTTPMethod::Get :
|
||||
crow::black_magic::is_equ_p(str, "DELETE", 6) ? crow::HTTPMethod::Delete :
|
||||
crow::black_magic::is_equ_p(str, "HEAD", 4) ? crow::HTTPMethod::Head :
|
||||
crow::black_magic::is_equ_p(str, "POST", 4) ? crow::HTTPMethod::Post :
|
||||
crow::black_magic::is_equ_p(str, "PUT", 3) ? crow::HTTPMethod::Put :
|
||||
crow::black_magic::is_equ_p(str, "OPTIONS", 7) ? crow::HTTPMethod::Options :
|
||||
crow::black_magic::is_equ_p(str, "CONNECT", 7) ? crow::HTTPMethod::Connect :
|
||||
crow::black_magic::is_equ_p(str, "TRACE", 5) ? crow::HTTPMethod::Trace :
|
||||
crow::black_magic::is_equ_p(str, "PATCH", 5) ? crow::HTTPMethod::Patch :
|
||||
crow::black_magic::is_equ_p(str, "PURGE", 5) ? crow::HTTPMethod::Purge :
|
||||
throw std::runtime_error("invalid http method");
|
||||
}
|
||||
#endif
|
||||
|
@ -16,10 +16,10 @@ namespace crow
|
||||
DEFLATE = 15,
|
||||
// windowBits can also be greater than 15 for optional gzip encoding.
|
||||
// Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
|
||||
GZIP = 15|16,
|
||||
GZIP = 15 | 16,
|
||||
};
|
||||
|
||||
inline std::string compress_string(std::string const & str, algorithm algo)
|
||||
inline std::string compress_string(std::string const& str, algorithm algo)
|
||||
{
|
||||
std::string compressed_str;
|
||||
z_stream stream{};
|
||||
@ -30,13 +30,13 @@ namespace crow
|
||||
|
||||
stream.avail_in = str.size();
|
||||
// zlib does not take a const pointer. The data is not altered.
|
||||
stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(str.c_str()));
|
||||
stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(str.c_str()));
|
||||
|
||||
int code = Z_OK;
|
||||
do
|
||||
{
|
||||
stream.avail_out = sizeof(buffer);
|
||||
stream.next_out = reinterpret_cast<Bytef *>(&buffer[0]);
|
||||
stream.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
|
||||
|
||||
code = ::deflate(&stream, Z_FINISH);
|
||||
// Successful and non-fatal error code returned by deflate when used with Z_FINISH flush
|
||||
@ -56,7 +56,7 @@ namespace crow
|
||||
return compressed_str;
|
||||
}
|
||||
|
||||
inline std::string decompress_string(std::string const & deflated_string)
|
||||
inline std::string decompress_string(std::string const& deflated_string)
|
||||
{
|
||||
std::string inflated_string;
|
||||
Bytef tmp[8192];
|
||||
@ -64,7 +64,7 @@ namespace crow
|
||||
z_stream zstream{};
|
||||
zstream.avail_in = deflated_string.size();
|
||||
// Nasty const_cast but zlib won't alter its contents
|
||||
zstream.next_in = const_cast<Bytef *>(reinterpret_cast<Bytef const *>(deflated_string.c_str()));
|
||||
zstream.next_in = const_cast<Bytef*>(reinterpret_cast<Bytef const*>(deflated_string.c_str()));
|
||||
// Initialize with automatic header detection, for gzip support
|
||||
if (::inflateInit2(&zstream, MAX_WBITS | 32) == Z_OK)
|
||||
{
|
||||
@ -93,7 +93,7 @@ namespace crow
|
||||
|
||||
return inflated_string;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace compression
|
||||
} // namespace crow
|
||||
|
||||
#endif
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "crow/http_parser_merged.h"
|
||||
|
||||
#include "crow/common.h"
|
||||
#include "crow/parser.h"
|
||||
#include "crow/http_response.h"
|
||||
@ -26,110 +25,106 @@ namespace crow
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename MW>
|
||||
template<typename MW>
|
||||
struct check_before_handle_arity_3_const
|
||||
{
|
||||
template <typename T,
|
||||
void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle
|
||||
>
|
||||
template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::before_handle>
|
||||
struct get
|
||||
{ };
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
template <typename MW>
|
||||
template<typename MW>
|
||||
struct check_before_handle_arity_3
|
||||
{
|
||||
template <typename T,
|
||||
void (T::*)(request&, response&, typename MW::context&) = &T::before_handle
|
||||
>
|
||||
template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::before_handle>
|
||||
struct get
|
||||
{ };
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
template <typename MW>
|
||||
template<typename MW>
|
||||
struct check_after_handle_arity_3_const
|
||||
{
|
||||
template <typename T,
|
||||
void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle
|
||||
>
|
||||
template<typename T, void (T::*)(request&, response&, typename MW::context&) const = &T::after_handle>
|
||||
struct get
|
||||
{ };
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
template <typename MW>
|
||||
template<typename MW>
|
||||
struct check_after_handle_arity_3
|
||||
{
|
||||
template <typename T,
|
||||
void (T::*)(request&, response&, typename MW::context&) = &T::after_handle
|
||||
>
|
||||
template<typename T, void (T::*)(request&, response&, typename MW::context&) = &T::after_handle>
|
||||
struct get
|
||||
{ };
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct is_before_handle_arity_3_impl
|
||||
{
|
||||
template <typename C>
|
||||
template<typename C>
|
||||
static std::true_type f(typename check_before_handle_arity_3_const<T>::template get<C>*);
|
||||
|
||||
template <typename C>
|
||||
template<typename C>
|
||||
static std::true_type f(typename check_before_handle_arity_3<T>::template get<C>*);
|
||||
|
||||
template <typename C>
|
||||
template<typename C>
|
||||
static std::false_type f(...);
|
||||
|
||||
public:
|
||||
static const bool value = decltype(f<T>(nullptr))::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct is_after_handle_arity_3_impl
|
||||
{
|
||||
template <typename C>
|
||||
template<typename C>
|
||||
static std::true_type f(typename check_after_handle_arity_3_const<T>::template get<C>*);
|
||||
|
||||
template <typename C>
|
||||
template<typename C>
|
||||
static std::true_type f(typename check_after_handle_arity_3<T>::template get<C>*);
|
||||
|
||||
template <typename C>
|
||||
template<typename C>
|
||||
static std::false_type f(...);
|
||||
|
||||
public:
|
||||
static const bool value = decltype(f<T>(nullptr))::value;
|
||||
};
|
||||
|
||||
template <typename MW, typename Context, typename ParentContext>
|
||||
template<typename MW, typename Context, typename ParentContext>
|
||||
typename std::enable_if<!is_before_handle_arity_3_impl<MW>::value>::type
|
||||
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
{
|
||||
mw.before_handle(req, res, ctx.template get<MW>(), ctx);
|
||||
}
|
||||
|
||||
template <typename MW, typename Context, typename ParentContext>
|
||||
template<typename MW, typename Context, typename ParentContext>
|
||||
typename std::enable_if<is_before_handle_arity_3_impl<MW>::value>::type
|
||||
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
before_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
{
|
||||
mw.before_handle(req, res, ctx.template get<MW>());
|
||||
}
|
||||
|
||||
template <typename MW, typename Context, typename ParentContext>
|
||||
template<typename MW, typename Context, typename ParentContext>
|
||||
typename std::enable_if<!is_after_handle_arity_3_impl<MW>::value>::type
|
||||
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
{
|
||||
mw.after_handle(req, res, ctx.template get<MW>(), ctx);
|
||||
}
|
||||
|
||||
template <typename MW, typename Context, typename ParentContext>
|
||||
template<typename MW, typename Context, typename ParentContext>
|
||||
typename std::enable_if<is_after_handle_arity_3_impl<MW>::value>::type
|
||||
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
after_handler_call(MW& mw, request& req, response& res, Context& ctx, ParentContext& /*parent_ctx*/)
|
||||
{
|
||||
mw.after_handle(req, res, ctx.template get<MW>());
|
||||
}
|
||||
|
||||
template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares>
|
||||
template<int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares>
|
||||
bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx)
|
||||
{
|
||||
using parent_context_t = typename Context::template partial<N-1>;
|
||||
using parent_context_t = typename Context::template partial<N - 1>;
|
||||
before_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
|
||||
|
||||
if (res.is_completed())
|
||||
@ -138,7 +133,7 @@ namespace crow
|
||||
return true;
|
||||
}
|
||||
|
||||
if (middleware_call_helper<N+1, Context, Container, Middlewares...>(middlewares, req, res, ctx))
|
||||
if (middleware_call_helper<N + 1, Context, Container, Middlewares...>(middlewares, req, res, ctx))
|
||||
{
|
||||
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
|
||||
return true;
|
||||
@ -147,76 +142,76 @@ namespace crow
|
||||
return false;
|
||||
}
|
||||
|
||||
template <int N, typename Context, typename Container>
|
||||
template<int N, typename Context, typename Container>
|
||||
bool middleware_call_helper(Container& /*middlewares*/, request& /*req*/, response& /*res*/, Context& /*ctx*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N<0)>::type
|
||||
after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
|
||||
template<int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N < 0)>::type
|
||||
after_handlers_call_helper(Container& /*middlewares*/, Context& /*context*/, request& /*req*/, response& /*res*/)
|
||||
{
|
||||
}
|
||||
|
||||
template <int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
|
||||
template<int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N == 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
|
||||
{
|
||||
using parent_context_t = typename Context::template partial<N-1>;
|
||||
using parent_context_t = typename Context::template partial<N - 1>;
|
||||
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
|
||||
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
|
||||
}
|
||||
|
||||
template <int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
|
||||
template<int N, typename Context, typename Container>
|
||||
typename std::enable_if<(N > 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res)
|
||||
{
|
||||
using parent_context_t = typename Context::template partial<N-1>;
|
||||
using parent_context_t = typename Context::template partial<N - 1>;
|
||||
using CurrentMW = typename std::tuple_element<N, typename std::remove_reference<Container>::type>::type;
|
||||
after_handler_call<CurrentMW, Context, parent_context_t>(std::get<N>(middlewares), req, res, ctx, static_cast<parent_context_t&>(ctx));
|
||||
after_handlers_call_helper<N-1, Context, Container>(middlewares, ctx, req, res);
|
||||
after_handlers_call_helper<N - 1, Context, Container>(middlewares, ctx, req, res);
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
static std::atomic<int> connectionCount;
|
||||
#endif
|
||||
|
||||
/// An HTTP connection.
|
||||
template <typename Adaptor, typename Handler, typename ... Middlewares>
|
||||
template<typename Adaptor, typename Handler, typename... Middlewares>
|
||||
class Connection
|
||||
{
|
||||
friend struct crow::response;
|
||||
|
||||
public:
|
||||
Connection(
|
||||
boost::asio::io_service& io_service,
|
||||
Handler* handler,
|
||||
const std::string& server_name,
|
||||
std::tuple<Middlewares...>* middlewares,
|
||||
std::function<std::string()>& get_cached_date_str_f,
|
||||
detail::task_timer& task_timer,
|
||||
typename Adaptor::context* adaptor_ctx_
|
||||
)
|
||||
: adaptor_(io_service, adaptor_ctx_),
|
||||
handler_(handler),
|
||||
parser_(this),
|
||||
server_name_(server_name),
|
||||
middlewares_(middlewares),
|
||||
get_cached_date_str(get_cached_date_str_f),
|
||||
task_timer_(task_timer),
|
||||
res_stream_threshold_(handler->stream_threshold())
|
||||
boost::asio::io_service& io_service,
|
||||
Handler* handler,
|
||||
const std::string& server_name,
|
||||
std::tuple<Middlewares...>* middlewares,
|
||||
std::function<std::string()>& get_cached_date_str_f,
|
||||
detail::task_timer& task_timer,
|
||||
typename Adaptor::context* adaptor_ctx_):
|
||||
adaptor_(io_service, adaptor_ctx_),
|
||||
handler_(handler),
|
||||
parser_(this),
|
||||
server_name_(server_name),
|
||||
middlewares_(middlewares),
|
||||
get_cached_date_str(get_cached_date_str_f),
|
||||
task_timer_(task_timer),
|
||||
res_stream_threshold_(handler->stream_threshold())
|
||||
{
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
connectionCount ++;
|
||||
connectionCount++;
|
||||
CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
~Connection()
|
||||
{
|
||||
res.complete_request_handler_ = nullptr;
|
||||
cancel_deadline_timer();
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
connectionCount --;
|
||||
connectionCount--;
|
||||
CROW_LOG_DEBUG << "Connection closed, total " << connectionCount << ", " << this;
|
||||
#endif
|
||||
}
|
||||
@ -229,7 +224,8 @@ namespace crow
|
||||
|
||||
void start()
|
||||
{
|
||||
adaptor_.start([this](const boost::system::error_code& ec) {
|
||||
adaptor_.start([this](const boost::system::error_code& ec)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
start_deadline();
|
||||
@ -240,8 +236,7 @@ namespace crow
|
||||
{
|
||||
CROW_LOG_ERROR << "Could not start adaptor: " << ec.message();
|
||||
check_destroy();
|
||||
}
|
||||
});
|
||||
} });
|
||||
}
|
||||
|
||||
void handle_header()
|
||||
@ -272,7 +267,7 @@ namespace crow
|
||||
// HTTP/1.0
|
||||
if (req.headers.count("connection"))
|
||||
{
|
||||
if (boost::iequals(req.get_header_value("connection"),"Keep-Alive"))
|
||||
if (boost::iequals(req.get_header_value("connection"), "Keep-Alive"))
|
||||
add_keep_alive_ = true;
|
||||
}
|
||||
else
|
||||
@ -285,7 +280,7 @@ namespace crow
|
||||
{
|
||||
if (req.get_header_value("connection") == "close")
|
||||
close_connection_ = true;
|
||||
else if (boost::iequals(req.get_header_value("connection"),"Keep-Alive"))
|
||||
else if (boost::iequals(req.get_header_value("connection"), "Keep-Alive"))
|
||||
add_keep_alive_ = true;
|
||||
}
|
||||
if (!req.headers.count("host"))
|
||||
@ -293,31 +288,32 @@ namespace crow
|
||||
is_invalid_request = true;
|
||||
res = response(400);
|
||||
}
|
||||
if (parser_.is_upgrade())
|
||||
{
|
||||
if (req.get_header_value("upgrade") == "h2c")
|
||||
{
|
||||
// TODO HTTP/2
|
||||
if (parser_.is_upgrade())
|
||||
{
|
||||
if (req.get_header_value("upgrade") == "h2c")
|
||||
{
|
||||
// TODO HTTP/2
|
||||
// currently, ignore upgrade header
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
close_connection_ = true;
|
||||
handler_->handle_upgrade(req, res, std::move(adaptor_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << parser_.http_major << "." << parser_.http_minor << ' '
|
||||
<< method_name(req.method) << " " << req.url;
|
||||
<< method_name(req.method) << " " << req.url;
|
||||
|
||||
|
||||
need_to_call_after_handlers_ = false;
|
||||
if (!is_invalid_request)
|
||||
{
|
||||
res.complete_request_handler_ = []{};
|
||||
res.is_alive_helper_ = [this]()->bool{ return adaptor_.is_open(); };
|
||||
res.complete_request_handler_ = [] {};
|
||||
res.is_alive_helper_ = [this]() -> bool
|
||||
{ return adaptor_.is_open(); };
|
||||
|
||||
ctx_ = detail::context<Middlewares...>();
|
||||
req.middleware_context = static_cast<void*>(&ctx_);
|
||||
@ -326,7 +322,8 @@ namespace crow
|
||||
|
||||
if (!res.completed_)
|
||||
{
|
||||
res.complete_request_handler_ = [this]{ this->complete_request(); };
|
||||
res.complete_request_handler_ = [this]
|
||||
{ this->complete_request(); };
|
||||
need_to_call_after_handlers_ = true;
|
||||
handler_->handle(req, res);
|
||||
if (add_keep_alive_)
|
||||
@ -354,13 +351,13 @@ namespace crow
|
||||
|
||||
// call all after_handler of middlewares
|
||||
detail::after_handlers_call_helper<
|
||||
(static_cast<int>(sizeof...(Middlewares))-1),
|
||||
decltype(ctx_),
|
||||
decltype(*middlewares_)>
|
||||
(*middlewares_, ctx_, req_, res);
|
||||
(static_cast<int>(sizeof...(Middlewares)) - 1),
|
||||
decltype(ctx_),
|
||||
decltype(*middlewares_)>(*middlewares_, ctx_, req_, res);
|
||||
}
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
if (handler_->compression_used()) {
|
||||
if (handler_->compression_used())
|
||||
{
|
||||
std::string accept_encoding = req_.get_header_value("Accept-Encoding");
|
||||
if (!accept_encoding.empty() && res.compressed)
|
||||
{
|
||||
@ -390,28 +387,28 @@ namespace crow
|
||||
std::string location = res.get_header_value("Location");
|
||||
if (!location.empty() && location.find("://", 0) == std::string::npos)
|
||||
{
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
#ifdef CROW_ENABLE_SSL
|
||||
if (handler_->ssl_used())
|
||||
location.insert(0, "https://" + req_.get_header_value("Host"));
|
||||
else
|
||||
#endif
|
||||
location.insert(0, "http://" + req_.get_header_value("Host"));
|
||||
#endif
|
||||
location.insert(0, "http://" + req_.get_header_value("Host"));
|
||||
res.set_header("location", location);
|
||||
}
|
||||
|
||||
prepare_buffers();
|
||||
prepare_buffers();
|
||||
|
||||
if (res.is_static_type())
|
||||
{
|
||||
do_write_static();
|
||||
}else {
|
||||
}
|
||||
else
|
||||
{
|
||||
do_write_general();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void prepare_buffers()
|
||||
{
|
||||
//auto self = this->shared_from_this();
|
||||
@ -426,53 +423,53 @@ namespace crow
|
||||
|
||||
// Keep in sync with common.h/status
|
||||
static std::unordered_map<int, std::string> statusCodes = {
|
||||
{status::CONTINUE, "HTTP/1.1 100 Continue\r\n"},
|
||||
{status::SWITCHING_PROTOCOLS, "HTTP/1.1 101 Switching Protocols\r\n"},
|
||||
{status::CONTINUE, "HTTP/1.1 100 Continue\r\n"},
|
||||
{status::SWITCHING_PROTOCOLS, "HTTP/1.1 101 Switching Protocols\r\n"},
|
||||
|
||||
{status::OK, "HTTP/1.1 200 OK\r\n"},
|
||||
{status::CREATED, "HTTP/1.1 201 Created\r\n"},
|
||||
{status::ACCEPTED, "HTTP/1.1 202 Accepted\r\n"},
|
||||
{status::NON_AUTHORITATIVE_INFORMATION, "HTTP/1.1 203 Non-Authoritative Information\r\n"},
|
||||
{status::NO_CONTENT, "HTTP/1.1 204 No Content\r\n"},
|
||||
{status::RESET_CONTENT, "HTTP/1.1 205 Reset Content\r\n"},
|
||||
{status::PARTIAL_CONTENT, "HTTP/1.1 206 Partial Content\r\n"},
|
||||
{status::OK, "HTTP/1.1 200 OK\r\n"},
|
||||
{status::CREATED, "HTTP/1.1 201 Created\r\n"},
|
||||
{status::ACCEPTED, "HTTP/1.1 202 Accepted\r\n"},
|
||||
{status::NON_AUTHORITATIVE_INFORMATION, "HTTP/1.1 203 Non-Authoritative Information\r\n"},
|
||||
{status::NO_CONTENT, "HTTP/1.1 204 No Content\r\n"},
|
||||
{status::RESET_CONTENT, "HTTP/1.1 205 Reset Content\r\n"},
|
||||
{status::PARTIAL_CONTENT, "HTTP/1.1 206 Partial Content\r\n"},
|
||||
|
||||
{status::MULTIPLE_CHOICES, "HTTP/1.1 300 Multiple Choices\r\n"},
|
||||
{status::MOVED_PERMANENTLY, "HTTP/1.1 301 Moved Permanently\r\n"},
|
||||
{status::FOUND, "HTTP/1.1 302 Found\r\n"},
|
||||
{status::SEE_OTHER, "HTTP/1.1 303 See Other\r\n"},
|
||||
{status::NOT_MODIFIED, "HTTP/1.1 304 Not Modified\r\n"},
|
||||
{status::TEMPORARY_REDIRECT, "HTTP/1.1 307 Temporary Redirect\r\n"},
|
||||
{status::PERMANENT_REDIRECT, "HTTP/1.1 308 Permanent Redirect\r\n"},
|
||||
{status::MULTIPLE_CHOICES, "HTTP/1.1 300 Multiple Choices\r\n"},
|
||||
{status::MOVED_PERMANENTLY, "HTTP/1.1 301 Moved Permanently\r\n"},
|
||||
{status::FOUND, "HTTP/1.1 302 Found\r\n"},
|
||||
{status::SEE_OTHER, "HTTP/1.1 303 See Other\r\n"},
|
||||
{status::NOT_MODIFIED, "HTTP/1.1 304 Not Modified\r\n"},
|
||||
{status::TEMPORARY_REDIRECT, "HTTP/1.1 307 Temporary Redirect\r\n"},
|
||||
{status::PERMANENT_REDIRECT, "HTTP/1.1 308 Permanent Redirect\r\n"},
|
||||
|
||||
{status::BAD_REQUEST, "HTTP/1.1 400 Bad Request\r\n"},
|
||||
{status::UNAUTHORIZED, "HTTP/1.1 401 Unauthorized\r\n"},
|
||||
{status::FORBIDDEN, "HTTP/1.1 403 Forbidden\r\n"},
|
||||
{status::NOT_FOUND, "HTTP/1.1 404 Not Found\r\n"},
|
||||
{status::METHOD_NOT_ALLOWED, "HTTP/1.1 405 Method Not Allowed\r\n"},
|
||||
{status::PROXY_AUTHENTICATION_REQUIRED, "HTTP/1.1 407 Proxy Authentication Required\r\n"},
|
||||
{status::CONFLICT, "HTTP/1.1 409 Conflict\r\n"},
|
||||
{status::GONE, "HTTP/1.1 410 Gone\r\n"},
|
||||
{status::PAYLOAD_TOO_LARGE, "HTTP/1.1 413 Payload Too Large\r\n"},
|
||||
{status::UNSUPPORTED_MEDIA_TYPE, "HTTP/1.1 415 Unsupported Media Type\r\n"},
|
||||
{status::RANGE_NOT_SATISFIABLE, "HTTP/1.1 416 Range Not Satisfiable\r\n"},
|
||||
{status::EXPECTATION_FAILED, "HTTP/1.1 417 Expectation Failed\r\n"},
|
||||
{status::PRECONDITION_REQUIRED, "HTTP/1.1 428 Precondition Required\r\n"},
|
||||
{status::TOO_MANY_REQUESTS, "HTTP/1.1 429 Too Many Requests\r\n"},
|
||||
{status::UNAVAILABLE_FOR_LEGAL_REASONS, "HTTP/1.1 451 Unavailable For Legal Reasons\r\n"},
|
||||
{status::BAD_REQUEST, "HTTP/1.1 400 Bad Request\r\n"},
|
||||
{status::UNAUTHORIZED, "HTTP/1.1 401 Unauthorized\r\n"},
|
||||
{status::FORBIDDEN, "HTTP/1.1 403 Forbidden\r\n"},
|
||||
{status::NOT_FOUND, "HTTP/1.1 404 Not Found\r\n"},
|
||||
{status::METHOD_NOT_ALLOWED, "HTTP/1.1 405 Method Not Allowed\r\n"},
|
||||
{status::PROXY_AUTHENTICATION_REQUIRED, "HTTP/1.1 407 Proxy Authentication Required\r\n"},
|
||||
{status::CONFLICT, "HTTP/1.1 409 Conflict\r\n"},
|
||||
{status::GONE, "HTTP/1.1 410 Gone\r\n"},
|
||||
{status::PAYLOAD_TOO_LARGE, "HTTP/1.1 413 Payload Too Large\r\n"},
|
||||
{status::UNSUPPORTED_MEDIA_TYPE, "HTTP/1.1 415 Unsupported Media Type\r\n"},
|
||||
{status::RANGE_NOT_SATISFIABLE, "HTTP/1.1 416 Range Not Satisfiable\r\n"},
|
||||
{status::EXPECTATION_FAILED, "HTTP/1.1 417 Expectation Failed\r\n"},
|
||||
{status::PRECONDITION_REQUIRED, "HTTP/1.1 428 Precondition Required\r\n"},
|
||||
{status::TOO_MANY_REQUESTS, "HTTP/1.1 429 Too Many Requests\r\n"},
|
||||
{status::UNAVAILABLE_FOR_LEGAL_REASONS, "HTTP/1.1 451 Unavailable For Legal Reasons\r\n"},
|
||||
|
||||
{status::INTERNAL_SERVER_ERROR, "HTTP/1.1 500 Internal Server Error\r\n"},
|
||||
{status::NOT_IMPLEMENTED, "HTTP/1.1 501 Not Implemented\r\n"},
|
||||
{status::BAD_GATEWAY, "HTTP/1.1 502 Bad Gateway\r\n"},
|
||||
{status::SERVICE_UNAVAILABLE, "HTTP/1.1 503 Service Unavailable\r\n"},
|
||||
{status::VARIANT_ALSO_NEGOTIATES, "HTTP/1.1 506 Variant Also Negotiates\r\n"},
|
||||
{status::INTERNAL_SERVER_ERROR, "HTTP/1.1 500 Internal Server Error\r\n"},
|
||||
{status::NOT_IMPLEMENTED, "HTTP/1.1 501 Not Implemented\r\n"},
|
||||
{status::BAD_GATEWAY, "HTTP/1.1 502 Bad Gateway\r\n"},
|
||||
{status::SERVICE_UNAVAILABLE, "HTTP/1.1 503 Service Unavailable\r\n"},
|
||||
{status::VARIANT_ALSO_NEGOTIATES, "HTTP/1.1 506 Variant Also Negotiates\r\n"},
|
||||
};
|
||||
|
||||
static std::string seperator = ": ";
|
||||
static std::string crlf = "\r\n";
|
||||
|
||||
buffers_.clear();
|
||||
buffers_.reserve(4*(res.headers.size()+5)+3);
|
||||
buffers_.reserve(4 * (res.headers.size() + 5) + 3);
|
||||
|
||||
if (!statusCodes.count(res.code))
|
||||
res.code = 500;
|
||||
@ -484,13 +481,12 @@ namespace crow
|
||||
if (res.code >= 400 && res.body.empty())
|
||||
res.body = statusCodes[res.code].substr(9);
|
||||
|
||||
for(auto& kv : res.headers)
|
||||
for (auto& kv : res.headers)
|
||||
{
|
||||
buffers_.emplace_back(kv.first.data(), kv.first.size());
|
||||
buffers_.emplace_back(seperator.data(), seperator.size());
|
||||
buffers_.emplace_back(kv.second.data(), kv.second.size());
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
|
||||
}
|
||||
|
||||
if (!res.manual_length_header && !res.headers.count("content-length"))
|
||||
@ -524,7 +520,6 @@ namespace crow
|
||||
}
|
||||
|
||||
buffers_.emplace_back(crlf.data(), crlf.size());
|
||||
|
||||
}
|
||||
|
||||
void do_write_static()
|
||||
@ -570,76 +565,78 @@ namespace crow
|
||||
{
|
||||
//auto self = this->shared_from_this();
|
||||
is_reading = true;
|
||||
adaptor_.socket().async_read_some(boost::asio::buffer(buffer_),
|
||||
[this](const boost::system::error_code& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
bool error_while_reading = true;
|
||||
if (!ec)
|
||||
{
|
||||
bool ret = parser_.feed(buffer_.data(), bytes_transferred);
|
||||
if (ret && adaptor_.is_open())
|
||||
{
|
||||
error_while_reading = false;
|
||||
}
|
||||
}
|
||||
adaptor_.socket().async_read_some(
|
||||
boost::asio::buffer(buffer_),
|
||||
[this](const boost::system::error_code& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
bool error_while_reading = true;
|
||||
if (!ec)
|
||||
{
|
||||
bool ret = parser_.feed(buffer_.data(), bytes_transferred);
|
||||
if (ret && adaptor_.is_open())
|
||||
{
|
||||
error_while_reading = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (error_while_reading)
|
||||
{
|
||||
cancel_deadline_timer();
|
||||
parser_.done();
|
||||
adaptor_.shutdown_read();
|
||||
adaptor_.close();
|
||||
is_reading = false;
|
||||
CROW_LOG_DEBUG << this << " from read(1)";
|
||||
check_destroy();
|
||||
}
|
||||
else if (close_connection_)
|
||||
{
|
||||
cancel_deadline_timer();
|
||||
parser_.done();
|
||||
is_reading = false;
|
||||
check_destroy();
|
||||
// adaptor will close after write
|
||||
}
|
||||
else if (!need_to_call_after_handlers_)
|
||||
{
|
||||
start_deadline();
|
||||
do_read();
|
||||
}
|
||||
else
|
||||
{
|
||||
// res will be completed later by user
|
||||
need_to_start_read_after_complete_ = true;
|
||||
}
|
||||
});
|
||||
if (error_while_reading)
|
||||
{
|
||||
cancel_deadline_timer();
|
||||
parser_.done();
|
||||
adaptor_.shutdown_read();
|
||||
adaptor_.close();
|
||||
is_reading = false;
|
||||
CROW_LOG_DEBUG << this << " from read(1)";
|
||||
check_destroy();
|
||||
}
|
||||
else if (close_connection_)
|
||||
{
|
||||
cancel_deadline_timer();
|
||||
parser_.done();
|
||||
is_reading = false;
|
||||
check_destroy();
|
||||
// adaptor will close after write
|
||||
}
|
||||
else if (!need_to_call_after_handlers_)
|
||||
{
|
||||
start_deadline();
|
||||
do_read();
|
||||
}
|
||||
else
|
||||
{
|
||||
// res will be completed later by user
|
||||
need_to_start_read_after_complete_ = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void do_write()
|
||||
{
|
||||
//auto self = this->shared_from_this();
|
||||
is_writing = true;
|
||||
boost::asio::async_write(adaptor_.socket(), buffers_,
|
||||
[&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/)
|
||||
{
|
||||
is_writing = false;
|
||||
res.clear();
|
||||
res_body_copy_.clear();
|
||||
if (!ec)
|
||||
{
|
||||
if (close_connection_)
|
||||
{
|
||||
adaptor_.shutdown_write();
|
||||
adaptor_.close();
|
||||
CROW_LOG_DEBUG << this << " from write(1)";
|
||||
check_destroy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CROW_LOG_DEBUG << this << " from write(2)";
|
||||
check_destroy();
|
||||
}
|
||||
});
|
||||
boost::asio::async_write(
|
||||
adaptor_.socket(), buffers_,
|
||||
[&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/)
|
||||
{
|
||||
is_writing = false;
|
||||
res.clear();
|
||||
res_body_copy_.clear();
|
||||
if (!ec)
|
||||
{
|
||||
if (close_connection_)
|
||||
{
|
||||
adaptor_.shutdown_write();
|
||||
adaptor_.close();
|
||||
CROW_LOG_DEBUG << this << " from write(1)";
|
||||
check_destroy();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CROW_LOG_DEBUG << this << " from write(2)";
|
||||
check_destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void check_destroy()
|
||||
@ -663,14 +660,13 @@ namespace crow
|
||||
cancel_deadline_timer();
|
||||
|
||||
task_id_ = task_timer_.schedule([this]
|
||||
{
|
||||
{
|
||||
if (!adaptor_.is_open())
|
||||
{
|
||||
return;
|
||||
}
|
||||
adaptor_.shutdown_readwrite();
|
||||
adaptor_.close();
|
||||
});
|
||||
adaptor_.close(); });
|
||||
CROW_LOG_DEBUG << this << " timer added: " << &task_timer_ << ' ' << task_id_;
|
||||
}
|
||||
|
||||
@ -710,4 +706,4 @@ namespace crow
|
||||
size_t res_stream_threshold_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -9,7 +9,7 @@
|
||||
namespace crow
|
||||
{
|
||||
/// Find and return the value associated with the key. (returns an empty string if nothing is found)
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
inline const std::string& get_header_value(const T& headers, const std::string& key)
|
||||
{
|
||||
if (headers.count(key))
|
||||
@ -20,14 +20,14 @@ namespace crow
|
||||
return empty;
|
||||
}
|
||||
|
||||
struct DetachHelper;
|
||||
struct DetachHelper;
|
||||
|
||||
/// An HTTP request.
|
||||
struct request
|
||||
{
|
||||
HTTPMethod method;
|
||||
std::string raw_url; ///< The full URL containing the `?` and URL parameters.
|
||||
std::string url; ///< The endpoint without any parameters.
|
||||
std::string raw_url; ///< The full URL containing the `?` and URL parameters.
|
||||
std::string url; ///< The endpoint without any parameters.
|
||||
query_string url_params; ///< The parameters associated with the request. (everything after the `?`)
|
||||
ci_map headers;
|
||||
std::string body;
|
||||
@ -37,14 +37,14 @@ namespace crow
|
||||
boost::asio::io_service* io_service{};
|
||||
|
||||
/// Construct an empty request. (sets the method to `GET`)
|
||||
request()
|
||||
: method(HTTPMethod::Get)
|
||||
request():
|
||||
method(HTTPMethod::Get)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct a request with all values assigned.
|
||||
request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body)
|
||||
: method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body))
|
||||
request(HTTPMethod method, std::string raw_url, std::string url, query_string url_params, ci_map headers, std::string body):
|
||||
method(method), raw_url(std::move(raw_url)), url(std::move(url)), url_params(std::move(url_params)), headers(std::move(headers)), body(std::move(body))
|
||||
{
|
||||
}
|
||||
|
||||
@ -71,6 +71,5 @@ namespace crow
|
||||
{
|
||||
io_service->dispatch(handler);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -16,23 +16,23 @@
|
||||
|
||||
namespace crow
|
||||
{
|
||||
template <typename Adaptor, typename Handler, typename ... Middlewares>
|
||||
template<typename Adaptor, typename Handler, typename... Middlewares>
|
||||
class Connection;
|
||||
|
||||
/// HTTP response
|
||||
struct response
|
||||
{
|
||||
template <typename Adaptor, typename Handler, typename ... Middlewares>
|
||||
template<typename Adaptor, typename Handler, typename... Middlewares>
|
||||
friend class crow::Connection;
|
||||
|
||||
int code{200}; ///< The Status code for the response.
|
||||
int code{200}; ///< The Status code for the response.
|
||||
std::string body; ///< The actual payload containing the response data.
|
||||
ci_map headers; ///< HTTP headers.
|
||||
ci_map headers; ///< HTTP headers.
|
||||
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
bool compressed = true; ///< If compression is enabled and this is false, the individual response will not be compressed.
|
||||
#endif
|
||||
bool is_head_response = false; ///< Whether this is a response to a HEAD request.
|
||||
bool is_head_response = false; ///< Whether this is a response to a HEAD request.
|
||||
bool manual_length_header = false; ///< Whether Crow should automatically add a "Content-Length" header.
|
||||
|
||||
/// Set the value of an existing header in the response.
|
||||
@ -53,25 +53,28 @@ namespace crow
|
||||
return crow::get_header_value(headers, key);
|
||||
}
|
||||
|
||||
|
||||
// TODO find a better way to format this so that stuff aren't moved down a line
|
||||
// clang-format off
|
||||
response() {}
|
||||
explicit response(int code) : code(code) {}
|
||||
response(std::string body) : body(std::move(body)) {}
|
||||
response(int code, std::string body) : code(code), body(std::move(body)) {}
|
||||
response (returnable&& value)
|
||||
// clang-format on
|
||||
response(returnable&& value)
|
||||
{
|
||||
body = value.dump();
|
||||
set_header("Content-Type",value.content_type);
|
||||
set_header("Content-Type", value.content_type);
|
||||
}
|
||||
response (returnable& value)
|
||||
response(returnable& value)
|
||||
{
|
||||
body = value.dump();
|
||||
set_header("Content-Type",value.content_type);
|
||||
set_header("Content-Type", value.content_type);
|
||||
}
|
||||
response (int code, returnable& value) : code(code)
|
||||
response(int code, returnable& value):
|
||||
code(code)
|
||||
{
|
||||
body = value.dump();
|
||||
set_header("Content-Type",value.content_type);
|
||||
set_header("Content-Type", value.content_type);
|
||||
}
|
||||
|
||||
response(response&& r)
|
||||
@ -79,19 +82,21 @@ namespace crow
|
||||
*this = std::move(r);
|
||||
}
|
||||
|
||||
response(std::string contentType, std::string body) : body(std::move(body))
|
||||
response(std::string contentType, std::string body):
|
||||
body(std::move(body))
|
||||
{
|
||||
set_header("Content-Type", mime_types.at(contentType));
|
||||
}
|
||||
|
||||
response(int code, std::string contentType, std::string body): code(code),body(std::move(body))
|
||||
response(int code, std::string contentType, std::string body):
|
||||
code(code), body(std::move(body))
|
||||
{
|
||||
set_header("Content-Type", mime_types.at(contentType));
|
||||
}
|
||||
|
||||
response& operator = (const response& r) = delete;
|
||||
response& operator=(const response& r) = delete;
|
||||
|
||||
response& operator = (response&& r) noexcept
|
||||
response& operator=(response&& r) noexcept
|
||||
{
|
||||
body = std::move(r.body);
|
||||
code = r.code;
|
||||
@ -196,17 +201,18 @@ namespace crow
|
||||
}
|
||||
|
||||
/// This constains metadata (coming from the `stat` command) related to any static files associated with this response.
|
||||
|
||||
/// Either a static file or a string body can be returned as 1 response.
|
||||
///
|
||||
struct static_file_info{
|
||||
/// Either a static file or a string body can be returned as 1 response.
|
||||
struct static_file_info
|
||||
{
|
||||
std::string path = "";
|
||||
struct stat statbuf;
|
||||
int statResult;
|
||||
};
|
||||
|
||||
///Return a static file as the response body
|
||||
void set_static_file_info(std::string path){
|
||||
void set_static_file_info(std::string path)
|
||||
{
|
||||
file_info.path = path;
|
||||
file_info.statResult = stat(file_info.path.c_str(), &file_info.statbuf);
|
||||
#ifdef CROW_ENABLE_COMPRESSION
|
||||
@ -215,17 +221,18 @@ namespace crow
|
||||
if (file_info.statResult == 0)
|
||||
{
|
||||
std::size_t last_dot = path.find_last_of(".");
|
||||
std::string extension = path.substr(last_dot+1);
|
||||
std::string extension = path.substr(last_dot + 1);
|
||||
std::string mimeType = "";
|
||||
code = 200;
|
||||
this->add_header("Content-length", std::to_string(file_info.statbuf.st_size));
|
||||
|
||||
if (extension != ""){
|
||||
if (extension != "")
|
||||
{
|
||||
mimeType = mime_types.at(extension);
|
||||
if (mimeType != "")
|
||||
this-> add_header("Content-Type", mimeType);
|
||||
this->add_header("Content-Type", mimeType);
|
||||
else
|
||||
this-> add_header("content-Type", "text/plain");
|
||||
this->add_header("content-Type", "text/plain");
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -256,58 +263,58 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool completed_{};
|
||||
std::function<void()> complete_request_handler_;
|
||||
std::function<bool()> is_alive_helper_;
|
||||
static_file_info file_info;
|
||||
private:
|
||||
bool completed_{};
|
||||
std::function<void()> complete_request_handler_;
|
||||
std::function<bool()> is_alive_helper_;
|
||||
static_file_info file_info;
|
||||
|
||||
template<typename Stream, typename Adaptor>
|
||||
void write_streamed(Stream& is, Adaptor& adaptor)
|
||||
template<typename Stream, typename Adaptor>
|
||||
void write_streamed(Stream& is, Adaptor& adaptor)
|
||||
{
|
||||
char buf[16384];
|
||||
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||
{
|
||||
char buf[16384];
|
||||
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||
{
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
buffers.push_back(boost::asio::buffer(buf));
|
||||
write_buffer_list(buffers, adaptor);
|
||||
}
|
||||
}
|
||||
|
||||
//THIS METHOD DOES MODIFY THE BODY, AS IN IT EMPTIES IT
|
||||
template<typename Adaptor>
|
||||
void write_streamed_string(std::string& is, Adaptor& adaptor)
|
||||
{
|
||||
std::string buf;
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
|
||||
while (is.length() > 16384)
|
||||
{
|
||||
//buf.reserve(16385);
|
||||
buf = is.substr(0, 16384);
|
||||
is = is.substr(16384);
|
||||
push_and_write(buffers, buf, adaptor);
|
||||
}
|
||||
//Collect whatever is left (less than 16KB) and send it down the socket
|
||||
//buf.reserve(is.length());
|
||||
buf = is;
|
||||
is.clear();
|
||||
push_and_write(buffers, buf, adaptor);
|
||||
}
|
||||
|
||||
template<typename Adaptor>
|
||||
inline void push_and_write(std::vector<asio::const_buffer>& buffers, std::string& buf, Adaptor& adaptor)
|
||||
{
|
||||
buffers.clear();
|
||||
buffers.push_back(boost::asio::buffer(buf));
|
||||
write_buffer_list(buffers, adaptor);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Adaptor>
|
||||
inline void write_buffer_list(std::vector<asio::const_buffer>& buffers, Adaptor& adaptor)
|
||||
// THIS METHOD DOES MODIFY THE BODY, AS IN IT EMPTIES IT
|
||||
template<typename Adaptor>
|
||||
void write_streamed_string(std::string& is, Adaptor& adaptor)
|
||||
{
|
||||
std::string buf;
|
||||
std::vector<asio::const_buffer> buffers;
|
||||
|
||||
while (is.length() > 16384)
|
||||
{
|
||||
boost::asio::write(adaptor.socket(), buffers, [this](std::error_code ec, std::size_t)
|
||||
{
|
||||
//buf.reserve(16385);
|
||||
buf = is.substr(0, 16384);
|
||||
is = is.substr(16384);
|
||||
push_and_write(buffers, buf, adaptor);
|
||||
}
|
||||
// Collect whatever is left (less than 16KB) and send it down the socket
|
||||
// buf.reserve(is.length());
|
||||
buf = is;
|
||||
is.clear();
|
||||
push_and_write(buffers, buf, adaptor);
|
||||
}
|
||||
|
||||
template<typename Adaptor>
|
||||
inline void push_and_write(std::vector<asio::const_buffer>& buffers, std::string& buf, Adaptor& adaptor)
|
||||
{
|
||||
buffers.clear();
|
||||
buffers.push_back(boost::asio::buffer(buf));
|
||||
write_buffer_list(buffers, adaptor);
|
||||
}
|
||||
|
||||
template<typename Adaptor>
|
||||
inline void write_buffer_list(std::vector<asio::const_buffer>& buffers, Adaptor& adaptor)
|
||||
{
|
||||
boost::asio::write(adaptor.socket(), buffers, [this](std::error_code ec, std::size_t)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
return false;
|
||||
@ -317,9 +324,7 @@ namespace crow
|
||||
CROW_LOG_ERROR << ec << " - happened while sending buffers";
|
||||
this->end();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} });
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "crow/version.h"
|
||||
@ -23,22 +22,22 @@ namespace crow
|
||||
using namespace boost;
|
||||
using tcp = asio::ip::tcp;
|
||||
|
||||
template <typename Handler, typename Adaptor = SocketAdaptor, typename ... Middlewares>
|
||||
template<typename Handler, typename Adaptor = SocketAdaptor, typename... Middlewares>
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
Server(Handler* handler, std::string bindaddr, uint16_t port, std::string server_name = std::string("Crow/") + VERSION, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, uint8_t timeout = 5, typename Adaptor::context* adaptor_ctx = nullptr)
|
||||
: acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)),
|
||||
signals_(io_service_, SIGINT, SIGTERM),
|
||||
tick_timer_(io_service_),
|
||||
handler_(handler),
|
||||
concurrency_(concurrency == 0 ? 1 : concurrency),
|
||||
timeout_(timeout),
|
||||
server_name_(server_name),
|
||||
port_(port),
|
||||
bindaddr_(bindaddr),
|
||||
middlewares_(middlewares),
|
||||
adaptor_ctx_(adaptor_ctx)
|
||||
Server(Handler* handler, std::string bindaddr, uint16_t port, std::string server_name = std::string("Crow/") + VERSION, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, uint8_t timeout = 5, typename Adaptor::context* adaptor_ctx = nullptr):
|
||||
acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)),
|
||||
signals_(io_service_, SIGINT, SIGTERM),
|
||||
tick_timer_(io_service_),
|
||||
handler_(handler),
|
||||
concurrency_(concurrency == 0 ? 1 : concurrency),
|
||||
timeout_(timeout),
|
||||
server_name_(server_name),
|
||||
port_(port),
|
||||
bindaddr_(bindaddr),
|
||||
middlewares_(middlewares),
|
||||
adaptor_ctx_(adaptor_ctx)
|
||||
{
|
||||
}
|
||||
|
||||
@ -53,116 +52,121 @@ namespace crow
|
||||
tick_function_();
|
||||
tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count()));
|
||||
tick_timer_.async_wait([this](const boost::system::error_code& ec)
|
||||
{
|
||||
{
|
||||
if (ec)
|
||||
return;
|
||||
on_tick();
|
||||
});
|
||||
on_tick(); });
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
for(int i = 0; i < concurrency_; i++)
|
||||
for (int i = 0; i < concurrency_; i++)
|
||||
io_service_pool_.emplace_back(new boost::asio::io_service());
|
||||
get_cached_date_str_pool_.resize(concurrency_);
|
||||
task_timer_pool_.resize(concurrency_);
|
||||
|
||||
std::vector<std::future<void>> v;
|
||||
std::atomic<int> init_count(0);
|
||||
for(uint16_t i = 0; i < concurrency_; i ++)
|
||||
for (uint16_t i = 0; i < concurrency_; i++)
|
||||
v.push_back(
|
||||
std::async(std::launch::async, [this, i, &init_count]{
|
||||
std::async(
|
||||
std::launch::async, [this, i, &init_count]
|
||||
{
|
||||
|
||||
// thread local date string get function
|
||||
auto last = std::chrono::steady_clock::now();
|
||||
// thread local date string get function
|
||||
auto last = std::chrono::steady_clock::now();
|
||||
|
||||
std::string date_str;
|
||||
auto update_date_str = [&]
|
||||
{
|
||||
auto last_time_t = time(0);
|
||||
tm my_tm;
|
||||
std::string date_str;
|
||||
auto update_date_str = [&]
|
||||
{
|
||||
auto last_time_t = time(0);
|
||||
tm my_tm;
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
gmtime_s(&my_tm, &last_time_t);
|
||||
gmtime_s(&my_tm, &last_time_t);
|
||||
#else
|
||||
gmtime_r(&last_time_t, &my_tm);
|
||||
gmtime_r(&last_time_t, &my_tm);
|
||||
#endif
|
||||
date_str.resize(100);
|
||||
size_t date_str_sz = strftime(&date_str[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &my_tm);
|
||||
date_str.resize(date_str_sz);
|
||||
};
|
||||
update_date_str();
|
||||
get_cached_date_str_pool_[i] = [&]()->std::string
|
||||
date_str.resize(100);
|
||||
size_t date_str_sz = strftime(&date_str[0], 99, "%a, %d %b %Y %H:%M:%S GMT", &my_tm);
|
||||
date_str.resize(date_str_sz);
|
||||
};
|
||||
update_date_str();
|
||||
get_cached_date_str_pool_[i] = [&]()->std::string
|
||||
{
|
||||
if (std::chrono::steady_clock::now() - last >= std::chrono::seconds(1))
|
||||
{
|
||||
if (std::chrono::steady_clock::now() - last >= std::chrono::seconds(1))
|
||||
{
|
||||
last = std::chrono::steady_clock::now();
|
||||
update_date_str();
|
||||
}
|
||||
return date_str;
|
||||
};
|
||||
|
||||
// initializing task timers
|
||||
detail::task_timer task_timer(*io_service_pool_[i]);
|
||||
task_timer.set_default_timeout(timeout_);
|
||||
task_timer_pool_[i] = &task_timer;
|
||||
|
||||
init_count ++;
|
||||
while(1)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (io_service_pool_[i]->run() == 0)
|
||||
{
|
||||
// when io_service.run returns 0, there are no more works to do.
|
||||
break;
|
||||
}
|
||||
} catch(std::exception& e)
|
||||
{
|
||||
CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what();
|
||||
}
|
||||
last = std::chrono::steady_clock::now();
|
||||
update_date_str();
|
||||
}
|
||||
}));
|
||||
return date_str;
|
||||
};
|
||||
|
||||
// initializing task timers
|
||||
detail::task_timer task_timer(*io_service_pool_[i]);
|
||||
task_timer.set_default_timeout(timeout_);
|
||||
task_timer_pool_[i] = &task_timer;
|
||||
|
||||
init_count ++;
|
||||
while(1)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (io_service_pool_[i]->run() == 0)
|
||||
{
|
||||
// when io_service.run returns 0, there are no more works to do.
|
||||
break;
|
||||
}
|
||||
} catch(std::exception& e)
|
||||
{
|
||||
CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what();
|
||||
}
|
||||
} }));
|
||||
|
||||
if (tick_function_ && tick_interval_.count() > 0)
|
||||
{
|
||||
tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count()));
|
||||
tick_timer_.async_wait([this](const boost::system::error_code& ec)
|
||||
{
|
||||
if (ec)
|
||||
return;
|
||||
on_tick();
|
||||
});
|
||||
tick_timer_.async_wait(
|
||||
[this](const boost::system::error_code& ec)
|
||||
{
|
||||
if (ec)
|
||||
return;
|
||||
on_tick();
|
||||
});
|
||||
}
|
||||
|
||||
port_ = acceptor_.local_endpoint().port();
|
||||
handler_->port(port_);
|
||||
|
||||
|
||||
CROW_LOG_INFO << server_name_ << " server is running at " << (handler_->ssl_used() ? "https://" : "http://") << bindaddr_ <<":" << acceptor_.local_endpoint().port()
|
||||
CROW_LOG_INFO << server_name_ << " server is running at " << (handler_->ssl_used() ? "https://" : "http://") << bindaddr_ << ":" << acceptor_.local_endpoint().port()
|
||||
<< " using " << concurrency_ << " threads";
|
||||
CROW_LOG_INFO << "Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs.";
|
||||
|
||||
signals_.async_wait(
|
||||
[&](const boost::system::error_code& /*error*/, int /*signal_number*/){
|
||||
stop();
|
||||
});
|
||||
[&](const boost::system::error_code& /*error*/, int /*signal_number*/)
|
||||
{
|
||||
stop();
|
||||
});
|
||||
|
||||
while(concurrency_ != init_count)
|
||||
while (concurrency_ != init_count)
|
||||
std::this_thread::yield();
|
||||
|
||||
do_accept();
|
||||
|
||||
std::thread([this]{
|
||||
io_service_.run();
|
||||
CROW_LOG_INFO << "Exiting.";
|
||||
}).join();
|
||||
std::thread(
|
||||
[this]
|
||||
{
|
||||
io_service_.run();
|
||||
CROW_LOG_INFO << "Exiting.";
|
||||
})
|
||||
.join();
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
io_service_.stop();
|
||||
for(auto& io_service:io_service_pool_)
|
||||
for (auto& io_service : io_service_pool_)
|
||||
io_service->stop();
|
||||
}
|
||||
|
||||
@ -190,24 +194,24 @@ namespace crow
|
||||
{
|
||||
asio::io_service& is = pick_io_service();
|
||||
auto p = new Connection<Adaptor, Handler, Middlewares...>(
|
||||
is, handler_, server_name_, middlewares_,
|
||||
get_cached_date_str_pool_[roundrobin_index_], *task_timer_pool_[roundrobin_index_], adaptor_ctx_);
|
||||
acceptor_.async_accept(p->socket(),
|
||||
[this, p, &is](boost::system::error_code ec)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
is.post([p]
|
||||
{
|
||||
p->start();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
do_accept();
|
||||
});
|
||||
is, handler_, server_name_, middlewares_,
|
||||
get_cached_date_str_pool_[roundrobin_index_], *task_timer_pool_[roundrobin_index_], adaptor_ctx_);
|
||||
acceptor_.async_accept(
|
||||
p->socket(),
|
||||
[this, p, &is](boost::system::error_code ec)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
is.post(
|
||||
[p]
|
||||
{ p->start(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
@ -234,4 +238,4 @@ namespace crow
|
||||
|
||||
typename Adaptor::context* adaptor_ctx_;
|
||||
};
|
||||
}
|
||||
} // namespace crow
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,115 +30,124 @@ namespace crow
|
||||
Critical,
|
||||
};
|
||||
|
||||
class ILogHandler {
|
||||
public:
|
||||
virtual void log(std::string message, LogLevel level) = 0;
|
||||
class ILogHandler
|
||||
{
|
||||
public:
|
||||
virtual void log(std::string message, LogLevel level) = 0;
|
||||
};
|
||||
|
||||
class CerrLogHandler : public ILogHandler {
|
||||
public:
|
||||
void log(std::string message, LogLevel /*level*/) override {
|
||||
std::cerr << message;
|
||||
}
|
||||
class CerrLogHandler : public ILogHandler
|
||||
{
|
||||
public:
|
||||
void log(std::string message, LogLevel /*level*/) override
|
||||
{
|
||||
std::cerr << message;
|
||||
}
|
||||
};
|
||||
|
||||
class logger {
|
||||
class logger
|
||||
{
|
||||
|
||||
private:
|
||||
//
|
||||
static std::string timestamp()
|
||||
{
|
||||
char date[32];
|
||||
time_t t = time(0);
|
||||
private:
|
||||
//
|
||||
static std::string timestamp()
|
||||
{
|
||||
char date[32];
|
||||
time_t t = time(0);
|
||||
|
||||
tm my_tm;
|
||||
tm my_tm;
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
gmtime_s(&my_tm, &t);
|
||||
gmtime_s(&my_tm, &t);
|
||||
#else
|
||||
gmtime_r(&t, &my_tm);
|
||||
gmtime_r(&t, &my_tm);
|
||||
#endif
|
||||
|
||||
size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &my_tm);
|
||||
return std::string(date, date+sz);
|
||||
}
|
||||
size_t sz = strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", &my_tm);
|
||||
return std::string(date, date + sz);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
|
||||
logger(std::string prefix, LogLevel level) : level_(level) {
|
||||
#ifdef CROW_ENABLE_LOGGING
|
||||
stringstream_ << "(" << timestamp() << ") [" << prefix << "] ";
|
||||
#endif
|
||||
|
||||
}
|
||||
~logger() {
|
||||
#ifdef CROW_ENABLE_LOGGING
|
||||
if(level_ >= get_current_log_level()) {
|
||||
stringstream_ << std::endl;
|
||||
get_handler_ref()->log(stringstream_.str(), level_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
template <typename T>
|
||||
logger& operator<<(T const &value) {
|
||||
|
||||
#ifdef CROW_ENABLE_LOGGING
|
||||
if(level_ >= get_current_log_level()) {
|
||||
stringstream_ << value;
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
//
|
||||
static void setLogLevel(LogLevel level) {
|
||||
get_log_level_ref() = level;
|
||||
}
|
||||
|
||||
static void setHandler(ILogHandler* handler) {
|
||||
get_handler_ref() = handler;
|
||||
}
|
||||
|
||||
static LogLevel get_current_log_level() {
|
||||
return get_log_level_ref();
|
||||
}
|
||||
|
||||
private:
|
||||
//
|
||||
static LogLevel& get_log_level_ref()
|
||||
public:
|
||||
logger(std::string prefix, LogLevel level):
|
||||
level_(level)
|
||||
{
|
||||
#ifdef CROW_ENABLE_LOGGING
|
||||
stringstream_ << "(" << timestamp() << ") [" << prefix << "] ";
|
||||
#endif
|
||||
}
|
||||
~logger()
|
||||
{
|
||||
#ifdef CROW_ENABLE_LOGGING
|
||||
if (level_ >= get_current_log_level())
|
||||
{
|
||||
static LogLevel current_level = static_cast<LogLevel>(CROW_LOG_LEVEL);
|
||||
return current_level;
|
||||
}
|
||||
static ILogHandler*& get_handler_ref()
|
||||
{
|
||||
static CerrLogHandler default_handler;
|
||||
static ILogHandler* current_handler = &default_handler;
|
||||
return current_handler;
|
||||
stringstream_ << std::endl;
|
||||
get_handler_ref()->log(stringstream_.str(), level_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
std::ostringstream stringstream_;
|
||||
LogLevel level_;
|
||||
//
|
||||
template<typename T>
|
||||
logger& operator<<(T const& value)
|
||||
{
|
||||
|
||||
#ifdef CROW_ENABLE_LOGGING
|
||||
if (level_ >= get_current_log_level())
|
||||
{
|
||||
stringstream_ << value;
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
//
|
||||
static void setLogLevel(LogLevel level)
|
||||
{
|
||||
get_log_level_ref() = level;
|
||||
}
|
||||
|
||||
static void setHandler(ILogHandler* handler)
|
||||
{
|
||||
get_handler_ref() = handler;
|
||||
}
|
||||
|
||||
static LogLevel get_current_log_level()
|
||||
{
|
||||
return get_log_level_ref();
|
||||
}
|
||||
|
||||
private:
|
||||
//
|
||||
static LogLevel& get_log_level_ref()
|
||||
{
|
||||
static LogLevel current_level = static_cast<LogLevel>(CROW_LOG_LEVEL);
|
||||
return current_level;
|
||||
}
|
||||
static ILogHandler*& get_handler_ref()
|
||||
{
|
||||
static CerrLogHandler default_handler;
|
||||
static ILogHandler* current_handler = &default_handler;
|
||||
return current_handler;
|
||||
}
|
||||
|
||||
//
|
||||
std::ostringstream stringstream_;
|
||||
LogLevel level_;
|
||||
};
|
||||
}
|
||||
|
||||
#define CROW_LOG_CRITICAL \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \
|
||||
crow::logger("CRITICAL", crow::LogLevel::Critical)
|
||||
#define CROW_LOG_ERROR \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \
|
||||
crow::logger("ERROR ", crow::LogLevel::Error)
|
||||
#define CROW_LOG_WARNING \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \
|
||||
crow::logger("WARNING ", crow::LogLevel::Warning)
|
||||
#define CROW_LOG_INFO \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \
|
||||
crow::logger("INFO ", crow::LogLevel::Info)
|
||||
#define CROW_LOG_DEBUG \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \
|
||||
crow::logger("DEBUG ", crow::LogLevel::Debug)
|
||||
} // namespace crow
|
||||
|
||||
#define CROW_LOG_CRITICAL \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Critical) \
|
||||
crow::logger("CRITICAL", crow::LogLevel::Critical)
|
||||
#define CROW_LOG_ERROR \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Error) \
|
||||
crow::logger("ERROR ", crow::LogLevel::Error)
|
||||
#define CROW_LOG_WARNING \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Warning) \
|
||||
crow::logger("WARNING ", crow::LogLevel::Warning)
|
||||
#define CROW_LOG_INFO \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Info) \
|
||||
crow::logger("INFO ", crow::LogLevel::Info)
|
||||
#define CROW_LOG_DEBUG \
|
||||
if (crow::logger::get_current_log_level() <= crow::LogLevel::Debug) \
|
||||
crow::logger("DEBUG ", crow::LogLevel::Debug)
|
||||
|
@ -10,16 +10,14 @@ namespace crow
|
||||
{
|
||||
|
||||
|
||||
template <typename ... Middlewares>
|
||||
struct partial_context
|
||||
: public black_magic::pop_back<Middlewares...>::template rebind<partial_context>
|
||||
, public black_magic::last_element_type<Middlewares...>::type::context
|
||||
template<typename... Middlewares>
|
||||
struct partial_context : public black_magic::pop_back<Middlewares...>::template rebind<partial_context>, public black_magic::last_element_type<Middlewares...>::type::context
|
||||
{
|
||||
using parent_context = typename black_magic::pop_back<Middlewares...>::template rebind<::crow::detail::partial_context>;
|
||||
template <int N>
|
||||
using partial = typename std::conditional<N == sizeof...(Middlewares)-1, partial_context, typename parent_context::template partial<N>>::type;
|
||||
template<int N>
|
||||
using partial = typename std::conditional<N == sizeof...(Middlewares) - 1, partial_context, typename parent_context::template partial<N>>::type;
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
typename T::context& get()
|
||||
{
|
||||
return static_cast<typename T::context&>(*this);
|
||||
@ -28,40 +26,40 @@ namespace crow
|
||||
|
||||
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct partial_context<>
|
||||
{
|
||||
template <int>
|
||||
template<int>
|
||||
using partial = partial_context;
|
||||
};
|
||||
|
||||
|
||||
|
||||
template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares>
|
||||
template<int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares>
|
||||
bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
|
||||
|
||||
|
||||
|
||||
template <typename ... Middlewares>
|
||||
template<typename... Middlewares>
|
||||
struct context : private partial_context<Middlewares...>
|
||||
//struct context : private Middlewares::context... // simple but less type-safe
|
||||
{
|
||||
template <int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N==0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
|
||||
template <int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N>0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
|
||||
template<int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N == 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
|
||||
template<int N, typename Context, typename Container>
|
||||
friend typename std::enable_if<(N > 0)>::type after_handlers_call_helper(Container& middlewares, Context& ctx, request& req, response& res);
|
||||
|
||||
template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares2>
|
||||
template<int N, typename Context, typename Container, typename CurrentMW, typename... Middlewares2>
|
||||
friend bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx);
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
typename T::context& get()
|
||||
{
|
||||
return static_cast<typename T::context&>(*this);
|
||||
}
|
||||
|
||||
template <int N>
|
||||
template<int N>
|
||||
using partial = typename partial_context<Middlewares...>::template partial<N>;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace crow
|
||||
|
@ -12,7 +12,7 @@ namespace crow
|
||||
|
||||
// before_handle
|
||||
// called before handling the request.
|
||||
// if res.end() is called, the operation is halted.
|
||||
// if res.end() is called, the operation is halted.
|
||||
// (still call after_handle of this middleware)
|
||||
// 2 signatures:
|
||||
// void before_handle(request& req, response& res, context& ctx)
|
||||
@ -62,25 +62,26 @@ namespace crow
|
||||
}
|
||||
std::string cookies = req.get_header_value("Cookie");
|
||||
size_t pos = 0;
|
||||
while(pos < cookies.size())
|
||||
while (pos < cookies.size())
|
||||
{
|
||||
size_t pos_equal = cookies.find('=', pos);
|
||||
if (pos_equal == cookies.npos)
|
||||
break;
|
||||
std::string name = cookies.substr(pos, pos_equal-pos);
|
||||
std::string name = cookies.substr(pos, pos_equal - pos);
|
||||
boost::trim(name);
|
||||
pos = pos_equal+1;
|
||||
while(pos < cookies.size() && cookies[pos] == ' ') pos++;
|
||||
pos = pos_equal + 1;
|
||||
while (pos < cookies.size() && cookies[pos] == ' ')
|
||||
pos++;
|
||||
if (pos == cookies.size())
|
||||
break;
|
||||
|
||||
size_t pos_semicolon = cookies.find(';', pos);
|
||||
std::string value = cookies.substr(pos, pos_semicolon-pos);
|
||||
std::string value = cookies.substr(pos, pos_semicolon - pos);
|
||||
|
||||
boost::trim(value);
|
||||
if (value[0] == '"' && value[value.size()-1] == '"')
|
||||
if (value[0] == '"' && value[value.size() - 1] == '"')
|
||||
{
|
||||
value = value.substr(1, value.size()-2);
|
||||
value = value.substr(1, value.size() - 2);
|
||||
}
|
||||
|
||||
ctx.jar.emplace(std::move(name), std::move(value));
|
||||
@ -89,13 +90,14 @@ namespace crow
|
||||
if (pos == cookies.npos)
|
||||
break;
|
||||
pos++;
|
||||
while(pos < cookies.size() && cookies[pos] == ' ') pos++;
|
||||
while (pos < cookies.size() && cookies[pos] == ' ')
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
void after_handle(request& /*req*/, response& res, context& ctx)
|
||||
{
|
||||
for(auto& cookie:ctx.cookies_to_add)
|
||||
for (auto& cookie : ctx.cookies_to_add)
|
||||
{
|
||||
if (cookie.second.empty())
|
||||
res.add_header("Set-Cookie", cookie.first + "=\"\"");
|
||||
@ -128,4 +130,4 @@ namespace crow
|
||||
|
||||
SimpleApp
|
||||
*/
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -24,4 +24,4 @@ namespace crow
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -1,116 +1,116 @@
|
||||
//This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py
|
||||
// This file is generated from nginx/conf/mime.types using nginx_mime2cpp.py
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
namespace crow {
|
||||
const std::unordered_map<std::string, std::string> mime_types {
|
||||
{"shtml", "text/html"},
|
||||
{"htm", "text/html"},
|
||||
{"html", "text/html"},
|
||||
{"css", "text/css"},
|
||||
{"xml", "text/xml"},
|
||||
{"gif", "image/gif"},
|
||||
{"jpg", "image/jpeg"},
|
||||
{"jpeg", "image/jpeg"},
|
||||
{"js", "application/javascript"},
|
||||
{"atom", "application/atom+xml"},
|
||||
{"rss", "application/rss+xml"},
|
||||
{"mml", "text/mathml"},
|
||||
{"txt", "text/plain"},
|
||||
{"jad", "text/vnd.sun.j2me.app-descriptor"},
|
||||
{"wml", "text/vnd.wap.wml"},
|
||||
{"htc", "text/x-component"},
|
||||
{"png", "image/png"},
|
||||
{"svgz", "image/svg+xml"},
|
||||
{"svg", "image/svg+xml"},
|
||||
{"tiff", "image/tiff"},
|
||||
{"tif", "image/tiff"},
|
||||
{"wbmp", "image/vnd.wap.wbmp"},
|
||||
{"webp", "image/webp"},
|
||||
{"ico", "image/x-icon"},
|
||||
{"jng", "image/x-jng"},
|
||||
{"bmp", "image/x-ms-bmp"},
|
||||
{"woff", "font/woff"},
|
||||
{"woff2", "font/woff2"},
|
||||
{"ear", "application/java-archive"},
|
||||
{"war", "application/java-archive"},
|
||||
{"jar", "application/java-archive"},
|
||||
{"json", "application/json"},
|
||||
{"hqx", "application/mac-binhex40"},
|
||||
{"doc", "application/msword"},
|
||||
{"pdf", "application/pdf"},
|
||||
{"ai", "application/postscript"},
|
||||
{"eps", "application/postscript"},
|
||||
{"ps", "application/postscript"},
|
||||
{"rtf", "application/rtf"},
|
||||
{"m3u8", "application/vnd.apple.mpegurl"},
|
||||
{"kml", "application/vnd.google-earth.kml+xml"},
|
||||
{"kmz", "application/vnd.google-earth.kmz"},
|
||||
{"xls", "application/vnd.ms-excel"},
|
||||
{"eot", "application/vnd.ms-fontobject"},
|
||||
{"ppt", "application/vnd.ms-powerpoint"},
|
||||
{"odg", "application/vnd.oasis.opendocument.graphics"},
|
||||
{"odp", "application/vnd.oasis.opendocument.presentation"},
|
||||
{"ods", "application/vnd.oasis.opendocument.spreadsheet"},
|
||||
{"odt", "application/vnd.oasis.opendocument.text"},
|
||||
{"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
|
||||
{"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
|
||||
{"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
|
||||
{"wmlc", "application/vnd.wap.wmlc"},
|
||||
{"7z", "application/x-7z-compressed"},
|
||||
{"cco", "application/x-cocoa"},
|
||||
{"jardiff", "application/x-java-archive-diff"},
|
||||
{"jnlp", "application/x-java-jnlp-file"},
|
||||
{"run", "application/x-makeself"},
|
||||
{"pm", "application/x-perl"},
|
||||
{"pl", "application/x-perl"},
|
||||
{"pdb", "application/x-pilot"},
|
||||
{"prc", "application/x-pilot"},
|
||||
{"rar", "application/x-rar-compressed"},
|
||||
{"rpm", "application/x-redhat-package-manager"},
|
||||
{"sea", "application/x-sea"},
|
||||
{"swf", "application/x-shockwave-flash"},
|
||||
{"sit", "application/x-stuffit"},
|
||||
{"tk", "application/x-tcl"},
|
||||
{"tcl", "application/x-tcl"},
|
||||
{"crt", "application/x-x509-ca-cert"},
|
||||
{"pem", "application/x-x509-ca-cert"},
|
||||
{"der", "application/x-x509-ca-cert"},
|
||||
{"xpi", "application/x-xpinstall"},
|
||||
{"xhtml", "application/xhtml+xml"},
|
||||
{"xspf", "application/xspf+xml"},
|
||||
{"zip", "application/zip"},
|
||||
{"dll", "application/octet-stream"},
|
||||
{"exe", "application/octet-stream"},
|
||||
{"bin", "application/octet-stream"},
|
||||
{"deb", "application/octet-stream"},
|
||||
{"dmg", "application/octet-stream"},
|
||||
{"img", "application/octet-stream"},
|
||||
{"iso", "application/octet-stream"},
|
||||
{"msm", "application/octet-stream"},
|
||||
{"msp", "application/octet-stream"},
|
||||
{"msi", "application/octet-stream"},
|
||||
{"kar", "audio/midi"},
|
||||
{"midi", "audio/midi"},
|
||||
{"mid", "audio/midi"},
|
||||
{"mp3", "audio/mpeg"},
|
||||
{"ogg", "audio/ogg"},
|
||||
{"m4a", "audio/x-m4a"},
|
||||
{"ra", "audio/x-realaudio"},
|
||||
{"3gp", "video/3gpp"},
|
||||
{"3gpp", "video/3gpp"},
|
||||
{"ts", "video/mp2t"},
|
||||
{"mp4", "video/mp4"},
|
||||
{"mpg", "video/mpeg"},
|
||||
{"mpeg", "video/mpeg"},
|
||||
{"mov", "video/quicktime"},
|
||||
{"webm", "video/webm"},
|
||||
{"flv", "video/x-flv"},
|
||||
{"m4v", "video/x-m4v"},
|
||||
{"mng", "video/x-mng"},
|
||||
{"asf", "video/x-ms-asf"},
|
||||
{"asx", "video/x-ms-asf"},
|
||||
{"wmv", "video/x-ms-wmv"},
|
||||
{"avi", "video/x-msvideo"}
|
||||
};
|
||||
namespace crow
|
||||
{
|
||||
const std::unordered_map<std::string, std::string> mime_types{
|
||||
{"shtml", "text/html"},
|
||||
{"htm", "text/html"},
|
||||
{"html", "text/html"},
|
||||
{"css", "text/css"},
|
||||
{"xml", "text/xml"},
|
||||
{"gif", "image/gif"},
|
||||
{"jpg", "image/jpeg"},
|
||||
{"jpeg", "image/jpeg"},
|
||||
{"js", "application/javascript"},
|
||||
{"atom", "application/atom+xml"},
|
||||
{"rss", "application/rss+xml"},
|
||||
{"mml", "text/mathml"},
|
||||
{"txt", "text/plain"},
|
||||
{"jad", "text/vnd.sun.j2me.app-descriptor"},
|
||||
{"wml", "text/vnd.wap.wml"},
|
||||
{"htc", "text/x-component"},
|
||||
{"png", "image/png"},
|
||||
{"svgz", "image/svg+xml"},
|
||||
{"svg", "image/svg+xml"},
|
||||
{"tiff", "image/tiff"},
|
||||
{"tif", "image/tiff"},
|
||||
{"wbmp", "image/vnd.wap.wbmp"},
|
||||
{"webp", "image/webp"},
|
||||
{"ico", "image/x-icon"},
|
||||
{"jng", "image/x-jng"},
|
||||
{"bmp", "image/x-ms-bmp"},
|
||||
{"woff", "font/woff"},
|
||||
{"woff2", "font/woff2"},
|
||||
{"ear", "application/java-archive"},
|
||||
{"war", "application/java-archive"},
|
||||
{"jar", "application/java-archive"},
|
||||
{"json", "application/json"},
|
||||
{"hqx", "application/mac-binhex40"},
|
||||
{"doc", "application/msword"},
|
||||
{"pdf", "application/pdf"},
|
||||
{"ai", "application/postscript"},
|
||||
{"eps", "application/postscript"},
|
||||
{"ps", "application/postscript"},
|
||||
{"rtf", "application/rtf"},
|
||||
{"m3u8", "application/vnd.apple.mpegurl"},
|
||||
{"kml", "application/vnd.google-earth.kml+xml"},
|
||||
{"kmz", "application/vnd.google-earth.kmz"},
|
||||
{"xls", "application/vnd.ms-excel"},
|
||||
{"eot", "application/vnd.ms-fontobject"},
|
||||
{"ppt", "application/vnd.ms-powerpoint"},
|
||||
{"odg", "application/vnd.oasis.opendocument.graphics"},
|
||||
{"odp", "application/vnd.oasis.opendocument.presentation"},
|
||||
{"ods", "application/vnd.oasis.opendocument.spreadsheet"},
|
||||
{"odt", "application/vnd.oasis.opendocument.text"},
|
||||
{"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
|
||||
{"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
|
||||
{"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
|
||||
{"wmlc", "application/vnd.wap.wmlc"},
|
||||
{"7z", "application/x-7z-compressed"},
|
||||
{"cco", "application/x-cocoa"},
|
||||
{"jardiff", "application/x-java-archive-diff"},
|
||||
{"jnlp", "application/x-java-jnlp-file"},
|
||||
{"run", "application/x-makeself"},
|
||||
{"pm", "application/x-perl"},
|
||||
{"pl", "application/x-perl"},
|
||||
{"pdb", "application/x-pilot"},
|
||||
{"prc", "application/x-pilot"},
|
||||
{"rar", "application/x-rar-compressed"},
|
||||
{"rpm", "application/x-redhat-package-manager"},
|
||||
{"sea", "application/x-sea"},
|
||||
{"swf", "application/x-shockwave-flash"},
|
||||
{"sit", "application/x-stuffit"},
|
||||
{"tk", "application/x-tcl"},
|
||||
{"tcl", "application/x-tcl"},
|
||||
{"crt", "application/x-x509-ca-cert"},
|
||||
{"pem", "application/x-x509-ca-cert"},
|
||||
{"der", "application/x-x509-ca-cert"},
|
||||
{"xpi", "application/x-xpinstall"},
|
||||
{"xhtml", "application/xhtml+xml"},
|
||||
{"xspf", "application/xspf+xml"},
|
||||
{"zip", "application/zip"},
|
||||
{"dll", "application/octet-stream"},
|
||||
{"exe", "application/octet-stream"},
|
||||
{"bin", "application/octet-stream"},
|
||||
{"deb", "application/octet-stream"},
|
||||
{"dmg", "application/octet-stream"},
|
||||
{"img", "application/octet-stream"},
|
||||
{"iso", "application/octet-stream"},
|
||||
{"msm", "application/octet-stream"},
|
||||
{"msp", "application/octet-stream"},
|
||||
{"msi", "application/octet-stream"},
|
||||
{"kar", "audio/midi"},
|
||||
{"midi", "audio/midi"},
|
||||
{"mid", "audio/midi"},
|
||||
{"mp3", "audio/mpeg"},
|
||||
{"ogg", "audio/ogg"},
|
||||
{"m4a", "audio/x-m4a"},
|
||||
{"ra", "audio/x-realaudio"},
|
||||
{"3gp", "video/3gpp"},
|
||||
{"3gpp", "video/3gpp"},
|
||||
{"ts", "video/mp2t"},
|
||||
{"mp4", "video/mp4"},
|
||||
{"mpg", "video/mpeg"},
|
||||
{"mpeg", "video/mpeg"},
|
||||
{"mov", "video/quicktime"},
|
||||
{"webm", "video/webm"},
|
||||
{"flv", "video/x-flv"},
|
||||
{"m4v", "video/x-m4v"},
|
||||
{"mng", "video/x-mng"},
|
||||
{"asf", "video/x-ms-asf"},
|
||||
{"asx", "video/x-ms-asf"},
|
||||
{"wmv", "video/x-ms-wmv"},
|
||||
{"avi", "video/x-msvideo"}};
|
||||
}
|
||||
|
@ -9,34 +9,33 @@
|
||||
|
||||
namespace crow
|
||||
{
|
||||
///Encapsulates anything related to processing and organizing `multipart/xyz` messages
|
||||
/// Encapsulates anything related to processing and organizing `multipart/xyz` messages
|
||||
namespace multipart
|
||||
{
|
||||
const std::string dd = "--";
|
||||
const std::string crlf = "\r\n";
|
||||
|
||||
///The first part in a section, contains metadata about the part
|
||||
/// The first part in a section, contains metadata about the part
|
||||
struct header
|
||||
{
|
||||
std::pair<std::string, std::string> value; ///< The first part of the header, usually `Content-Type` or `Content-Disposition`
|
||||
std::unordered_map<std::string, std::string> params; ///< The parameters of the header, come after the `value`
|
||||
std::pair<std::string, std::string> value; ///< The first part of the header, usually `Content-Type` or `Content-Disposition`
|
||||
std::unordered_map<std::string, std::string> params; ///< The parameters of the header, come after the `value`
|
||||
};
|
||||
|
||||
///One part of the multipart message
|
||||
|
||||
///It is usually separated from other sections by a `boundary`
|
||||
///
|
||||
/// It is usually separated from other sections by a `boundary`
|
||||
struct part
|
||||
{
|
||||
std::vector<header> headers; ///< (optional) The first part before the data, Contains information regarding the type of data and encoding
|
||||
std::string body; ///< The actual data in the part
|
||||
std::string body; ///< The actual data in the part
|
||||
};
|
||||
|
||||
///The parsed multipart request/response
|
||||
/// The parsed multipart request/response
|
||||
struct message : public returnable
|
||||
{
|
||||
ci_map headers;
|
||||
std::string boundary; ///< The text boundary that separates different `parts`
|
||||
std::string boundary; ///< The text boundary that separates different `parts`
|
||||
std::vector<part> parts; ///< The individual parts of the message
|
||||
|
||||
const std::string& get_header_value(const std::string& key) const
|
||||
@ -44,13 +43,13 @@ namespace crow
|
||||
return crow::get_header_value(headers, key);
|
||||
}
|
||||
|
||||
///Represent all parts as a string (**does not include message headers**)
|
||||
/// Represent all parts as a string (**does not include message headers**)
|
||||
std::string dump() const override
|
||||
{
|
||||
std::stringstream str;
|
||||
std::string delimiter = dd + boundary;
|
||||
|
||||
for (unsigned i=0 ; i<parts.size(); i++)
|
||||
for (unsigned i = 0; i < parts.size(); i++)
|
||||
{
|
||||
str << delimiter << crlf;
|
||||
str << dump(i);
|
||||
@ -59,15 +58,15 @@ namespace crow
|
||||
return str.str();
|
||||
}
|
||||
|
||||
///Represent an individual part as a string
|
||||
/// Represent an individual part as a string
|
||||
std::string dump(int part_) const
|
||||
{
|
||||
std::stringstream str;
|
||||
part item = parts[part_];
|
||||
for (header item_h: item.headers)
|
||||
for (header item_h : item.headers)
|
||||
{
|
||||
str << item_h.value.first << ": " << item_h.value.second;
|
||||
for (auto& it: item_h.params)
|
||||
for (auto& it : item_h.params)
|
||||
{
|
||||
str << "; " << it.first << '=' << pad(it.second);
|
||||
}
|
||||
@ -78,62 +77,61 @@ namespace crow
|
||||
return str.str();
|
||||
}
|
||||
|
||||
///Default constructor using default values
|
||||
message(const ci_map& headers, const std::string& boundary, const std::vector<part>& sections)
|
||||
: returnable("multipart/form-data"), headers(headers), boundary(boundary), parts(sections){}
|
||||
/// Default constructor using default values
|
||||
message(const ci_map& headers, const std::string& boundary, const std::vector<part>& sections):
|
||||
returnable("multipart/form-data"), headers(headers), boundary(boundary), parts(sections) {}
|
||||
|
||||
///Create a multipart message from a request data
|
||||
message(const request& req)
|
||||
: returnable("multipart/form-data"),
|
||||
headers(req.headers),
|
||||
boundary(get_boundary(get_header_value("Content-Type"))),
|
||||
parts(parse_body(req.body))
|
||||
{}
|
||||
|
||||
private:
|
||||
/// Create a multipart message from a request data
|
||||
message(const request& req):
|
||||
returnable("multipart/form-data"),
|
||||
headers(req.headers),
|
||||
boundary(get_boundary(get_header_value("Content-Type"))),
|
||||
parts(parse_body(req.body))
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
std::string get_boundary(const std::string& header) const
|
||||
{
|
||||
size_t found = header.find("boundary=");
|
||||
if (found)
|
||||
return header.substr(found+9);
|
||||
return header.substr(found + 9);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<part> parse_body(std::string body)
|
||||
{
|
||||
|
||||
std::vector<part> sections;
|
||||
std::vector<part> sections;
|
||||
|
||||
std::string delimiter = dd + boundary;
|
||||
std::string delimiter = dd + boundary;
|
||||
|
||||
while(body != (crlf))
|
||||
{
|
||||
size_t found = body.find(delimiter);
|
||||
std::string section = body.substr(0, found);
|
||||
while (body != (crlf))
|
||||
{
|
||||
size_t found = body.find(delimiter);
|
||||
std::string section = body.substr(0, found);
|
||||
|
||||
//+2 is the CRLF
|
||||
//We don't check it and delete it so that the same delimiter can be used for
|
||||
//the last delimiter (--delimiter--CRLF).
|
||||
body.erase(0, found + delimiter.length() + 2);
|
||||
if (!section.empty())
|
||||
{
|
||||
sections.emplace_back(parse_section(section));
|
||||
}
|
||||
}
|
||||
return sections;
|
||||
// +2 is the CRLF.
|
||||
// We don't check it and delete it so that the same delimiter can be used for The last delimiter (--delimiter--CRLF).
|
||||
body.erase(0, found + delimiter.length() + 2);
|
||||
if (!section.empty())
|
||||
{
|
||||
sections.emplace_back(parse_section(section));
|
||||
}
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
part parse_section(std::string& section)
|
||||
{
|
||||
struct part to_return;
|
||||
|
||||
size_t found = section.find(crlf+crlf);
|
||||
std::string head_line = section.substr(0, found+2);
|
||||
size_t found = section.find(crlf + crlf);
|
||||
std::string head_line = section.substr(0, found + 2);
|
||||
section.erase(0, found + 4);
|
||||
|
||||
parse_section_head(head_line, to_return);
|
||||
to_return.body = section.substr(0, section.length()-2);
|
||||
to_return.body = section.substr(0, section.length() - 2);
|
||||
return to_return;
|
||||
}
|
||||
|
||||
@ -145,35 +143,35 @@ namespace crow
|
||||
|
||||
size_t found = lines.find(crlf);
|
||||
std::string line = lines.substr(0, found);
|
||||
lines.erase(0, found+2);
|
||||
//add the header if available
|
||||
lines.erase(0, found + 2);
|
||||
// Add the header if available
|
||||
if (!line.empty())
|
||||
{
|
||||
size_t found = line.find("; ");
|
||||
std::string header = line.substr(0, found);
|
||||
if (found != std::string::npos)
|
||||
line.erase(0, found+2);
|
||||
line.erase(0, found + 2);
|
||||
else
|
||||
line = std::string();
|
||||
|
||||
size_t header_split = header.find(": ");
|
||||
|
||||
to_add.value = std::pair<std::string, std::string>(header.substr(0, header_split), header.substr(header_split+2));
|
||||
to_add.value = std::pair<std::string, std::string>(header.substr(0, header_split), header.substr(header_split + 2));
|
||||
}
|
||||
|
||||
//add the parameters
|
||||
// Add the parameters
|
||||
while (!line.empty())
|
||||
{
|
||||
size_t found = line.find("; ");
|
||||
std::string param = line.substr(0, found);
|
||||
if (found != std::string::npos)
|
||||
line.erase(0, found+2);
|
||||
line.erase(0, found + 2);
|
||||
else
|
||||
line = std::string();
|
||||
|
||||
size_t param_split = param.find('=');
|
||||
|
||||
std::string value = param.substr(param_split+1);
|
||||
std::string value = param.substr(param_split + 1);
|
||||
|
||||
to_add.params.emplace(param.substr(0, param_split), trim(value));
|
||||
}
|
||||
@ -181,18 +179,17 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string trim (std::string& string, const char& excess = '"') const
|
||||
inline std::string trim(std::string& string, const char& excess = '"') const
|
||||
{
|
||||
if (string.length() > 1 && string[0] == excess && string[string.length()-1] == excess)
|
||||
return string.substr(1, string.length()-2);
|
||||
if (string.length() > 1 && string[0] == excess && string[string.length() - 1] == excess)
|
||||
return string.substr(1, string.length() - 2);
|
||||
return string;
|
||||
}
|
||||
|
||||
inline std::string pad (std::string& string, const char& padding = '"') const
|
||||
inline std::string pad(std::string& string, const char& padding = '"') const
|
||||
{
|
||||
return (padding + string + padding);
|
||||
return (padding + string + padding);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace multipart
|
||||
} // namespace crow
|
||||
|
@ -16,9 +16,9 @@ namespace crow
|
||||
|
||||
class invalid_template_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
invalid_template_exception(const std::string& msg)
|
||||
: msg("crow::mustache error: " + msg)
|
||||
public:
|
||||
invalid_template_exception(const std::string& msg):
|
||||
msg("crow::mustache error: " + msg)
|
||||
{
|
||||
}
|
||||
virtual const char* what() const throw()
|
||||
@ -45,17 +45,18 @@ namespace crow
|
||||
int end;
|
||||
int pos;
|
||||
ActionType t;
|
||||
Action(ActionType t, size_t start, size_t end, size_t pos = 0)
|
||||
: start(static_cast<int>(start)), end(static_cast<int>(end)), pos(static_cast<int>(pos)), t(t)
|
||||
{}
|
||||
Action(ActionType t, size_t start, size_t end, size_t pos = 0):
|
||||
start(static_cast<int>(start)), end(static_cast<int>(end)), pos(static_cast<int>(pos)), t(t)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/// A mustache template object.
|
||||
class template_t
|
||||
class template_t
|
||||
{
|
||||
public:
|
||||
template_t(std::string body)
|
||||
: body_(std::move(body))
|
||||
template_t(std::string body):
|
||||
body_(std::move(body))
|
||||
{
|
||||
// {{ {{# {{/ {{^ {{! {{> {{=
|
||||
parse();
|
||||
@ -66,7 +67,7 @@ namespace crow
|
||||
{
|
||||
return body_.substr(action.start, action.end - action.start);
|
||||
}
|
||||
auto find_context(const std::string& name, const std::vector<context*>& stack, bool shouldUseOnlyFirstStackValue = false)->std::pair<bool, context&>
|
||||
auto find_context(const std::string& name, const std::vector<context*>& stack, bool shouldUseOnlyFirstStackValue = false) -> std::pair<bool, context&>
|
||||
{
|
||||
if (name == ".")
|
||||
{
|
||||
@ -78,7 +79,7 @@ namespace crow
|
||||
int dotPosition = name.find(".");
|
||||
if (dotPosition == static_cast<int>(name.npos))
|
||||
{
|
||||
for(auto it = stack.rbegin(); it != stack.rend(); ++it)
|
||||
for (auto it = stack.rbegin(); it != stack.rend(); ++it)
|
||||
{
|
||||
if ((*it)->t() == json::type::Object)
|
||||
{
|
||||
@ -91,22 +92,22 @@ namespace crow
|
||||
{
|
||||
std::vector<int> dotPositions;
|
||||
dotPositions.push_back(-1);
|
||||
while(dotPosition != static_cast<int>(name.npos))
|
||||
while (dotPosition != static_cast<int>(name.npos))
|
||||
{
|
||||
dotPositions.push_back(dotPosition);
|
||||
dotPosition = name.find(".", dotPosition+1);
|
||||
dotPosition = name.find(".", dotPosition + 1);
|
||||
}
|
||||
dotPositions.push_back(name.size());
|
||||
std::vector<std::string> names;
|
||||
names.reserve(dotPositions.size()-1);
|
||||
for(int i = 1; i < static_cast<int>(dotPositions.size()); i ++)
|
||||
names.emplace_back(name.substr(dotPositions[i-1]+1, dotPositions[i]-dotPositions[i-1]-1));
|
||||
names.reserve(dotPositions.size() - 1);
|
||||
for (int i = 1; i < static_cast<int>(dotPositions.size()); i++)
|
||||
names.emplace_back(name.substr(dotPositions[i - 1] + 1, dotPositions[i] - dotPositions[i - 1] - 1));
|
||||
|
||||
for(auto it = stack.rbegin(); it != stack.rend(); ++it)
|
||||
for (auto it = stack.rbegin(); it != stack.rend(); ++it)
|
||||
{
|
||||
context* view = *it;
|
||||
bool found = true;
|
||||
for(auto jt = names.begin(); jt != names.end(); ++jt)
|
||||
for (auto jt = names.begin(); jt != names.end(); ++jt)
|
||||
{
|
||||
if (view->t() == json::type::Object &&
|
||||
view->count(*jt))
|
||||
@ -115,7 +116,8 @@ namespace crow
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shouldUseOnlyFirstStackValue) {
|
||||
if (shouldUseOnlyFirstStackValue)
|
||||
{
|
||||
return {false, empty_str};
|
||||
}
|
||||
found = false;
|
||||
@ -125,7 +127,6 @@ namespace crow
|
||||
if (found)
|
||||
return {true, *view};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {false, empty_str};
|
||||
@ -134,9 +135,9 @@ namespace crow
|
||||
void escape(const std::string& in, std::string& out)
|
||||
{
|
||||
out.reserve(out.size() + in.size());
|
||||
for(auto it = in.begin(); it != in.end(); ++it)
|
||||
for (auto it = in.begin(); it != in.end(); ++it)
|
||||
{
|
||||
switch(*it)
|
||||
switch (*it)
|
||||
{
|
||||
case '&': out += "&"; break;
|
||||
case '<': out += "<"; break;
|
||||
@ -149,20 +150,25 @@ namespace crow
|
||||
}
|
||||
}
|
||||
|
||||
bool isTagInsideObjectBlock(const int& current, const std::vector<context*>& stack)
|
||||
bool isTagInsideObjectBlock(const int& current, const std::vector<context*>& stack)
|
||||
{
|
||||
int openedBlock = 0;
|
||||
int totalBlocksBefore = 0;
|
||||
for (int i = current; i > 0; --i) {
|
||||
for (int i = current; i > 0; --i)
|
||||
{
|
||||
++totalBlocksBefore;
|
||||
auto& action = actions_[i - 1];
|
||||
|
||||
if (action.t == ActionType::OpenBlock) {
|
||||
if (openedBlock == 0 && (*stack.rbegin())->t() == json::type::Object) {
|
||||
if (action.t == ActionType::OpenBlock)
|
||||
{
|
||||
if (openedBlock == 0 && (*stack.rbegin())->t() == json::type::Object)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
--openedBlock;
|
||||
} else if (action.t == ActionType::CloseBlock) {
|
||||
}
|
||||
else if (action.t == ActionType::CloseBlock)
|
||||
{
|
||||
++openedBlock;
|
||||
}
|
||||
}
|
||||
@ -177,116 +183,117 @@ namespace crow
|
||||
if (indent)
|
||||
out.insert(out.size(), indent, ' ');
|
||||
|
||||
while(current < actionEnd)
|
||||
while (current < actionEnd)
|
||||
{
|
||||
auto& fragment = fragments_[current];
|
||||
auto& action = actions_[current];
|
||||
render_fragment(fragment, indent, out);
|
||||
switch(action.t)
|
||||
switch (action.t)
|
||||
{
|
||||
case ActionType::Ignore:
|
||||
// do nothing
|
||||
break;
|
||||
case ActionType::Partial:
|
||||
{
|
||||
std::string partial_name = tag_name(action);
|
||||
auto partial_templ = load(partial_name);
|
||||
int partial_indent = action.pos;
|
||||
partial_templ.render_internal(0, partial_templ.fragments_.size()-1, stack, out, partial_indent?indent+partial_indent:0);
|
||||
}
|
||||
break;
|
||||
{
|
||||
std::string partial_name = tag_name(action);
|
||||
auto partial_templ = load(partial_name);
|
||||
int partial_indent = action.pos;
|
||||
partial_templ.render_internal(0, partial_templ.fragments_.size() - 1, stack, out, partial_indent ? indent + partial_indent : 0);
|
||||
}
|
||||
break;
|
||||
case ActionType::UnescapeTag:
|
||||
case ActionType::Tag:
|
||||
{
|
||||
bool shouldUseOnlyFirstStackValue = false;
|
||||
if (isTagInsideObjectBlock(current, stack))
|
||||
{
|
||||
bool shouldUseOnlyFirstStackValue = false;
|
||||
if (isTagInsideObjectBlock(current, stack)) {
|
||||
shouldUseOnlyFirstStackValue = true;
|
||||
}
|
||||
auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch(ctx.t())
|
||||
{
|
||||
case json::type::Number:
|
||||
out += ctx.dump();
|
||||
break;
|
||||
case json::type::String:
|
||||
if (action.t == ActionType::Tag)
|
||||
escape(ctx.s, out);
|
||||
else
|
||||
out += ctx.s;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("not implemented tag type" + boost::lexical_cast<std::string>(static_cast<int>(ctx.t())));
|
||||
}
|
||||
shouldUseOnlyFirstStackValue = true;
|
||||
}
|
||||
break;
|
||||
case ActionType::ElseBlock:
|
||||
auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch (ctx.t())
|
||||
{
|
||||
static context nullContext;
|
||||
auto optional_ctx = find_context(tag_name(action), stack);
|
||||
if (!optional_ctx.first)
|
||||
{
|
||||
case json::type::Number:
|
||||
out += ctx.dump();
|
||||
break;
|
||||
case json::type::String:
|
||||
if (action.t == ActionType::Tag)
|
||||
escape(ctx.s, out);
|
||||
else
|
||||
out += ctx.s;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("not implemented tag type" + boost::lexical_cast<std::string>(static_cast<int>(ctx.t())));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ActionType::ElseBlock:
|
||||
{
|
||||
static context nullContext;
|
||||
auto optional_ctx = find_context(tag_name(action), stack);
|
||||
if (!optional_ctx.first)
|
||||
{
|
||||
stack.emplace_back(&nullContext);
|
||||
break;
|
||||
}
|
||||
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch (ctx.t())
|
||||
{
|
||||
case json::type::List:
|
||||
if (ctx.l && !ctx.l->empty())
|
||||
current = action.pos;
|
||||
else
|
||||
stack.emplace_back(&nullContext);
|
||||
break;
|
||||
case json::type::False:
|
||||
case json::type::Null:
|
||||
stack.emplace_back(&nullContext);
|
||||
break;
|
||||
}
|
||||
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch(ctx.t())
|
||||
{
|
||||
case json::type::List:
|
||||
if (ctx.l && !ctx.l->empty())
|
||||
current = action.pos;
|
||||
else
|
||||
stack.emplace_back(&nullContext);
|
||||
break;
|
||||
case json::type::False:
|
||||
case json::type::Null:
|
||||
stack.emplace_back(&nullContext);
|
||||
break;
|
||||
default:
|
||||
current = action.pos;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ActionType::OpenBlock:
|
||||
{
|
||||
auto optional_ctx = find_context(tag_name(action), stack);
|
||||
if (!optional_ctx.first)
|
||||
{
|
||||
default:
|
||||
current = action.pos;
|
||||
break;
|
||||
}
|
||||
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch(ctx.t())
|
||||
{
|
||||
case json::type::List:
|
||||
if (ctx.l)
|
||||
for(auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
|
||||
{
|
||||
stack.push_back(&*it);
|
||||
render_internal(current+1, action.pos, stack, out, indent);
|
||||
stack.pop_back();
|
||||
}
|
||||
current = action.pos;
|
||||
break;
|
||||
case json::type::Number:
|
||||
case json::type::String:
|
||||
case json::type::Object:
|
||||
case json::type::True:
|
||||
stack.push_back(&ctx);
|
||||
break;
|
||||
case json::type::False:
|
||||
case json::type::Null:
|
||||
current = action.pos;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("{{#: not implemented context type: " + boost::lexical_cast<std::string>(static_cast<int>(ctx.t())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ActionType::OpenBlock:
|
||||
{
|
||||
auto optional_ctx = find_context(tag_name(action), stack);
|
||||
if (!optional_ctx.first)
|
||||
{
|
||||
current = action.pos;
|
||||
break;
|
||||
}
|
||||
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch (ctx.t())
|
||||
{
|
||||
case json::type::List:
|
||||
if (ctx.l)
|
||||
for (auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
|
||||
{
|
||||
stack.push_back(&*it);
|
||||
render_internal(current + 1, action.pos, stack, out, indent);
|
||||
stack.pop_back();
|
||||
}
|
||||
current = action.pos;
|
||||
break;
|
||||
case json::type::Number:
|
||||
case json::type::String:
|
||||
case json::type::Object:
|
||||
case json::type::True:
|
||||
stack.push_back(&ctx);
|
||||
break;
|
||||
case json::type::False:
|
||||
case json::type::Null:
|
||||
current = action.pos;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("{{#: not implemented context type: " + boost::lexical_cast<std::string>(static_cast<int>(ctx.t())));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ActionType::CloseBlock:
|
||||
stack.pop_back();
|
||||
break;
|
||||
@ -302,16 +309,17 @@ namespace crow
|
||||
{
|
||||
if (indent)
|
||||
{
|
||||
for(int i = fragment.first; i < fragment.second; i ++)
|
||||
for (int i = fragment.first; i < fragment.second; i++)
|
||||
{
|
||||
out += body_[i];
|
||||
if (body_[i] == '\n' && i+1 != static_cast<int>(body_.size()))
|
||||
if (body_[i] == '\n' && i + 1 != static_cast<int>(body_.size()))
|
||||
out.insert(out.size(), indent, ' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
out.insert(out.size(), body_, fragment.first, fragment.second-fragment.first);
|
||||
out.insert(out.size(), body_, fragment.first, fragment.second - fragment.first);
|
||||
}
|
||||
|
||||
public:
|
||||
std::string render()
|
||||
{
|
||||
@ -320,7 +328,7 @@ namespace crow
|
||||
stack.emplace_back(&empty_ctx);
|
||||
|
||||
std::string ret;
|
||||
render_internal(0, fragments_.size()-1, stack, ret, 0);
|
||||
render_internal(0, fragments_.size() - 1, stack, ret, 0);
|
||||
return ret;
|
||||
}
|
||||
std::string render(context& ctx)
|
||||
@ -329,21 +337,20 @@ namespace crow
|
||||
stack.emplace_back(&ctx);
|
||||
|
||||
std::string ret;
|
||||
render_internal(0, fragments_.size()-1, stack, ret, 0);
|
||||
render_internal(0, fragments_.size() - 1, stack, ret, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void parse()
|
||||
{
|
||||
std::string tag_open = "{{";
|
||||
std::string tag_close = "}}";
|
||||
|
||||
std::vector<int> blockPositions;
|
||||
|
||||
|
||||
size_t current = 0;
|
||||
while(1)
|
||||
while (1)
|
||||
{
|
||||
size_t idx = body_.find(tag_open, current);
|
||||
if (idx == body_.npos)
|
||||
@ -366,27 +373,31 @@ namespace crow
|
||||
throw invalid_template_exception("not matched opening tag");
|
||||
}
|
||||
current = endIdx + tag_close.size();
|
||||
switch(body_[idx])
|
||||
switch (body_[idx])
|
||||
{
|
||||
case '#':
|
||||
idx++;
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx - 1] == ' ')
|
||||
endIdx--;
|
||||
blockPositions.emplace_back(static_cast<int>(actions_.size()));
|
||||
actions_.emplace_back(ActionType::OpenBlock, idx, endIdx);
|
||||
break;
|
||||
case '/':
|
||||
idx++;
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx - 1] == ' ')
|
||||
endIdx--;
|
||||
{
|
||||
auto& matched = actions_[blockPositions.back()];
|
||||
if (body_.compare(idx, endIdx-idx,
|
||||
body_, matched.start, matched.end - matched.start) != 0)
|
||||
if (body_.compare(idx, endIdx - idx,
|
||||
body_, matched.start, matched.end - matched.start) != 0)
|
||||
{
|
||||
throw invalid_template_exception("not matched {{# {{/ pair: " +
|
||||
body_.substr(matched.start, matched.end - matched.start) + ", " +
|
||||
body_.substr(idx, endIdx-idx));
|
||||
throw invalid_template_exception("not matched {{# {{/ pair: " +
|
||||
body_.substr(matched.start, matched.end - matched.start) + ", " +
|
||||
body_.substr(idx, endIdx - idx));
|
||||
}
|
||||
matched.pos = actions_.size();
|
||||
}
|
||||
@ -395,68 +406,79 @@ namespace crow
|
||||
break;
|
||||
case '^':
|
||||
idx++;
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx - 1] == ' ')
|
||||
endIdx--;
|
||||
blockPositions.emplace_back(static_cast<int>(actions_.size()));
|
||||
actions_.emplace_back(ActionType::ElseBlock, idx, endIdx);
|
||||
break;
|
||||
case '!':
|
||||
// do nothing action
|
||||
actions_.emplace_back(ActionType::Ignore, idx+1, endIdx);
|
||||
actions_.emplace_back(ActionType::Ignore, idx + 1, endIdx);
|
||||
break;
|
||||
case '>': // partial
|
||||
idx++;
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx - 1] == ' ')
|
||||
endIdx--;
|
||||
actions_.emplace_back(ActionType::Partial, idx, endIdx);
|
||||
break;
|
||||
case '{':
|
||||
if (tag_open != "{{" || tag_close != "}}")
|
||||
throw invalid_template_exception("cannot use triple mustache when delimiter changed");
|
||||
|
||||
idx ++;
|
||||
if (body_[endIdx+2] != '}')
|
||||
idx++;
|
||||
if (body_[endIdx + 2] != '}')
|
||||
{
|
||||
throw invalid_template_exception("{{{: }}} not matched");
|
||||
}
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx - 1] == ' ')
|
||||
endIdx--;
|
||||
actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
|
||||
current++;
|
||||
break;
|
||||
case '&':
|
||||
idx ++;
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
idx++;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx - 1] == ' ')
|
||||
endIdx--;
|
||||
actions_.emplace_back(ActionType::UnescapeTag, idx, endIdx);
|
||||
break;
|
||||
case '=':
|
||||
// tag itself is no-op
|
||||
idx ++;
|
||||
idx++;
|
||||
actions_.emplace_back(ActionType::Ignore, idx, endIdx);
|
||||
endIdx --;
|
||||
endIdx--;
|
||||
if (body_[endIdx] != '=')
|
||||
throw invalid_template_exception("{{=: not matching = tag: "+body_.substr(idx, endIdx-idx));
|
||||
endIdx --;
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx] == ' ') endIdx--;
|
||||
throw invalid_template_exception("{{=: not matching = tag: " + body_.substr(idx, endIdx - idx));
|
||||
endIdx--;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx] == ' ')
|
||||
endIdx--;
|
||||
endIdx++;
|
||||
{
|
||||
bool succeeded = false;
|
||||
for(size_t i = idx; i < endIdx; i++)
|
||||
for (size_t i = idx; i < endIdx; i++)
|
||||
{
|
||||
if (body_[i] == ' ')
|
||||
{
|
||||
tag_open = body_.substr(idx, i-idx);
|
||||
while(body_[i] == ' ') i++;
|
||||
tag_close = body_.substr(i, endIdx-i);
|
||||
tag_open = body_.substr(idx, i - idx);
|
||||
while (body_[i] == ' ')
|
||||
i++;
|
||||
tag_close = body_.substr(i, endIdx - i);
|
||||
if (tag_open.empty())
|
||||
throw invalid_template_exception("{{=: empty open tag");
|
||||
if (tag_close.empty())
|
||||
throw invalid_template_exception("{{=: empty close tag");
|
||||
|
||||
if (tag_close.find(" ") != tag_close.npos)
|
||||
throw invalid_template_exception("{{=: invalid open/close tag: "+tag_open+" " + tag_close);
|
||||
throw invalid_template_exception("{{=: invalid open/close tag: " + tag_open + " " + tag_close);
|
||||
succeeded = true;
|
||||
break;
|
||||
}
|
||||
@ -467,24 +489,26 @@ namespace crow
|
||||
break;
|
||||
default:
|
||||
// normal tag case;
|
||||
while(body_[idx] == ' ') idx++;
|
||||
while(body_[endIdx-1] == ' ') endIdx--;
|
||||
while (body_[idx] == ' ')
|
||||
idx++;
|
||||
while (body_[endIdx - 1] == ' ')
|
||||
endIdx--;
|
||||
actions_.emplace_back(ActionType::Tag, idx, endIdx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// removing standalones
|
||||
for(int i = actions_.size()-2; i >= 0; i --)
|
||||
for (int i = actions_.size() - 2; i >= 0; i--)
|
||||
{
|
||||
if (actions_[i].t == ActionType::Tag || actions_[i].t == ActionType::UnescapeTag)
|
||||
continue;
|
||||
auto& fragment_before = fragments_[i];
|
||||
auto& fragment_after = fragments_[i+1];
|
||||
bool is_last_action = i == static_cast<int>(actions_.size())-2;
|
||||
auto& fragment_after = fragments_[i + 1];
|
||||
bool is_last_action = i == static_cast<int>(actions_.size()) - 2;
|
||||
bool all_space_before = true;
|
||||
int j, k;
|
||||
for(j = fragment_before.second-1;j >= fragment_before.first;j--)
|
||||
for (j = fragment_before.second - 1; j >= fragment_before.first; j--)
|
||||
{
|
||||
if (body_[j] != ' ')
|
||||
{
|
||||
@ -497,7 +521,7 @@ namespace crow
|
||||
if (!all_space_before && body_[j] != '\n')
|
||||
continue;
|
||||
bool all_space_after = true;
|
||||
for(k = fragment_after.first; k < static_cast<int>(body_.size()) && k < fragment_after.second; k ++)
|
||||
for (k = fragment_after.first; k < static_cast<int>(body_.size()) && k < fragment_after.second; k++)
|
||||
{
|
||||
if (body_[k] != ' ')
|
||||
{
|
||||
@ -507,31 +531,30 @@ namespace crow
|
||||
}
|
||||
if (all_space_after && !is_last_action)
|
||||
continue;
|
||||
if (!all_space_after &&
|
||||
!(
|
||||
body_[k] == '\n'
|
||||
||
|
||||
(body_[k] == '\r' &&
|
||||
k + 1 < static_cast<int>(body_.size()) &&
|
||||
body_[k+1] == '\n')))
|
||||
if (!all_space_after &&
|
||||
!(
|
||||
body_[k] == '\n' ||
|
||||
(body_[k] == '\r' &&
|
||||
k + 1 < static_cast<int>(body_.size()) &&
|
||||
body_[k + 1] == '\n')))
|
||||
continue;
|
||||
if (actions_[i].t == ActionType::Partial)
|
||||
{
|
||||
actions_[i].pos = fragment_before.second - j - 1;
|
||||
}
|
||||
fragment_before.second = j+1;
|
||||
fragment_before.second = j + 1;
|
||||
if (!all_space_after)
|
||||
{
|
||||
if (body_[k] == '\n')
|
||||
k++;
|
||||
else
|
||||
else
|
||||
k += 2;
|
||||
fragment_after.first = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int,int>> fragments_;
|
||||
|
||||
std::vector<std::pair<int, int>> fragments_;
|
||||
std::vector<Action> actions_;
|
||||
std::string body_;
|
||||
};
|
||||
@ -547,7 +570,7 @@ namespace crow
|
||||
static std::string template_base_directory = "templates";
|
||||
return template_base_directory;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
inline std::string default_loader(const std::string& filename)
|
||||
{
|
||||
@ -566,18 +589,18 @@ namespace crow
|
||||
|
||||
namespace detail
|
||||
{
|
||||
inline std::function<std::string (std::string)>& get_loader_ref()
|
||||
inline std::function<std::string(std::string)>& get_loader_ref()
|
||||
{
|
||||
static std::function<std::string (std::string)> loader = default_loader;
|
||||
static std::function<std::string(std::string)> loader = default_loader;
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
inline void set_base(const std::string& path)
|
||||
{
|
||||
auto& base = detail::get_template_base_directory_ref();
|
||||
base = path;
|
||||
if (base.back() != '\\' &&
|
||||
if (base.back() != '\\' &&
|
||||
base.back() != '/')
|
||||
{
|
||||
base += '/';
|
||||
@ -598,5 +621,5 @@ namespace crow
|
||||
{
|
||||
return compile(detail::get_loader_ref()(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace mustache
|
||||
} // namespace crow
|
||||
|
@ -11,10 +11,9 @@
|
||||
namespace crow
|
||||
{
|
||||
/// A wrapper for `nodejs/http-parser`.
|
||||
|
||||
/// Used to generate a \ref crow.request from the TCP socket buffer.
|
||||
///
|
||||
template <typename Handler>
|
||||
/// Used to generate a \ref crow.request from the TCP socket buffer.
|
||||
template<typename Handler>
|
||||
struct HTTPParser : public http_parser
|
||||
{
|
||||
static int on_message_begin(http_parser* self_)
|
||||
@ -26,7 +25,7 @@ namespace crow
|
||||
static int on_url(http_parser* self_, const char* at, size_t length)
|
||||
{
|
||||
HTTPParser* self = static_cast<HTTPParser*>(self_);
|
||||
self->raw_url.insert(self->raw_url.end(), at, at+length);
|
||||
self->raw_url.insert(self->raw_url.end(), at, at + length);
|
||||
return 0;
|
||||
}
|
||||
static int on_header_field(http_parser* self_, const char* at, size_t length)
|
||||
@ -39,11 +38,11 @@ namespace crow
|
||||
{
|
||||
self->headers.emplace(std::move(self->header_field), std::move(self->header_value));
|
||||
}
|
||||
self->header_field.assign(at, at+length);
|
||||
self->header_field.assign(at, at + length);
|
||||
self->header_building_state = 1;
|
||||
break;
|
||||
case 1:
|
||||
self->header_field.insert(self->header_field.end(), at, at+length);
|
||||
self->header_field.insert(self->header_field.end(), at, at + length);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
@ -54,11 +53,11 @@ namespace crow
|
||||
switch (self->header_building_state)
|
||||
{
|
||||
case 0:
|
||||
self->header_value.insert(self->header_value.end(), at, at+length);
|
||||
self->header_value.insert(self->header_value.end(), at, at + length);
|
||||
break;
|
||||
case 1:
|
||||
self->header_building_state = 0;
|
||||
self->header_value.assign(at, at+length);
|
||||
self->header_value.assign(at, at + length);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
@ -76,7 +75,7 @@ namespace crow
|
||||
static int on_body(http_parser* self_, const char* at, size_t length)
|
||||
{
|
||||
HTTPParser* self = static_cast<HTTPParser*>(self_);
|
||||
self->body.insert(self->body.end(), at, at+length);
|
||||
self->body.insert(self->body.end(), at, at + length);
|
||||
return 0;
|
||||
}
|
||||
static int on_message_complete(http_parser* self_)
|
||||
@ -90,8 +89,8 @@ namespace crow
|
||||
self->process_message();
|
||||
return 0;
|
||||
}
|
||||
HTTPParser(Handler* handler) :
|
||||
handler_(handler)
|
||||
HTTPParser(Handler* handler):
|
||||
handler_(handler)
|
||||
{
|
||||
http_parser_init(this, HTTP_REQUEST);
|
||||
}
|
||||
@ -101,14 +100,14 @@ namespace crow
|
||||
bool feed(const char* buffer, int length)
|
||||
{
|
||||
const static http_parser_settings settings_{
|
||||
on_message_begin,
|
||||
on_url,
|
||||
nullptr,
|
||||
on_header_field,
|
||||
on_header_value,
|
||||
on_headers_complete,
|
||||
on_body,
|
||||
on_message_complete,
|
||||
on_message_begin,
|
||||
on_url,
|
||||
nullptr,
|
||||
on_header_field,
|
||||
on_header_value,
|
||||
on_headers_complete,
|
||||
on_body,
|
||||
on_message_complete,
|
||||
};
|
||||
|
||||
int nparsed = http_parser_execute(this, &settings_, buffer, length);
|
||||
@ -152,10 +151,10 @@ namespace crow
|
||||
return request{static_cast<HTTPMethod>(method), std::move(raw_url), std::move(url), std::move(url_params), std::move(headers), std::move(body)};
|
||||
}
|
||||
|
||||
bool is_upgrade() const
|
||||
{
|
||||
return upgrade;
|
||||
}
|
||||
bool is_upgrade() const
|
||||
{
|
||||
return upgrade;
|
||||
}
|
||||
|
||||
bool check_version(int major, int minor) const
|
||||
{
|
||||
@ -174,4 +173,4 @@ namespace crow
|
||||
|
||||
Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection).
|
||||
};
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -4,15 +4,17 @@
|
||||
|
||||
namespace crow
|
||||
{
|
||||
/// An abstract class that allows any other class to be returned by a handler.
|
||||
struct returnable
|
||||
{
|
||||
std::string content_type;
|
||||
virtual std::string dump() const = 0;
|
||||
/// An abstract class that allows any other class to be returned by a handler.
|
||||
struct returnable
|
||||
{
|
||||
std::string content_type;
|
||||
virtual std::string dump() const = 0;
|
||||
|
||||
returnable(std::string ctype) : content_type {ctype}
|
||||
{}
|
||||
returnable(std::string ctype):
|
||||
content_type{ctype}
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~returnable(){};
|
||||
};
|
||||
}
|
||||
virtual ~returnable(){};
|
||||
};
|
||||
} // namespace crow
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,8 +18,8 @@ namespace crow
|
||||
struct SocketAdaptor
|
||||
{
|
||||
using context = void;
|
||||
SocketAdaptor(boost::asio::io_service& io_service, context*)
|
||||
: socket_(io_service)
|
||||
SocketAdaptor(boost::asio::io_service& io_service, context*):
|
||||
socket_(io_service)
|
||||
{
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ namespace crow
|
||||
socket_.shutdown(boost::asio::socket_base::shutdown_type::shutdown_receive, ec);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
template<typename F>
|
||||
void start(F f)
|
||||
{
|
||||
f(boost::system::error_code());
|
||||
@ -88,8 +88,8 @@ namespace crow
|
||||
{
|
||||
using context = boost::asio::ssl::context;
|
||||
using ssl_socket_t = boost::asio::ssl::stream<tcp::socket>;
|
||||
SSLAdaptor(boost::asio::io_service& io_service, context* ctx)
|
||||
: ssl_socket_(new ssl_socket_t(io_service, *ctx))
|
||||
SSLAdaptor(boost::asio::io_service& io_service, context* ctx):
|
||||
ssl_socket_(new ssl_socket_t(io_service, *ctx))
|
||||
{
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ namespace crow
|
||||
}
|
||||
|
||||
tcp::socket::lowest_layer_type&
|
||||
raw_socket()
|
||||
raw_socket()
|
||||
{
|
||||
return ssl_socket_->lowest_layer();
|
||||
}
|
||||
@ -155,16 +155,17 @@ namespace crow
|
||||
return GET_IO_SERVICE(raw_socket());
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
template<typename F>
|
||||
void start(F f)
|
||||
{
|
||||
ssl_socket_->async_handshake(boost::asio::ssl::stream_base::server,
|
||||
[f](const boost::system::error_code& ec) {
|
||||
f(ec);
|
||||
});
|
||||
[f](const boost::system::error_code& ec)
|
||||
{
|
||||
f(ec);
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> ssl_socket_;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -8,128 +8,131 @@
|
||||
|
||||
#include "crow/logging.h"
|
||||
|
||||
namespace crow {
|
||||
namespace detail {
|
||||
///
|
||||
/// A class for scheduling functions to be called after a specific amount
|
||||
/// of ticks. A tick is equal to 1 second.
|
||||
///
|
||||
class task_timer
|
||||
namespace crow
|
||||
{
|
||||
public:
|
||||
using task_type = std::function<void()>;
|
||||
using identifier_type = size_t;
|
||||
namespace detail
|
||||
{
|
||||
|
||||
private:
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
using time_type = clock_type::time_point;
|
||||
/// A class for scheduling functions to be called after a specific amount of ticks. A tick is equal to 1 second.
|
||||
class task_timer
|
||||
{
|
||||
public:
|
||||
using task_type = std::function<void()>;
|
||||
using identifier_type = size_t;
|
||||
|
||||
public:
|
||||
task_timer(boost::asio::io_service& io_service)
|
||||
: io_service_(io_service), deadline_timer_(io_service_)
|
||||
{
|
||||
deadline_timer_.expires_from_now(boost::posix_time::seconds(1));
|
||||
deadline_timer_.async_wait(
|
||||
std::bind(&task_timer::tick_handler, this, std::placeholders::_1));
|
||||
}
|
||||
private:
|
||||
using clock_type = std::chrono::steady_clock;
|
||||
using time_type = clock_type::time_point;
|
||||
|
||||
~task_timer() { deadline_timer_.cancel(); }
|
||||
public:
|
||||
task_timer(boost::asio::io_service& io_service):
|
||||
io_service_(io_service), deadline_timer_(io_service_)
|
||||
{
|
||||
deadline_timer_.expires_from_now(boost::posix_time::seconds(1));
|
||||
deadline_timer_.async_wait(
|
||||
std::bind(&task_timer::tick_handler, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void cancel(identifier_type id)
|
||||
{
|
||||
tasks_.erase(id);
|
||||
CROW_LOG_DEBUG << "task_timer cancelled: " << this << ' ' << id;
|
||||
}
|
||||
~task_timer() { deadline_timer_.cancel(); }
|
||||
|
||||
///
|
||||
/// Schedule the given task to be executed after the default amount of
|
||||
/// ticks.
|
||||
///
|
||||
/// \return identifier_type Used to cancel the thread.
|
||||
/// It is not bound to this task_timer instance and in some cases could lead to
|
||||
/// undefined behavior if used with other task_timer objects or after the task
|
||||
/// has been successfully executed.
|
||||
///
|
||||
identifier_type schedule(const task_type& task)
|
||||
{
|
||||
tasks_.insert(
|
||||
{++highest_id_,
|
||||
{clock_type::now() + std::chrono::seconds(get_default_timeout()),
|
||||
task}});
|
||||
CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' << highest_id_;
|
||||
return highest_id_;
|
||||
}
|
||||
void cancel(identifier_type id)
|
||||
{
|
||||
tasks_.erase(id);
|
||||
CROW_LOG_DEBUG << "task_timer cancelled: " << this << ' ' << id;
|
||||
}
|
||||
|
||||
///
|
||||
/// Schedule the given task to be executed after the given time.
|
||||
///
|
||||
/// \param timeout The amount of ticks (seconds) to wait before execution.
|
||||
///
|
||||
/// \return identifier_type Used to cancel the thread.
|
||||
/// It is not bound to this task_timer instance and in some cases could lead to
|
||||
/// undefined behavior if used with other task_timer objects or after the task
|
||||
/// has been successfully executed.
|
||||
///
|
||||
identifier_type schedule(const task_type& task, std::uint8_t timeout)
|
||||
{
|
||||
tasks_.insert({++highest_id_,
|
||||
{clock_type::now() + std::chrono::seconds(timeout), task}});
|
||||
CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' << highest_id_;
|
||||
return highest_id_;
|
||||
}
|
||||
///
|
||||
/// Schedule the given task to be executed after the default amount of
|
||||
/// ticks.
|
||||
///
|
||||
/// \return identifier_type Used to cancel the thread.
|
||||
/// It is not bound to this task_timer instance and in some cases could lead to
|
||||
/// undefined behavior if used with other task_timer objects or after the task
|
||||
/// has been successfully executed.
|
||||
///
|
||||
identifier_type schedule(const task_type& task)
|
||||
{
|
||||
tasks_.insert(
|
||||
{++highest_id_,
|
||||
{clock_type::now() + std::chrono::seconds(get_default_timeout()),
|
||||
task}});
|
||||
CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' << highest_id_;
|
||||
return highest_id_;
|
||||
}
|
||||
|
||||
///
|
||||
/// Set the default timeout for this task_timer instance. (Default: 5)
|
||||
///
|
||||
/// \param timeout The amount of ticks (seconds) to wait before execution.
|
||||
///
|
||||
void set_default_timeout(std::uint8_t timeout) { default_timeout_ = timeout; }
|
||||
///
|
||||
/// Schedule the given task to be executed after the given time.
|
||||
///
|
||||
/// \param timeout The amount of ticks (seconds) to wait before execution.
|
||||
///
|
||||
/// \return identifier_type Used to cancel the thread.
|
||||
/// It is not bound to this task_timer instance and in some cases could lead to
|
||||
/// undefined behavior if used with other task_timer objects or after the task
|
||||
/// has been successfully executed.
|
||||
///
|
||||
identifier_type schedule(const task_type& task, std::uint8_t timeout)
|
||||
{
|
||||
tasks_.insert({++highest_id_,
|
||||
{clock_type::now() + std::chrono::seconds(timeout), task}});
|
||||
CROW_LOG_DEBUG << "task_timer scheduled: " << this << ' ' << highest_id_;
|
||||
return highest_id_;
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the default timeout. (Default: 5)
|
||||
///
|
||||
std::uint8_t get_default_timeout() const { return default_timeout_; }
|
||||
///
|
||||
/// Set the default timeout for this task_timer instance. (Default: 5)
|
||||
///
|
||||
/// \param timeout The amount of ticks (seconds) to wait before execution.
|
||||
///
|
||||
void set_default_timeout(std::uint8_t timeout) { default_timeout_ = timeout; }
|
||||
|
||||
private:
|
||||
void process_tasks()
|
||||
{
|
||||
time_type current_time = clock_type::now();
|
||||
std::vector<identifier_type> finished_tasks;
|
||||
///
|
||||
/// Get the default timeout. (Default: 5)
|
||||
///
|
||||
std::uint8_t get_default_timeout() const { return default_timeout_; }
|
||||
|
||||
for (const auto& task : tasks_) {
|
||||
if (task.second.first < current_time) {
|
||||
(task.second.second)();
|
||||
finished_tasks.push_back(task.first);
|
||||
CROW_LOG_DEBUG << "task_timer called: " << this << ' ' << task.first;
|
||||
}
|
||||
}
|
||||
private:
|
||||
void process_tasks()
|
||||
{
|
||||
time_type current_time = clock_type::now();
|
||||
std::vector<identifier_type> finished_tasks;
|
||||
|
||||
for (const auto& task : finished_tasks) tasks_.erase(task);
|
||||
for (const auto& task : tasks_)
|
||||
{
|
||||
if (task.second.first < current_time)
|
||||
{
|
||||
(task.second.second)();
|
||||
finished_tasks.push_back(task.first);
|
||||
CROW_LOG_DEBUG << "task_timer called: " << this << ' ' << task.first;
|
||||
}
|
||||
}
|
||||
|
||||
// If no task is currently scheduled, reset the issued ids back to 0.
|
||||
if (tasks_.empty()) highest_id_ = 0;
|
||||
}
|
||||
for (const auto& task : finished_tasks)
|
||||
tasks_.erase(task);
|
||||
|
||||
void tick_handler(const boost::system::error_code& ec)
|
||||
{
|
||||
if (ec) return;
|
||||
// If no task is currently scheduled, reset the issued ids back to 0.
|
||||
if (tasks_.empty()) highest_id_ = 0;
|
||||
}
|
||||
|
||||
process_tasks();
|
||||
void tick_handler(const boost::system::error_code& ec)
|
||||
{
|
||||
if (ec) return;
|
||||
|
||||
deadline_timer_.expires_from_now(boost::posix_time::seconds(1));
|
||||
deadline_timer_.async_wait(
|
||||
std::bind(&task_timer::tick_handler, this, std::placeholders::_1));
|
||||
}
|
||||
process_tasks();
|
||||
|
||||
private:
|
||||
std::uint8_t default_timeout_{5};
|
||||
boost::asio::io_service& io_service_;
|
||||
boost::asio::deadline_timer deadline_timer_;
|
||||
std::map<identifier_type, std::pair<time_type, task_type>> tasks_;
|
||||
deadline_timer_.expires_from_now(boost::posix_time::seconds(1));
|
||||
deadline_timer_.async_wait(
|
||||
std::bind(&task_timer::tick_handler, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
// A continuosly increasing number to be issued to threads to identify them.
|
||||
// If no tasks are scheduled, it will be reset to 0.
|
||||
identifier_type highest_id_{0};
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace crow
|
||||
private:
|
||||
std::uint8_t default_timeout_{5};
|
||||
boost::asio::io_service& io_service_;
|
||||
boost::asio::deadline_timer deadline_timer_;
|
||||
std::map<identifier_type, std::pair<time_type, task_type>> tasks_;
|
||||
|
||||
// A continuosly increasing number to be issued to threads to identify them.
|
||||
// If no tasks are scheduled, it will be reset to 0.
|
||||
identifier_type highest_id_{0};
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace crow
|
||||
|
@ -20,7 +20,7 @@ namespace crow
|
||||
{
|
||||
OutOfRange(unsigned /*pos*/, unsigned /*length*/) {}
|
||||
};
|
||||
constexpr unsigned requires_in_range( unsigned i, unsigned len )
|
||||
constexpr unsigned requires_in_range(unsigned i, unsigned len)
|
||||
{
|
||||
return i >= len ? throw OutOfRange(i, len) : i;
|
||||
}
|
||||
@ -28,73 +28,64 @@ namespace crow
|
||||
/// A constant string implementation.
|
||||
class const_str
|
||||
{
|
||||
const char * const begin_;
|
||||
const char* const begin_;
|
||||
unsigned size_;
|
||||
|
||||
public:
|
||||
template< unsigned N >
|
||||
constexpr const_str( const char(&arr)[N] ) : begin_(arr), size_(N - 1) {
|
||||
static_assert( N >= 1, "not a string literal");
|
||||
}
|
||||
constexpr char operator[]( unsigned i ) const {
|
||||
return requires_in_range(i, size_), begin_[i];
|
||||
public:
|
||||
template<unsigned N>
|
||||
constexpr const_str(const char (&arr)[N]):
|
||||
begin_(arr), size_(N - 1)
|
||||
{
|
||||
static_assert(N >= 1, "not a string literal");
|
||||
}
|
||||
constexpr char operator[](unsigned i) const
|
||||
{
|
||||
return requires_in_range(i, size_), begin_[i];
|
||||
}
|
||||
|
||||
constexpr operator const char *() const {
|
||||
return begin_;
|
||||
constexpr operator const char*() const
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
|
||||
constexpr const char* begin() const { return begin_; }
|
||||
constexpr const char* end() const { return begin_ + size_; }
|
||||
|
||||
constexpr unsigned size() const {
|
||||
return size_;
|
||||
constexpr unsigned size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr unsigned find_closing_tag(const_str s, unsigned p)
|
||||
{
|
||||
return s[p] == '>' ? p : find_closing_tag(s, p+1);
|
||||
return s[p] == '>' ? p : find_closing_tag(s, p + 1);
|
||||
}
|
||||
|
||||
constexpr bool is_valid(const_str s, unsigned i = 0, int f = 0)
|
||||
{
|
||||
return
|
||||
i == s.size()
|
||||
? f == 0 :
|
||||
f < 0 || f >= 2
|
||||
? false :
|
||||
s[i] == '<'
|
||||
? is_valid(s, i+1, f+1) :
|
||||
s[i] == '>'
|
||||
? is_valid(s, i+1, f-1) :
|
||||
is_valid(s, i+1, f);
|
||||
return i == s.size() ? f == 0 :
|
||||
f < 0 || f >= 2 ? false :
|
||||
s[i] == '<' ? is_valid(s, i + 1, f + 1) :
|
||||
s[i] == '>' ? is_valid(s, i + 1, f - 1) :
|
||||
is_valid(s, i + 1, f);
|
||||
}
|
||||
|
||||
constexpr bool is_equ_p(const char* a, const char* b, unsigned n)
|
||||
{
|
||||
return
|
||||
*a == 0 && *b == 0 && n == 0
|
||||
? true :
|
||||
(*a == 0 || *b == 0)
|
||||
? false :
|
||||
n == 0
|
||||
? true :
|
||||
*a != *b
|
||||
? false :
|
||||
is_equ_p(a+1, b+1, n-1);
|
||||
return *a == 0 && *b == 0 && n == 0 ? true :
|
||||
(*a == 0 || *b == 0) ? false :
|
||||
n == 0 ? true :
|
||||
*a != *b ? false :
|
||||
is_equ_p(a + 1, b + 1, n - 1);
|
||||
}
|
||||
|
||||
constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n)
|
||||
{
|
||||
return
|
||||
ai + n > a.size() || bi + n > b.size()
|
||||
? false :
|
||||
n == 0
|
||||
? true :
|
||||
a[ai] != b[bi]
|
||||
? false :
|
||||
is_equ_n(a,ai+1,b,bi+1,n-1);
|
||||
return ai + n > a.size() || bi + n > b.size() ? false :
|
||||
n == 0 ? true :
|
||||
a[ai] != b[bi] ? false :
|
||||
is_equ_n(a, ai + 1, b, bi + 1, n - 1);
|
||||
}
|
||||
|
||||
constexpr bool is_int(const_str s, unsigned i)
|
||||
@ -110,13 +101,13 @@ namespace crow
|
||||
constexpr bool is_float(const_str s, unsigned i)
|
||||
{
|
||||
return is_equ_n(s, i, "<float>", 0, 7) ||
|
||||
is_equ_n(s, i, "<double>", 0, 8);
|
||||
is_equ_n(s, i, "<double>", 0, 8);
|
||||
}
|
||||
|
||||
constexpr bool is_str(const_str s, unsigned i)
|
||||
{
|
||||
return is_equ_n(s, i, "<str>", 0, 5) ||
|
||||
is_equ_n(s, i, "<string>", 0, 8);
|
||||
is_equ_n(s, i, "<string>", 0, 8);
|
||||
}
|
||||
|
||||
constexpr bool is_path(const_str s, unsigned i)
|
||||
@ -124,17 +115,17 @@ namespace crow
|
||||
return is_equ_n(s, i, "<path>", 0, 6);
|
||||
}
|
||||
#endif
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct parameter_tag
|
||||
{
|
||||
static const int value = 0;
|
||||
};
|
||||
#define CROW_INTERNAL_PARAMETER_TAG(t, i) \
|
||||
template <> \
|
||||
struct parameter_tag<t> \
|
||||
{ \
|
||||
static const int value = i; \
|
||||
}
|
||||
template<> \
|
||||
struct parameter_tag<t> \
|
||||
{ \
|
||||
static const int value = i; \
|
||||
}
|
||||
CROW_INTERNAL_PARAMETER_TAG(int, 1);
|
||||
CROW_INTERNAL_PARAMETER_TAG(char, 1);
|
||||
CROW_INTERNAL_PARAMETER_TAG(short, 1);
|
||||
@ -148,24 +139,22 @@ struct parameter_tag<t> \
|
||||
CROW_INTERNAL_PARAMETER_TAG(double, 3);
|
||||
CROW_INTERNAL_PARAMETER_TAG(std::string, 4);
|
||||
#undef CROW_INTERNAL_PARAMETER_TAG
|
||||
template <typename ... Args>
|
||||
template<typename... Args>
|
||||
struct compute_parameter_tag_from_args_list;
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct compute_parameter_tag_from_args_list<>
|
||||
{
|
||||
static const int value = 0;
|
||||
};
|
||||
|
||||
template <typename Arg, typename ... Args>
|
||||
template<typename Arg, typename... Args>
|
||||
struct compute_parameter_tag_from_args_list<Arg, Args...>
|
||||
{
|
||||
static const int sub_value =
|
||||
compute_parameter_tag_from_args_list<Args...>::value;
|
||||
static const int value =
|
||||
parameter_tag<typename std::decay<Arg>::type>::value
|
||||
? sub_value* 6 + parameter_tag<typename std::decay<Arg>::type>::value
|
||||
: sub_value;
|
||||
static const int sub_value =
|
||||
compute_parameter_tag_from_args_list<Args...>::value;
|
||||
static const int value =
|
||||
parameter_tag<typename std::decay<Arg>::type>::value ? sub_value * 6 + parameter_tag<typename std::decay<Arg>::type>::value : sub_value;
|
||||
};
|
||||
|
||||
static inline bool is_parameter_tag_compatible(uint64_t a, uint64_t b)
|
||||
@ -174,237 +163,238 @@ struct parameter_tag<t> \
|
||||
return b == 0;
|
||||
if (b == 0)
|
||||
return a == 0;
|
||||
int sa = a%6;
|
||||
int sb = a%6;
|
||||
int sa = a % 6;
|
||||
int sb = a % 6;
|
||||
if (sa == 5) sa = 4;
|
||||
if (sb == 5) sb = 4;
|
||||
if (sa != sb)
|
||||
return false;
|
||||
return is_parameter_tag_compatible(a/6, b/6);
|
||||
return is_parameter_tag_compatible(a / 6, b / 6);
|
||||
}
|
||||
|
||||
static inline unsigned find_closing_tag_runtime(const char* s, unsigned p)
|
||||
{
|
||||
return
|
||||
s[p] == 0
|
||||
? throw std::runtime_error("unmatched tag <") :
|
||||
s[p] == '>'
|
||||
? p : find_closing_tag_runtime(s, p + 1);
|
||||
return s[p] == 0 ? throw std::runtime_error("unmatched tag <") :
|
||||
s[p] == '>' ? p :
|
||||
find_closing_tag_runtime(s, p + 1);
|
||||
}
|
||||
|
||||
|
||||
static inline uint64_t get_parameter_tag_runtime(const char* s, unsigned p = 0)
|
||||
{
|
||||
return
|
||||
s[p] == 0
|
||||
? 0 :
|
||||
s[p] == '<' ? (
|
||||
std::strncmp(s+p, "<int>", 5) == 0
|
||||
? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 :
|
||||
std::strncmp(s+p, "<uint>", 6) == 0
|
||||
? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 :
|
||||
(std::strncmp(s+p, "<float>", 7) == 0 ||
|
||||
std::strncmp(s+p, "<double>", 8) == 0)
|
||||
? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 :
|
||||
(std::strncmp(s+p, "<str>", 5) == 0 ||
|
||||
std::strncmp(s+p, "<string>", 8) == 0)
|
||||
? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 :
|
||||
std::strncmp(s+p, "<path>", 6) == 0
|
||||
? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 :
|
||||
throw std::runtime_error("invalid parameter type")
|
||||
) :
|
||||
get_parameter_tag_runtime(s, p+1);
|
||||
return s[p] == 0 ? 0 :
|
||||
s[p] == '<' ? (
|
||||
std::strncmp(s + p, "<int>", 5) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 1 :
|
||||
std::strncmp(s + p, "<uint>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 2 :
|
||||
(std::strncmp(s + p, "<float>", 7) == 0 ||
|
||||
std::strncmp(s + p, "<double>", 8) == 0) ?
|
||||
get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 3 :
|
||||
(std::strncmp(s + p, "<str>", 5) == 0 ||
|
||||
std::strncmp(s + p, "<string>", 8) == 0) ?
|
||||
get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 4 :
|
||||
std::strncmp(s + p, "<path>", 6) == 0 ? get_parameter_tag_runtime(s, find_closing_tag_runtime(s, p)) * 6 + 5 :
|
||||
throw std::runtime_error("invalid parameter type")) :
|
||||
get_parameter_tag_runtime(s, p + 1);
|
||||
}
|
||||
#ifndef CROW_MSVC_WORKAROUND
|
||||
constexpr uint64_t get_parameter_tag(const_str s, unsigned p = 0)
|
||||
{
|
||||
return
|
||||
p == s.size()
|
||||
? 0 :
|
||||
s[p] == '<' ? (
|
||||
is_int(s, p)
|
||||
? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 :
|
||||
is_uint(s, p)
|
||||
? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 :
|
||||
is_float(s, p)
|
||||
? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 :
|
||||
is_str(s, p)
|
||||
? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 :
|
||||
is_path(s, p)
|
||||
? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 :
|
||||
throw std::runtime_error("invalid parameter type")
|
||||
) :
|
||||
get_parameter_tag(s, p+1);
|
||||
return p == s.size() ? 0 :
|
||||
s[p] == '<' ? (
|
||||
is_int(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 1 :
|
||||
is_uint(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 2 :
|
||||
is_float(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 3 :
|
||||
is_str(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 4 :
|
||||
is_path(s, p) ? get_parameter_tag(s, find_closing_tag(s, p)) * 6 + 5 :
|
||||
throw std::runtime_error("invalid parameter type")) :
|
||||
get_parameter_tag(s, p + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename ... T>
|
||||
template<typename... T>
|
||||
struct S
|
||||
{
|
||||
template <typename U>
|
||||
template<typename U>
|
||||
using push = S<U, T...>;
|
||||
template <typename U>
|
||||
template<typename U>
|
||||
using push_back = S<T..., U>;
|
||||
template <template<typename ... Args> class U>
|
||||
template<template<typename... Args> class U>
|
||||
using rebind = U<T...>;
|
||||
};
|
||||
template <typename F, typename Set>
|
||||
template<typename F, typename Set>
|
||||
struct CallHelper;
|
||||
template <typename F, typename ...Args>
|
||||
template<typename F, typename... Args>
|
||||
struct CallHelper<F, S<Args...>>
|
||||
{
|
||||
template <typename F1, typename ...Args1, typename =
|
||||
decltype(std::declval<F1>()(std::declval<Args1>()...))
|
||||
>
|
||||
template<typename F1, typename... Args1, typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
|
||||
static char __test(int);
|
||||
|
||||
template <typename ...>
|
||||
template<typename...>
|
||||
static int __test(...);
|
||||
|
||||
static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
|
||||
};
|
||||
|
||||
|
||||
template <int N>
|
||||
template<int N>
|
||||
struct single_tag_to_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct single_tag_to_type<1>
|
||||
{
|
||||
using type = int64_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct single_tag_to_type<2>
|
||||
{
|
||||
using type = uint64_t;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct single_tag_to_type<3>
|
||||
{
|
||||
using type = double;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct single_tag_to_type<4>
|
||||
{
|
||||
using type = std::string;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct single_tag_to_type<5>
|
||||
{
|
||||
using type = std::string;
|
||||
};
|
||||
|
||||
|
||||
template <uint64_t Tag>
|
||||
template<uint64_t Tag>
|
||||
struct arguments
|
||||
{
|
||||
using subarguments = typename arguments<Tag/6>::type;
|
||||
using type =
|
||||
typename subarguments::template push<typename single_tag_to_type<Tag%6>::type>;
|
||||
using subarguments = typename arguments<Tag / 6>::type;
|
||||
using type =
|
||||
typename subarguments::template push<typename single_tag_to_type<Tag % 6>::type>;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct arguments<0>
|
||||
{
|
||||
using type = S<>;
|
||||
};
|
||||
|
||||
template <typename ... T>
|
||||
template<typename... T>
|
||||
struct last_element_type
|
||||
{
|
||||
using type = typename std::tuple_element<sizeof...(T)-1, std::tuple<T...>>::type;
|
||||
using type = typename std::tuple_element<sizeof...(T) - 1, std::tuple<T...>>::type;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct last_element_type<>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
// from http://stackoverflow.com/questions/13072359/c11-compile-time-array-with-logarithmic-evaluation-depth
|
||||
template<class T> using Invoke = typename T::type;
|
||||
template<class T>
|
||||
using Invoke = typename T::type;
|
||||
|
||||
template<unsigned...> struct seq{ using type = seq; };
|
||||
template<unsigned...>
|
||||
struct seq
|
||||
{
|
||||
using type = seq;
|
||||
};
|
||||
|
||||
template<class S1, class S2> struct concat;
|
||||
template<class S1, class S2>
|
||||
struct concat;
|
||||
|
||||
template<unsigned... I1, unsigned... I2>
|
||||
struct concat<seq<I1...>, seq<I2...>>
|
||||
: seq<I1..., (sizeof...(I1)+I2)...>{};
|
||||
struct concat<seq<I1...>, seq<I2...>> : seq<I1..., (sizeof...(I1) + I2)...>
|
||||
{
|
||||
};
|
||||
|
||||
template<class S1, class S2>
|
||||
using Concat = Invoke<concat<S1, S2>>;
|
||||
|
||||
template<unsigned N> struct gen_seq;
|
||||
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;
|
||||
template<unsigned N>
|
||||
struct gen_seq;
|
||||
template<unsigned N>
|
||||
using GenSeq = Invoke<gen_seq<N>>;
|
||||
|
||||
template<unsigned N>
|
||||
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
|
||||
struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>>
|
||||
{
|
||||
};
|
||||
|
||||
template<> struct gen_seq<0> : seq<>{};
|
||||
template<> struct gen_seq<1> : seq<0>{};
|
||||
template<>
|
||||
struct gen_seq<0> : seq<>
|
||||
{
|
||||
};
|
||||
template<>
|
||||
struct gen_seq<1> : seq<0>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename Seq, typename Tuple>
|
||||
template<typename Seq, typename Tuple>
|
||||
struct pop_back_helper;
|
||||
|
||||
template <unsigned ... N, typename Tuple>
|
||||
template<unsigned... N, typename Tuple>
|
||||
struct pop_back_helper<seq<N...>, Tuple>
|
||||
{
|
||||
template <template <typename ... Args> class U>
|
||||
template<template<typename... Args> class U>
|
||||
using rebind = U<typename std::tuple_element<N, Tuple>::type...>;
|
||||
};
|
||||
|
||||
template <typename ... T>
|
||||
template<typename... T>
|
||||
struct pop_back //: public pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>>
|
||||
{
|
||||
template <template <typename ... Args> class U>
|
||||
using rebind = typename pop_back_helper<typename gen_seq<sizeof...(T)-1>::type, std::tuple<T...>>::template rebind<U>;
|
||||
template<template<typename... Args> class U>
|
||||
using rebind = typename pop_back_helper<typename gen_seq<sizeof...(T) - 1>::type, std::tuple<T...>>::template rebind<U>;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct pop_back<>
|
||||
{
|
||||
template <template <typename ... Args> class U>
|
||||
template<template<typename... Args> class U>
|
||||
using rebind = U<>;
|
||||
};
|
||||
|
||||
// from http://stackoverflow.com/questions/2118541/check-if-c0x-parameter-pack-contains-a-type
|
||||
template < typename Tp, typename... List >
|
||||
struct contains : std::true_type {};
|
||||
template<typename Tp, typename... List>
|
||||
struct contains : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template < typename Tp, typename Head, typename... Rest >
|
||||
struct contains<Tp, Head, Rest...>
|
||||
: std::conditional< std::is_same<Tp, Head>::value,
|
||||
std::true_type,
|
||||
contains<Tp, Rest...>
|
||||
>::type {};
|
||||
template<typename Tp, typename Head, typename... Rest>
|
||||
struct contains<Tp, Head, Rest...> : std::conditional<std::is_same<Tp, Head>::value, std::true_type, contains<Tp, Rest...>>::type
|
||||
{
|
||||
};
|
||||
|
||||
template < typename Tp >
|
||||
struct contains<Tp> : std::false_type {};
|
||||
template<typename Tp>
|
||||
struct contains<Tp> : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct empty_context
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct promote
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
#define CROW_INTERNAL_PROMOTE_TYPE(t1, t2) \
|
||||
template<> \
|
||||
struct promote<t1> \
|
||||
{ \
|
||||
using type = t2; \
|
||||
}
|
||||
template<> \
|
||||
struct promote<t1> \
|
||||
{ \
|
||||
using type = t2; \
|
||||
}
|
||||
|
||||
CROW_INTERNAL_PROMOTE_TYPE(char, int64_t);
|
||||
CROW_INTERNAL_PROMOTE_TYPE(short, int64_t);
|
||||
@ -419,7 +409,7 @@ template <typename F, typename Set>
|
||||
CROW_INTERNAL_PROMOTE_TYPE(float, double);
|
||||
#undef CROW_INTERNAL_PROMOTE_TYPE
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
using promote_t = typename promote<T>::type;
|
||||
|
||||
} // namespace black_magic
|
||||
@ -427,19 +417,19 @@ template <typename F, typename Set>
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <class T, std::size_t N, class... Args>
|
||||
template<class T, std::size_t N, class... Args>
|
||||
struct get_index_of_element_from_tuple_by_type_impl
|
||||
{
|
||||
static constexpr auto value = N;
|
||||
};
|
||||
|
||||
template <class T, std::size_t N, class... Args>
|
||||
template<class T, std::size_t N, class... Args>
|
||||
struct get_index_of_element_from_tuple_by_type_impl<T, N, T, Args...>
|
||||
{
|
||||
static constexpr auto value = N;
|
||||
};
|
||||
|
||||
template <class T, std::size_t N, class U, class... Args>
|
||||
template<class T, std::size_t N, class U, class... Args>
|
||||
struct get_index_of_element_from_tuple_by_type_impl<T, N, U, Args...>
|
||||
{
|
||||
static constexpr auto value = get_index_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value;
|
||||
@ -449,80 +439,79 @@ template <typename F, typename Set>
|
||||
|
||||
namespace utility
|
||||
{
|
||||
template <class T, class... Args>
|
||||
template<class T, class... Args>
|
||||
T& get_element_by_type(std::tuple<Args...>& t)
|
||||
{
|
||||
return std::get<detail::get_index_of_element_from_tuple_by_type_impl<T, 0, Args...>::value>(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct function_traits;
|
||||
template<typename T>
|
||||
struct function_traits;
|
||||
|
||||
#ifndef CROW_MSVC_WORKAROUND
|
||||
template<typename T>
|
||||
template<typename T>
|
||||
struct function_traits : public function_traits<decltype(&T::operator())>
|
||||
{
|
||||
using parent_t = function_traits<decltype(&T::operator())>;
|
||||
static const size_t arity = parent_t::arity;
|
||||
using result_type = typename parent_t::result_type;
|
||||
template <size_t i>
|
||||
template<size_t i>
|
||||
using arg = typename parent_t::template arg<i>;
|
||||
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename ClassType, typename R, typename ...Args>
|
||||
struct function_traits<R(ClassType::*)(Args...) const>
|
||||
template<typename ClassType, typename R, typename... Args>
|
||||
struct function_traits<R (ClassType::*)(Args...) const>
|
||||
{
|
||||
static const size_t arity = sizeof...(Args);
|
||||
|
||||
typedef R result_type;
|
||||
|
||||
template <size_t i>
|
||||
template<size_t i>
|
||||
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
|
||||
};
|
||||
|
||||
template<typename ClassType, typename R, typename ...Args>
|
||||
struct function_traits<R(ClassType::*)(Args...)>
|
||||
template<typename ClassType, typename R, typename... Args>
|
||||
struct function_traits<R (ClassType::*)(Args...)>
|
||||
{
|
||||
static const size_t arity = sizeof...(Args);
|
||||
|
||||
typedef R result_type;
|
||||
|
||||
template <size_t i>
|
||||
template<size_t i>
|
||||
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
|
||||
};
|
||||
|
||||
template<typename R, typename ...Args>
|
||||
template<typename R, typename... Args>
|
||||
struct function_traits<std::function<R(Args...)>>
|
||||
{
|
||||
static const size_t arity = sizeof...(Args);
|
||||
|
||||
typedef R result_type;
|
||||
|
||||
template <size_t i>
|
||||
template<size_t i>
|
||||
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
|
||||
};
|
||||
|
||||
inline static std::string base64encode(const unsigned char* data, size_t size, const char* key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
|
||||
{
|
||||
std::string ret;
|
||||
ret.resize((size+2) / 3 * 4);
|
||||
ret.resize((size + 2) / 3 * 4);
|
||||
auto it = ret.begin();
|
||||
while(size >= 3)
|
||||
while (size >= 3)
|
||||
{
|
||||
*it++ = key[(static_cast<unsigned char>(*data)&0xFC)>>2];
|
||||
*it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
|
||||
unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
|
||||
*it++ = key[h|((static_cast<unsigned char>(*data)&0xF0)>>4)];
|
||||
*it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
|
||||
h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
|
||||
*it++ = key[h|((static_cast<unsigned char>(*data)&0xC0)>>6)];
|
||||
*it++ = key[static_cast<unsigned char>(*data++)&0x3F];
|
||||
*it++ = key[h | ((static_cast<unsigned char>(*data) & 0xC0) >> 6)];
|
||||
*it++ = key[static_cast<unsigned char>(*data++) & 0x3F];
|
||||
|
||||
size -= 3;
|
||||
}
|
||||
if (size == 1)
|
||||
{
|
||||
*it++ = key[(static_cast<unsigned char>(*data)&0xFC)>>2];
|
||||
*it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
|
||||
unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
|
||||
*it++ = key[h];
|
||||
*it++ = '=';
|
||||
@ -530,9 +519,9 @@ template <typename F, typename Set>
|
||||
}
|
||||
else if (size == 2)
|
||||
{
|
||||
*it++ = key[(static_cast<unsigned char>(*data)&0xFC)>>2];
|
||||
*it++ = key[(static_cast<unsigned char>(*data) & 0xFC) >> 2];
|
||||
unsigned char h = (static_cast<unsigned char>(*data++) & 0x03) << 4;
|
||||
*it++ = key[h|((static_cast<unsigned char>(*data)&0xF0)>>4)];
|
||||
*it++ = key[h | ((static_cast<unsigned char>(*data) & 0xF0) >> 4)];
|
||||
h = (static_cast<unsigned char>(*data++) & 0x0F) << 2;
|
||||
*it++ = key[h];
|
||||
*it++ = '=';
|
||||
@ -557,6 +546,7 @@ template <typename F, typename Set>
|
||||
|
||||
inline static std::string base64decode(const char* data, size_t size, bool urlsafe = false)
|
||||
{
|
||||
// clang-format off
|
||||
std::unordered_map<char, unsigned char> key ({
|
||||
{'A', 0},{'B', 1},{'C', 2},{'D', 3},{'E', 4},{'F', 5},{'G', 6},{'H', 7},{'I', 8},{'J', 9},
|
||||
{'K', 10},{'L', 11},{'M', 12},{'N', 13},{'O', 14},{'P', 15},{'Q', 16},{'R', 17},{'S', 18},{'T', 19},
|
||||
@ -566,17 +556,19 @@ template <typename F, typename Set>
|
||||
{'y', 50},{'z', 51},{'0', 52},{'1', 53},{'2', 54},{'3', 55},{'4', 56},{'5', 57},{'6', 58},{'7', 59},
|
||||
{'8', 60},{'9', 61},{urlsafe ? '-' : '+', 62},{urlsafe ? '_' : '/', 63}});
|
||||
|
||||
// clang-format on
|
||||
|
||||
// Not padded
|
||||
if (size % 4 == 2) // missing last 2 characters
|
||||
if (size % 4 == 2) // missing last 2 characters
|
||||
size = (size / 4 * 3) + 1; // Not subtracting extra characters because they're truncated in int division
|
||||
else if (size % 4 == 3) // missing last character
|
||||
else if (size % 4 == 3) // missing last character
|
||||
size = (size / 4 * 3) + 2; // Not subtracting extra characters because they're truncated in int division
|
||||
|
||||
// Padded
|
||||
else if (data[size-2] == '=') // padded with '=='
|
||||
size = (size / 4 * 3) - 2; // == padding means the last block only has 1 character instead of 3, hence the '-2'
|
||||
else if (data[size-1] == '=') // padded with '='
|
||||
size = (size / 4 * 3) - 1; // = padding means the last block only has 2 character instead of 3, hence the '-1'
|
||||
else if (data[size - 2] == '=') // padded with '=='
|
||||
size = (size / 4 * 3) - 2; // == padding means the last block only has 1 character instead of 3, hence the '-2'
|
||||
else if (data[size - 1] == '=') // padded with '='
|
||||
size = (size / 4 * 3) - 1; // = padding means the last block only has 2 character instead of 3, hence the '-1'
|
||||
|
||||
// Padding not needed
|
||||
else
|
||||
@ -593,14 +585,14 @@ template <typename F, typename Set>
|
||||
// Take 4 character blocks to turn into 3
|
||||
while (size >= 3)
|
||||
{
|
||||
// dec_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
|
||||
// dec_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
|
||||
odd = key[*data++];
|
||||
even = key[*data++];
|
||||
*it++ = (odd << 2) | ((even & 0x30) >> 4);
|
||||
// dec_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
|
||||
// dec_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
|
||||
odd = key[*data++];
|
||||
*it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
|
||||
// dec_char3 = ((char3 AND 00000011) shifted 6 bits left) OR (char4)
|
||||
// dec_char3 = ((char3 AND 00000011) shifted 6 bits left) OR (char4)
|
||||
even = key[*data++];
|
||||
*it++ = ((odd & 0x03) << 6) | (even);
|
||||
|
||||
@ -609,19 +601,19 @@ template <typename F, typename Set>
|
||||
if (size == 2)
|
||||
{
|
||||
// d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
|
||||
odd = key[*data++];
|
||||
even = key[*data++];
|
||||
*it++ = (odd << 2) | ((even & 0x30) >> 4);
|
||||
odd = key[*data++];
|
||||
even = key[*data++];
|
||||
*it++ = (odd << 2) | ((even & 0x30) >> 4);
|
||||
// d_char2 = ((char2 AND 00001111) shifted 4 bits left) OR ((char3 AND 00111100) shifted 2 bits right))
|
||||
odd = key[*data++];
|
||||
*it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
|
||||
odd = key[*data++];
|
||||
*it++ = ((even & 0x0F) << 4) | ((odd & 0x3C) >> 2);
|
||||
}
|
||||
else if (size == 1)
|
||||
{
|
||||
// d_char1 = (char1 shifted 2 bits to the left) OR ((char2 AND 00110000) shifted 4 bits to the right))
|
||||
odd = key[*data++];
|
||||
even = key[*data++];
|
||||
*it++ = (odd << 2) | ((even & 0x30) >> 4);
|
||||
odd = key[*data++];
|
||||
even = key[*data++];
|
||||
*it++ = (odd << 2) | ((even & 0x30) >> 4);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -632,4 +624,4 @@ template <typename F, typename Set>
|
||||
}
|
||||
|
||||
} // namespace utility
|
||||
}
|
||||
} // namespace crow
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace crow {
|
||||
constexpr const char VERSION[] = "master";
|
||||
namespace crow
|
||||
{
|
||||
constexpr const char VERSION[] = "master";
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -24,16 +24,16 @@ TEST_CASE("SSL")
|
||||
//crow::SimpleApp app2;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]() {
|
||||
return "Hello world, I'm keycrt.";
|
||||
});
|
||||
/*
|
||||
([]()
|
||||
{ return "Hello world, I'm keycrt."; });
|
||||
/*
|
||||
CROW_ROUTE(app2, "/")
|
||||
([]() {
|
||||
return "Hello world, I'm pem.";
|
||||
});
|
||||
*/
|
||||
auto _ = async(std::launch::async, [&] { app.bindaddr(LOCALHOST_ADDRESS).port(45460).ssl_file("test.crt", "test.key").run(); });
|
||||
auto _ = async(std::launch::async, [&]
|
||||
{ app.bindaddr(LOCALHOST_ADDRESS).port(45460).ssl_file("test.crt", "test.key").run(); });
|
||||
//auto _1 = async(std::launch::async,[&] { app2.bindaddr(LOCALHOST_ADDRESS).port(45461).ssl_file("test.pem").run(); });
|
||||
|
||||
app.wait_for_server_start();
|
||||
@ -45,22 +45,23 @@ TEST_CASE("SSL")
|
||||
|
||||
asio::io_service is;
|
||||
{
|
||||
asio::ssl::stream<asio::ip::tcp::socket> c(is, ctx);
|
||||
c.lowest_layer().connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45460));
|
||||
asio::ssl::stream<asio::ip::tcp::socket> c(is, ctx);
|
||||
c.lowest_layer().connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), 45460));
|
||||
|
||||
c.handshake(asio::ssl::stream_base::client);
|
||||
c.write_some(asio::buffer(sendmsg));
|
||||
c.handshake(asio::ssl::stream_base::client);
|
||||
c.write_some(asio::buffer(sendmsg));
|
||||
|
||||
size_t x = 0;
|
||||
size_t y = 0;
|
||||
size_t x = 0;
|
||||
size_t y = 0;
|
||||
|
||||
while(x < 121){
|
||||
y = c.read_some(asio::buffer(buf, 2048));
|
||||
x+=y;
|
||||
buf[y]='\0';
|
||||
}
|
||||
while (x < 121)
|
||||
{
|
||||
y = c.read_some(asio::buffer(buf, 2048));
|
||||
x += y;
|
||||
buf[y] = '\0';
|
||||
}
|
||||
|
||||
CHECK(std::string("Hello world, I'm keycrt.") == std::string(buf));
|
||||
CHECK(std::string("Hello world, I'm keycrt.") == std::string(buf));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -12,22 +12,22 @@ using namespace crow::mustache;
|
||||
|
||||
string read_all(const string& filename)
|
||||
{
|
||||
ifstream is(filename);
|
||||
return {istreambuf_iterator<char>(is), istreambuf_iterator<char>()};
|
||||
ifstream is(filename);
|
||||
return {istreambuf_iterator<char>(is), istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
auto data = json::load(read_all("data"));
|
||||
auto templ = compile(read_all("template"));
|
||||
auto partials = json::load(read_all("partials"));
|
||||
set_loader([&](std::string name) -> std::string {
|
||||
auto data = json::load(read_all("data"));
|
||||
auto templ = compile(read_all("template"));
|
||||
auto partials = json::load(read_all("partials"));
|
||||
set_loader([&](std::string name) -> std::string
|
||||
{
|
||||
if (partials.count(name)) {
|
||||
return partials[name].s();
|
||||
}
|
||||
return "";
|
||||
});
|
||||
context ctx(data);
|
||||
cout << templ.render(ctx);
|
||||
return 0;
|
||||
return ""; });
|
||||
context ctx(data);
|
||||
cout << templ.render(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
3346
tests/unittest.cpp
3346
tests/unittest.cpp
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user