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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//! Caller-owned completion dispatch and the public server handle.
use std::{cell::UnsafeCell, net::SocketAddr, rc::Rc};
use liburing::{Completion, CompletionDispatcher, Proactor, SubmissionContext};
use super::{
config::{ServerBuilder, ServerConfig},
model::State,
net::listen,
service::HttpService,
};
use crate::{
Error, Result, SocketState, WebSocketMessage, WebSocketSessionId,
websocket_server::WebSocketUpgradeHandler,
};
/// HTTP server bound to one proactor subsystem and one listening socket.
pub struct HttpServer<S> {
state: Rc<UnsafeCell<State<S>>>,
}
impl<S: HttpService> HttpServer<S> {
/// Creates a builder for `service`.
///
/// # Errors
/// This function does not return errors.
///
/// # Panics
/// This function does not panic.
#[must_use]
pub fn builder(service: S) -> ServerBuilder<S> {
ServerBuilder::new(service)
}
}
impl<S: HttpService> HttpServer<S> {
/// Listens on `addr` and arms accept under `config.net_subsystem`.
///
/// # Errors
/// Returns a socket, bind, listen, buffer, or submission error.
///
/// # Panics
/// This function does not panic.
pub fn bind(
proactor: &mut Proactor,
addr: SocketAddr,
config: ServerConfig,
service: S,
) -> Result<Self> {
if config.io_buffer_size == 0 {
return Err(Error::InvalidState(String::from(
"io buffer size must be non-zero",
)));
}
let (fd, local) = listen(addr, config.backlog.max(1))?;
let mut server = Self {
state: Rc::new(UnsafeCell::new(State::new(config, service, fd, local))),
};
let state = server.state_mut();
state.arm(proactor)?;
proactor.submit()?;
Ok(server)
}
/// Installs the WebSocket upgrade handler for an exact request path.
///
/// # Errors
/// This function does not return errors.
///
/// # Panics
/// This function does not panic.
#[must_use]
pub fn with_websocket_handler<W>(mut self, path: impl Into<String>, handler: W) -> Self
where
W: WebSocketUpgradeHandler + 'static,
{
let state = self.state_mut();
state.websocket_path = path.into();
state.websocket = Some(Box::new(handler));
self
}
/// Returns the address actually bound, including an ephemeral port.
///
/// # Errors
/// This function does not return errors.
///
/// # Panics
/// This function does not panic.
#[must_use]
pub fn local_addr(&self) -> SocketAddr {
self.state().local
}
/// Applies completions already delivered to this subsystem, then submits I/O.
///
/// The caller routes CQEs through this server's completion dispatcher.
/// This method applies its inbox and stages I/O without harvesting the ring.
///
/// # Errors
/// Returns a protocol, socket-state, or submission error.
///
/// # Panics
/// This function does not panic.
pub fn drive(&mut self, proactor: &mut Proactor) -> Result<usize> {
let state = self.state_mut();
let applied = state.apply_completions();
let drained = state.drain_inbox();
let pumped = state.pump_all();
state.arm(proactor)?;
proactor.submit()?;
let count = applied?;
drained?;
pumped?;
Ok(count)
}
/// Encodes `message` for an accepted WebSocket session.
///
/// # Errors
/// Returns [`Error::ConnectionClosed`] when `session_id` is unknown, or a
/// codec error when the session can no longer send.
///
/// # Panics
/// This function does not panic.
pub fn websocket_send(
&mut self,
session_id: WebSocketSessionId,
message: WebSocketMessage,
) -> Result<()> {
self.state_mut().enqueue_websocket(session_id, message)
}
/// Queues a WebSocket close and finishes the connection after it is written.
///
/// # Errors
/// Returns [`Error::ConnectionClosed`] when `session_id` is unknown, or a
/// codec error when the close frame cannot be encoded.
///
/// # Panics
/// This function does not panic.
pub fn websocket_close(
&mut self,
session_id: WebSocketSessionId,
close: Option<crate::WebSocketCloseFrame>,
) -> Result<()> {
self.state_mut()
.enqueue_websocket(session_id, WebSocketMessage::Close(close))
}
/// Stops accept, sends HTTP/2 `GOAWAY`, and marks HTTP/1 responses `Connection: close`.
///
/// # Errors
/// Returns a protocol or submission error while queueing `GOAWAY`.
///
/// # Panics
/// This function does not panic.
pub fn initiate_graceful_shutdown(&mut self, proactor: &mut Proactor) -> Result<()> {
let state = self.state_mut();
state.shutting_down = true;
let slots: Vec<usize> = (0..state.conns.len()).collect();
for slot in slots {
state.graceful(slot)?;
}
state.arm(proactor)?;
proactor.submit()?;
Ok(())
}
/// Counts accepted sockets that have not finished close.
///
/// # Errors
/// This function does not return errors.
///
/// # Panics
/// This function does not panic.
#[must_use]
pub fn active_connections(&self) -> usize {
self.state()
.conns
.iter()
.filter(|slot| {
slot.as_ref()
.is_some_and(|conn| conn.sock.state() != SocketState::Closed)
})
.count()
}
fn state(&self) -> &State<S> {
// SAFETY: the server is `!Send` through `Rc` and runs on the ring thread.
// Dispatch and progress require an exclusive server borrow; no state
// reference is retained across a completion callback.
unsafe { &*self.state.get() }
}
fn state_mut(&mut self) -> &mut State<S> {
// SAFETY: same exclusive ring-thread ownership as `state`. The mutable
// borrow ends before any poll, so later dispatch cannot alias it.
unsafe { &mut *self.state.get() }
}
}
impl<S> Drop for HttpServer<S> {
fn drop(&mut self) {
// SAFETY: see `state`. This read only decides whether in-flight SQEs
// still point at the allocation.
let busy = unsafe { &*self.state.get() }.busy();
if busy {
// Keep the allocation alive until the kernel finishes SQEs that
// reference its buffers and the accept address.
std::mem::forget(Rc::clone(&self.state));
}
}
}
impl<S: HttpService> CompletionDispatcher for HttpServer<S> {
fn dispatch(
&mut self,
completion: Completion,
submissions: &mut SubmissionContext<'_>,
) -> liburing::Result<()> {
if completion.operation_id.subsystem_id() != self.state().config.net_subsystem {
return Err(liburing::UringError::InvalidSubsystemId(
completion.operation_id.subsystem_id().raw(),
));
}
submissions.claim_completion(completion)?;
self.state_mut().completions.push(completion);
Ok(())
}
}
impl<S: HttpService> crate::dispatch::CompletionRoute for HttpServer<S> {
fn accepts(&self, subsystem: liburing::SubsystemId) -> bool {
subsystem == self.state().config.net_subsystem
}
}