16#include "../../session/session.hpp"
27namespace morph::backend::detail {
37 ::morph::session::Context context;
39 std::string principal;
43enum class AdmissionTopic : std::uint8_t {
76 ::morph::wire::WatchReply add(ConnectionId cid, ::morph::wire::WatchBody
const& body, Watch watch) {
80 auto const shared = std::make_shared<Watch const>(std::move(watch));
81 SessionKey
const key = keyOf(*shared);
82 ::morph::wire::WatchReply overCap;
83 Connection& connection = _connections[cid];
84 for (
auto const& record : body.
records) {
85 if (!place(connection, connection.records, record, shared, key)) {
89 for (
auto const& topic : body.
instances) {
90 if (!place(connection, connection.instances, InstanceKey{topic.modelType, topic.primary}, shared, key)) {
94 if (connection.size() == 0) {
95 _connections.erase(cid);
105 void remove(ConnectionId cid, ::morph::wire::WatchBody
const& body) {
106 auto found = _connections.find(cid);
107 if (found == _connections.end()) {
110 for (
auto const& record : body.
records) {
111 erase(found->second.records, record);
113 for (
auto const& topic : body.
instances) {
114 erase(found->second.instances, InstanceKey{topic.modelType, topic.primary});
116 if (found->second.size() == 0) {
117 _connections.erase(found);
123 void dropConnection(ConnectionId cid) {
124 auto found = _connections.find(cid);
125 if (found == _connections.end()) {
128 for (
auto const& [record, watch] : found->second.records) {
129 release(keyOf(*watch));
131 for (
auto const& [topic, watch] : found->second.instances) {
132 release(keyOf(*watch));
134 _connections.erase(found);
140 [[nodiscard]] std::vector<ConnectionId> watchersOf(std::string_view record)
const {
141 std::vector<ConnectionId> watchers;
142 for (
auto const& [cid, connection] : _connections) {
143 if (connection.records.contains(record)) {
144 watchers.push_back(cid);
154 [[nodiscard]] std::vector<ConnectionId> watchersOf(std::string_view modelType, std::string_view primary)
const {
155 InstanceKey
const key{std::string{modelType}, std::string{primary}};
156 std::vector<ConnectionId> watchers;
157 for (
auto const& [cid, connection] : _connections) {
158 if (connection.instances.contains(key)) {
159 watchers.push_back(cid);
170 [[nodiscard]] Watch
const* watchOf(ConnectionId cid, std::string_view record)
const {
171 auto const connection = _connections.find(cid);
172 if (connection == _connections.end()) {
175 auto const found = connection->second.records.find(record);
176 return found == connection->second.records.end() ? nullptr : found->second.get();
186 [[nodiscard]] Watch
const* watchOf(ConnectionId cid, std::string_view modelType, std::string_view primary)
const {
187 auto const connection = _connections.find(cid);
188 if (connection == _connections.end()) {
192 connection->second.instances.find(InstanceKey{std::string{modelType}, std::string{primary}});
193 return found == connection->second.instances.end() ? nullptr : found->second.get();
204 void dropSession(ConnectionId cid, std::string
const& principal, std::string
const& token) {
205 auto found = _connections.find(cid);
206 if (found == _connections.end()) {
209 SessionKey
const key{principal, token};
210 auto const dropMatching = [
this, &key](
auto& topics) {
211 for (
auto it = topics.begin(); it != topics.end();) {
212 if (keyOf(*it->second) == key) {
214 it = topics.erase(it);
220 dropMatching(found->second.records);
221 dropMatching(found->second.instances);
222 if (found->second.size() == 0) {
223 _connections.erase(found);
230 [[nodiscard]] std::size_t topicCount(ConnectionId cid)
const {
231 auto const found = _connections.find(cid);
232 return found == _connections.end() ? 0 : found->second.size();
240 [[nodiscard]] std::optional<bool> admission(::morph::session::Context
const& session, AdmissionTopic kind,
241 std::string_view name)
const {
242 auto const bySession = _sessions.find(SessionKey{
session.principal,
session.token});
243 if (bySession == _sessions.end()) {
246 auto const& answers = bySession->second.answers.at(
static_cast<std::size_t
>(kind));
247 auto const found = answers.find(name);
248 if (found == answers.end()) {
251 return found->second;
262 void noteAdmission(::morph::session::Context
const& session, AdmissionTopic kind, std::string_view name,
264 auto& answers = _sessions[SessionKey{
session.principal,
session.token}].answers;
265 answers.at(
static_cast<std::size_t
>(kind)).insert_or_assign(std::string{name}, admitted);
269 using InstanceKey = std::pair<std::string, std::string>;
271 using SessionKey = std::pair<std::string, std::string>;
272 using WatchPtr = std::shared_ptr<Watch const>;
273 using Answers = std::map<std::string, bool, std::less<>>;
277 std::map<std::string, WatchPtr, std::less<>> records;
278 std::map<InstanceKey, WatchPtr> instances;
280 [[nodiscard]] std::size_t size()
const {
return records.size() + instances.size(); }
285 std::size_t topics = 0;
286 std::array<Answers, 2> answers;
289 [[nodiscard]]
static SessionKey keyOf(Watch
const& watch) {
return {watch.principal, watch.context.token}; }
292 template <
class Topics,
class Topic>
293 bool place(Connection
const& connection, Topics& topics, Topic
const& topic, WatchPtr
const& watch,
294 SessionKey
const& key) {
295 auto const found = topics.find(topic);
296 if (found != topics.end()) {
299 ++_sessions[key].topics;
300 release(keyOf(*found->second));
301 found->second = watch;
304 if (connection.size() >= ::morph::wire::kMaxWatchTopics) {
307 topics.emplace(topic, watch);
308 ++_sessions[key].topics;
313 template <
class Topics,
class Topic>
314 void erase(Topics& topics, Topic
const& topic) {
315 auto const found = topics.find(topic);
316 if (found == topics.end()) {
319 release(keyOf(*found->second));
324 void release(SessionKey
const& key) {
325 auto const found = _sessions.find(key);
326 if (found != _sessions.end() && --found->second.topics == 0) {
327 _sessions.erase(found);
332 void forgetIfIdle(SessionKey
const& key) {
333 auto const found = _sessions.find(key);
334 if (found != _sessions.end() && found->second.topics == 0) {
335 _sessions.erase(found);
340 std::map<ConnectionId, Connection> _connections;
341 std::map<SessionKey, Session> _sessions;
AnyHandle session(std::string const &name)
A session attribute, {"session": name}.
What a transport hands RemoteServer to write to one connection, and the backlog bounds both sides app...
std::vector< InstanceTopic > instances
Instance topics to watch (or stop watching).
bool replace
For watch: replace the connection's whole watch set instead of adding to it.
std::vector< std::string > records
Record wire names to watch (or stop watching).
std::vector< InstanceTopic > refusedInstances
Instance topics the server refused.
std::vector< std::string > refusedRecords
Record names the server does not publish or the caller may not read.