Repository navigation
Expand file tree
/
Copy pathserver.hpp
More file actions
223 lines (184 loc) · 9.45 KB
/
Copy pathserver.hpp
File metadata and controls
223 lines (184 loc) · 9.45 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
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
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 v3 native server lifecycle (architecture §3.4,
// DR-V3-006). native_server owns, behind a pimpl, one route registry,
// one poll-driven transport engine, one worker pool, and one listener
// engine per configured endpoint; every accepted connection runs its
// own HTTP/1 connection engine on the shared pool. Configuration is
// options-only (PRD-V3N-REQ-014); listen() is gated on validate()
// (REQ-016) and performs every bind.
//
// The stop split follows DR-V3-008: request_stop() only initiates --
// it flips the state, releases the listeners, and disconnects every
// live exchange; it never blocks and is safe to call from inside a
// handler. stop() additionally closes the transport engine and joins
// the workers. begin_drain() is the graceful half (TASK-110): it
// stops the accepting side, applies the HTTP/1 close behavior --
// in-flight work finishes and flushes, pipelined-unstarted heads are
// dropped, idle connections close -- and returns a deadline-bound
// ticket reporting completion or expiry (PRD-V3N-REQ-032).
//
// This header is NOT part of the v2 umbrella <httpserver.hpp>: the
// native server is an additive surface and v2 consumers are unaffected.
#ifndef SRC_HTTPSERVER_SERVER_SERVER_HPP_
#define SRC_HTTPSERVER_SERVER_SERVER_HPP_
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string_view>
#include <httpserver/http/method.hpp>
#include <httpserver/http/outcome.hpp>
#include <httpserver/server/hooks.hpp>
#include <httpserver/server/options.hpp>
#include <httpserver/server/readiness.hpp>
#include <httpserver/server/peer_policy.hpp>
#include <httpserver/server/routes.hpp>
namespace httpserver {
namespace detail { struct drain_ticket_access; }
namespace server {
// The two-state outcome of a drain ticket wait (PRD-V3N-REQ-032):
// every counted unit completed, or the drain's deadline expired with
// work still counted. A stop-driven completion reports completed --
// the two-state result is the documented posture.
enum class drain_status : std::uint8_t { completed, deadline_expired };
// What one ticket wait observed. remaining is the unit count still
// live at expiry (a pre-cancellation snapshot; 0 when completed).
struct drain_result {
drain_status status = drain_status::completed;
std::size_t remaining = 0;
};
// The deadline-bound half of the stop split: begin_drain() hands one
// out. The blocking wait reports what the counted work did -- every
// unit completed, or the budget expired with work still live. The
// ticket shares ownership of the counted scope, so it stays safe to
// wait on even after the server object is destroyed.
class drain_ticket {
public:
drain_ticket() noexcept;
~drain_ticket();
drain_ticket(drain_ticket&&) noexcept;
drain_ticket& operator=(drain_ticket&&) noexcept;
// Blocking wait bounded by the drain's budget: ok with the
// observed result; would_deadlock when the calling thread runs
// work this drain counts (a handler of the same server waiting on
// its own drain); invalid_state on an empty (default-constructed
// or moved-from) ticket. request_stop()/stop() racing the wait
// drive it to completed -- the two-state result is the documented
// posture.
http::outcome wait(drain_result& out);
private:
friend class native_server;
friend struct ::httpserver::detail::drain_ticket_access;
class impl;
explicit drain_ticket(std::unique_ptr<impl> inner);
std::unique_ptr<impl> impl_;
};
// The owned-engine HTTP/1 server. All methods are thread-safe unless
// noted; lifecycle calls (listen/stop) are expected from one
// controlling thread.
class native_server {
public:
// Stores the configuration; no I/O, no threads. The options may be
// re-read (mutated copies re-validated) until listen().
explicit native_server(server_options options);
// stop() if still running.
~native_server();
native_server(const native_server&) = delete;
native_server& operator=(const native_server&) = delete;
// Registers one route (pattern and handler semantics per the
// route_registry contract). Fails invalid_state once listen() ran.
http::outcome route(const http::method& method, std::string_view pattern,
route_handler handler);
// TASK-118: the method-set and prefix-family registration forms
// (plan D1; admission and precedence per the route_registry
// contract). Both fail invalid_state once listen() ran.
http::outcome route(const http::method_set& methods,
std::string_view pattern, route_handler handler);
http::outcome route_prefix(const http::method_set& methods,
std::string_view pattern,
route_handler handler);
// Registers one bounded synchronous value-returning route: the
// handler receives the complete request head and the body buffered
// within body_cap, returns the response value, and writes no
// coroutine code (make_sync_route wraps it; DR-V3-003). body_cap
// must be at least 1 (a zero cap means "engine default" in the
// admission policy, so it is rejected here); an empty handler is
// invalid_argument; a duplicate (method, pattern) is invalid_state
// from the registry. Fails invalid_state once listen() ran. The
// bounded sync form is the restricted half of the route surface
// (PRD-V3N-REQ-021): no suspension, upgrade decision, admission
// control, or streaming -- unbounded uploads and duplex work need
// the coroutine route() form.
http::outcome route_sync(const http::method& method,
std::string_view pattern,
sync_route_handler handler,
std::uint64_t body_cap);
// Validates the whole configuration (REQ-016: the pre-listen gate;
// no I/O before it passes), binds every configured listener, and
// starts the accept loops. A typed failure binds nothing that was
// not already bound and leaves the server stopped; a partially
// bound listen failure is terminal for this server object.
http::outcome listen();
// Stop initiation (DR-V3-008): idempotent, non-blocking, safe from
// any thread including inside a handler.
void request_stop() noexcept;
// request_stop() plus closing the transport engine and joining the
// workers. Returns once every engine task finished.
void stop();
// Drain initiation (PRD-V3N-REQ-032): stop accepting new work,
// finish HTTP/1 responses and start WebSocket Close (1001, server drain),
// and fill @p out with a ticket whose wait() is bounded by
// @p budget. Nonblocking and handler-safe like request_stop(); it
// never waits. budget <= 0 is invalid_argument; before listen(),
// after a stop, or on a second drain it is invalid_state. One
// drain per server object. is_running() keeps its meaning (a
// drain is a run-down, not a stop); request_stop()/stop() during
// the drain drive the ticket to completed when before expiry.
// An expiry remains deadline_expired even after cancellation unwinds.
http::outcome begin_drain(std::chrono::milliseconds budget,
drain_ticket& out);
// True from a successful listen() until request_stop() (or the
// destructor) runs.
bool is_running() const noexcept;
// Borrowed, server-owned until destruction; nullptr in managed mode.
// Before listen and after stop the stable adapter is inactive.
readiness_driver* readiness() noexcept;
// TASK-118: the server-wide lifecycle hook bus (server/hooks.hpp
// for the phase contract). Registration is runtime-safe before and
// after listen(); the returned reference is valid for the server's
// lifetime.
hook_bus& hooks() const noexcept;
// TASK-119: the server-wide peer policy (server/peer_policy.hpp
// for the pattern vocabulary and truth table). Seeded from
// options().peer_policy() at listen(); mutable afterwards from any
// thread including inside handlers (every member synchronized,
// DR-V3-008). The returned reference is valid for the server's
// lifetime.
peer_policy& peer_policy() const noexcept;
// The resolved port of listener @p listener_index (0 for an
// ephemeral-port request reports the bound port; 0 before listen()
// or for an out-of-range index).
std::uint16_t get_bound_port(std::size_t listener_index) const noexcept;
private:
class impl;
std::unique_ptr<impl> impl_;
};
} // namespace server
} // namespace httpserver
#endif // SRC_HTTPSERVER_SERVER_SERVER_HPP_