aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoripknHama <ipknhama@gmail.com>2014-04-20 17:45:10 +0900
committeripknHama <ipknhama@gmail.com>2014-04-20 17:45:59 +0900
commit1173eba33240071a52991f6755623a324742d0b7 (patch)
treedca3c85e2a2a47c935a52ff70508336b50bb42fe
parent5cdffdf9f75997bfa821203123305b9f8c24b463 (diff)
downloadcrow-1173eba33240071a52991f6755623a324742d0b7.tar.gz
crow-1173eba33240071a52991f6755623a324742d0b7.zip
add support for handlers with request argument, add http method
-rw-r--r--Makefile2
-rw-r--r--common.h53
-rw-r--r--example.cpp12
-rw-r--r--flask.h2
-rw-r--r--http_connection.h20
-rw-r--r--http_request.h1
-rw-r--r--parser.h5
-rw-r--r--routing.h127
-rw-r--r--test.py14
-rw-r--r--unittest.cpp2
-rw-r--r--utility.h12
11 files changed, 200 insertions, 50 deletions
diff --git a/Makefile b/Makefile
index 43029b8..a5552e6 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ runtest: example
pkill example
unittest: unittest.cpp routing.h
- g++ -Wall -g -std=c++11 -o unittest unittest.cpp
+ g++ -Wall -g -std=c++11 -o unittest unittest.cpp http-parser/http_parser.c -pthread -lboost_system -lboost_thread -I http-parser/
./unittest
covtest: unittest.cpp routing.h utility.h flask.h http_server.h http_connection.h parser.h http_response.h common.h json.h
diff --git a/common.h b/common.h
index d222738..e0a64bc 100644
--- a/common.h
+++ b/common.h
@@ -1,9 +1,47 @@
#pragma once
#include <string>
+#include <stdexcept>
+#include "utility.h"
namespace flask
{
+ enum class HTTPMethod
+ {
+ DELETE,
+ GET,
+ HEAD,
+ POST,
+ PUT,
+ CONNECT,
+ OPTIONS,
+ TRACE,
+ };
+
+ std::string method_name(HTTPMethod method)
+ {
+ switch(method)
+ {
+ case HTTPMethod::DELETE:
+ return "DELETE";
+ case HTTPMethod::GET:
+ return "GET";
+ case HTTPMethod::HEAD:
+ return "HEAD";
+ case HTTPMethod::POST:
+ return "POST";
+ case HTTPMethod::PUT:
+ return "PUT";
+ case HTTPMethod::CONNECT:
+ return "CONNECT";
+ case HTTPMethod::OPTIONS:
+ return "OPTIONS";
+ case HTTPMethod::TRACE:
+ return "TRACE";
+ }
+ return "invalid";
+ }
+
enum class ParamType
{
INT,
@@ -68,3 +106,18 @@ namespace flask
return string_params[index];
}
}
+
+constexpr flask::HTTPMethod operator "" _method(const char* str, size_t len)
+{
+ return
+ flask::black_magic::is_equ_p(str, "GET", 3) ? flask::HTTPMethod::GET :
+ flask::black_magic::is_equ_p(str, "DELETE", 6) ? flask::HTTPMethod::DELETE :
+ flask::black_magic::is_equ_p(str, "HEAD", 4) ? flask::HTTPMethod::HEAD :
+ flask::black_magic::is_equ_p(str, "POST", 4) ? flask::HTTPMethod::POST :
+ flask::black_magic::is_equ_p(str, "PUT", 3) ? flask::HTTPMethod::PUT :
+ flask::black_magic::is_equ_p(str, "OPTIONS", 7) ? flask::HTTPMethod::OPTIONS :
+ flask::black_magic::is_equ_p(str, "CONNECT", 7) ? flask::HTTPMethod::CONNECT :
+ flask::black_magic::is_equ_p(str, "TRACE", 5) ? flask::HTTPMethod::TRACE :
+ throw std::runtime_error("invalid http method");
+};
+
diff --git a/example.cpp b/example.cpp
index 16116a8..96ea3d4 100644
--- a/example.cpp
+++ b/example.cpp
@@ -13,6 +13,17 @@ int main()
return "Hello World!";
});
+ FLASK_ROUTE(app, "/add_json")
+ ([](const flask::request& req){
+ auto x = flask::json::load(req.body);
+ if (!x)
+ return flask::response(400);
+ int sum = x["a"].i()+x["b"].i();
+ std::ostringstream os;
+ os << sum;
+ return flask::response{os.str()};
+ });
+
FLASK_ROUTE(app, "/json")
([]{
flask::json::wvalue x;
@@ -41,6 +52,5 @@ int main()
//});
app.port(18080)
- .multithreaded()
.run();
}
diff --git a/flask.h b/flask.h
index cf1d9d6..c73ef69 100644
--- a/flask.h
+++ b/flask.h
@@ -7,6 +7,8 @@
#include <type_traits>
#include <thread>
+#define FLASK_ENABLE_LOGGING
+
#include "http_server.h"
#include "utility.h"
#include "routing.h"
diff --git a/http_connection.h b/http_connection.h
index 7c4da26..6338c6f 100644
--- a/http_connection.h
+++ b/http_connection.h
@@ -53,8 +53,28 @@ namespace flask
};
request req = parser_.to_request();
+ if (parser_.http_major == 1 && parser_.http_minor == 0)
+ {
+ // HTTP/1.0
+ if (!(req.headers.count("connection") && boost::iequals(req.headers["connection"],"Keep-Alive")))
+ close_connection_ = true;
+ }
+ else
+ {
+ // HTTP/1.1
+ if (req.headers.count("connection") && req.headers["connection"] == "close")
+ close_connection_ = true;
+ }
+
res = handler_->handle(req);
+#ifdef FLASK_ENABLE_LOGGING
+ std::cerr << "HTTP/" << parser_.http_major << "." << parser_.http_minor << ' ';
+ std::cerr << method_name(req.method);
+ std::cerr << " " << res.code << std::endl;
+ std::cerr << "res body: " << res.body << std::endl;
+#endif
+
static std::string seperator = ": ";
static std::string crlf = "\r\n";
diff --git a/http_request.h b/http_request.h
index c08d51f..9190dac 100644
--- a/http_request.h
+++ b/http_request.h
@@ -6,6 +6,7 @@ namespace flask
{
struct request
{
+ HTTPMethod method;
std::string url;
std::unordered_map<std::string, std::string> headers;
std::string body;
diff --git a/parser.h b/parser.h
index 642e735..f6dc8ae 100644
--- a/parser.h
+++ b/parser.h
@@ -2,6 +2,7 @@
#include <string>
#include <unordered_map>
+#include <boost/algorithm/string.hpp>
#include "http_request.h"
@@ -35,6 +36,7 @@ namespace flask
case 0:
if (!self->header_value.empty())
{
+ boost::algorithm::to_lower(self->header_field);
self->headers.emplace(std::move(self->header_field), std::move(self->header_value));
}
self->header_field.assign(at, at+length);
@@ -66,6 +68,7 @@ namespace flask
HTTPParser* self = static_cast<HTTPParser*>(self_);
if (!self->header_field.empty())
{
+ boost::algorithm::to_lower(self->header_field);
self->headers.emplace(std::move(self->header_field), std::move(self->header_value));
}
return 0;
@@ -127,7 +130,7 @@ namespace flask
request to_request()
{
- return request{std::move(url), std::move(headers), std::move(body)};
+ return request{(HTTPMethod)method, std::move(url), std::move(headers), std::move(body)};
}
std::string url;
diff --git a/routing.h b/routing.h
index 4b3c812..214b8a4 100644
--- a/routing.h
+++ b/routing.h
@@ -20,7 +20,7 @@ namespace flask
class BaseRule
{
public:
- BaseRule(std::string rule)
+ BaseRule(std::string rule) noexcept
: rule_(std::move(rule))
{
}
@@ -29,7 +29,7 @@ namespace flask
{
}
- BaseRule& name(std::string name)
+ BaseRule& name(std::string name) noexcept
{
name_ = std::move(name);
return *this;
@@ -42,17 +42,19 @@ namespace flask
protected:
std::string rule_;
std::string name_;
+
+ friend class Router;
};
class Rule : public BaseRule
{
public:
- Rule(std::string rule)
+ Rule(std::string rule) noexcept
: BaseRule(std::move(rule))
{
}
- Rule& name(std::string name)
+ Rule& name(std::string name) noexcept
{
name_ = std::move(name);
return *this;
@@ -73,12 +75,8 @@ namespace flask
template <typename Func>
void operator()(std::string name, Func&& f)
{
- static_assert(black_magic::CallHelper<Func, black_magic::S<>>::value,
- "Handler type is mismatched with URL paramters");
name_ = std::move(name);
- handler_ = [f = std::move(f)]{
- return response(f());
- };
+ this->operator()<Func>(f);
}
void validate()
@@ -102,62 +100,82 @@ namespace flask
class TaggedRule : public BaseRule
{
private:
- template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2> struct call
+ template <typename H1, typename H2>
+ struct call_params
+ {
+ H1& handler;
+ H2& handler_with_req;
+ const routing_params& params;
+ const request& req;
+ };
+
+ template <typename F, int NInt, int NUint, int NDouble, int NString, typename S1, typename S2>
+ struct call
{
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<int64_t, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F& handler, const routing_params& params)
+ response operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<int64_t, NInt>>;
return call<F, NInt+1, NUint, NDouble, NString,
- black_magic::S<Args1...>, pushed>()(handler, params);
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<uint64_t, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F& handler, const routing_params& params)
+ response operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<uint64_t, NUint>>;
return call<F, NInt, NUint+1, NDouble, NString,
- black_magic::S<Args1...>, pushed>()(handler, params);
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<double, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F& handler, const routing_params& params)
+ response operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<double, NDouble>>;
return call<F, NInt, NUint, NDouble+1, NString,
- black_magic::S<Args1...>, pushed>()(handler, params);
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1, typename ... Args2>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<std::string, Args1...>, black_magic::S<Args2...>>
{
- response operator()(F& handler, const routing_params& params)
+ response operator()(F cparams)
{
using pushed = typename black_magic::S<Args2...>::template push_back<call_pair<std::string, NString>>;
return call<F, NInt, NUint, NDouble, NString+1,
- black_magic::S<Args1...>, pushed>()(handler, params);
+ black_magic::S<Args1...>, pushed>()(cparams);
}
};
template <typename F, int NInt, int NUint, int NDouble, int NString, typename ... Args1>
struct call<F, NInt, NUint, NDouble, NString, black_magic::S<>, black_magic::S<Args1...>>
{
- response operator()(F& handler, const routing_params& params)
+ response operator()(F cparams)
{
- return handler(
- params.get<typename Args1::type>(Args1::pos)...
- );
+ if (cparams.handler)
+ return cparams.handler(
+ cparams.params.template get<typename Args1::type>(Args1::pos)...
+ );
+ if (cparams.handler_with_req)
+ return cparams.handler_with_req(
+ cparams.req,
+ cparams.params.template get<typename Args1::type>(Args1::pos)...
+ );
+#ifdef FLASK_ENABLE_LOGGING
+ std::cerr << "ERROR cannot find handler" << std::endl;
+#endif
+ return response(500);
}
};
public:
@@ -166,7 +184,7 @@ namespace flask
{
}
- TaggedRule<Args...>& name(std::string name)
+ TaggedRule<Args...>& name(std::string name) noexcept
{
name_ = std::move(name);
return *this;
@@ -177,39 +195,67 @@ namespace flask
}
template <typename Func>
- void operator()(Func&& f)
+ typename std::enable_if<black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type
+ operator()(Func&& f)
{
- static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
+ static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
+ black_magic::CallHelper<Func, black_magic::S<flask::request, Args...>>::value
+ ,
"Handler type is mismatched with URL paramters");
static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
- "Handler function cannot have void return type; valid return types: string, int, flask::resposne");
- handler_ = [f = std::move(f)](Args ... args){
- return response(f(args...));
- };
+ "Handler function cannot have void return type; valid return types: string, int, flask::resposne, flask::json::wvalue");
+
+ handler_ = [f = std::move(f)](Args ... args){
+ return response(f(args...));
+ };
}
template <typename Func>
- void operator()(std::string name, Func&& f)
+ typename std::enable_if<!black_magic::CallHelper<Func, black_magic::S<Args...>>::value, void>::type
+ operator()(Func&& f)
{
- static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
+ static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value ||
+ black_magic::CallHelper<Func, black_magic::S<flask::request, Args...>>::value
+ ,
"Handler type is mismatched with URL paramters");
- static_assert(!std::is_same<void, decltype(f(std::declval<Args>()...))>::value,
- "Handler function cannot have void return type; valid return types: string, int, flask::resposne");
+ static_assert(!std::is_same<void, decltype(f(std::declval<flask::request>(), std::declval<Args>()...))>::value,
+ "Handler function cannot have void return type; valid return types: string, int, flask::resposne, flask::json::wvalue");
+
+ handler_with_req_ = [f = std::move(f)](const flask::request& request, Args ... args){
+ return response(f(request, args...));
+ };
+ }
+
+ template <typename Func>
+ void operator()(std::string name, Func&& f)
+ {
name_ = std::move(name);
- handler_ = [f = std::move(f)](Args ... args){
- return response(f(args...));
- };
+ (*this).operator()<Func>(std::forward(f));
}
- response handle(const request&, const routing_params& params)
+ response handle(const request& req, const routing_params& params)
{
//return handler_();
- return call<decltype(handler_), 0, 0, 0, 0, black_magic::S<Args...>, black_magic::S<>>()(handler_, params);
+ return
+ call<
+ call_params<
+ decltype(handler_),
+ decltype(handler_with_req_)>,
+ 0, 0, 0, 0,
+ black_magic::S<Args...>,
+ black_magic::S<>
+ >()(
+ call_params<
+ decltype(handler_),
+ decltype(handler_with_req_)>
+ {handler_, handler_with_req_, params, req}
+ );
//return response(500);
}
private:
std::function<response(Args...)> handler_;
+ std::function<response(flask::request, Args...)> handler_with_req_;
template <typename T, int Pos>
struct call_pair
@@ -599,7 +645,10 @@ public:
if (rule_index >= rules_.size())
throw std::runtime_error("Trie internal structure corrupted!");
-
+#ifdef FLASK_ENABLE_LOGGING
+ std::cerr << req.url << std::endl;
+ std::cerr << rules_[rule_index]->rule_ << std::endl;
+#endif
return rules_[rule_index]->handle(req, found.second);
}
diff --git a/test.py b/test.py
index c7bc552..8af676b 100644
--- a/test.py
+++ b/test.py
@@ -1,8 +1,8 @@
import urllib
-assert "Hello World!" == urllib.urlopen('http://localhost:8080').read()
-assert "About Flask example." == urllib.urlopen('http://localhost:8080/about').read()
-assert 404 == urllib.urlopen('http://localhost:8080/list').getcode()
-assert "3 bottles of beer!" == urllib.urlopen('http://localhost:8080/hello/3').read()
-assert "100 bottles of beer!" == urllib.urlopen('http://localhost:8080/hello/100').read()
-assert "" == urllib.urlopen('http://localhost:8080/hello/500').read()
-assert 400 == urllib.urlopen('http://localhost:8080/hello/500').getcode()
+assert "Hello World!" == urllib.urlopen('http://localhost:18080').read()
+assert "About Flask example." == urllib.urlopen('http://localhost:18080/about').read()
+assert 404 == urllib.urlopen('http://localhost:18080/list').getcode()
+assert "3 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/3').read()
+assert "100 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/100').read()
+assert 400 == urllib.urlopen('http://localhost:18080/hello/500').getcode()
+assert "3" == urllib.urlopen('http://localhost:18080/add_json', data='{"a":1,"b":2}').read()
diff --git a/unittest.cpp b/unittest.cpp
index 5f90551..78cb3a2 100644
--- a/unittest.cpp
+++ b/unittest.cpp
@@ -129,7 +129,7 @@ TEST(RoutingTest)
});
app.validate();
- app.debug_print();
+ //app.debug_print();
{
request req;
diff --git a/utility.h b/utility.h
index c829b85..fd6917e 100644
--- a/utility.h
+++ b/utility.h
@@ -62,6 +62,18 @@ namespace flask
is_valid(s, i+1, f);
}
+ constexpr bool is_equ_p(const char* a, const char* b, unsigned n)
+ {
+ return
+ *a == 0 || *b == 0
+ ? false :
+ n == 0
+ ? true :
+ *a != *b
+ ? false :
+ is_equ_p(a+1, b+1, n-1);
+ }
+
constexpr bool is_equ_n(const_str a, unsigned ai, const_str b, unsigned bi, unsigned n)
{
return