aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--flask.h8
-rw-r--r--json.h22
-rw-r--r--routing.h84
-rw-r--r--unittest.cpp51
5 files changed, 63 insertions, 107 deletions
diff --git a/README.md b/README.md
index 6dbf3f2..dd2e1ea 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 c73ef69..ccc2978 100644
--- a/flask.h
+++ b/flask.h
@@ -7,7 +7,7 @@
#include <type_traits>
#include <thread>
-#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<Tag>(std::move(rule));
}
- auto route(std::string&& rule)
- -> typename std::result_of<decltype(&Router::new_rule)(Router, std::string&&)>::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 b46bf8a..705a9bc 100644
--- a/json.h
+++ b/json.h
@@ -267,15 +267,6 @@ namespace flask
return boost::lexical_cast<double>(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 214b8a4..d3601a2 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 <typename Func>
- void operator()(Func&& f)
- {
- static_assert(black_magic::CallHelper<Func, black_magic::S<>>::value,
- "Handler type is mismatched with URL paramters");
- static_assert(!std::is_same<void, decltype(f())>::value,
- "Handler function cannot have void return type; valid return types: string, int, flask::resposne");
- handler_ = [f = std::move(f)]{
- return response(f());
- };
- }
-
- template <typename Func>
- void operator()(std::string name, Func&& f)
- {
- name_ = std::move(name);
- this->operator()<Func>(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<response()> handler_;
- };
-
template <typename ... Args>
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 <typename Func>
@@ -208,6 +162,7 @@ namespace flask
handler_ = [f = std::move(f)](Args ... args){
return response(f(args...));
};
+ handler_with_req_ = nullptr;
}
template <typename Func>
@@ -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 <typename Func>
@@ -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, "<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<std::string>(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 78cb3a2..d10c2e3 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<Flask> server1(&app1, 45451);
Server<Flask> 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));