forked from qicosmos/rest_rpc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
router.h
182 lines (158 loc) · 7.27 KB
/
router.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#ifndef REST_RPC_ROUTER_H_
#define REST_RPC_ROUTER_H_
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp>
#include "codec.h"
#include "meta_util.hpp"
namespace rest_rpc {
enum class ExecMode { sync, async };
namespace rpc_service {
class connection;
class router : boost::noncopyable {
public:
static router& get() {
static router instance;
return instance;
}
template<ExecMode model, typename Function>
void register_handler(std::string const& name, Function f) {
return register_nonmember_func<model>(name, std::move(f));
}
template<ExecMode model, typename Function, typename Self>
void register_handler(std::string const& name, const Function& f, Self* self) {
return register_member_func<model>(name, f, self);
}
void remove_handler(std::string const& name) { this->map_invokers_.erase(name); }
void set_callback(const std::function<void(const std::string&, const std::string&, connection*,
bool)>& callback) {
callback_to_server_ = callback;
}
template<typename T>
void route(const char* data, std::size_t size, T conn) {
std::string result;
try {
msgpack_codec codec;
auto p = codec.unpack<std::tuple<std::string>>(data, size);
auto& func_name = std::get<0>(p);
auto it = map_invokers_.find(func_name);
if (it == map_invokers_.end()) {
result = codec.pack_args_str(result_code::FAIL, "unknown function: " + func_name);
callback_to_server_(func_name, result, conn, true);
return;
}
ExecMode model;
it->second(conn, data, size, result, model);
if (model == ExecMode::sync && callback_to_server_) {
callback_to_server_(func_name, result, conn, false);
}
} catch (const std::exception& ex) {
msgpack_codec codec;
result = codec.pack_args_str(result_code::FAIL, ex.what());
callback_to_server_("", result, conn, true);
}
}
router() = default;
private:
router(const router&) = delete;
router(router&&) = delete;
template<typename F, size_t... I, typename Arg, typename... Args>
static typename std::result_of<F(connection*, Args...)>::type call_helper(
const F& f, const std::index_sequence<I...>&, const std::tuple<Arg, Args...>& tup, connection* ptr) {
return f(ptr, std::get<I + 1>(tup)...);
}
template<typename F, typename Arg, typename... Args>
static
typename std::enable_if<std::is_void<typename std::result_of<F(connection*, Args...)>::type>::value>::type
call(const F& f, connection* ptr, std::string& result, std::tuple<Arg, Args...>& tp) {
call_helper(f, std::make_index_sequence<sizeof...(Args)>{}, tp, ptr);
result = msgpack_codec::pack_args_str(result_code::OK);
}
template<typename F, typename Arg, typename... Args>
static
typename std::enable_if<!std::is_void<typename std::result_of<F(connection*, Args...)>::type>::value>::type
call(const F& f, connection* ptr, std::string& result, const std::tuple<Arg, Args...>& tp) {
auto r = call_helper(f, std::make_index_sequence<sizeof...(Args)>{}, tp, ptr);
msgpack_codec codec;
result = msgpack_codec::pack_args_str(result_code::OK, r);
}
template<typename F, typename Self, size_t... Indexes, typename Arg, typename... Args>
static typename std::result_of<F(Self, connection*, Args...)>::type call_member_helper(
const F& f, Self* self, const std::index_sequence<Indexes...>&,
const std::tuple<Arg, Args...>& tup, connection* ptr = 0) {
return (*self.*f)(ptr, std::get<Indexes + 1>(tup)...);
}
template<typename F, typename Self, typename Arg, typename... Args>
static typename std::enable_if<
std::is_void<typename std::result_of<F(Self, connection*, Args...)>::type>::value>::type
call_member(const F& f, Self* self, connection* ptr, std::string& result,
const std::tuple<Arg, Args...>& tp) {
call_member_helper(f, self, typename std::make_index_sequence<sizeof...(Args)>{}, tp, ptr);
result = msgpack_codec::pack_args_str(result_code::OK);
}
template<typename F, typename Self, typename Arg, typename... Args>
static typename std::enable_if<
!std::is_void<typename std::result_of<F(Self, connection*, Args...)>::type>::value>::type
call_member(const F& f, Self* self, connection* ptr, std::string& result,
const std::tuple<Arg, Args...>& tp) {
auto r =
call_member_helper(f, self, typename std::make_index_sequence<sizeof...(Args)>{}, tp, ptr);
result = msgpack_codec::pack_args_str(result_code::OK, r);
}
template<typename Function, ExecMode mode = ExecMode::sync>
struct invoker {
template<ExecMode model>
static inline void apply(const Function& func, connection* conn, const char* data, size_t size,
std::string& result, ExecMode& exe_model) {
using args_tuple = typename function_traits<Function>::args_tuple_2nd;
exe_model = ExecMode::sync;
msgpack_codec codec;
try {
auto tp = codec.unpack<args_tuple>(data, size);
call(func, conn, result, tp);
exe_model = model;
} catch (std::invalid_argument& e) {
result = codec.pack_args_str(result_code::FAIL, e.what());
} catch (const std::exception& e) {
result = codec.pack_args_str(result_code::FAIL, e.what());
}
}
template<ExecMode model, typename Self>
static inline void apply_member(const Function& func, Self* self, connection* conn,
const char* data, size_t size, std::string& result,
ExecMode& exe_model) {
using args_tuple = typename function_traits<Function>::args_tuple_2nd;
exe_model = ExecMode::sync;
msgpack_codec codec;
try {
auto tp = codec.unpack<args_tuple>(data, size);
call_member(func, self, conn, result, tp);
exe_model = model;
} catch (std::invalid_argument& e) {
result = codec.pack_args_str(result_code::FAIL, e.what());
} catch (const std::exception& e) {
result = codec.pack_args_str(result_code::FAIL, e.what());
}
}
};
template<ExecMode model, typename Function>
void register_nonmember_func(std::string const& name, Function f) {
this->map_invokers_[name] = {std::bind(&invoker<Function>::template apply<model>, std::move(f), std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3,
std::placeholders::_4, std::placeholders::_5)};
}
template<ExecMode model, typename Function, typename Self>
void register_member_func(const std::string& name, const Function& f, Self* self) {
this->map_invokers_[name] = {std::bind(&invoker<Function>::template apply_member<model, Self>,
f, self, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5)};
}
std::map<std::string,
std::function<void(connection*, const char*, size_t, std::string&, ExecMode& model)>>
map_invokers_;
std::function<void(const std::string&, const std::string&, connection*, bool)>
callback_to_server_;
};
} // namespace rpc_service
} // namespace rest_rpc
#endif // REST_RPC_ROUTER_H_