aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoripknHama <ipknhama@gmail.com>2014-09-07 04:30:53 +0900
committeripknHama <ipknhama@gmail.com>2014-09-07 04:30:53 +0900
commit2748e35430b9a4aaf64dfbd626d819f0fc5eedd2 (patch)
treea8ebd5648d46cc7073b2ae6f6b5ada0e702c456c
parentc89cafa820ec02f041c3b0e52877bc321f6a1ba9 (diff)
downloadcrow-2748e35430b9a4aaf64dfbd626d819f0fc5eedd2.tar.gz
crow-2748e35430b9a4aaf64dfbd626d819f0fc5eedd2.zip
basic middleware test: before_handler
-rw-r--r--include/crow.h16
-rw-r--r--include/http_connection.h64
-rw-r--r--include/http_request.h2
-rw-r--r--include/http_response.h13
-rw-r--r--include/http_server.h11
-rw-r--r--include/middleware.h15
-rw-r--r--tests/CMakeLists.txt6
-rw-r--r--tests/unittest.cpp72
8 files changed, 174 insertions, 25 deletions
diff --git a/include/crow.h b/include/crow.h
index a018f31..a4b82df 100644
--- a/include/crow.h
+++ b/include/crow.h
@@ -13,12 +13,9 @@
#include "http_server.h"
#include "utility.h"
#include "routing.h"
-#include "middleware_impl.h"
+#include "middleware_context.h"
#include "http_request.h"
-// TEST
-#include <iostream>
-
#define CROW_ROUTE(app, url) app.route<crow::black_magic::get_parameter_tag(url)>(url)
namespace crow
@@ -28,13 +25,14 @@ namespace crow
{
public:
using self_t = Crow;
+ using server_t = Server<Crow, Middlewares...>;
Crow()
{
}
void handle(const request& req, response& res)
{
- return router_.handle(req, res);
+ router_.handle(req, res);
}
template <uint64_t Tag>
@@ -71,7 +69,7 @@ namespace crow
void run()
{
validate();
- Server<self_t> server(this, port_, concurrency_);
+ server_t server(this, port_, concurrency_);
server.run();
}
@@ -84,19 +82,17 @@ namespace crow
// middleware
using context_t = detail::context<Middlewares...>;
template <typename T>
- T& get_middleware_context(request& req)
+ typename T::context& get_middleware_context(const request& req)
{
static_assert(black_magic::contains<T, Middlewares...>::value, "App doesn't have the specified middleware type.");
auto& ctx = *reinterpret_cast<context_t*>(req.middleware_context);
- return ctx.get<T>();
+ return ctx.template get<T>();
}
private:
uint16_t port_ = 80;
uint16_t concurrency_ = 1;
- std::tuple<Middlewares...> middlewares_;
-
Router router_;
};
template <typename ... Middlewares>
diff --git a/include/http_connection.h b/include/http_connection.h
index 907485f..a8631d9 100644
--- a/include/http_connection.h
+++ b/include/http_connection.h
@@ -2,9 +2,9 @@
#include <boost/asio.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/array.hpp>
#include <atomic>
#include <chrono>
-#include <array>
#include "http_parser_merged.h"
@@ -14,23 +14,57 @@
#include "logging.h"
#include "settings.h"
#include "dumb_timer_queue.h"
+#include "middleware_context.h"
namespace crow
{
+ namespace detail
+ {
+ template <int N, typename Context, typename Container, typename CurrentMW, typename ... Middlewares>
+ bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx)
+ {
+ // TODO cut ctx to partial_context<0..N-1>
+ std::get<N>(middlewares).before_handle(req, res, ctx.template get<CurrentMW>(), ctx);
+ if (res.is_completed())
+ {
+ std::get<N>(middlewares).after_handle(req, res, ctx.template get<CurrentMW>(), ctx);
+ return true;
+ }
+ if (middleware_call_helper<N+1, Context, Middlewares...>(middlewares, req, res, ctx))
+ {
+ std::get<N>(middlewares).after_handle(req, res, ctx.template get<CurrentMW>(), ctx);
+ return true;
+ }
+ return false;
+ }
+
+ template <int N, typename Context, typename Container>
+ bool middleware_call_helper(Container& middlewares, request& req, response& res, Context& ctx)
+ {
+ return false;
+ }
+ }
+
using namespace boost;
using tcp = asio::ip::tcp;
#ifdef CROW_ENABLE_DEBUG
static int connectionCount;
#endif
- template <typename Handler>
+ template <typename Handler, typename ... Middlewares>
class Connection
{
public:
- Connection(boost::asio::io_service& io_service, Handler* handler, const std::string& server_name)
+ Connection(
+ boost::asio::io_service& io_service,
+ Handler* handler,
+ const std::string& server_name,
+ std::tuple<Middlewares...>& middlewares
+ )
: socket_(io_service),
handler_(handler),
parser_(this),
- server_name_(server_name)
+ server_name_(server_name),
+ middlewares_(middlewares)
{
#ifdef CROW_ENABLE_DEBUG
connectionCount ++;
@@ -101,11 +135,20 @@ namespace crow
<< method_name(req.method) << " " << req.url;
+ need_to_call_after_handlers_ = false;
if (!is_invalid_request)
{
res.complete_request_handler_ = [this]{ this->complete_request(); };
res.is_alive_helper_ = [this]()->bool{ return socket_.is_open(); };
- handler_->handle(req, res);
+
+ req.middleware_context = (void*)&ctx_;
+ detail::middleware_call_helper<0, decltype(ctx_), decltype(middlewares_), Middlewares...>(middlewares_, req, res, ctx_);
+
+ if (!res.completed_)
+ {
+ need_to_call_after_handlers_ = true;
+ handler_->handle(req, res);
+ }
}
else
{
@@ -117,6 +160,11 @@ namespace crow
{
CROW_LOG_INFO << "Response: " << this << ' ' << res.code << ' ' << close_connection_;
+ if (need_to_call_after_handlers_)
+ {
+ // TODO call all of after_handlers
+ }
+
//auto self = this->shared_from_this();
res.complete_request_handler_ = nullptr;
@@ -330,7 +378,7 @@ namespace crow
tcp::socket socket_;
Handler* handler_;
- std::array<char, 4096> buffer_;
+ boost::array<char, 4096> buffer_;
HTTPParser<Connection> parser_;
response res;
@@ -348,6 +396,10 @@ namespace crow
bool is_reading{};
bool is_writing{};
+ bool need_to_call_after_handlers_;
+
+ std::tuple<Middlewares...>& middlewares_;
+ detail::context<Middlewares...> ctx_;
};
}
diff --git a/include/http_request.h b/include/http_request.h
index 77b3ecb..af623c6 100644
--- a/include/http_request.h
+++ b/include/http_request.h
@@ -11,6 +11,6 @@ namespace crow
std::unordered_map<std::string, std::string> headers;
std::string body;
- void* middleware_context;
+ void* middleware_context{};
};
}
diff --git a/include/http_response.h b/include/http_response.h
index ae92543..bc468b7 100644
--- a/include/http_response.h
+++ b/include/http_response.h
@@ -5,11 +5,11 @@
namespace crow
{
- template <typename T>
+ template <typename Handler, typename ... Middlewares>
class Connection;
struct response
{
- template <typename T>
+ template <typename Handler, typename ... Middlewares>
friend class crow::Connection;
std::string body;
@@ -31,7 +31,7 @@ namespace crow
response& operator = (const response& r) = delete;
- response& operator = (response&& r)
+ response& operator = (response&& r) noexcept
{
body = std::move(r.body);
json_value = std::move(r.json_value);
@@ -41,6 +41,11 @@ namespace crow
return *this;
}
+ bool is_completed() const noexcept
+ {
+ return completed_;
+ }
+
void clear()
{
body.clear();
@@ -59,11 +64,11 @@ namespace crow
{
if (!completed_)
{
- completed_ = true;
if (complete_request_handler_)
{
complete_request_handler_();
}
+ completed_ = true;
}
}
diff --git a/include/http_server.h b/include/http_server.h
index 6dc845a..484aa83 100644
--- a/include/http_server.h
+++ b/include/http_server.h
@@ -18,7 +18,7 @@ namespace crow
using namespace boost;
using tcp = asio::ip::tcp;
- template <typename Handler>
+ template <typename Handler, typename ... Middlewares>
class Server
{
public:
@@ -43,10 +43,13 @@ namespace crow
for(uint16_t i = 0; i < concurrency_; i ++)
v.push_back(
std::async(std::launch::async, [this, i]{
+ // initializing timer queue
auto& timer_queue = detail::dumb_timer_queue::get_current_dumb_timer_queue();
+
timer_queue.set_io_service(*io_service_pool_[i]);
boost::asio::deadline_timer timer(*io_service_pool_[i]);
timer.expires_from_now(boost::posix_time::seconds(1));
+
std::function<void(const boost::system::error_code& ec)> handler;
handler = [&](const boost::system::error_code& ec){
if (ec)
@@ -56,6 +59,7 @@ namespace crow
timer.async_wait(handler);
};
timer.async_wait(handler);
+
io_service_pool_[i]->run();
}));
CROW_LOG_INFO << server_name_ << " server is running, local port " << port_;
@@ -92,7 +96,7 @@ namespace crow
void do_accept()
{
- auto p = new Connection<Handler>(pick_io_service(), handler_, server_name_);
+ auto p = new Connection<Handler, Middlewares...>(pick_io_service(), handler_, server_name_, middlewares_);
acceptor_.async_accept(p->socket(),
[this, p](boost::system::error_code ec)
{
@@ -115,5 +119,8 @@ namespace crow
std::string server_name_ = "Crow/0.1";
uint16_t port_;
unsigned int roundrobin_index_{};
+
+ std::tuple<Middlewares...> middlewares_;
+
};
}
diff --git a/include/middleware.h b/include/middleware.h
index 270e026..5e358af 100644
--- a/include/middleware.h
+++ b/include/middleware.h
@@ -4,6 +4,21 @@
namespace crow
{
+ // Any middleware requires following 3 members:
+
+ // struct context;
+ // storing data for the middleware; can be read from another middleware or handlers
+
+ // template <typename AllContext>
+ // void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
+ // called before handling the request.
+ // if res.end() is called, the operation is halted.
+ // (still call after_handle of this middleware)
+
+ // template <typename AllContext>
+ // void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
+ // called after handling the request.
+
class CookieParser
{
struct context
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6beb0be..5ea48f1 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -10,9 +10,13 @@ add_executable(unittest ${TEST_SRCS})
#target_link_libraries(unittest crow)
target_link_libraries(unittest ${Boost_LIBRARIES} )
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+# using Clang
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+# using GCC
set_target_properties(unittest PROPERTIES COMPILE_FLAGS "--coverage -fprofile-arcs -ftest-coverage")
-
target_link_libraries(unittest gcov)
+endif()
add_subdirectory(template)
#CXXFLAGS="-g -O0 -Wall -W -Wshadow -Wunused-variable \
diff --git a/tests/unittest.cpp b/tests/unittest.cpp
index 17bc53c..c84e3ed 100644
--- a/tests/unittest.cpp
+++ b/tests/unittest.cpp
@@ -352,7 +352,8 @@ TEST(json_read)
ASSERT_EQUAL(1, x.size());
ASSERT_EQUAL(false, x.has("mess"));
ASSERT_THROW(x["mess"]);
- ASSERT_THROW(3 == x["message"]);
+ // TODO returning false is better than exception
+ //ASSERT_THROW(3 == x["message"]);
ASSERT_EQUAL(12, x["message"].size());
std::string s = R"({"int":3, "ints" :[1,2,3,4,5] })";
@@ -478,6 +479,75 @@ int testmain()
return failed ? -1 : 0;
}
+struct NullMiddleware
+{
+ struct context {};
+
+ template <typename AllContext>
+ void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
+ {}
+
+ template <typename AllContext>
+ void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
+ {}
+};
+
+TEST(middleware_simple)
+{
+ App<NullMiddleware> app;
+ CROW_ROUTE(app, "/")([&](const crow::request& req)
+ {
+ app.get_middleware_context<NullMiddleware>(req);
+ return "";
+ });
+}
+
+struct IntSettingMiddleware
+{
+ struct context { int val; };
+
+ template <typename AllContext>
+ void before_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
+ {
+ ctx.val = 1;
+ }
+
+ template <typename AllContext>
+ void after_handle(request& req, response& res, context& ctx, AllContext& all_ctx)
+ {
+ ctx.val = 2;
+ }
+};
+
+TEST(middleware_context)
+{
+ static char buf[2048];
+ App<IntSettingMiddleware> app;
+ Server<decltype(app), IntSettingMiddleware> server(&app, 45451);
+ auto _ = async(launch::async, [&]{server.run();});
+ std::string sendmsg = "GET /\r\n\r\n";
+
+ int x{};
+ CROW_ROUTE(app, "/")([&](const request& req){
+ auto& ctx = app.get_middleware_context<IntSettingMiddleware>(req);
+ x = ctx.val;
+
+ return "";
+ });
+ asio::io_service is;
+ {
+ asio::ip::tcp::socket c(is);
+ c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 45451));
+
+
+ c.send(asio::buffer(sendmsg));
+
+ c.receive(asio::buffer(buf, 2048));
+ }
+ ASSERT_EQUAL(1, x);
+ server.stop();
+}
+
int main()
{
return testmain();