1#![doc = include_str!("../readme.md")]
7#![forbid(unsafe_code)]
8
9use base_endpoint::stream::{ReadableStream, WritableStream};
10use base_http::{
11 InterimHandler, InterimResponses, Request, Response, Scheme, Status, body::ReadableBody,
12};
13use base_http1::{RequestReader, ResponseWriter};
14use base_scheduler::{
15 Cancellation, SocketAccept, SocketClose, SocketStreamReading, SocketStreamWriting,
16};
17use futures_util::{
18 future::{Either, select},
19 stream::{FuturesUnordered, StreamExt},
20};
21use std::{
22 fmt,
23 future::{Future, poll_fn},
24 pin::pin,
25 task::Poll,
26};
27
28pub trait Handler: Sync {
31 type Error: std::error::Error + Send + Sync + 'static;
32 type Body<B: ReadableBody>: ReadableBody;
33
34 fn call<B: ReadableBody, I: InterimHandler>(
35 &self,
36 request: Request<B>,
37 interim: &mut InterimResponses<I>,
38 cancellation: &Cancellation,
39 ) -> impl Future<Output = Result<Response<Self::Body<B>>, Self::Error>> + Send;
40}
41
42#[derive(Clone, Debug)]
43pub struct Options {
44 pub http1: base_http1::Options,
45 pub protocols: base_http_protocol::HttpProtocols,
47 pub max_connections: usize,
48}
49impl Default for Options {
50 fn default() -> Self {
51 Self {
52 http1: base_http1::Options::default(),
53 protocols: base_http_protocol::HttpProtocols::default(),
54 max_connections: 256,
55 }
56 }
57}
58
59#[derive(Debug)]
61pub enum Error {
62 Protocol(base_http1::Error),
63 Handler(Box<dyn std::error::Error + Send + Sync>),
64 Io(std::io::Error),
65 Cancelled,
66 Negotiation(base_http_protocol::NegotiationError),
67}
68impl fmt::Display for Error {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Self::Protocol(e) => e.fmt(f),
72 Self::Handler(_) => f.write_str("request handler failed"),
73 Self::Io(_) => f.write_str("HTTP server I/O failed"),
74 Self::Cancelled => f.write_str("HTTP server cancelled"),
75 Self::Negotiation(error) => error.fmt(f),
76 }
77 }
78}
79impl std::error::Error for Error {
80 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
81 match self {
82 Self::Protocol(e) => e.source(),
83 Self::Handler(e) => Some(e.as_ref()),
84 Self::Io(e) => Some(e),
85 Self::Cancelled => None,
86 Self::Negotiation(error) => error.source(),
87 }
88 }
89}
90impl From<base_http1::Error> for Error {
91 fn from(e: base_http1::Error) -> Self {
92 match e {
93 base_http1::Error::Cancelled => Self::Cancelled,
94 e => Self::Protocol(e),
95 }
96 }
97}
98impl From<std::io::Error> for Error {
99 fn from(e: std::io::Error) -> Self {
100 Self::Io(e)
101 }
102}
103impl From<base_scheduler::OperationError> for Error {
104 fn from(error: base_scheduler::OperationError) -> Self {
105 base_http1::Error::from(error).into()
106 }
107}
108
109async fn cancellable<F: Future>(
110 future: F,
111 cancellation: &Cancellation,
112) -> Result<F::Output, Error> {
113 cancellation.check().map_err(|_| Error::Cancelled)?;
114 if cancellation.is_never() {
115 return Ok(future.await);
116 }
117 let mut future = pin!(future);
118 let mut cancelled = pin!(cancellation.cancelled());
119 poll_fn(|cx| {
120 if cancelled.as_mut().poll(cx).is_ready() {
121 return Poll::Ready(Err(Error::Cancelled));
122 }
123 future.as_mut().poll(cx).map(Ok)
124 })
125 .await
126}
127
128pub async fn serve_connection<S, H>(
132 scheduler: &S,
133 socket: S::Socket,
134 handler: &H,
135 options: &base_http1::Options,
136 cancellation: &Cancellation,
137) -> Result<u64, Error>
138where
139 S: SocketStreamReading + SocketStreamWriting + SocketClose,
140 S::Socket: 'static,
141 H: Handler,
142{
143 let outcome = connection_outcome(scheduler, socket, handler, options, cancellation).await;
144 outcome.result.map(|()| outcome.requests)
145}
146
147struct Outcome {
148 requests: u64,
149 result: Result<(), Error>,
150}
151
152async fn connection_outcome<S, H>(
153 scheduler: &S,
154 socket: S::Socket,
155 handler: &H,
156 options: &base_http1::Options,
157 cancellation: &Cancellation,
158) -> Outcome
159where
160 S: SocketStreamReading + SocketStreamWriting + SocketClose,
161 S::Socket: 'static,
162 H: Handler,
163{
164 let mut requests = 0;
165 let result = async {
167 options.validate()?;
168 let mut reader = scheduler.socket_stream_reader(&socket)?;
169 let writer = match scheduler.socket_stream_writer(&socket) {
170 Ok(writer) => writer,
171 Err(error) => {
172 let _ = ReadableStream::close(&mut reader).await;
174 return Err(Error::Io(error));
175 }
176 };
177 let (input, output) = base_http_protocol::HttpProtocols::default()
179 .plaintext()
180 .server(reader, writer, options.clone())
181 .expect("validated HTTP/1 options construct server codecs");
182 serve_codecs(
183 input,
184 output,
185 handler,
186 cancellation,
187 &mut requests,
188 &Scheme::HTTP,
189 )
190 .await
191 }
192 .await;
193 let closed = scheduler.socket_close(socket).await;
194 Outcome {
195 requests,
196 result: result.and_then(|()| Ok(closed?)),
197 }
198}
199
200async fn serve_codecs<R, W, H>(
202 mut input: RequestReader<R>,
203 mut output: ResponseWriter<W>,
204 handler: &H,
205 cancellation: &Cancellation,
206 count: &mut u64,
207 scheme: &Scheme,
208) -> Result<(), Error>
209where
210 R: ReadableStream,
211 W: WritableStream,
212 H: Handler,
213{
214 let result = exchanges(
215 &mut input,
216 &mut output,
217 handler,
218 cancellation,
219 count,
220 scheme,
221 )
222 .await;
223 let input_closed = input.close().await;
225 let output_closed = output.close().await;
226 result?;
227 input_closed?;
228 output_closed?;
229 Ok(())
230}
231
232async fn exchanges<R, W, H>(
233 input: &mut RequestReader<R>,
234 output: &mut ResponseWriter<W>,
235 handler: &H,
236 cancellation: &Cancellation,
237 count: &mut u64,
238 scheme: &Scheme,
239) -> Result<(), Error>
240where
241 R: ReadableStream,
242 W: WritableStream,
243 H: Handler,
244{
245 loop {
246 let (mut request, exchange) = match input.read_request(cancellation).await {
247 Ok(Some(pair)) => pair,
248 Ok(None) => return Ok(()),
249 Err(error) => {
250 if !matches!(error, base_http1::Error::Cancelled) {
251 let status = if matches!(error, base_http1::Error::Limit(_)) {
252 Status::new(431).unwrap()
253 } else {
254 Status::BAD_REQUEST
255 };
256 let _ = output.reject(status, cancellation).await;
258 }
259 return Err(error.into());
260 }
261 };
262 request.scheme.get_or_insert_with(|| scheme.clone());
263 let result = {
264 let mut interim = InterimResponses::new(output.interim(exchange.version()));
265 let result = {
266 let called = pin!(handler.call(request, &mut interim, cancellation));
267 cancellable(called, cancellation).await?
268 };
269 interim.finish();
270 result
271 };
272 let mut response = result.map_err(|error| {
273 if cancellation.is_cancelled() {
276 Error::Cancelled
277 } else {
278 Error::Handler(Box::new(error))
279 }
280 })?;
281 let persistent = output
282 .write_response(&exchange, &mut response, cancellation)
283 .await?;
284 drop(response);
285 *count += 1;
286 if !persistent || !input.is_reusable() {
287 return Ok(());
288 }
289 }
290}
291
292#[derive(Default, Debug)]
293pub struct Statistics {
294 pub connections: u64,
295 pub requests: u64,
296 pub failed_connections: u64,
297}
298
299pub async fn serve<S, H>(
304 scheduler: &S,
305 listener: S::Socket,
306 handler: &H,
307 options: &Options,
308 cancellation: &Cancellation,
309) -> Result<Statistics, Error>
310where
311 S: SocketAccept + SocketStreamReading + SocketStreamWriting + SocketClose,
312 S::Socket: 'static,
313 H: Handler,
314{
315 serve_with(
316 scheduler,
317 listener,
318 options,
319 cancellation,
320 |socket, cancellation| async move {
321 connection_outcome(scheduler, socket, handler, &options.http1, &cancellation).await
322 },
323 )
324 .await
325}
326
327async fn serve_with<S, F, Fut>(
328 scheduler: &S,
329 listener: S::Socket,
330 options: &Options,
331 cancellation: &Cancellation,
332 connection: F,
333) -> Result<Statistics, Error>
334where
335 S: SocketAccept + SocketClose,
336 F: Fn(S::Socket, Cancellation) -> Fut,
337 Fut: Future<Output = Outcome>,
338{
339 let connections_cancel = cancellation.child();
340 let mut connections = FuturesUnordered::new();
341 let mut statistics = Statistics::default();
342 let result = async {
343 options.http1.validate()?;
344 if options.max_connections == 0 {
345 return Err(Error::Protocol(base_http1::Error::Invalid(
346 "zero connection limit",
347 )));
348 }
349 loop {
350 if connections.len() == options.max_connections {
351 record(
352 &mut statistics,
353 cancellable(connections.next(), cancellation)
354 .await?
355 .unwrap(),
356 );
357 continue;
358 }
359 let accepted = {
363 let mut accept = pin!(scheduler.socket_accept(&listener, cancellation));
364 loop {
365 let completed = pin!(async {
366 if connections.is_empty() {
367 std::future::pending().await
368 } else {
369 connections.next().await.unwrap()
370 }
371 });
372 match select(accept.as_mut(), completed).await {
373 Either::Left((result, _)) => break result,
374 Either::Right((result, _)) => record(&mut statistics, result),
375 }
376 }
377 };
378 let (socket, _) = accepted.map_err(base_http1::Error::from)?;
379 statistics.connections += 1;
380 connections.push(connection(socket, connections_cancel.clone()));
381 }
382 }
383 .await;
384 connections_cancel.cancel();
385 while let Some(result) = connections.next().await {
386 record(&mut statistics, result);
387 }
388 let closed = scheduler.socket_close(listener).await;
389 closed?;
390 match result {
391 Ok(()) | Err(Error::Cancelled) => Ok(statistics),
392 Err(error) => Err(error),
393 }
394}
395
396fn record(statistics: &mut Statistics, outcome: Outcome) {
397 statistics.requests += outcome.requests;
398 match outcome.result {
399 Ok(()) => {}
400 Err(Error::Cancelled) => {}
401 Err(_) => statistics.failed_connections += 1,
402 }
403}
404
405#[cfg(feature = "rustls")]
408pub async fn serve_tls_connection<S, H>(
409 scheduler: &S,
410 connection: base_endpoint::tls::rustls::RustlsConnection<S::Socket>,
411 handler: &H,
412 options: &Options,
413 cancellation: &Cancellation,
414) -> Result<u64, Error>
415where
416 S: SocketStreamReading + SocketStreamWriting + SocketClose,
417 S::Socket: 'static,
418 H: Handler,
419{
420 let outcome =
421 tls_connection_outcome(scheduler, connection, handler, options, cancellation).await;
422 outcome.result.map(|()| outcome.requests)
423}
424
425#[cfg(feature = "rustls")]
426async fn tls_connection_outcome<S, H>(
427 scheduler: &S,
428 connection: base_endpoint::tls::rustls::RustlsConnection<S::Socket>,
429 handler: &H,
430 options: &Options,
431 cancellation: &Cancellation,
432) -> Outcome
433where
434 S: SocketStreamReading + SocketStreamWriting + SocketClose,
435 S::Socket: 'static,
436 H: Handler,
437{
438 use base_endpoint::tls::TlsSession;
439 let selected = connection.negotiated_protocol().map(<[u8]>::to_vec);
440 let mut requests = 0;
441 let result = connection
442 .with_duplex(scheduler, cancellation, |reader, writer| async {
443 let protocol = options.protocols.negotiated(selected.as_deref())?;
444 let (input, output) = protocol.server(reader, writer, options.http1.clone())?;
445 serve_codecs(
446 input,
447 output,
448 handler,
449 cancellation,
450 &mut requests,
451 &Scheme::HTTPS,
452 )
453 .await
454 })
455 .await;
456 Outcome { requests, result }
457}
458
459#[cfg(feature = "rustls")]
462pub async fn serve_tls<S, H>(
463 scheduler: &S,
464 listener: S::Socket,
465 config: std::sync::Arc<rustls::ServerConfig>,
466 handler: &H,
467 options: &Options,
468 cancellation: &Cancellation,
469) -> Result<Statistics, Error>
470where
471 S: SocketAccept + SocketStreamReading + SocketStreamWriting + SocketClose,
472 S::Socket: 'static,
473 H: Handler,
474{
475 use base_endpoint::tls::{TlsAcceptor, rustls::RustlsAcceptor};
476 let mut config = (*config).clone();
477 config.alpn_protocols = options.protocols.alpn_protocols();
478 let acceptor = RustlsAcceptor::new(std::sync::Arc::new(config));
479 serve_with(
480 scheduler,
481 listener,
482 options,
483 cancellation,
484 |socket, cancellation| {
485 let acceptor = &acceptor;
486 async move {
487 match acceptor.accept(scheduler, socket, &cancellation).await {
488 Ok(connection) => {
489 tls_connection_outcome(
490 scheduler,
491 connection,
492 handler,
493 options,
494 &cancellation,
495 )
496 .await
497 }
498 Err(error) => Outcome {
499 requests: 0,
500 result: Err(error.into()),
501 },
502 }
503 }
504 },
505 )
506 .await
507}
508
509impl From<base_http_protocol::NegotiationError> for Error {
510 fn from(error: base_http_protocol::NegotiationError) -> Self {
511 Self::Negotiation(error)
512 }
513}
514
515#[cfg(test)]
516mod tests {
517 use super::*;
518 use std::{error::Error as _, io};
519
520 #[test]
521 fn protocol_errors_are_transparent_and_io_errors_have_context() {
522 let error = Error::from(base_http1::Error::Io(io::Error::from(
523 io::ErrorKind::ConnectionReset,
524 )));
525 assert_eq!(error.to_string(), "HTTP/1 connection I/O failed");
526 assert_eq!(error.source().unwrap().to_string(), "connection reset");
527
528 let error = Error::from(io::Error::from(io::ErrorKind::ConnectionReset));
529 assert_eq!(error.to_string(), "HTTP server I/O failed");
530 assert_eq!(error.source().unwrap().to_string(), "connection reset");
531 }
532
533 #[test]
534 fn handler_errors_are_reported_as_the_source() {
535 let error = Error::Handler(Box::new(io::Error::other("database unavailable")));
536 assert_eq!(error.to_string(), "request handler failed");
537 assert_eq!(error.source().unwrap().to_string(), "database unavailable");
538 }
539
540 #[test]
541 fn scheduler_operation_errors_keep_cancellation_distinct() {
542 let error = Error::from(base_scheduler::OperationError::from(
543 base_scheduler::Cancelled,
544 ));
545 assert_eq!(format!("{error:?}"), "Cancelled");
546 assert_eq!(error.to_string(), "HTTP server cancelled");
547 assert!(error.source().is_none());
548
549 let error = Error::from(base_scheduler::OperationError::from(io::Error::from(
550 io::ErrorKind::ConnectionReset,
551 )));
552 let expected = Error::Protocol(base_http1::Error::Io(io::Error::from(
553 io::ErrorKind::ConnectionReset,
554 )));
555 assert_eq!(format!("{error:?}"), format!("{expected:?}"));
556 assert_eq!(error.source().unwrap().to_string(), "connection reset");
557 }
558
559 #[test]
560 fn negotiation_errors_are_transparent() {
561 let error = Error::from(base_http_protocol::NegotiationError::Missing);
562 assert_eq!(format!("{error:?}"), "Negotiation(Missing)");
563 assert_eq!(
564 error.to_string(),
565 "HTTP application protocol was not negotiated"
566 );
567 assert!(error.source().is_none());
568 }
569}