diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | example.cpp | 18 | ||||
-rw-r--r-- | example.py | 12 | ||||
-rw-r--r-- | flask.h | 27 | ||||
-rw-r--r-- | http_connection.h | 3 | ||||
-rw-r--r-- | http_response.h | 6 | ||||
-rw-r--r-- | routing.h | 143 | ||||
-rw-r--r-- | unittest.cpp | 34 |
9 files changed, 101 insertions, 151 deletions
@@ -22,3 +22,5 @@ example unittest + +*.swp @@ -1,6 +1,7 @@ -all: example unittest -example: example.cpp flask.h http_server.h http_connection.h parser.h http_response.h - g++ -g -std=c++11 -o example example.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/ +all: example +# unittest +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 pkill example || exit 0 ./example & diff --git a/example.cpp b/example.cpp index 9357967..c518dc4 100644 --- a/example.cpp +++ b/example.cpp @@ -2,20 +2,26 @@ #include <iostream> -flask::Flask app; - int main() { - app.route("/", - []{ + flask::Flask app; + + app.route("/") + .name("hello") + ([]{ return "Hello World!"; }); - app.route("/about", - []{ + app.route("/about") + ([]{ return "About Flask example."; }); + //app.route("/hello/<int>"); + //([]{ + //return "About Flask example."; + //}); + app.port(8080) .run(); } @@ -5,5 +5,15 @@ app = Flask(__name__) def hello(): 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__": - app.run(host="0.0.0.0") + app.run(host="0.0.0.0", port=8888) @@ -4,9 +4,10 @@ #include <memory> #include <future> #include <stdint.h> +#include <type_traits> -#include "http_response.h" #include "http_server.h" +#include "routing.h" // TEST #include <iostream> @@ -22,20 +23,13 @@ namespace flask response handle(const request& req) { - if (yameHandlers_.count(req.url) == 0) - { - return response(404); - } - return yameHandlers_[req.url](); + return router_.handle(req); } - template <typename F> - void route(const std::string& url, F f) + auto route(std::string&& rule) + -> typename std::result_of<decltype(&Router::new_rule)(Router, std::string&&)>::type { - auto yameHandler = [f = std::move(f)]{ - return response(f()); - }; - yameHandlers_.emplace(url, yameHandler); + return router_.new_rule(std::move(rule)); } Flask& port(std::uint16_t port) @@ -44,16 +38,21 @@ namespace flask return *this; } + void validate() + { + router_.validate(); + } + void run() { + validate(); Server<Flask> server(this, port_); server.run(); } private: uint16_t port_ = 80; - // Someday I will become real handler! - std::unordered_map<std::string, std::function<response()>> yameHandlers_; + Router router_; }; }; diff --git a/http_connection.h b/http_connection.h index 7fd9a43..d5ee1c9 100644 --- a/http_connection.h +++ b/http_connection.h @@ -4,6 +4,7 @@ #include <atomic> #include <boost/asio.hpp> #include <boost/algorithm/string/predicate.hpp> +#include <boost/array.hpp> #include "parser.h" #include "http_response.h" @@ -118,7 +119,7 @@ namespace flask tcp::socket socket_; Handler* handler_; - std::array<char, 8192> buffer_; + boost::array<char, 8192> buffer_; HTTPParser<Connection> parser_; response res; diff --git a/http_response.h b/http_response.h index 7927d6d..4f19848 100644 --- a/http_response.h +++ b/http_response.h @@ -33,9 +33,7 @@ namespace flask std::unordered_map<std::string, std::string> headers; response() {} response(int status) : status(status) {} - response(const std::string& body) : body(body) {} - response(std::string&& body) : body(std::move(body)) {} - response(const std::string& body, int status) : body(body), status(status) {} - response(std::string&& body, int status) : body(std::move(body)), status(status) {} + response(std::string body) : body(std::move(body)) {} + response(std::string body, int status) : body(std::move(body)), status(status) {} }; } @@ -5,127 +5,94 @@ #include <string> #include <tuple> -#include "utility.h" +#include "http_response.h" 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 : - 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, int i) + + template <typename Func> + void operator()(Func&& f) { - return is_equ_n(s, i, "<int>", 0, 5); + handler_ = [f = std::move(f)]{ + return response(f()); + }; } - constexpr bool is_float(const_str s, int i) + template <typename Func> + void operator()(std::string&& name, Func&& f) { - return is_equ_n(s, i, "<float>", 0, 7) || - is_equ_n(s, i, "<double>", 0, 8); + name_ = std::move(name); + handler_ = [f = std::move(f)]{ + return response(f()); + }; } - constexpr bool is_str(const_str s, int i) + bool match(const request& req) { - return is_equ_n(s, i, "<str>", 0, 5); + // FIXME need url parsing + return req.url == rule_; } - constexpr bool is_path(const_str s, int i) + Rule& name(const std::string& name) { - return is_equ_n(s, i, "<path>", 0, 6); + name_ = name; + return *this; } - - template <typename ...Args> - struct Caller + void validate() { - template <typename F> - void operator()(F f, Args... args) + if (!handler_) { - 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) + response handle(const request&) { - 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) - ; + return handler_(); } - static_assert(validate_helper<void()>("/"),""); - static_assert(validate_helper<void(int)>("/<int>"),""); - static_assert(!validate_helper<void()>("/<int>"),""); - } + private: + std::string rule_; + std::string name_; + std::function<response()> handler_; + }; class Router { 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(); + } + + void validate() { + for(auto& rule:rules_) + { + rule.validate(); + } } - template <typename F> - constexpr bool validate() const + response handle(const request& req) { - return black_magic::validate_helper<F>(rule); + for(auto& rule : rules_) + { + if (rule.match(req)) + { + return rule.handle(req); + } + } + return response(404); } private: - black_magic::const_str rule; + std::vector<Rule> rules_; }; } diff --git a/unittest.cpp b/unittest.cpp index 66c58a0..09718c2 100644 --- a/unittest.cpp +++ b/unittest.cpp @@ -1,40 +1,6 @@ #include "routing.h" #include <functional> -#include "utility.h" - -using namespace flask; -using namespace flask::black_magic; - -template <int N> struct ThrowTest{}; 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"); - } } |