removing black magic; merging router with app

This commit is contained in:
ipknHama 2014-04-10 08:17:08 +09:00
parent d533a619c9
commit b33c7a4f29
9 changed files with 105 additions and 155 deletions

2
.gitignore vendored
View File

@ -22,3 +22,5 @@
example example
unittest unittest
*.swp

View File

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

View File

@ -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();
} }

View File

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

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

View File

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

View File

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

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

View File

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