15 : m_host(endpoint.first),
16 m_port(endpoint.second),
17 m_enable_cors(enable_cors) {}
24 auto sock = MHD_quiesce_daemon(m_daemon);
29 = MHD_get_daemon_info(m_daemon,
30 MHD_DAEMON_INFO_CURRENT_CONNECTIONS);
31 if(inf->num_connections == 0) {
36 constexpr auto wait_time = std::chrono::milliseconds(100);
37 std::this_thread::sleep_for(wait_time);
43 MHD_stop_daemon(m_daemon);
53 = (MHD_is_feature_supported(MHD_FEATURE_EPOLL) == MHD_YES);
55 = has_epoll ? MHD_USE_EPOLL_INTERNALLY : MHD_USE_POLL_INTERNALLY;
56 auto connection_limit = has_epoll ? 65536 : FD_SETSIZE - 4;
57 auto addr = sockaddr_in{};
58 addr.sin_family = AF_INET;
59 addr.sin_port = htons(m_port);
60 inet_aton(m_host.c_str(),
61 reinterpret_cast<in_addr*
>(&addr.sin_addr.s_addr));
62 m_daemon = MHD_start_daemon(use_flag | MHD_ALLOW_SUSPEND_RESUME
69 MHD_OPTION_NOTIFY_COMPLETED,
72 MHD_OPTION_THREAD_POOL_SIZE,
73 std::thread::hardware_concurrency(),
74 MHD_OPTION_CONNECTION_TIMEOUT,
76 MHD_OPTION_CONNECTION_LIMIT,
81 return m_daemon !=
nullptr;
84 auto json_rpc_http_server::callback(
void* cls,
85 struct MHD_Connection* connection,
89 const char* upload_data,
90 size_t* upload_data_size,
91 void** con_cls) -> MHD_Result {
92 if(*con_cls ==
nullptr) {
93 auto new_req = std::make_unique<request>();
94 new_req->m_connection = connection;
96 new_req->m_server =
server;
97 new_req->m_origin = MHD_lookup_connection_value(connection,
100 *con_cls = new_req.get();
102 std::unique_lock l(
server->m_requests_mut);
103 server->m_requests.emplace(new_req.get(), std::move(new_req));
109 auto* req =
static_cast<request*
>(*con_cls);
112 if(method == std::string(
"OPTIONS") && req->m_server->m_enable_cors) {
113 send_cors_response(req);
117 if(method != std::string(
"POST")) {
118 req->m_code = MHD_HTTP_METHOD_NOT_ALLOWED;
119 send_response(
"HTTP method not allowed", req);
123 if(*upload_data_size != 0) {
124 req->m_request.write(
126 static_cast<std::streamsize
>(*upload_data_size));
127 *upload_data_size = 0;
131 if(!req->m_server->m_running) {
132 req->m_code = MHD_HTTP_SERVICE_UNAVAILABLE;
133 send_response(
"Server is shutting down", req);
139 auto success = req->m_server->handle_request(req);
141 req->m_code = MHD_HTTP_BAD_REQUEST;
142 send_response(
"Invalid request payload", req);
148 auto json_rpc_http_server::send_cors_response(request* request_info)
151 auto* result = MHD_create_response_from_buffer(
153 static_cast<void*
>(
response.data()),
154 MHD_RESPMEM_MUST_COPY);
155 if(!request_info->m_origin) {
156 request_info->m_origin =
"*";
158 MHD_add_response_header(result,
159 "Access-Control-Allow-Origin",
160 request_info->m_origin);
162 MHD_add_response_header(result,
163 "Access-Control-Allow-Methods",
165 MHD_add_response_header(result,
166 "Access-Control-Allow-Headers",
168 MHD_add_response_header(result,
"Access-Control-Max-Age",
"600");
169 MHD_add_response_header(result,
"Vary",
"Origin");
170 MHD_add_response_header(result,
172 "Access-Control-Request-Method");
173 MHD_add_response_header(result,
175 "Access-Control-Request-Headers");
176 auto ret = MHD_queue_response(request_info->m_connection, 200, result);
177 MHD_destroy_response(result);
178 const auto* inf = MHD_get_connection_info(
179 request_info->m_connection,
180 MHD_CONNECTION_INFO_CONNECTION_SUSPENDED);
181 if(inf->suspended == MHD_YES) {
182 MHD_resume_connection(request_info->m_connection);
184 return ret == MHD_YES;
187 auto json_rpc_http_server::send_response(std::string response,
188 request* request_info) ->
bool {
189 auto* result = MHD_create_response_from_buffer(
191 static_cast<void*
>(
response.data()),
192 MHD_RESPMEM_MUST_COPY);
194 if(request_info->m_server->m_enable_cors) {
195 if(!request_info->m_origin) {
196 request_info->m_origin =
"*";
198 MHD_add_response_header(result,
199 "Access-Control-Allow-Origin",
200 request_info->m_origin);
201 MHD_add_response_header(result,
"Vary",
"Origin");
204 MHD_add_response_header(result,
"Content-Type",
"application/json");
205 auto ret = MHD_queue_response(request_info->m_connection,
206 request_info->m_code,
208 MHD_destroy_response(result);
209 const auto* inf = MHD_get_connection_info(
210 request_info->m_connection,
211 MHD_CONNECTION_INFO_CONNECTION_SUSPENDED);
212 if(inf->suspended == MHD_YES) {
213 MHD_resume_connection(request_info->m_connection);
215 return ret == MHD_YES;
218 auto json_rpc_http_server::handle_request(request* request_info) ->
bool {
219 auto req = Json::Value();
220 auto r = Json::Reader();
221 auto success = r.parse(request_info->m_request.str(), req,
false);
226 if(!req.isMember(
"method")) {
230 if(!req[
"method"].isString()) {
234 auto method = req[
"method"].asString();
235 auto params = Json::Value();
237 if(req.isMember(
"params")) {
238 params = req[
"params"];
242 if(req[
"id"].isUInt64()) {
243 id = req[
"id"].asUInt64();
246 MHD_suspend_connection(request_info->m_connection);
251 [
this, request_info,
id](std::optional<Json::Value> resp) {
252 handle_response(
id, request_info, std::move(resp));
258 json_rpc_http_server::handle_response(uint64_t
id,
259 request* request_info,
260 std::optional<Json::Value> resp) {
261 if(!resp.has_value()) {
262 request_info->m_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
263 request_info->m_server->send_response(
"Error processing request",
269 auto resp_payload = resp.value();
270 resp_payload[
"jsonrpc"] =
"2.0";
271 resp_payload[
"id"] = id;
273 request_info->m_code = MHD_HTTP_OK;
274 auto resp_str = Json::writeString(m_builder, resp_payload);
275 request_info->m_server->send_response(resp_str, request_info);
280 m_cb = std::move(handler_callback);
283 void json_rpc_http_server::request_complete(
285 struct MHD_Connection* ,
287 MHD_RequestTerminationCode ) {
288 if(*con_cls ==
nullptr) {
291 auto* req =
static_cast<request*
>(*con_cls);
294 std::unique_lock l(server->m_requests_mut);
295 server->m_requests.erase(req);
Asynchrounous HTTP JSON-RPC server implemented using libmicrohttpd and libjsoncpp.
json_rpc_http_server(network::endpoint_t endpoint, bool enable_cors=false)
Construct a new server.
~json_rpc_http_server()
Stop the server.
void register_handler_callback(handler_callback_type handler_callback)
Register the application request handler function with the server.
std::function< bool(std::string, Json::Value, result_callback_type)> handler_callback_type
Callback function type provided by the application for processing requests.
auto init() -> bool
Start listening for incoming connections and processing requests.
std::pair< ip_address, port_number_t > endpoint_t
[host name, port number].
interface::exec_return_type response
Agent RPC response type.