mirror of
https://github.com/CrowCpp/Crow.git
synced 2024-06-07 21:10:44 +00:00
basic middleware test: before_handler
This commit is contained in:
parent
c89cafa820
commit
2748e35430
@ -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>
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,6 @@ namespace crow
|
||||
std::unordered_map<std::string, std::string> headers;
|
||||
std::string body;
|
||||
|
||||
void* middleware_context;
|
||||
void* middleware_context{};
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user