diff --git a/README.md b/README.md index 6dbf3f210..dd2e1ea95 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,3 @@ Flask++ Simple C++ HTTP Server (inspired by Python Flask) - -Other JSON library -================== - -https://github.com/d-led/picojson_serializer diff --git a/flask.h b/flask.h index c73ef69b6..ccc2978f6 100644 --- a/flask.h +++ b/flask.h @@ -7,7 +7,7 @@ #include #include -#define FLASK_ENABLE_LOGGING +//#define FLASK_ENABLE_LOGGING #include "http_server.h" #include "utility.h" @@ -39,12 +39,6 @@ namespace flask return router_.new_rule_tagged(std::move(rule)); } - auto route(std::string&& rule) - -> typename std::result_of::type - { - return router_.new_rule(std::move(rule)); - } - Flask& port(std::uint16_t port) { port_ = port; diff --git a/json.h b/json.h index b46bf8a37..705a9bcd2 100644 --- a/json.h +++ b/json.h @@ -267,15 +267,6 @@ namespace flask return boost::lexical_cast(start_, end_-start_); } - detail::r_string s_raw() const - { -#ifndef FLASKPP_JSON_NO_ERROR_CHECK - if (t() != type::String) - throw std::runtime_error("value is not string"); -#endif - return detail::r_string{start_, (uint32_t)(end_-start_), has_escaping()}; - } - detail::r_string s() const { #ifndef FLASKPP_JSON_NO_ERROR_CHECK @@ -481,7 +472,7 @@ namespace flask case type::False: os << "false"; break; case type::True: os << "true"; break; case type::Number: os << r.d(); break; - case type::String: os << '"' << r.s_raw() << '"'; break; + case type::String: os << '"' << r.s() << '"'; break; case type::List: { os << '['; @@ -591,7 +582,7 @@ namespace flask uint8_t has_escaping = 0; while(1) { - if (flask_json_likely(*data != '"' && *data != '\\')) + if (flask_json_likely(*data != '"' && *data != '\\' && *data != '\0')) { data ++; } @@ -625,6 +616,8 @@ namespace flask return {}; } } + else + return {}; } return {}; } @@ -906,9 +899,9 @@ namespace flask rvalue parse() { ws_skip(); - auto ret = decode_object(); // or decode value? + auto ret = decode_value(); // or decode object? ws_skip(); - if (*data != '\0') + if (ret && *data != '\0') ret.set_error(); return ret; } @@ -920,7 +913,8 @@ namespace flask inline rvalue load(const char* data, size_t size) { char* s = new char[size+1]; - memcpy(s, data, size+1); + memcpy(s, data, size); + s[size] = 0; auto ret = load_nocopy_internal(s, size); if (ret) ret.key_.force(s, size); diff --git a/routing.h b/routing.h index 214b8a47e..d3601a2be 100644 --- a/routing.h +++ b/routing.h @@ -46,56 +46,6 @@ namespace flask friend class Router; }; - class Rule : public BaseRule - { - public: - Rule(std::string rule) noexcept - : BaseRule(std::move(rule)) - { - } - - Rule& name(std::string name) noexcept - { - name_ = std::move(name); - return *this; - } - - template - void operator()(Func&& f) - { - static_assert(black_magic::CallHelper>::value, - "Handler type is mismatched with URL paramters"); - static_assert(!std::is_same::value, - "Handler function cannot have void return type; valid return types: string, int, flask::resposne"); - handler_ = [f = std::move(f)]{ - return response(f()); - }; - } - - template - void operator()(std::string name, Func&& f) - { - name_ = std::move(name); - this->operator()(f); - } - - void validate() - { - if (!handler_) - { - throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_); - } - } - - response handle(const request&, const routing_params&) - { - return handler_(); - } - - protected: - std::function handler_; - }; - template class TaggedRule : public BaseRule { @@ -192,6 +142,10 @@ namespace flask void validate() { + if (!handler_ && !handler_with_req_) + { + throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_); + } } template @@ -208,6 +162,7 @@ namespace flask handler_ = [f = std::move(f)](Args ... args){ return response(f(args...)); }; + handler_with_req_ = nullptr; } template @@ -224,6 +179,7 @@ namespace flask handler_with_req_ = [f = std::move(f)](const flask::request& request, Args ... args){ return response(f(request, args...)); }; + handler_ = nullptr; } template @@ -481,8 +437,6 @@ public: char c = url[i]; if (c == '<') { - bool found = false; - static struct ParamTraits { ParamType type; @@ -498,27 +452,21 @@ public: { ParamType::PATH, "" }, }; - for(auto it = std::begin(paramTraits); it != std::end(paramTraits); ++it) + for(auto& x:paramTraits) { - if (url.compare(i, it->name.size(), it->name) == 0) + if (url.compare(i, x.name.size(), x.name) == 0) { - if (!nodes_[idx].param_childrens[(int)it->type]) + if (!nodes_[idx].param_childrens[(int)x.type]) { auto new_node_idx = new_node(); - nodes_[idx].param_childrens[(int)it->type] = new_node_idx; + nodes_[idx].param_childrens[(int)x.type] = new_node_idx; } - idx = nodes_[idx].param_childrens[(int)it->type]; - i += it->name.size(); - found = true; + idx = nodes_[idx].param_childrens[(int)x.type]; + i += x.name.size(); break; } } - if (!found) - { - throw std::runtime_error("Invalid parameter type: " + url + - " (" + boost::lexical_cast(i) + ")"); - } i --; } else @@ -616,14 +564,6 @@ public: return *ruleObject; } - Rule& new_rule(const std::string& rule) - { - Rule* r(new Rule(rule)); - rules_.emplace_back(r); - trie_.add(rule, rules_.size() - 1); - return *r; - } - void validate() { trie_.validate(); diff --git a/unittest.cpp b/unittest.cpp index 78cb3a2f5..d10c2e3f7 100644 --- a/unittest.cpp +++ b/unittest.cpp @@ -29,7 +29,7 @@ void fail(Args...args) { error_print(args...);failed__ = true; } #define ASSERT_TRUE(x) if (!(x)) fail("Assert fail: expected ", #x, " is true, at " __FILE__ ":",__LINE__) #define ASSERT_EQUAL(a, b) if ((a) != (b)) fail("Assert fail: expected ", (a), " actual " , (b), ", " #a " == " #b ", at " __FILE__ ":",__LINE__) -#define ASSERT_NOTEQUAL(a, b) if ((a) != (b)) fail("Assert fail: not expected ", (a), ", " #a " != " #b ", at " __FILE__ ":",__LINE__) +#define ASSERT_NOTEQUAL(a, b) if ((a) == (b)) fail("Assert fail: not expected ", (a), ", " #a " != " #b ", at " __FILE__ ":",__LINE__) #define ASSERT_THROW(x) \ try \ { \ @@ -49,14 +49,14 @@ void fail(Args...args) { error_print(args...);failed__ = true; } TEST(Rule) { - Rule r("/http/"); + TaggedRule<> r("/http/"); r.name("abc"); // empty handler - fail to validate try { r.validate(); - fail(); + fail("empty handler should fail to validate"); } catch(runtime_error& e) { @@ -73,6 +73,16 @@ TEST(Rule) ASSERT_EQUAL(0, x); r.handle(request(), routing_params()); ASSERT_EQUAL(1, x); + + // registering handler with request argument + r([&x](const flask::request&){x = 2;return "";}); + + r.validate(); + + // executing handler + ASSERT_EQUAL(1, x); + r.handle(request(), routing_params()); + ASSERT_EQUAL(2, x); } TEST(ParameterTagging) @@ -211,8 +221,8 @@ TEST(multi_server) { static char buf[2048]; Flask app1, app2; - app1.route("/")([]{return "A";}); - app2.route("/")([]{return "B";}); + FLASK_ROUTE(app1, "/")([]{return "A";}); + FLASK_ROUTE(app2, "/")([]{return "B";}); Server server1(&app1, 45451); Server server2(&app2, 45452); @@ -252,9 +262,29 @@ TEST(multi_server) TEST(json_read) { { - auto x = json::load("{} 3"); - if (x) - fail("should fail to parse"); + const char* json_error_tests[] = + { + "{} 3", "{{}", "{3}", + "3.4.5", "+3", "3-2", "00", "03", "1e3e3", "1e+.3", + "nll", "f", "t", + "{\"x\":3,}", + "{\"x\"}", + "{\"x\":3 q}", + "{\"x\":[3 4]}", + "{\"x\":[\"", + "{\"x\":[[], 4],\"y\",}", + "{\"x\":[3", + "{\"x\":[ null, false, true}", + }; + for(auto s:json_error_tests) + { + auto x = json::load(s); + if (x) + { + fail("should fail to parse ", s); + return; + } + } } auto x = json::load(R"({"message":"hello, world"})"); @@ -270,6 +300,8 @@ TEST(json_read) std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })"; auto y = json::load(s); ASSERT_EQUAL(3, y["int"]); + ASSERT_EQUAL(3.0, y["int"]); + ASSERT_NOTEQUAL(3.01, y["int"]); ASSERT_EQUAL(5, y["ints"].size()); ASSERT_EQUAL(1, y["ints"][0]); ASSERT_EQUAL(2, y["ints"][1]); @@ -308,7 +340,8 @@ TEST(json_write) y["scores"][2][0] = "real"; y["scores"][2][1] = false; - ASSERT_EQUAL(R"({"scores":[1,"king",["real",false]]})", json::dump(y)); + y["scores"][2][2] = true; + ASSERT_EQUAL(R"({"scores":[1,"king",["real",false,true]]})", json::dump(y)); y["scores"]["a"]["b"]["c"] = nullptr; ASSERT_EQUAL(R"({"scores":{"a":{"b":{"c":null}}}})", json::dump(y));