ORE Studio 0.0.4
Loading...
Searching...
No Matches
domain_service_runner_impl.hpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * Copyright (C) 2026 Marco Craveiro <marco.craveiro@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 3 of the License, or (at your option) any later
8 * version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 */
20#ifndef ORES_SERVICE_SERVICE_DOMAIN_SERVICE_RUNNER_IMPL_HPP
21#define ORES_SERVICE_SERVICE_DOMAIN_SERVICE_RUNNER_IMPL_HPP
22
23#include <csignal>
24#include <functional>
25#include <optional>
26#include <boost/asio/bind_cancellation_slot.hpp>
27#include <boost/asio/cancellation_signal.hpp>
28#include <boost/asio/co_spawn.hpp>
29#include <boost/asio/error.hpp>
30#include <boost/asio/signal_set.hpp>
31#include <boost/asio/use_awaitable.hpp>
32#include <boost/system/error_code.hpp>
33#include "ores.logging/make_logger.hpp"
34#include "ores.nats/service/jwks.hpp"
35#include "ores.security/jwt/jwt_authenticator.hpp"
36
37namespace ores::service::service {
38
39template<typename RegisterFn>
40boost::asio::awaitable<void>
41run(boost::asio::io_context& io_ctx,
44 std::string_view name,
45 RegisterFn&& register_fn,
46 std::function<void(boost::asio::io_context&)> on_started) {
47
48 using namespace ores::logging;
49 static const std::string_view logger_name = "ores.service.service.runner";
50 static auto& lg = []() -> auto& {
51 static auto instance = make_logger(logger_name);
52 return instance;
53 }();
54
55 // During the JWKS startup phase, use a cancellation_signal rather than
56 // io_ctx.stop() so that the coroutine can unwind cleanly (log, co_return)
57 // before the io_context exits. Calling io_ctx.stop() here would cause
58 // io_ctx.run() in main() to return before the catch block executes.
59 boost::asio::cancellation_signal startup_cancel;
60 boost::asio::signal_set signals(io_ctx, SIGINT, SIGTERM);
61 signals.async_wait([&startup_cancel](const boost::system::error_code& ec, int) {
62 if (!ec) startup_cancel.emit(boost::asio::cancellation_type::all);
63 });
64
65 // co_spawn with bind_cancellation_slot propagates the slot to the spawned
66 // coroutine: when startup_cancel.emit() fires, the coroutine is cancelled
67 // at its next async point (the backoff timer) with operation_aborted.
68 std::string pub_key;
69 try {
70 pub_key = co_await boost::asio::co_spawn(
71 io_ctx,
72 ores::nats::service::fetch_jwks_public_key(nats),
73 boost::asio::bind_cancellation_slot(
74 startup_cancel.slot(), boost::asio::use_awaitable));
75 } catch (const boost::system::system_error& e) {
76 if (e.code() != boost::asio::error::operation_aborted)
77 throw;
78 BOOST_LOG_SEV(lg, info) << "Shutdown signal received during startup.";
79 BOOST_LOG_SEV(lg, info) << "Shutdown complete: " << name;
80 co_return;
81 }
82 BOOST_LOG_SEV(lg, info) << "Fetched JWKS public key from IAM";
83
84 // Dismiss the startup handler before re-arming for the operational phase.
85 // signals.cancel() posts the handler with operation_aborted (ignored by !ec).
86 // The subsequent async_wait(use_awaitable) then waits for the real signal.
87 signals.cancel();
88
89 std::optional<ores::security::jwt::jwt_authenticator> verifier =
91
92 auto subs = register_fn(nats, std::move(ctx), std::move(verifier));
93 BOOST_LOG_SEV(lg, info) << "Registered " << subs.size() << " subscription(s).";
94 for (const auto& sub : subs)
95 BOOST_LOG_SEV(lg, info) << "NATS subscribe: " << sub.subject();
96
97 if (on_started)
98 on_started(io_ctx);
99
100 BOOST_LOG_SEV(lg, info) << "Service ready.";
101 BOOST_LOG_SEV(lg, info) << "Waiting for requests...";
102 co_await signals.async_wait(boost::asio::use_awaitable);
103
104 BOOST_LOG_SEV(lg, info) << "Shutdown signal received. Draining...";
105 nats.drain();
106 BOOST_LOG_SEV(lg, info) << "Shutdown complete: " << name;
107 co_return;
108}
109
110} // namespace ores::service::service
111
112#endif
Implements logging infrastructure for ORE Studio.
Definition boost_severity.hpp:28
Context for the operations on a postgres database.
Definition context.hpp:47
NATS client: connection, pub/sub, request/reply, and JetStream.
Definition client.hpp:73
void drain()
Graceful shutdown.
Definition client.cpp:489
static jwt_authenticator create_rs256_verifier(const std::string &public_key_pem, const std::string &issuer="", const std::string &audience="")
Creates an RS256 verifier using an RSA public key (PEM).
Definition jwt_authenticator.cpp:71