Merge pull request #286 from CrowCpp/clang-format

clang-format
This commit is contained in:
Luca Schlecker 2021-11-27 20:51:12 +01:00 committed by GitHub
commit 081f869984
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 4748 additions and 4562 deletions

View File

@ -1,8 +1,97 @@
---
BasedOnStyle: Google
BreakBeforeBraces: Mozilla
IndentWidth: '2'
TabWidth: '2'
BasedOnStyle: Mozilla
AccessModifierOffset: '-4'
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: 'false'
AlignEscapedNewlines: Left
AlignOperands: 'true' #AlignAfterOperator
AlignTrailingComments: 'true'
AllowShortBlocksOnASingleLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'true'
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: 'true'
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: 'false'
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: 'false'
AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'true'
BinPackParameters: 'true'
BreakBeforeBraces: Custom
BraceWrapping:
AfterCaseLabel: 'true'
AfterClass: 'true'
AfterControlStatement: Always
AfterEnum: 'true'
AfterFunction: 'true'
AfterNamespace: 'true'
AfterObjCDeclaration: 'true'
AfterStruct: 'true'
AfterUnion: 'true'
AfterExternBlock: 'true'
BeforeCatch: 'true'
BeforeElse: 'true'
# BeforeLambdaBody: 'false'
# BeforeWhile: 'false'
IndentBraces: 'false'
SplitEmptyFunction: 'false'
SplitEmptyRecord: 'false'
SplitEmptyNamespace: 'false'
BreakBeforeTernaryOperators: 'false'
BreakBeforeBinaryOperators: '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
...

View File

@ -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 wget curl
- git clone https://github.com/CrowCpp/cpp-coveralls.git
- cd cpp-coveralls
- pip3 install . --no-input
@ -33,6 +39,11 @@ steps:
- ctest -V -j4
- cd ..
- coveralls --verbose --exclude-pattern .*/http_parser_merged.h --exclude-pattern .*/TinySHA1.hpp
- git 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
- cd ..
- export CC=/usr/bin/clang
- export CXX=/usr/bin/clang++
- mkdir build-clang

View File

@ -2,29 +2,31 @@
#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;
}
struct context
{
};
{};
void before_handle(crow::request& /*req*/, crow::response& /*res*/, context& /*ctx*/)
{
@ -44,26 +46,25 @@ 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.";
});
// a request to /path should be forwarded to /path/
CROW_ROUTE(app, "/path/")
([](){
([]() {
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;
@ -71,31 +72,31 @@ int main()
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}));
([] {
crow::json::wvalue x(crow::json::wvalue::list({1, 2, 3}));
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;
@ -104,8 +105,8 @@ int main()
});
// 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;
@ -114,10 +115,10 @@ int main()
});
// 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;
os << a + b;
res.write(os.str());
res.end();
});
@ -125,7 +126,7 @@ int main()
// 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 +141,21 @@ 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){
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()};
});
.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()};
});
// 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
@ -165,16 +165,18 @@ int main()
// To get a double from the request
// To see in action submit something like '/params?pew=42'
if(req.url_params.get("pew") != nullptr) {
if (req.url_params.get("pew") != nullptr)
{
double countD = boost::lexical_cast<double>(req.url_params.get("pew"));
os << "The value of 'pew' is " << countD << '\n';
os << "The value of 'pew' is " << countD << '\n';
}
// To get a list from the request
// You have to submit something like '/params?count[]=a&count[]=b' to have a list with two values (a and b)
auto count = req.url_params.get_list("count");
os << "The key 'count' contains " << count.size() << " value(s).\n";
for(const auto& countVal : count) {
for (const auto& countVal : count)
{
os << " - " << countVal << '\n';
}
@ -182,7 +184,8 @@ int main()
// You have to submit something like '/params?mydict[a]=b&mydict[abcd]=42' to have a list of pairs ((a, b) and (abcd, 42))
auto mydict = req.url_params.get_dict("mydict");
os << "The key 'dict' contains " << mydict.size() << " value(s).\n";
for(const auto& mydictVal : mydict) {
for (const auto& mydictVal : mydict)
{
os << " - " << mydictVal.first << " -> " << mydictVal.second << '\n';
}
@ -190,13 +193,13 @@ int main()
});
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!";
@ -207,6 +210,6 @@ int main()
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
app.port(18080)
.multithreaded()
.run();
.multithreaded()
.run();
}

View File

@ -14,7 +14,7 @@ int main()
return "Hello world!";
});
/* CROW_BP_ROUTE(bp, "/templatt")
/* CROW_BP_ROUTE(bp, "/templatt")
([]() {
crow::mustache::context ctxdat;
ctxdat["messg"] = "fifty five!!";
@ -24,7 +24,10 @@ 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);

View File

@ -6,7 +6,10 @@ 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)

View File

@ -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,31 +30,31 @@ int main()
crow::mustache::set_base(".");
CROW_ROUTE(app, "/")
([]{
([] {
crow::mustache::context ctx;
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);
for(int i = start; i < (int)msgs.size(); i++)
x["msgs"][i-start] = msgs[i];
int start = max(0, (int)msgs.size() - 100);
for (int i = start; i < (int)msgs.size(); i++)
x["msgs"][i - start] = msgs[i];
x["last"] = msgs.size();
CROW_LOG_INFO << "logs completed";
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())
{
crow::json::wvalue x;
for(int i = after; i < (int)msgs.size(); i ++)
x["msgs"][i-after] = msgs[i];
for (int i = after; i < (int)msgs.size(); i++)
x["msgs"][i - after] = msgs[i];
x["last"] = msgs.size();
res.write(x.dump());
@ -63,7 +63,7 @@ int main()
else
{
vector<pair<crow::response*, decltype(chrono::steady_clock::now())>> filtered;
for(auto p : ress)
for (auto p : ress)
{
if (p.first->is_alive() && chrono::steady_clock::now() - p.second < chrono::seconds(30))
filtered.push_back(p);
@ -77,15 +77,13 @@ int main()
});
CROW_ROUTE(app, "/send")
.methods("GET"_method, "POST"_method)
([](const crow::request& req)
{
CROW_LOG_INFO << "msg from client: " << req.body;
broadcast(req.body);
return "";
});
.methods("GET"_method, "POST"_method)([](const crow::request& req) {
CROW_LOG_INFO << "msg from client: " << req.body;
broadcast(req.body);
return "";
});
app.port(40080)
//.multithreaded()
.run();
//.multithreaded()
.run();
}

View File

@ -7,7 +7,7 @@ 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!";
@ -15,15 +15,15 @@ int main()
});
CROW_ROUTE(app, "/hello_compressed")
([](){
([]() {
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();
}

View File

@ -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")
([]{
crow::json::wvalue x({{"zmessage", "Hello, World!"},
{"amessage", "Hello, World2!"}});
return x;
});
// 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;
});
app.port(18080)
.multithreaded()
.run();
app.port(18080)
.multithreaded()
.run();
}

View File

@ -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) {
//replace cat.jpg with your file path
res.set_static_file_info("cat.jpg");
res.end();
});
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();
});
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).

View File

@ -2,29 +2,31 @@
#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;
}
struct context
{
};
{};
void before_handle(crow::request& req, crow::response& res, context& ctx)
{
@ -44,32 +46,31 @@ 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.";
});
// a request to /path should be forwarded to /path/
CROW_ROUTE(app, "/path/")
([](){
([]() {
return "Trailing slash test case..";
});
// simple json response
CROW_ROUTE(app, "/json")
([]{
([] {
crow::json::wvalue x;
x["message"] = "Hello, World!";
return x;
});
CROW_ROUTE(app, "/hello/<int>")
([](int count){
([](int count) {
if (count > 100)
return crow::response(400);
std::ostringstream os;
@ -78,9 +79,9 @@ int main()
});
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;
os << a + b;
res.write(os.str());
res.end();
});
@ -88,34 +89,34 @@ int main()
// 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){
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()};
});
.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()};
});
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";
if(req.url_params.get("pew") != nullptr) {
if (req.url_params.get("pew") != nullptr)
{
double countD = boost::lexical_cast<double>(req.url_params.get("pew"));
os << "The value of 'pew' is " << countD << '\n';
os << "The value of 'pew' is " << countD << '\n';
}
auto count = req.url_params.get_list("count");
os << "The key 'count' contains " << count.size() << " value(s).\n";
for(const auto& countVal : count) {
for (const auto& countVal : count)
{
os << " - " << countVal << '\n';
}
return crow::response{os.str()};
@ -126,6 +127,6 @@ int main()
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
app.port(18080)
.multithreaded()
.run();
.multithreaded()
.run();
}

View File

@ -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,19 +16,18 @@ 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.";
});
// simple json response
CROW_ROUTE(app, "/json")
([]{
([] {
crow::json::wvalue x({{"message", "Hello, World!"}});
x["message2"] = "Hello, World.. Again!";
return x;
@ -34,29 +35,29 @@ int main()
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}));
([] {
crow::json::wvalue x(crow::json::wvalue::list({1, 2, 3}));
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;
@ -66,10 +67,10 @@ int main()
// 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;
os << a + b;
res.write(os.str());
res.end();
});
@ -77,33 +78,35 @@ int main()
// 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();
int sum = x["a"].i() + x["b"].i();
std::ostringstream os;
os << sum;
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";
if(req.url_params.get("pew") != nullptr) {
if (req.url_params.get("pew") != nullptr)
{
double countD = boost::lexical_cast<double>(req.url_params.get("pew"));
os << "The value of 'pew' is " << countD << '\n';
os << "The value of 'pew' is " << countD << '\n';
}
auto count = req.url_params.get_list("count");
os << "The key 'count' contains " << count.size() << " value(s).\n";
for(const auto& countVal : count) {
for (const auto& countVal : count)
{
os << " - " << countVal << '\n';
}
return crow::response{os.str()};
@ -114,7 +117,7 @@ int main()
//crow::logger::setHandler(std::make_shared<ExampleLogHandler>());
app.port(18080)
.server_name("CrowCpp")
.multithreaded()
.run();
.server_name("CrowCpp")
.multithreaded()
.run();
}

View File

@ -11,28 +11,28 @@ int main()
std::unordered_set<crow::websocket::connection*> users;
CROW_ROUTE(app, "/ws")
.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){
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){
std::lock_guard<std::mutex> _(mtx);
for(auto u:users)
if (is_binary)
u->send_binary(data);
else
u->send_text(data);
});
.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) {
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) {
std::lock_guard<std::mutex> _(mtx);
for (auto u : users)
if (is_binary)
u->send_binary(data);
else
u->send_text(data);
});
CROW_ROUTE(app, "/")
([]{
([] {
char name[256];
gethostname(name, 256);
crow::mustache::context x;
@ -40,9 +40,9 @@ int main()
auto page = crow::mustache::load("ws.html");
return page.render(x);
});
});
app.port(40080)
.multithreaded()
.run();
.multithreaded()
.run();
}

View File

@ -38,63 +38,58 @@ 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 +107,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 +119,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 +156,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 +171,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 +190,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,21 +217,18 @@ 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)
{
res.set_static_file_info(CROW_STATIC_DIRECTORY + file_path_partial);
res.end();
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();
});
#if defined(__APPLE__) || defined(__MACH__)
@ -247,11 +239,9 @@ 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)
{
res.set_static_file_info(bp->static_dir() + '/' + file_path_partial);
res.end();
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();
});
}
}
@ -263,7 +253,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 +261,7 @@ namespace crow
cv_started_.notify_all();
}
///Run the server
/// Run the server
void run()
{
@ -300,22 +290,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 +314,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 +323,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 +335,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 +351,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 +381,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 +389,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 +438,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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,102 @@ 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 +129,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 +138,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
}
@ -272,7 +263,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 +276,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 +284,33 @@ 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 +319,9 @@ 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 +349,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 +385,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 +421,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 +479,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 +518,6 @@ namespace crow
}
buffers_.emplace_back(crlf.data(), crlf.size());
}
void do_write_static()
@ -570,76 +563,76 @@ 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()
@ -662,8 +655,7 @@ namespace crow
{
cancel_deadline_timer();
task_id_ = task_timer_.schedule([this]
{
task_id_ = task_timer_.schedule([this] {
if (!adaptor_.is_open())
{
return;
@ -710,4 +702,4 @@ namespace crow
size_t res_stream_threshold_;
};
}
} // namespace crow

View File

@ -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,16 +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))
{}
void add_header(std::string key, std::string value)
{
@ -71,6 +69,5 @@ namespace crow
{
io_service->dispatch(handler);
}
};
}
} // namespace crow

View File

@ -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,70 +263,68 @@ 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)
{
if (!ec)
{
return false;
}
else
{
CROW_LOG_ERROR << ec << " - happened while sending buffers";
this->end();
return true;
}
});
//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;
}
else
{
CROW_LOG_ERROR << ec << " - happened while sending buffers";
this->end();
return true;
}
});
}
};
}
} // namespace crow

View File

@ -10,7 +10,6 @@
#include <atomic>
#include <future>
#include <vector>
#include <memory>
#include "crow/version.h"
@ -23,24 +22,23 @@ 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)
{}
void set_tick_function(std::chrono::milliseconds d, std::function<void()> f)
{
@ -52,117 +50,117 @@ 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();
});
tick_timer_.async_wait([this](const boost::system::error_code& ec) {
if (ec)
return;
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;
};
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;
// 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)
init_count++;
while (1)
{
try
{
try
if (io_service_pool_[i]->run() == 0)
{
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();
// 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 +188,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]
{
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();
});
}
else
{
delete p;
}
do_accept();
});
}
private:
@ -234,4 +232,4 @@ namespace crow
typename Adaptor::context* adaptor_ctx_;
};
}
} // namespace crow

File diff suppressed because it is too large Load Diff

View File

@ -87,8 +87,7 @@ namespace crow
public:
logger(LogLevel level):
level_(level)
{
}
{}
~logger()
{
#ifdef CROW_ENABLE_LOGGING

View File

@ -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

View File

@ -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

View File

@ -8,12 +8,10 @@ namespace crow
struct UTF8
{
struct context
{
};
{};
void before_handle(request& /*req*/, response& /*res*/, context& /*ctx*/)
{
}
{}
void after_handle(request& /*req*/, response& res, context& /*ctx*/)
{
@ -24,4 +22,4 @@ namespace crow
}
};
}
} // namespace crow

View File

@ -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"}};
}

View File

@ -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,60 @@ 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))
/// 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:
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 +142,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 +178,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

View File

@ -16,11 +16,10 @@ 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()
{
return msg.c_str();
@ -45,17 +44,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 +66,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 +78,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 +91,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 +115,8 @@ namespace crow
}
else
{
if (shouldUseOnlyFirstStackValue) {
if (shouldUseOnlyFirstStackValue)
{
return {false, empty_str};
}
found = false;
@ -125,7 +126,6 @@ namespace crow
if (found)
return {true, *view};
}
}
return {false, empty_str};
@ -134,9 +134,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 += "&amp;"; break;
case '<': out += "&lt;"; break;
@ -149,20 +149,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 +182,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 +308,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 +327,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 +336,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 +372,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 +405,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 +488,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 +520,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 +530,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 +569,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 +588,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 +620,5 @@ namespace crow
{
return compile(detail::get_loader_ref()(filename));
}
}
}
} // namespace mustache
} // namespace crow

View File

@ -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

View File

@ -4,15 +4,16 @@
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

View File

@ -18,10 +18,9 @@ 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)
{}
boost::asio::io_service& get_io_service()
{
@ -74,7 +73,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,10 +87,9 @@ 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))
{}
boost::asio::ssl::stream<tcp::socket>& socket()
{
@ -99,7 +97,7 @@ namespace crow
}
tcp::socket::lowest_layer_type&
raw_socket()
raw_socket()
{
return ssl_socket_->lowest_layer();
}
@ -155,16 +153,16 @@ 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

View File

@ -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

View File

@ -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,228 @@ 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 +399,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 +407,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 +429,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 +509,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 +536,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 +546,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 +575,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 +591,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 +614,4 @@ template <typename F, typename Set>
}
} // namespace utility
}
} // namespace crow

View File

@ -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

View File

@ -27,13 +27,15 @@ TEST_CASE("SSL")
([]() {
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 +47,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));
}
/*

View File

@ -12,22 +12,23 @@ 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 {
if (partials.count(name)) {
return partials[name].s();
}
return "";
});
context ctx(data);
cout << templ.render(ctx);
return 0;
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;
}

File diff suppressed because it is too large Load Diff