Repository navigation
Expand file tree
/
Copy pathlistener_engine.hpp
More file actions
154 lines (131 loc) · 6.72 KB
/
Copy pathlistener_engine.hpp
File metadata and controls
154 lines (131 loc) · 6.72 KB
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
/*
This file is part of libhttpserver
Copyright (C) 2011-2026 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the file
LICENSE in the distribution; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
// TASK-108: the native server's listener engine. One listener_engine
// per configured endpoint: it binds the transport through the one
// pollsys helper that owns platform divergence, adopts it in the poll
// backend, and runs one accept-loop task on the worker pool. Every
// accepted connection gets its own connection_engine (the backend
// registers the accepted transport under a fabricated id before the
// accept operation completes, so the engine only ever sees ids).
//
// Teardown mirrors the server's stop split (DR-V3-008): request_stop()
// releases the listener transport (a pending accept completes
// connection_closed and the loop exits) and shuts every live
// connection engine down -- non-blocking, idempotent, safe from any
// thread including inside a handler. quiesce() is the drain path's
// graceful half (TASK-110): the listener stops accepting and every
// live engine finishes its in-flight exchange before the close.
// Engine stop callbacks erase their records; the server's stop()
// drains the pool so every record is erased before the members die.
#if !defined(HTTPSERVER_COMPILATION)
#error "listener_engine.hpp is internal; only reachable when compiling libhttpserver."
#endif
#ifndef SRC_HTTPSERVER_DETAIL_LISTENER_ENGINE_HPP_
#define SRC_HTTPSERVER_DETAIL_LISTENER_ENGINE_HPP_
#include <cstdint>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
#include <httpserver/concurrency/task.hpp>
#include <httpserver/detail/connection_engine.hpp>
#include <httpserver/detail/io_connection_owner.hpp>
#include <httpserver/detail/io_operation.hpp>
#include <httpserver/detail/io_poll_backend.hpp>
#include <httpserver/detail/worker_pool.hpp>
#include <httpserver/http/outcome.hpp>
#include <httpserver/net/address.hpp>
#include <httpserver/server/options.hpp>
#include <httpserver/server/peer_policy.hpp>
#include <httpserver/server/routes.hpp>
namespace httpserver {
namespace detail {
class drain_scope;
class listener_engine final
: public std::enable_shared_from_this<listener_engine> {
public:
listener_engine(io_poll_backend& backend, worker_pool& pool,
const server::route_registry& routes,
const server::hook_bus& hooks,
const server::resource_budget& budget,
const server::peer_policy& peers,
drain_scope& scope, connection_engine_config config);
listener_engine(const listener_engine&) = delete;
listener_engine& operator=(const listener_engine&) = delete;
// Binds @p endpoint (address spellings per the options contract,
// port 0 = ephemeral), adopts the listener in the backend, and
// spawns the accept loop. @p index names the listener in the
// server's configuration order and derives its transport id (kept
// out of the backend's fabricated-connection id range). Fails typed
// when the bind does; the transport is closed either way.
http::outcome listen(const server::listener_options& endpoint,
std::size_t index);
// The resolved host-order port (0 until listen() succeeds).
std::uint16_t bound_port() const noexcept { return bound_port_; }
// The drain path (begin_drain, TASK-110): stop accepting (the
// listener releases; racing accepts release their own transports)
// and quiesce every live connection engine -- each in-flight
// exchange finishes and flushes before its close. Non-blocking,
// handler-safe; a later request_stop() still hard-stops whatever
// is live (the harder call wins at each engine).
void quiesce(std::chrono::steady_clock::time_point server_deadline) noexcept;
// Stop initiation: release the listener transport, shut every live
// connection engine down. Non-blocking, idempotent, handler-safe;
// it hard-stops even after a quiesce().
void request_stop(http::outcome_code reason = http::outcome_code::connection_closed) noexcept;
private:
static task<void> accept_loop(std::shared_ptr<listener_engine> self);
// Builds and registers the engine for one accepted connection (the
// backend already registered its transport under @p connection).
// TASK-119: first the accept-time policy gate -- classify @p peer,
// fire accept_decision after the verdict is fixed (observation
// only; a throwing hook is contained and cannot change it), then
// either release the transport unread (zero application bytes,
// the budget-refusal shape) or build the engine carrying the peer
// snapshot.
void adopt_accepted(std::uint64_t connection,
const net::peer_address& peer);
// Snapshots the live connection engines into @p out (mu_ must be
// held) so a stop-side pass can act on them outside the lock.
void collect_live_locked(
std::vector<std::shared_ptr<connection_engine>>& out);
// A connection engine's stopped callback: erases its record.
void erase(std::uint64_t connection) noexcept;
// Listener transport ids sit far above the backend's fabricated
// connection ids (a fresh counter from 1), so the two never alias.
static constexpr std::uint64_t k_listener_id_base = 1ULL << 62;
io_poll_backend& backend_;
worker_pool& pool_;
const server::route_registry& routes_;
const server::hook_bus& hooks_;
const server::peer_policy& peers_;
io_connection_owner owner_;
const server::resource_budget& budget_;
drain_scope& scope_;
connection_engine_config config_;
std::uint64_t id_ = 0;
std::uint16_t bound_port_ = 0;
mutable std::mutex mu_;
std::unordered_map<std::uint64_t,
std::shared_ptr<connection_engine>> connections_;
bool bound_ = false; // guarded by mu_
bool stopped_ = false; // guarded by mu_
};
} // namespace detail
} // namespace httpserver
#endif // SRC_HTTPSERVER_DETAIL_LISTENER_ENGINE_HPP_