mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
removing black magic; merging router with app
This commit is contained in:
parent
d533a619c9
commit
b33c7a4f29
2
.gitignore
vendored
2
.gitignore
vendored
@ -22,3 +22,5 @@
|
|||||||
|
|
||||||
example
|
example
|
||||||
unittest
|
unittest
|
||||||
|
|
||||||
|
*.swp
|
||||||
|
7
Makefile
7
Makefile
@ -1,6 +1,7 @@
|
|||||||
all: example unittest
|
all: example
|
||||||
example: example.cpp flask.h http_server.h http_connection.h parser.h http_response.h
|
# unittest
|
||||||
g++ -g -std=c++11 -o example example.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/
|
example: example.cpp flask.h http_server.h http_connection.h parser.h http_response.h routing.h
|
||||||
|
g++ -g -std=c++1y -o example example.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/
|
||||||
test: example
|
test: example
|
||||||
pkill example || exit 0
|
pkill example || exit 0
|
||||||
./example &
|
./example &
|
||||||
|
18
example.cpp
18
example.cpp
@ -2,20 +2,26 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
flask::Flask app;
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
app.route("/",
|
flask::Flask app;
|
||||||
[]{
|
|
||||||
|
app.route("/")
|
||||||
|
.name("hello")
|
||||||
|
([]{
|
||||||
return "Hello World!";
|
return "Hello World!";
|
||||||
});
|
});
|
||||||
|
|
||||||
app.route("/about",
|
app.route("/about")
|
||||||
[]{
|
([]{
|
||||||
return "About Flask example.";
|
return "About Flask example.";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//app.route("/hello/<int>");
|
||||||
|
//([]{
|
||||||
|
//return "About Flask example.";
|
||||||
|
//});
|
||||||
|
|
||||||
app.port(8080)
|
app.port(8080)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
12
example.py
12
example.py
@ -5,5 +5,15 @@ app = Flask(__name__)
|
|||||||
def hello():
|
def hello():
|
||||||
return "Hello World!"
|
return "Hello World!"
|
||||||
|
|
||||||
|
@app.route("/about/<path:path>/hello")
|
||||||
|
def hello1(path):
|
||||||
|
return "about1"
|
||||||
|
|
||||||
|
@app.route("/about")
|
||||||
|
def hello2():
|
||||||
|
return "about2"
|
||||||
|
|
||||||
|
print app.url_map
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0")
|
app.run(host="0.0.0.0", port=8888)
|
||||||
|
27
flask.h
27
flask.h
@ -4,9 +4,10 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "http_response.h"
|
|
||||||
#include "http_server.h"
|
#include "http_server.h"
|
||||||
|
#include "routing.h"
|
||||||
|
|
||||||
// TEST
|
// TEST
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -22,20 +23,13 @@ namespace flask
|
|||||||
|
|
||||||
response handle(const request& req)
|
response handle(const request& req)
|
||||||
{
|
{
|
||||||
if (yameHandlers_.count(req.url) == 0)
|
return router_.handle(req);
|
||||||
{
|
|
||||||
return response(404);
|
|
||||||
}
|
|
||||||
return yameHandlers_[req.url]();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
auto route(std::string&& rule)
|
||||||
void route(const std::string& url, F f)
|
-> typename std::result_of<decltype(&Router::new_rule)(Router, std::string&&)>::type
|
||||||
{
|
{
|
||||||
auto yameHandler = [f = std::move(f)]{
|
return router_.new_rule(std::move(rule));
|
||||||
return response(f());
|
|
||||||
};
|
|
||||||
yameHandlers_.emplace(url, yameHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Flask& port(std::uint16_t port)
|
Flask& port(std::uint16_t port)
|
||||||
@ -44,16 +38,21 @@ namespace flask
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validate()
|
||||||
|
{
|
||||||
|
router_.validate();
|
||||||
|
}
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
|
validate();
|
||||||
Server<Flask> server(this, port_);
|
Server<Flask> server(this, port_);
|
||||||
server.run();
|
server.run();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
uint16_t port_ = 80;
|
uint16_t port_ = 80;
|
||||||
|
|
||||||
// Someday I will become real handler!
|
Router router_;
|
||||||
std::unordered_map<std::string, std::function<response()>> yameHandlers_;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/array.hpp>
|
||||||
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "http_response.h"
|
#include "http_response.h"
|
||||||
@ -118,7 +119,7 @@ namespace flask
|
|||||||
tcp::socket socket_;
|
tcp::socket socket_;
|
||||||
Handler* handler_;
|
Handler* handler_;
|
||||||
|
|
||||||
std::array<char, 8192> buffer_;
|
boost::array<char, 8192> buffer_;
|
||||||
|
|
||||||
HTTPParser<Connection> parser_;
|
HTTPParser<Connection> parser_;
|
||||||
response res;
|
response res;
|
||||||
|
@ -33,9 +33,7 @@ namespace flask
|
|||||||
std::unordered_map<std::string, std::string> headers;
|
std::unordered_map<std::string, std::string> headers;
|
||||||
response() {}
|
response() {}
|
||||||
response(int status) : status(status) {}
|
response(int status) : status(status) {}
|
||||||
response(const std::string& body) : body(body) {}
|
response(std::string body) : body(std::move(body)) {}
|
||||||
response(std::string&& body) : body(std::move(body)) {}
|
response(std::string body, int status) : body(std::move(body)), status(status) {}
|
||||||
response(const std::string& body, int status) : body(body), status(status) {}
|
|
||||||
response(std::string&& body, int status) : body(std::move(body)), status(status) {}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
151
routing.h
151
routing.h
@ -5,127 +5,94 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "utility.h"
|
#include "http_response.h"
|
||||||
|
|
||||||
namespace flask
|
namespace flask
|
||||||
{
|
{
|
||||||
namespace black_magic
|
class Rule
|
||||||
{
|
{
|
||||||
constexpr bool is_equ_n(const_str a, int ai, const_str b, int bi, int n)
|
public:
|
||||||
|
explicit Rule(std::string&& rule)
|
||||||
|
: rule_(std::move(rule))
|
||||||
{
|
{
|
||||||
return
|
}
|
||||||
ai + n > a.size() || bi + n > b.size()
|
|
||||||
? false :
|
template <typename Func>
|
||||||
n == 0
|
void operator()(Func&& f)
|
||||||
? true :
|
{
|
||||||
a[ai] != b[bi]
|
handler_ = [f = std::move(f)]{
|
||||||
? false :
|
return response(f());
|
||||||
is_equ_n(a,ai+1,b,bi+1,n-1);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool is_int(const_str s, int i)
|
template <typename Func>
|
||||||
|
void operator()(std::string&& name, Func&& f)
|
||||||
{
|
{
|
||||||
return is_equ_n(s, i, "<int>", 0, 5);
|
name_ = std::move(name);
|
||||||
|
handler_ = [f = std::move(f)]{
|
||||||
|
return response(f());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool is_float(const_str s, int i)
|
bool match(const request& req)
|
||||||
{
|
{
|
||||||
return is_equ_n(s, i, "<float>", 0, 7) ||
|
// FIXME need url parsing
|
||||||
is_equ_n(s, i, "<double>", 0, 8);
|
return req.url == rule_;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool is_str(const_str s, int i)
|
Rule& name(const std::string& name)
|
||||||
{
|
{
|
||||||
return is_equ_n(s, i, "<str>", 0, 5);
|
name_ = name;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
void validate()
|
||||||
constexpr bool is_path(const_str s, int i)
|
|
||||||
{
|
{
|
||||||
return is_equ_n(s, i, "<path>", 0, 6);
|
if (!handler_)
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ...Args>
|
|
||||||
struct Caller
|
|
||||||
{
|
|
||||||
template <typename F>
|
|
||||||
void operator()(F f, Args... args)
|
|
||||||
{
|
{
|
||||||
f(args...);
|
throw std::runtime_error("no handler for url " + rule_);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <int N, typename ... Args> struct S;
|
|
||||||
template <int N, typename Arg, typename ... Args> struct S<N, Arg, Args...> {
|
|
||||||
static_assert(N <= 4+1, "too many routing arguments (maximum 5)");
|
|
||||||
template <typename T>
|
|
||||||
using push = typename std::conditional<(N>4), S<N, Arg, Args...>, S<N+1, Arg, Args..., T>>::type;
|
|
||||||
using pop = S<N-1, Args...>;
|
|
||||||
};
|
|
||||||
template <> struct S<0>
|
|
||||||
{
|
|
||||||
template <typename T>
|
|
||||||
using push = S<1, T>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename F, typename Set>
|
|
||||||
struct CallHelper;
|
|
||||||
template <typename F, int N, typename ...Args>
|
|
||||||
struct CallHelper<F, S<N, Args...>>
|
|
||||||
{
|
|
||||||
template <typename F1, typename ...Args1, typename =
|
|
||||||
decltype(std::declval<F1>()(std::declval<Args1>()...))
|
|
||||||
>
|
|
||||||
static char __test(int);
|
|
||||||
|
|
||||||
template <typename ...>
|
|
||||||
static int __test(...);
|
|
||||||
|
|
||||||
static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(CallHelper<void(), S<0>>::value, "");
|
|
||||||
static_assert(CallHelper<void(int), S<1, int>>::value, "");
|
|
||||||
static_assert(!CallHelper<void(int), S<0>>::value, "");
|
|
||||||
static_assert(!CallHelper<void(int), S<2, int, int>>::value, "");
|
|
||||||
|
|
||||||
template <typename F,
|
|
||||||
typename Set = S<0>>
|
|
||||||
constexpr bool validate_helper(const_str rule, unsigned i=0)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
i == rule.size()
|
|
||||||
? CallHelper<F, Set>::value :
|
|
||||||
is_int(rule, i)
|
|
||||||
? validate_helper<F, typename Set::template push<int>>(rule, find_closing_tag(rule, i+1)+1) :
|
|
||||||
is_float(rule, i)
|
|
||||||
? validate_helper<F, typename Set::template push<double>>(rule, find_closing_tag(rule, i+1)+1) :
|
|
||||||
is_str(rule, i)
|
|
||||||
? validate_helper<F, typename Set::template push<std::string>>(rule, find_closing_tag(rule, i+1)+1) :
|
|
||||||
is_path(rule, i)
|
|
||||||
? validate_helper<F, typename Set::template push<std::string>>(rule, find_closing_tag(rule, i+1)+1) :
|
|
||||||
validate_helper<F, Set>(rule, i+1)
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(validate_helper<void()>("/"),"");
|
response handle(const request&)
|
||||||
static_assert(validate_helper<void(int)>("/<int>"),"");
|
{
|
||||||
static_assert(!validate_helper<void()>("/<int>"),"");
|
return handler_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string rule_;
|
||||||
|
std::string name_;
|
||||||
|
std::function<response()> handler_;
|
||||||
|
};
|
||||||
|
|
||||||
class Router
|
class Router
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr Router(black_magic::const_str rule) : rule(rule)
|
Rule& new_rule(std::string&& rule)
|
||||||
{
|
{
|
||||||
|
rules_.emplace_back(std::move(rule));
|
||||||
|
return rules_.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F>
|
void validate()
|
||||||
constexpr bool validate() const
|
|
||||||
{
|
{
|
||||||
return black_magic::validate_helper<F>(rule);
|
for(auto& rule:rules_)
|
||||||
|
{
|
||||||
|
rule.validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response handle(const request& req)
|
||||||
|
{
|
||||||
|
for(auto& rule : rules_)
|
||||||
|
{
|
||||||
|
if (rule.match(req))
|
||||||
|
{
|
||||||
|
return rule.handle(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response(404);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
black_magic::const_str rule;
|
std::vector<Rule> rules_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
34
unittest.cpp
34
unittest.cpp
@ -1,40 +1,6 @@
|
|||||||
#include "routing.h"
|
#include "routing.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "utility.h"
|
|
||||||
|
|
||||||
using namespace flask;
|
|
||||||
using namespace flask::black_magic;
|
|
||||||
|
|
||||||
template <int N> struct ThrowTest{};
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
throw ThrowTest<is_int("1<int>22",0)>();
|
|
||||||
}
|
|
||||||
catch(ThrowTest<0>)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
throw ThrowTest<is_int("1<int>22",1)>();
|
|
||||||
}
|
|
||||||
catch(ThrowTest<1>)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
constexpr Router r = Router("/");
|
|
||||||
static_assert(r.validate<void()>(), "Good handler");
|
|
||||||
static_assert(!r.validate<void(int)>(), "Bad handler - no int argument");
|
|
||||||
}
|
|
||||||
{
|
|
||||||
constexpr Router r = Router("/blog/<int>");
|
|
||||||
static_assert(!r.validate<void()>(), "Bad handler - need argument");
|
|
||||||
static_assert(r.validate<void(int)>(), "Good handler");
|
|
||||||
static_assert(!r.validate<void(std::string)>(), "Bad handler - int is not convertible to std::string");
|
|
||||||
static_assert(r.validate<void(double)>(), "Acceptable handler - int will be converted to double");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user