mirror of https://github.com/CrowCpp/Crow.git
commit
081f869984
|
@ -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
|
||||
|
||||
...
|
||||
|
|
13
.drone.yml
13
.drone.yml
|
@ -12,6 +12,12 @@ steps:
|
|||
environment:
|
||||
COVERALLS_REPO_TOKEN:
|
||||
from_secret: coveralls_token
|
||||
APP_ID:
|
||||
from_secret: appid
|
||||
APP_INSTALLATION:
|
||||
from_secret: appins
|
||||
PK:
|
||||
from_secret: pk
|
||||
|
||||
commands:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
|
@ -19,7 +25,7 @@ steps:
|
|||
- export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER
|
||||
- export COVERALLS_PULL_REQUEST=$DRONE_PULL_REQUEST
|
||||
- apt-get -y update
|
||||
- apt-get -y install libboost-all-dev doxygen mkdocs graphviz zlib1g-dev gcc clang make cmake python3 python3-pip git openssl libssl-dev
|
||||
- apt-get -y install libboost-all-dev doxygen mkdocs graphviz zlib1g-dev gcc clang clang-format make cmake python3 python3-pip git openssl libssl-dev jq 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
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -13,18 +15,18 @@ 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")
|
||||
([]{
|
||||
.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;
|
||||
|
@ -87,15 +88,15 @@ int main()
|
|||
|
||||
// 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();
|
||||
});
|
||||
|
@ -140,12 +141,11 @@ int main()
|
|||
// A simpler way for json example:
|
||||
// * curl -d '{"a":1,"b":2}' {ip}:18080/add_json
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods("POST"_method)
|
||||
([](const crow::request& req){
|
||||
.methods("POST"_method)([](const crow::request& req) {
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
int sum = x["a"].i()+x["b"].i();
|
||||
int sum = x["a"].i() + x["b"].i();
|
||||
std::ostringstream os;
|
||||
os << sum;
|
||||
return crow::response{os.str()};
|
||||
|
@ -155,7 +155,7 @@ int main()
|
|||
// 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,7 +165,8 @@ 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';
|
||||
}
|
||||
|
@ -174,7 +175,8 @@ int main()
|
|||
// 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!";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,9 +77,7 @@ int main()
|
|||
});
|
||||
|
||||
CROW_ROUTE(app, "/send")
|
||||
.methods("GET"_method, "POST"_method)
|
||||
([](const crow::request& req)
|
||||
{
|
||||
.methods("GET"_method, "POST"_method)([](const crow::request& req) {
|
||||
CROW_LOG_INFO << "msg from client: " << req.body;
|
||||
broadcast(req.body);
|
||||
return "";
|
||||
|
|
|
@ -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,7 +15,7 @@ int main()
|
|||
});
|
||||
|
||||
CROW_ROUTE(app, "/hello_compressed")
|
||||
([](){
|
||||
([]() {
|
||||
return "Hello World! This is compressed by default!";
|
||||
});
|
||||
|
||||
|
|
|
@ -5,17 +5,17 @@ int main()
|
|||
{
|
||||
crow::SimpleApp app;
|
||||
|
||||
// simple json response using a map
|
||||
// To see it in action enter {ip}:18080/json
|
||||
// it shoud show amessage before zmessage despite adding zmessage first.
|
||||
CROW_ROUTE(app, "/json")
|
||||
([]{
|
||||
// simple json response using a map
|
||||
// To see it in action enter {ip}:18080/json
|
||||
// it shoud show amessage before zmessage despite adding zmessage first.
|
||||
CROW_ROUTE(app, "/json")
|
||||
([] {
|
||||
crow::json::wvalue x({{"zmessage", "Hello, World!"},
|
||||
{"amessage", "Hello, World2!"}});
|
||||
return x;
|
||||
});
|
||||
});
|
||||
|
||||
app.port(18080)
|
||||
app.port(18080)
|
||||
.multithreaded()
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ int main()
|
|||
crow::SimpleApp app;
|
||||
|
||||
//
|
||||
CROW_ROUTE(app, "/")
|
||||
([](const crow::request&, crow::response& res) {
|
||||
CROW_ROUTE(app, "/")
|
||||
([](const crow::request&, crow::response& res) {
|
||||
//replace cat.jpg with your file path
|
||||
res.set_static_file_info("cat.jpg");
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
app.port(18080).run();
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -13,18 +15,18 @@ 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")
|
||||
([]{
|
||||
.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();
|
||||
});
|
||||
|
@ -93,29 +94,29 @@ int main()
|
|||
|
||||
// more json example
|
||||
CROW_ROUTE(app, "/add_json")
|
||||
.methods(crow::HTTPMethod::Post)
|
||||
([](const crow::request& req){
|
||||
.methods(crow::HTTPMethod::Post)([](const crow::request& req) {
|
||||
auto x = crow::json::load(req.body);
|
||||
if (!x)
|
||||
return crow::response(400);
|
||||
auto sum = x["a"].i()+x["b"].i();
|
||||
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';
|
||||
}
|
||||
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()};
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,19 +16,18 @@ int main()
|
|||
crow::SimpleApp app;
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
.name("hello")
|
||||
([]{
|
||||
.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;
|
||||
|
@ -50,13 +51,13 @@ int main()
|
|||
|
||||
// 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();
|
||||
});
|
||||
|
@ -82,28 +83,30 @@ int main()
|
|||
|
||||
// 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';
|
||||
}
|
||||
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()};
|
||||
|
|
|
@ -12,19 +12,19 @@ int main()
|
|||
|
||||
CROW_ROUTE(app, "/ws")
|
||||
.websocket()
|
||||
.onopen([&](crow::websocket::connection& conn){
|
||||
.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){
|
||||
.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){
|
||||
.onmessage([&](crow::websocket::connection& /*conn*/, const std::string& data, bool is_binary) {
|
||||
std::lock_guard<std::mutex> _(mtx);
|
||||
for(auto u:users)
|
||||
for (auto u : users)
|
||||
if (is_binary)
|
||||
u->send_binary(data);
|
||||
else
|
||||
|
@ -32,7 +32,7 @@ int main()
|
|||
});
|
||||
|
||||
CROW_ROUTE(app, "/")
|
||||
([]{
|
||||
([] {
|
||||
char name[256];
|
||||
gethostname(name, 256);
|
||||
crow::mustache::context x;
|
||||
|
|
|
@ -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,19 +217,16 @@ namespace crow
|
|||
return compression_used_;
|
||||
}
|
||||
#endif
|
||||
///A wrapper for `validate()` in the router
|
||||
|
||||
/// A wrapper for `validate()` in the router
|
||||
///
|
||||
///Go through the rules, upgrade them if possible, and add them to the list of rules
|
||||
/// Go through the rules, upgrade them if possible, and add them to the list of rules
|
||||
void validate()
|
||||
{
|
||||
if (!validated_)
|
||||
{
|
||||
|
||||
#ifndef CROW_DISABLE_STATIC_DIR
|
||||
route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)
|
||||
([](crow::response& res, std::string file_path_partial)
|
||||
{
|
||||
route<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([](crow::response& res, std::string file_path_partial) {
|
||||
res.set_static_file_info(CROW_STATIC_DIRECTORY + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
|
@ -247,9 +239,7 @@ namespace crow
|
|||
{
|
||||
if (!bp->static_dir().empty())
|
||||
{
|
||||
bp->new_rule_tagged<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)
|
||||
([bp](crow::response& res, std::string file_path_partial)
|
||||
{
|
||||
bp->new_rule_tagged<crow::black_magic::get_parameter_tag(CROW_STATIC_ENDPOINT)>(CROW_STATIC_ENDPOINT)([bp](crow::response& res, std::string file_path_partial) {
|
||||
res.set_static_file_info(bp->static_dir() + '/' + file_path_partial);
|
||||
res.end();
|
||||
});
|
||||
|
@ -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,7 +351,7 @@ 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.
|
||||
|
@ -382,7 +362,7 @@ namespace crow
|
|||
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.
|
||||
|
@ -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
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace crow
|
|||
std::size_t seed = 0;
|
||||
std::locale locale;
|
||||
|
||||
for(auto c : key)
|
||||
for (auto c : key)
|
||||
{
|
||||
boost::hash_combine(seed, std::toupper(c, locale));
|
||||
}
|
||||
|
@ -33,4 +33,4 @@ namespace crow
|
|||
};
|
||||
|
||||
using ci_map = std::unordered_multimap<std::string, std::string, ci_hash, ci_key_eq>;
|
||||
}
|
||||
} // namespace crow
|
||||
|
|
|
@ -39,6 +39,8 @@ namespace crow
|
|||
// should not add an item below this line: used for array count
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
enum status
|
||||
{
|
||||
CONTINUE = 100,
|
||||
|
@ -87,32 +89,23 @@ namespace crow
|
|||
{
|
||||
switch(method)
|
||||
{
|
||||
case HTTPMethod::Delete:
|
||||
return "DELETE";
|
||||
case HTTPMethod::Get:
|
||||
return "GET";
|
||||
case HTTPMethod::Head:
|
||||
return "HEAD";
|
||||
case HTTPMethod::Post:
|
||||
return "POST";
|
||||
case HTTPMethod::Put:
|
||||
return "PUT";
|
||||
case HTTPMethod::Connect:
|
||||
return "CONNECT";
|
||||
case HTTPMethod::Options:
|
||||
return "OPTIONS";
|
||||
case HTTPMethod::Trace:
|
||||
return "TRACE";
|
||||
case HTTPMethod::Patch:
|
||||
return "PATCH";
|
||||
case HTTPMethod::Purge:
|
||||
return "PURGE";
|
||||
default:
|
||||
return "invalid";
|
||||
case HTTPMethod::Delete: return "DELETE";
|
||||
case HTTPMethod::Get: return "GET";
|
||||
case HTTPMethod::Head: return "HEAD";
|
||||
case HTTPMethod::Post: return "POST";
|
||||
case HTTPMethod::Put: return "PUT";
|
||||
case HTTPMethod::Connect: return "CONNECT";
|
||||
case HTTPMethod::Options: return "OPTIONS";
|
||||
case HTTPMethod::Trace: return "TRACE";
|
||||
case HTTPMethod::Patch: return "PATCH";
|
||||
case HTTPMethod::Purge: return "PURGE";
|
||||
default: return "invalid";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
enum class ParamType : char
|
||||
{
|
||||
INT,
|
||||
|
@ -134,23 +127,22 @@ namespace crow
|
|||
void debug_print() const
|
||||
{
|
||||
std::cerr << "routing_params" << std::endl;
|
||||
for(auto i:int_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for(auto i:uint_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for(auto i:double_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for(auto& i:string_params)
|
||||
std::cerr<<i <<", " ;
|
||||
std::cerr<<std::endl;
|
||||
for (auto i : int_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto i : uint_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto i : double_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
for (auto& i : string_params)
|
||||
std::cerr << i << ", ";
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
T get(unsigned) const;
|
||||
|
||||
};
|
||||
|
||||
template<>
|
||||
|
@ -176,13 +168,12 @@ 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 :
|
||||
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 :
|
||||
|
|
|
@ -16,10 +16,10 @@ namespace crow
|
|||
DEFLATE = 15,
|
||||
// windowBits can also be greater than 15 for optional gzip encoding.
|
||||
// Add 16 to windowBits to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
|
||||
GZIP = 15|16,
|
||||
GZIP = 15 | 16,
|
||||
};
|
||||
|
||||
inline std::string compress_string(std::string const & str, algorithm algo)
|
||||
inline std::string compress_string(std::string const& str, algorithm algo)
|
||||
{
|
||||
std::string compressed_str;
|
||||
z_stream stream{};
|
||||
|
@ -30,13 +30,13 @@ namespace crow
|
|||
|
||||
stream.avail_in = str.size();
|
||||
// zlib does not take a const pointer. The data is not altered.
|
||||
stream.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(str.c_str()));
|
||||
stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(str.c_str()));
|
||||
|
||||
int code = Z_OK;
|
||||
do
|
||||
{
|
||||
stream.avail_out = sizeof(buffer);
|
||||
stream.next_out = reinterpret_cast<Bytef *>(&buffer[0]);
|
||||
stream.next_out = reinterpret_cast<Bytef*>(&buffer[0]);
|
||||
|
||||
code = ::deflate(&stream, Z_FINISH);
|
||||
// Successful and non-fatal error code returned by deflate when used with Z_FINISH flush
|
||||
|
@ -56,7 +56,7 @@ namespace crow
|
|||
return compressed_str;
|
||||
}
|
||||
|
||||
inline std::string decompress_string(std::string const & deflated_string)
|
||||
inline std::string decompress_string(std::string const& deflated_string)
|
||||
{
|
||||
std::string inflated_string;
|
||||
Bytef tmp[8192];
|
||||
|
@ -64,7 +64,7 @@ namespace crow
|
|||
z_stream zstream{};
|
||||
zstream.avail_in = deflated_string.size();
|
||||
// Nasty const_cast but zlib won't alter its contents
|
||||
zstream.next_in = const_cast<Bytef *>(reinterpret_cast<Bytef const *>(deflated_string.c_str()));
|
||||
zstream.next_in = const_cast<Bytef*>(reinterpret_cast<Bytef const*>(deflated_string.c_str()));
|
||||
// Initialize with automatic header detection, for gzip support
|
||||
if (::inflateInit2(&zstream, MAX_WBITS | 32) == Z_OK)
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ namespace crow
|
|||
|
||||
return inflated_string;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace compression
|
||||
} // namespace crow
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <vector>
|
||||
|
||||
#include "crow/http_parser_merged.h"
|
||||
|
||||
#include "crow/common.h"
|
||||
#include "crow/parser.h"
|
||||
#include "crow/http_response.h"
|
||||
|
@ -26,110 +25,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*/)
|
||||
{
|
||||
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*/)
|
||||
{
|
||||
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*/)
|
||||
{
|
||||
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*/)
|
||||
{
|
||||
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,45 +138,46 @@ 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
|
||||
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,
|
||||
|
@ -194,9 +186,8 @@ namespace crow
|
|||
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_),
|
||||
typename Adaptor::context* adaptor_ctx_):
|
||||
adaptor_(io_service, adaptor_ctx_),
|
||||
handler_(handler),
|
||||
parser_(this),
|
||||
server_name_(server_name),
|
||||
|
@ -206,7 +197,7 @@ namespace crow
|
|||
res_stream_threshold_(handler->stream_threshold())
|
||||
{
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
connectionCount ++;
|
||||
connectionCount++;
|
||||
CROW_LOG_DEBUG << "Connection open, total " << connectionCount << ", " << this;
|
||||
#endif
|
||||
}
|
||||
|
@ -216,7 +207,7 @@ namespace crow
|
|||
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"))
|
||||
|
@ -316,8 +307,10 @@ namespace crow
|
|||
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),
|
||||
(static_cast<int>(sizeof...(Middlewares)) - 1),
|
||||
decltype(ctx_),
|
||||
decltype(*middlewares_)>
|
||||
(*middlewares_, ctx_, req_, res);
|
||||
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,11 +385,11 @@ 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
|
||||
#endif
|
||||
location.insert(0, "http://" + req_.get_header_value("Host"));
|
||||
res.set_header("location", location);
|
||||
}
|
||||
|
@ -404,14 +399,14 @@ namespace crow
|
|||
if (res.is_static_type())
|
||||
{
|
||||
do_write_static();
|
||||
}else {
|
||||
}
|
||||
else
|
||||
{
|
||||
do_write_general();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void prepare_buffers()
|
||||
{
|
||||
//auto self = this->shared_from_this();
|
||||
|
@ -472,7 +467,7 @@ namespace crow
|
|||
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,9 +563,9 @@ 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -618,9 +611,9 @@ namespace crow
|
|||
{
|
||||
//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*/)
|
||||
{
|
||||
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();
|
||||
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
@ -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
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
|
||||
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.
|
||||
|
@ -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
|
||||
|
@ -274,7 +281,7 @@ namespace crow
|
|||
}
|
||||
}
|
||||
|
||||
//THIS METHOD DOES MODIFY THE BODY, AS IN IT EMPTIES IT
|
||||
// THIS METHOD DOES MODIFY THE BODY, AS IN IT EMPTIES IT
|
||||
template<typename Adaptor>
|
||||
void write_streamed_string(std::string& is, Adaptor& adaptor)
|
||||
{
|
||||
|
@ -288,8 +295,8 @@ namespace crow
|
|||
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());
|
||||
// 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);
|
||||
|
@ -306,8 +313,7 @@ namespace crow
|
|||
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)
|
||||
{
|
||||
boost::asio::write(adaptor.socket(), buffers, [this](std::error_code ec, std::size_t) {
|
||||
if (!ec)
|
||||
{
|
||||
return false;
|
||||
|
@ -320,6 +326,5 @@ namespace crow
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
} // namespace crow
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <atomic>
|
||||
#include <future>
|
||||
#include <vector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "crow/version.h"
|
||||
|
@ -23,12 +22,12 @@ 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)),
|
||||
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),
|
||||
|
@ -39,8 +38,7 @@ namespace crow
|
|||
bindaddr_(bindaddr),
|
||||
middlewares_(middlewares),
|
||||
adaptor_ctx_(adaptor_ctx)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
void set_tick_function(std::chrono::milliseconds d, std::function<void()> f)
|
||||
{
|
||||
|
@ -52,8 +50,7 @@ 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)
|
||||
{
|
||||
tick_timer_.async_wait([this](const boost::system::error_code& ec) {
|
||||
if (ec)
|
||||
return;
|
||||
on_tick();
|
||||
|
@ -62,23 +59,22 @@ namespace crow
|
|||
|
||||
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();
|
||||
|
||||
std::string date_str;
|
||||
auto update_date_str = [&]
|
||||
{
|
||||
auto update_date_str = [&] {
|
||||
auto last_time_t = time(0);
|
||||
tm my_tm;
|
||||
|
||||
|
@ -92,8 +88,7 @@ namespace crow
|
|||
date_str.resize(date_str_sz);
|
||||
};
|
||||
update_date_str();
|
||||
get_cached_date_str_pool_[i] = [&]()->std::string
|
||||
{
|
||||
get_cached_date_str_pool_[i] = [&]() -> std::string {
|
||||
if (std::chrono::steady_clock::now() - last >= std::chrono::seconds(1))
|
||||
{
|
||||
last = std::chrono::steady_clock::now();
|
||||
|
@ -107,8 +102,8 @@ namespace crow
|
|||
task_timer.set_default_timeout(timeout_);
|
||||
task_timer_pool_[i] = &task_timer;
|
||||
|
||||
init_count ++;
|
||||
while(1)
|
||||
init_count++;
|
||||
while (1)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -117,7 +112,8 @@ namespace crow
|
|||
// when io_service.run returns 0, there are no more works to do.
|
||||
break;
|
||||
}
|
||||
} catch(std::exception& e)
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
CROW_LOG_ERROR << "Worker Crash: An uncaught exception occurred: " << e.what();
|
||||
}
|
||||
|
@ -127,8 +123,8 @@ namespace crow
|
|||
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)
|
||||
{
|
||||
tick_timer_.async_wait(
|
||||
[this](const boost::system::error_code& ec) {
|
||||
if (ec)
|
||||
return;
|
||||
on_tick();
|
||||
|
@ -139,30 +135,32 @@ namespace crow
|
|||
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*/){
|
||||
[&](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]{
|
||||
std::thread(
|
||||
[this] {
|
||||
io_service_.run();
|
||||
CROW_LOG_INFO << "Exiting.";
|
||||
}).join();
|
||||
})
|
||||
.join();
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
io_service_.stop();
|
||||
for(auto& io_service:io_service_pool_)
|
||||
for (auto& io_service : io_service_pool_)
|
||||
io_service->stop();
|
||||
}
|
||||
|
||||
|
@ -192,13 +190,13 @@ namespace crow
|
|||
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)
|
||||
{
|
||||
acceptor_.async_accept(
|
||||
p->socket(),
|
||||
[this, p, &is](boost::system::error_code ec) {
|
||||
if (!ec)
|
||||
{
|
||||
is.post([p]
|
||||
{
|
||||
is.post(
|
||||
[p] {
|
||||
p->start();
|
||||
});
|
||||
}
|
||||
|
@ -234,4 +232,4 @@ namespace crow
|
|||
|
||||
typename Adaptor::context* adaptor_ctx_;
|
||||
};
|
||||
}
|
||||
} // namespace crow
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -87,8 +87,7 @@ namespace crow
|
|||
public:
|
||||
logger(LogLevel level):
|
||||
level_(level)
|
||||
{
|
||||
}
|
||||
{}
|
||||
~logger()
|
||||
{
|
||||
#ifdef CROW_ENABLE_LOGGING
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//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 {
|
||||
namespace crow
|
||||
{
|
||||
const std::unordered_map<std::string, std::string> mime_types{
|
||||
{"shtml", "text/html"},
|
||||
{"htm", "text/html"},
|
||||
{"html", "text/html"},
|
||||
|
@ -111,6 +112,5 @@ namespace crow {
|
|||
{"asf", "video/x-ms-asf"},
|
||||
{"asx", "video/x-ms-asf"},
|
||||
{"wmv", "video/x-ms-wmv"},
|
||||
{"avi", "video/x-msvideo"}
|
||||
};
|
||||
{"avi", "video/x-msvideo"}};
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
|
||||
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`
|
||||
|
@ -23,16 +23,15 @@ namespace crow
|
|||
};
|
||||
|
||||
///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
|
||||
};
|
||||
|
||||
///The parsed multipart request/response
|
||||
/// The parsed multipart request/response
|
||||
struct message : public returnable
|
||||
{
|
||||
ci_map headers;
|
||||
|
@ -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,25 +77,24 @@ 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"),
|
||||
/// Create a multipart message from a request data
|
||||
message(const request& req):
|
||||
returnable("multipart/form-data"),
|
||||
headers(req.headers),
|
||||
boundary(get_boundary(get_header_value("Content-Type"))),
|
||||
parts(parse_body(req.body))
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
std::string get_boundary(const std::string& header) const
|
||||
{
|
||||
size_t found = header.find("boundary=");
|
||||
if (found)
|
||||
return header.substr(found+9);
|
||||
return header.substr(found + 9);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
|
@ -107,14 +105,13 @@ namespace crow
|
|||
|
||||
std::string delimiter = dd + boundary;
|
||||
|
||||
while(body != (crlf))
|
||||
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).
|
||||
// +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())
|
||||
{
|
||||
|
@ -128,12 +125,12 @@ namespace crow
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace multipart
|
||||
} // namespace crow
|
||||
|
|
|
@ -17,10 +17,9 @@ namespace crow
|
|||
class invalid_template_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
invalid_template_exception(const std::string& msg)
|
||||
: msg("crow::mustache error: " + msg)
|
||||
{
|
||||
}
|
||||
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
|
||||
{
|
||||
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 += "&"; break;
|
||||
case '<': out += "<"; break;
|
||||
|
@ -153,16 +153,21 @@ namespace crow
|
|||
{
|
||||
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,12 +182,12 @@ 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
|
||||
|
@ -192,19 +197,20 @@ namespace crow
|
|||
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);
|
||||
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)) {
|
||||
if (isTagInsideObjectBlock(current, stack))
|
||||
{
|
||||
shouldUseOnlyFirstStackValue = true;
|
||||
}
|
||||
auto optional_ctx = find_context(tag_name(action), stack, shouldUseOnlyFirstStackValue);
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch(ctx.t())
|
||||
switch (ctx.t())
|
||||
{
|
||||
case json::type::Number:
|
||||
out += ctx.dump();
|
||||
|
@ -231,7 +237,7 @@ namespace crow
|
|||
}
|
||||
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch(ctx.t())
|
||||
switch (ctx.t())
|
||||
{
|
||||
case json::type::List:
|
||||
if (ctx.l && !ctx.l->empty())
|
||||
|
@ -259,14 +265,14 @@ namespace crow
|
|||
}
|
||||
|
||||
auto& ctx = optional_ctx.second;
|
||||
switch(ctx.t())
|
||||
switch (ctx.t())
|
||||
{
|
||||
case json::type::List:
|
||||
if (ctx.l)
|
||||
for(auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
|
||||
for (auto it = ctx.l->begin(); it != ctx.l->end(); ++it)
|
||||
{
|
||||
stack.push_back(&*it);
|
||||
render_internal(current+1, action.pos, stack, out, indent);
|
||||
render_internal(current + 1, action.pos, stack, out, indent);
|
||||
stack.pop_back();
|
||||
}
|
||||
current = action.pos;
|
||||
|
@ -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,12 +336,11 @@ 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 = "{{";
|
||||
|
@ -343,7 +349,7 @@ namespace crow
|
|||
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,
|
||||
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));
|
||||
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] != ' ')
|
||||
{
|
||||
|
@ -509,17 +532,16 @@ namespace crow
|
|||
continue;
|
||||
if (!all_space_after &&
|
||||
!(
|
||||
body_[k] == '\n'
|
||||
||
|
||||
body_[k] == '\n' ||
|
||||
(body_[k] == '\r' &&
|
||||
k + 1 < static_cast<int>(body_.size()) &&
|
||||
body_[k+1] == '\n')))
|
||||
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')
|
||||
|
@ -531,7 +553,7 @@ namespace crow
|
|||
}
|
||||
}
|
||||
|
||||
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,12 +588,12 @@ 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)
|
||||
{
|
||||
|
@ -598,5 +620,5 @@ namespace crow
|
|||
{
|
||||
return compile(detail::get_loader_ref()(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace mustache
|
||||
} // namespace crow
|
||||
|
|
|
@ -11,10 +11,9 @@
|
|||
namespace crow
|
||||
{
|
||||
/// A wrapper for `nodejs/http-parser`.
|
||||
|
||||
/// Used to generate a \ref crow.request from the TCP socket buffer.
|
||||
///
|
||||
template <typename Handler>
|
||||
/// Used to generate a \ref crow.request from the TCP socket buffer.
|
||||
template<typename Handler>
|
||||
struct HTTPParser : public http_parser
|
||||
{
|
||||
static int on_message_begin(http_parser* self_)
|
||||
|
@ -26,7 +25,7 @@ namespace crow
|
|||
static int on_url(http_parser* self_, const char* at, size_t length)
|
||||
{
|
||||
HTTPParser* self = static_cast<HTTPParser*>(self_);
|
||||
self->raw_url.insert(self->raw_url.end(), at, at+length);
|
||||
self->raw_url.insert(self->raw_url.end(), at, at + length);
|
||||
return 0;
|
||||
}
|
||||
static int on_header_field(http_parser* self_, const char* at, size_t length)
|
||||
|
@ -39,11 +38,11 @@ namespace crow
|
|||
{
|
||||
self->headers.emplace(std::move(self->header_field), std::move(self->header_value));
|
||||
}
|
||||
self->header_field.assign(at, at+length);
|
||||
self->header_field.assign(at, at + length);
|
||||
self->header_building_state = 1;
|
||||
break;
|
||||
case 1:
|
||||
self->header_field.insert(self->header_field.end(), at, at+length);
|
||||
self->header_field.insert(self->header_field.end(), at, at + length);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
@ -54,11 +53,11 @@ namespace crow
|
|||
switch (self->header_building_state)
|
||||
{
|
||||
case 0:
|
||||
self->header_value.insert(self->header_value.end(), at, at+length);
|
||||
self->header_value.insert(self->header_value.end(), at, at + length);
|
||||
break;
|
||||
case 1:
|
||||
self->header_building_state = 0;
|
||||
self->header_value.assign(at, at+length);
|
||||
self->header_value.assign(at, at + length);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
@ -76,7 +75,7 @@ namespace crow
|
|||
static int on_body(http_parser* self_, const char* at, size_t length)
|
||||
{
|
||||
HTTPParser* self = static_cast<HTTPParser*>(self_);
|
||||
self->body.insert(self->body.end(), at, at+length);
|
||||
self->body.insert(self->body.end(), at, at + length);
|
||||
return 0;
|
||||
}
|
||||
static int on_message_complete(http_parser* self_)
|
||||
|
@ -90,7 +89,7 @@ namespace crow
|
|||
self->process_message();
|
||||
return 0;
|
||||
}
|
||||
HTTPParser(Handler* handler) :
|
||||
HTTPParser(Handler* handler):
|
||||
handler_(handler)
|
||||
{
|
||||
http_parser_init(this, HTTP_REQUEST);
|
||||
|
@ -174,4 +173,4 @@ namespace crow
|
|||
|
||||
Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection).
|
||||
};
|
||||
}
|
||||
} // namespace crow
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
|
||||
namespace crow
|
||||
{
|
||||
/// An abstract class that allows any other class to be returned by a handler.
|
||||
struct returnable
|
||||
{
|
||||
/// 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(){};
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace crow
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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()
|
||||
{
|
||||
|
@ -155,7 +153,7 @@ 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,
|
||||
|
@ -167,4 +165,4 @@ namespace crow
|
|||
std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> ssl_socket_;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
} // namespace crow
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
|
||||
#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
|
||||
{
|
||||
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
|
||||
{
|
||||
public:
|
||||
using task_type = std::function<void()>;
|
||||
using identifier_type = size_t;
|
||||
|
@ -25,8 +25,8 @@ class task_timer
|
|||
using time_type = clock_type::time_point;
|
||||
|
||||
public:
|
||||
task_timer(boost::asio::io_service& io_service)
|
||||
: io_service_(io_service), deadline_timer_(io_service_)
|
||||
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(
|
||||
|
@ -96,15 +96,18 @@ class task_timer
|
|||
time_type current_time = clock_type::now();
|
||||
std::vector<identifier_type> finished_tasks;
|
||||
|
||||
for (const auto& task : tasks_) {
|
||||
if (task.second.first < current_time) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& task : finished_tasks) tasks_.erase(task);
|
||||
for (const auto& task : finished_tasks)
|
||||
tasks_.erase(task);
|
||||
|
||||
// If no task is currently scheduled, reset the issued ids back to 0.
|
||||
if (tasks_.empty()) highest_id_ = 0;
|
||||
|
@ -130,6 +133,6 @@ class task_timer
|
|||
// 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 detail
|
||||
} // namespace crow
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace crow
|
|||
{
|
||||
OutOfRange(unsigned /*pos*/, unsigned /*length*/) {}
|
||||
};
|
||||
constexpr unsigned requires_in_range( unsigned i, unsigned len )
|
||||
constexpr unsigned requires_in_range(unsigned i, unsigned len)
|
||||
{
|
||||
return i >= len ? throw OutOfRange(i, len) : i;
|
||||
}
|
||||
|
@ -28,73 +28,64 @@ namespace crow
|
|||
/// A constant string implementation.
|
||||
class const_str
|
||||
{
|
||||
const char * const begin_;
|
||||
const char* const begin_;
|
||||
unsigned size_;
|
||||
|
||||
public:
|
||||
template< unsigned N >
|
||||
constexpr const_str( const char(&arr)[N] ) : begin_(arr), size_(N - 1) {
|
||||
static_assert( N >= 1, "not a string literal");
|
||||
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 {
|
||||
constexpr char operator[](unsigned i) const
|
||||
{
|
||||
return requires_in_range(i, size_), begin_[i];
|
||||
}
|
||||
|
||||
constexpr operator const char *() const {
|
||||
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 {
|
||||
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)
|
||||
|
@ -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> \
|
||||
{ \
|
||||
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;
|
||||
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,226 +163,217 @@ 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 :
|
||||
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);
|
||||
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 :
|
||||
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);
|
||||
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 subarguments = typename arguments<Tag / 6>::type;
|
||||
using type =
|
||||
typename subarguments::template push<typename single_tag_to_type<Tag%6>::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;
|
||||
|
@ -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,7 +429,7 @@ 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);
|
||||
|
@ -465,64 +445,63 @@ template <typename F, typename Set>
|
|||
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,6 +546,8 @@ 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
|
||||
size = (size / 4 * 3) + 1; // Not subtracting extra characters because they're truncated in int division
|
||||
|
@ -573,9 +555,9 @@ template <typename F, typename Set>
|
|||
size = (size / 4 * 3) + 2; // Not subtracting extra characters because they're truncated in int division
|
||||
|
||||
// Padded
|
||||
else if (data[size-2] == '=') // padded with '=='
|
||||
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 '='
|
||||
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
|
||||
|
@ -632,4 +614,4 @@ template <typename F, typename Set>
|
|||
}
|
||||
|
||||
} // namespace utility
|
||||
}
|
||||
} // namespace crow
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace crow {
|
||||
namespace crow
|
||||
{
|
||||
constexpr const char VERSION[] = "master";
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace crow
|
|||
Payload,
|
||||
};
|
||||
|
||||
///A base class for websocket connection.
|
||||
/// A base class for websocket connection.
|
||||
struct connection
|
||||
{
|
||||
virtual void send_binary(const std::string& msg) = 0;
|
||||
|
@ -27,7 +27,7 @@ namespace crow
|
|||
virtual void send_pong(const std::string& msg) = 0;
|
||||
virtual void close(const std::string& msg = "quit") = 0;
|
||||
virtual std::string get_remote_ip() = 0;
|
||||
virtual ~connection(){}
|
||||
virtual ~connection() {}
|
||||
|
||||
void userdata(void* u) { userdata_ = u; }
|
||||
void* userdata() { return userdata_; }
|
||||
|
@ -60,22 +60,24 @@ namespace crow
|
|||
//
|
||||
|
||||
/// A websocket connection.
|
||||
template <typename Adaptor>
|
||||
template<typename Adaptor>
|
||||
class Connection : public connection
|
||||
{
|
||||
public:
|
||||
///
|
||||
/// Constructor for a connection.
|
||||
///
|
||||
/// Requires a request with an "Upgrade: websocket" header.<br>
|
||||
/// Automatically handles the handshake.
|
||||
///
|
||||
Connection(const crow::request& req, Adaptor&& adaptor,
|
||||
std::function<void(crow::websocket::connection&)> open_handler,
|
||||
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
|
||||
std::function<void(crow::websocket::connection&, const std::string&)> close_handler,
|
||||
std::function<void(crow::websocket::connection&)> error_handler,
|
||||
std::function<bool(const crow::request&)> accept_handler)
|
||||
: adaptor_(std::move(adaptor)), open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler))
|
||||
, accept_handler_(std::move(accept_handler))
|
||||
std::function<bool(const crow::request&)> accept_handler):
|
||||
adaptor_(std::move(adaptor)),
|
||||
open_handler_(std::move(open_handler)), message_handler_(std::move(message_handler)), close_handler_(std::move(close_handler)), error_handler_(std::move(error_handler)), accept_handler_(std::move(accept_handler))
|
||||
{
|
||||
if (!boost::iequals(req.get_header_value("upgrade"), "websocket"))
|
||||
{
|
||||
|
@ -118,12 +120,14 @@ namespace crow
|
|||
adaptor_.get_io_service().post(handler);
|
||||
}
|
||||
|
||||
///
|
||||
/// Send a "Ping" message.
|
||||
///
|
||||
/// Usually invoked to check if the other point is still online.
|
||||
///
|
||||
void send_ping(const std::string& msg) override
|
||||
{
|
||||
dispatch([this, msg]{
|
||||
dispatch([this, msg] {
|
||||
auto header = build_header(0x9, msg.size());
|
||||
write_buffers_.emplace_back(std::move(header));
|
||||
write_buffers_.emplace_back(msg);
|
||||
|
@ -131,12 +135,14 @@ namespace crow
|
|||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// Send a "Pong" message.
|
||||
///
|
||||
/// Usually automatically invoked as a response to a "Ping" message.
|
||||
///
|
||||
void send_pong(const std::string& msg) override
|
||||
{
|
||||
dispatch([this, msg]{
|
||||
dispatch([this, msg] {
|
||||
auto header = build_header(0xA, msg.size());
|
||||
write_buffers_.emplace_back(std::move(header));
|
||||
write_buffers_.emplace_back(msg);
|
||||
|
@ -147,7 +153,7 @@ namespace crow
|
|||
/// Send a binary encoded message.
|
||||
void send_binary(const std::string& msg) override
|
||||
{
|
||||
dispatch([this, msg]{
|
||||
dispatch([this, msg] {
|
||||
auto header = build_header(2, msg.size());
|
||||
write_buffers_.emplace_back(std::move(header));
|
||||
write_buffers_.emplace_back(msg);
|
||||
|
@ -158,7 +164,7 @@ namespace crow
|
|||
/// Send a plaintext message.
|
||||
void send_text(const std::string& msg) override
|
||||
{
|
||||
dispatch([this, msg]{
|
||||
dispatch([this, msg] {
|
||||
auto header = build_header(1, msg.size());
|
||||
write_buffers_.emplace_back(std::move(header));
|
||||
write_buffers_.emplace_back(msg);
|
||||
|
@ -166,12 +172,14 @@ namespace crow
|
|||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// Send a close signal.
|
||||
///
|
||||
/// Sets a flag to destroy the object once the message is sent.
|
||||
///
|
||||
void close(const std::string& msg) override
|
||||
{
|
||||
dispatch([this, msg]{
|
||||
dispatch([this, msg] {
|
||||
has_sent_close_ = true;
|
||||
if (has_recv_close_ && !is_close_handler_called_)
|
||||
{
|
||||
|
@ -192,34 +200,35 @@ namespace crow
|
|||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// Generate the websocket headers using an opcode and the message size (in bytes).
|
||||
std::string build_header(int opcode, size_t size)
|
||||
{
|
||||
char buf[2+8] = "\x80\x00";
|
||||
char buf[2 + 8] = "\x80\x00";
|
||||
buf[0] += opcode;
|
||||
if (size < 126)
|
||||
{
|
||||
buf[1] += static_cast<char>(size);
|
||||
return {buf, buf+2};
|
||||
return {buf, buf + 2};
|
||||
}
|
||||
else if (size < 0x10000)
|
||||
{
|
||||
buf[1] += 126;
|
||||
*(uint16_t*)(buf+2) = htons(static_cast<uint16_t>(size));
|
||||
return {buf, buf+4};
|
||||
*(uint16_t*)(buf + 2) = htons(static_cast<uint16_t>(size));
|
||||
return {buf, buf + 4};
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[1] += 127;
|
||||
*reinterpret_cast<uint64_t*>(buf+2) = ((1==htonl(1)) ? static_cast<uint64_t>(size) : (static_cast<uint64_t>(htonl((size) & 0xFFFFFFFF)) << 32) | htonl(static_cast<uint64_t>(size) >> 32));
|
||||
return {buf, buf+10};
|
||||
*reinterpret_cast<uint64_t*>(buf + 2) = ((1 == htonl(1)) ? static_cast<uint64_t>(size) : (static_cast<uint64_t>(htonl((size)&0xFFFFFFFF)) << 32) | htonl(static_cast<uint64_t>(size) >> 32));
|
||||
return {buf, buf + 10};
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Send the HTTP upgrade response.
|
||||
///
|
||||
/// Finishes the handshake process, then starts reading messages from the socket.
|
||||
///
|
||||
void start(std::string&& hello)
|
||||
{
|
||||
static std::string header = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||
|
@ -237,22 +246,25 @@ namespace crow
|
|||
do_read();
|
||||
}
|
||||
|
||||
///
|
||||
/// Read a websocket message.
|
||||
///
|
||||
/// Involves:<br>
|
||||
/// Handling headers (opcodes, size).<br>
|
||||
/// Unmasking the payload.<br>
|
||||
/// Reading the actual payload.<br>
|
||||
///
|
||||
void do_read()
|
||||
{
|
||||
is_reading = true;
|
||||
switch(state_)
|
||||
switch (state_)
|
||||
{
|
||||
case WebSocketReadState::MiniHeader:
|
||||
{
|
||||
mini_header_ = 0;
|
||||
//boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&mini_header_, 1),
|
||||
adaptor_.socket().async_read_some(boost::asio::buffer(&mini_header_, 2),
|
||||
adaptor_.socket().async_read_some(
|
||||
boost::asio::buffer(&mini_header_, 2),
|
||||
[this](const boost::system::error_code& ec, std::size_t
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
bytes_transferred
|
||||
|
@ -317,13 +329,13 @@ namespace crow
|
|||
{
|
||||
remaining_length_ = 0;
|
||||
remaining_length16_ = 0;
|
||||
boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2),
|
||||
boost::asio::async_read(
|
||||
adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2),
|
||||
[this](const boost::system::error_code& ec, std::size_t
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
bytes_transferred
|
||||
#endif
|
||||
)
|
||||
{
|
||||
) {
|
||||
is_reading = false;
|
||||
remaining_length16_ = ntohs(remaining_length16_);
|
||||
remaining_length_ = remaining_length16_;
|
||||
|
@ -352,15 +364,15 @@ namespace crow
|
|||
break;
|
||||
case WebSocketReadState::Len64:
|
||||
{
|
||||
boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&remaining_length_, 8),
|
||||
boost::asio::async_read(
|
||||
adaptor_.socket(), boost::asio::buffer(&remaining_length_, 8),
|
||||
[this](const boost::system::error_code& ec, std::size_t
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
bytes_transferred
|
||||
#endif
|
||||
)
|
||||
{
|
||||
) {
|
||||
is_reading = false;
|
||||
remaining_length_ = ((1==ntohl(1)) ? (remaining_length_) : (static_cast<uint64_t>(ntohl((remaining_length_) & 0xFFFFFFFF)) << 32) | ntohl((remaining_length_) >> 32));
|
||||
remaining_length_ = ((1 == ntohl(1)) ? (remaining_length_) : (static_cast<uint64_t>(ntohl((remaining_length_)&0xFFFFFFFF)) << 32) | ntohl((remaining_length_) >> 32));
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
if (!ec && bytes_transferred != 8)
|
||||
{
|
||||
|
@ -387,13 +399,13 @@ namespace crow
|
|||
case WebSocketReadState::Mask:
|
||||
if (has_mask_)
|
||||
{
|
||||
boost::asio::async_read(adaptor_.socket(), boost::asio::buffer((char*)&mask_, 4),
|
||||
boost::asio::async_read(
|
||||
adaptor_.socket(), boost::asio::buffer((char*)&mask_, 4),
|
||||
[this](const boost::system::error_code& ec, std::size_t
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
bytes_transferred
|
||||
#endif
|
||||
)
|
||||
{
|
||||
) {
|
||||
is_reading = false;
|
||||
#ifdef CROW_ENABLE_DEBUG
|
||||
if (!ec && bytes_transferred != 4)
|
||||
|
@ -427,9 +439,9 @@ namespace crow
|
|||
auto to_read = static_cast<std::uint64_t>(buffer_.size());
|
||||
if (remaining_length_ < to_read)
|
||||
to_read = remaining_length_;
|
||||
adaptor_.socket().async_read_some(boost::asio::buffer(buffer_, static_cast<std::size_t>(to_read)),
|
||||
[this](const boost::system::error_code& ec, std::size_t bytes_transferred)
|
||||
{
|
||||
adaptor_.socket().async_read_some(
|
||||
boost::asio::buffer(buffer_, static_cast<std::size_t>(to_read)),
|
||||
[this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
|
||||
is_reading = false;
|
||||
|
||||
if (!ec)
|
||||
|
@ -470,19 +482,21 @@ namespace crow
|
|||
return (mini_header_ & 0x0f00) >> 8;
|
||||
}
|
||||
|
||||
///
|
||||
/// Process the payload fragment.
|
||||
///
|
||||
/// Unmasks the fragment, checks the opcode, merges fragments into 1 message body, and calls the appropriate handler.
|
||||
///
|
||||
void handle_fragment()
|
||||
{
|
||||
if (has_mask_)
|
||||
{
|
||||
for(decltype(fragment_.length()) i = 0; i < fragment_.length(); i ++)
|
||||
for (decltype(fragment_.length()) i = 0; i < fragment_.length(); i++)
|
||||
{
|
||||
fragment_[i] ^= ((char*)&mask_)[i%4];
|
||||
fragment_[i] ^= ((char*)&mask_)[i % 4];
|
||||
}
|
||||
}
|
||||
switch(opcode())
|
||||
switch (opcode())
|
||||
{
|
||||
case 0: // Continuation
|
||||
{
|
||||
|
@ -555,9 +569,11 @@ namespace crow
|
|||
fragment_.clear();
|
||||
}
|
||||
|
||||
///
|
||||
/// Send the buffers' data through the socket.
|
||||
///
|
||||
/// Also destroyes the object if the Close flag is set.
|
||||
///
|
||||
void do_write()
|
||||
{
|
||||
if (sending_buffers_.empty())
|
||||
|
@ -565,13 +581,13 @@ namespace crow
|
|||
sending_buffers_.swap(write_buffers_);
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
buffers.reserve(sending_buffers_.size());
|
||||
for(auto& s:sending_buffers_)
|
||||
for (auto& s : sending_buffers_)
|
||||
{
|
||||
buffers.emplace_back(boost::asio::buffer(s));
|
||||
}
|
||||
boost::asio::async_write(adaptor_.socket(), buffers,
|
||||
[&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/)
|
||||
{
|
||||
boost::asio::async_write(
|
||||
adaptor_.socket(), buffers,
|
||||
[&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/) {
|
||||
sending_buffers_.clear();
|
||||
if (!ec && !close_connection_)
|
||||
{
|
||||
|
@ -599,6 +615,7 @@ namespace crow
|
|||
if (sending_buffers_.empty() && !is_reading)
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
Adaptor adaptor_;
|
||||
|
||||
|
@ -629,5 +646,5 @@ namespace crow
|
|||
std::function<void(crow::websocket::connection&)> error_handler_;
|
||||
std::function<bool(const crow::request&)> accept_handler_;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace websocket
|
||||
} // namespace crow
|
||||
|
|
|
@ -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();
|
||||
|
@ -54,10 +56,11 @@ TEST_CASE("SSL")
|
|||
size_t x = 0;
|
||||
size_t y = 0;
|
||||
|
||||
while(x < 121){
|
||||
while (x < 121)
|
||||
{
|
||||
y = c.read_some(asio::buffer(buf, 2048));
|
||||
x+=y;
|
||||
buf[y]='\0';
|
||||
x += y;
|
||||
buf[y] = '\0';
|
||||
}
|
||||
|
||||
CHECK(std::string("Hello world, I'm keycrt.") == std::string(buf));
|
||||
|
|
|
@ -22,7 +22,8 @@ int main()
|
|||
auto templ = compile(read_all("template"));
|
||||
auto partials = json::load(read_all("partials"));
|
||||
set_loader([&](std::string name) -> std::string {
|
||||
if (partials.count(name)) {
|
||||
if (partials.count(name))
|
||||
{
|
||||
return partials[name].s();
|
||||
}
|
||||
return "";
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue