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, Clock, OperationError, SocketAccept, SocketClose, SocketSetOption,
16 SocketStreamReading, SocketStreamWriting, TcpNoDelay,
17};
18use futures_util::{
19 future::{Either, select},
20 stream::{FuturesUnordered, StreamExt},
21};
22use std::{
23 fmt,
24 future::{Future, poll_fn},
25 io,
26 pin::pin,
27 task::Poll,
28 time::Duration,
29};
30
31pub trait Handler: Sync {
35 type Error: std::error::Error + Send + Sync + 'static;
36 type Body<B: ReadableBody>: ReadableBody;
37
38 fn call<B: ReadableBody, I: InterimHandler>(
39 &self,
40 request: Request<B>,
41 interim: &mut InterimResponses<I>,
42 cancellation: &Cancellation,
43 ) -> impl Future<Output = Result<Response<Self::Body<B>>, Self::Error>> + Send;
44}
45
46#[derive(Clone, Debug)]
47pub struct Options {
48 pub http1: base_http1::Options,
49 pub protocols: base_http_protocol::HttpProtocols,
51 pub max_connections: usize,
52 pub tcp_nodelay: bool,
57}
58impl Default for Options {
59 fn default() -> Self {
60 Self {
61 http1: base_http1::Options::default(),
62 protocols: base_http_protocol::HttpProtocols::default(),
63 max_connections: 256,
64 tcp_nodelay: true,
65 }
66 }
67}
68
69#[derive(Debug)]
71pub enum Error {
72 Protocol(base_http1::Error),
73 Handler(Box<dyn std::error::Error + Send + Sync>),
74 Response(base_http1::Error),
76 Io(std::io::Error),
77 Cancelled,
78 Negotiation(base_http_protocol::NegotiationError),
79}
80impl fmt::Display for Error {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 match self {
83 Self::Protocol(e) => e.fmt(f),
84 Self::Handler(_) => f.write_str("request handler failed"),
85 Self::Response(_) => f.write_str("request handler returned an invalid response"),
86 Self::Io(_) => f.write_str("HTTP server I/O failed"),
87 Self::Cancelled => f.write_str("HTTP server cancelled"),
88 Self::Negotiation(error) => error.fmt(f),
89 }
90 }
91}
92impl std::error::Error for Error {
93 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
94 match self {
95 Self::Protocol(e) => e.source(),
96 Self::Handler(e) => Some(e.as_ref()),
97 Self::Response(e) => Some(e),
98 Self::Io(e) => Some(e),
99 Self::Cancelled => None,
100 Self::Negotiation(error) => error.source(),
101 }
102 }
103}
104impl From<base_http1::Error> for Error {
105 fn from(e: base_http1::Error) -> Self {
106 match e {
107 base_http1::Error::Cancelled => Self::Cancelled,
108 e => Self::Protocol(e),
109 }
110 }
111}
112impl From<std::io::Error> for Error {
113 fn from(e: std::io::Error) -> Self {
114 Self::Io(e)
115 }
116}
117impl From<base_scheduler::OperationError> for Error {
118 fn from(error: base_scheduler::OperationError) -> Self {
119 base_http1::Error::from(error).into()
120 }
121}
122
123async fn cancellable<F: Future>(
124 future: F,
125 cancellation: &Cancellation,
126) -> Result<F::Output, Error> {
127 cancellation.check().map_err(|_| Error::Cancelled)?;
128 if cancellation.is_never() {
129 return Ok(future.await);
130 }
131 let mut future = pin!(future);
132 let mut cancelled = pin!(cancellation.cancelled());
133 poll_fn(|cx| {
134 if cancelled.as_mut().poll(cx).is_ready() {
135 return Poll::Ready(Err(Error::Cancelled));
136 }
137 future.as_mut().poll(cx).map(Ok)
138 })
139 .await
140}
141
142pub async fn serve_connection<S, H>(
146 scheduler: &S,
147 socket: S::Socket,
148 handler: &H,
149 options: &base_http1::Options,
150 cancellation: &Cancellation,
151) -> Result<u64, Error>
152where
153 S: SocketStreamReading + SocketStreamWriting + SocketClose,
154 S::Socket: 'static,
155 H: Handler,
156{
157 let outcome = connection_outcome(scheduler, socket, handler, options, cancellation).await;
158 outcome.result.map(|()| outcome.requests)
159}
160
161struct Outcome {
162 requests: u64,
163 result: Result<(), Error>,
164}
165
166async fn connection_outcome<S, H>(
167 scheduler: &S,
168 socket: S::Socket,
169 handler: &H,
170 options: &base_http1::Options,
171 cancellation: &Cancellation,
172) -> Outcome
173where
174 S: SocketStreamReading + SocketStreamWriting + SocketClose,
175 S::Socket: 'static,
176 H: Handler,
177{
178 let mut requests = 0;
179 let result = async {
181 options.validate()?;
182 let mut reader = scheduler.socket_stream_reader(&socket)?;
183 let writer = match scheduler.socket_stream_writer(&socket) {
184 Ok(writer) => writer,
185 Err(error) => {
186 let _ = ReadableStream::close(&mut reader).await;
188 return Err(Error::Io(error));
189 }
190 };
191 let (input, output) = base_http_protocol::HttpProtocols::default()
193 .plaintext()
194 .server(reader, writer, options.clone())
195 .expect("validated HTTP/1 options construct server codecs");
196 serve_codecs(
197 input,
198 output,
199 handler,
200 cancellation,
201 &mut requests,
202 &Scheme::HTTP,
203 )
204 .await
205 }
206 .await;
207 let closed = scheduler.socket_close(socket).await;
208 Outcome {
209 requests,
210 result: result.and_then(|()| Ok(closed?)),
211 }
212}
213
214async fn serve_codecs<R, W, H>(
216 mut input: RequestReader<R>,
217 mut output: ResponseWriter<W>,
218 handler: &H,
219 cancellation: &Cancellation,
220 count: &mut u64,
221 scheme: &Scheme,
222) -> Result<(), Error>
223where
224 R: ReadableStream,
225 W: WritableStream,
226 H: Handler,
227{
228 let result = exchanges(
229 &mut input,
230 &mut output,
231 handler,
232 cancellation,
233 count,
234 scheme,
235 )
236 .await;
237 let input_closed = input.close().await;
239 let output_closed = output.close().await;
240 result?;
241 input_closed?;
242 output_closed?;
243 Ok(())
244}
245
246async fn exchanges<R, W, H>(
247 input: &mut RequestReader<R>,
248 output: &mut ResponseWriter<W>,
249 handler: &H,
250 cancellation: &Cancellation,
251 count: &mut u64,
252 scheme: &Scheme,
253) -> Result<(), Error>
254where
255 R: ReadableStream,
256 W: WritableStream,
257 H: Handler,
258{
259 loop {
260 let (mut request, exchange) = match input.read_request(cancellation).await {
261 Ok(Some(pair)) => pair,
262 Ok(None) => return Ok(()),
263 Err(error) => {
264 if !matches!(error, base_http1::Error::Cancelled) {
265 let status = if matches!(error, base_http1::Error::Limit(_)) {
266 Status::new(431).unwrap()
267 } else {
268 Status::BAD_REQUEST
269 };
270 let _ = output.reject(status, cancellation).await;
272 }
273 return Err(error.into());
274 }
275 };
276 request.scheme.get_or_insert_with(|| scheme.clone());
277 let result = {
278 let mut interim = InterimResponses::new(output.interim(exchange.version()));
279 let result = {
280 let called = pin!(handler.call(request, &mut interim, cancellation));
281 cancellable(called, cancellation).await?
282 };
283 interim.finish();
284 result
285 };
286 let mut response = 'handled: {
288 let error = match result.map_err(|error| Error::Handler(Box::new(error))) {
289 Ok(response) => break 'handled response,
290 Err(error) => error,
291 };
292 if cancellation.is_cancelled() {
294 return Err(Error::Cancelled);
295 }
296 let status = if input.is_poisoned() {
298 Status::BAD_REQUEST
299 } else {
300 Status::INTERNAL_SERVER_ERROR
301 };
302 let _ = output.reject(status, cancellation).await;
303 return Err(error);
304 };
305 let written = output
306 .write_response(&exchange, &mut response, cancellation)
307 .await;
308 drop(response);
309 let persistent = match written {
310 Ok(persistent) => persistent,
311 Err(error) if output.is_usable() && !matches!(error, base_http1::Error::Cancelled) => {
313 let _ = output
314 .reject(Status::INTERNAL_SERVER_ERROR, cancellation)
315 .await;
316 return Err(Error::Response(error));
317 }
318 Err(error) => return Err(error.into()),
319 };
320 *count += 1;
321 if !persistent || !input.is_reusable() {
322 return Ok(());
323 }
324 }
325}
326
327#[derive(Default, Debug)]
328pub struct Statistics {
329 pub connections: u64,
330 pub requests: u64,
331 pub failed_connections: u64,
332 pub accept_errors: u64,
335}
336
337const ACCEPT_BACKOFF: Duration = Duration::from_millis(100);
341
342const ACCEPT_RETRIES: u32 = 16;
347
348#[derive(Debug, PartialEq, Eq)]
350enum AcceptFailure {
351 Retry,
354 Backoff,
357 Fatal,
359}
360
361fn accept_failure(error: &io::Error) -> AcceptFailure {
362 use io::ErrorKind::*;
363 match error.kind() {
364 ConnectionAborted | ConnectionReset | ConnectionRefused | Interrupted | WouldBlock
365 | NetworkDown | NetworkUnreachable | HostUnreachable => return AcceptFailure::Retry,
366 OutOfMemory => return AcceptFailure::Backoff,
367 _ => {}
368 }
369 match error.raw_os_error() {
370 Some(code) if os::RETRY.contains(&code) => AcceptFailure::Retry,
371 Some(code) if os::BACKOFF.contains(&code) => AcceptFailure::Backoff,
372 _ => AcceptFailure::Fatal,
373 }
374}
375
376#[cfg(unix)]
378mod os {
379 #[cfg(any(target_os = "linux", target_os = "android"))]
385 pub const RETRY: &[i32] = &[
386 libc::EPERM,
387 libc::EPROTO,
388 libc::ENOPROTOOPT,
389 libc::EHOSTDOWN,
390 libc::ENONET,
391 ];
392 #[cfg(not(any(target_os = "linux", target_os = "android")))]
393 pub const RETRY: &[i32] = &[libc::EPROTO];
394
395 pub const BACKOFF: &[i32] = &[libc::EMFILE, libc::ENFILE, libc::ENOBUFS];
396}
397
398#[cfg(windows)]
400mod os {
401 pub const RETRY: &[i32] = &[10004];
403 pub const BACKOFF: &[i32] = &[10024, 10055];
405}
406
407#[cfg(not(any(unix, windows)))]
408mod os {
409 pub const RETRY: &[i32] = &[];
410 pub const BACKOFF: &[i32] = &[];
411}
412
413async fn accept<S: SocketAccept + Clock>(
417 scheduler: &S,
418 listener: &S::Socket,
419 accept_errors: &mut u64,
420 cancellation: &Cancellation,
421) -> Result<S::Socket, OperationError> {
422 let mut retries = 0;
423 loop {
424 let error = match scheduler.socket_accept(listener, cancellation).await {
425 Ok((socket, _)) => return Ok(socket),
426 Err(OperationError::Io(error)) => error,
427 Err(error) => return Err(error),
428 };
429 let backoff = match accept_failure(&error) {
430 AcceptFailure::Retry => {
431 retries += 1;
432 retries > ACCEPT_RETRIES
433 }
434 AcceptFailure::Backoff => true,
435 AcceptFailure::Fatal => return Err(error.into()),
436 };
437 *accept_errors += 1;
438 if backoff {
439 retries = 0;
440 scheduler.sleep(ACCEPT_BACKOFF, cancellation).await?;
441 }
442 }
443}
444
445pub async fn serve<S, H>(
460 scheduler: &S,
461 listener: S::Socket,
462 handler: &H,
463 options: &Options,
464 cancellation: &Cancellation,
465) -> Result<Statistics, Error>
466where
467 S: SocketAccept
468 + Clock
469 + SocketSetOption<TcpNoDelay>
470 + SocketStreamReading
471 + SocketStreamWriting
472 + SocketClose,
473 S::Socket: 'static,
474 H: Handler,
475{
476 serve_with(
477 scheduler,
478 listener,
479 options,
480 cancellation,
481 |socket, cancellation| async move {
482 connection_outcome(scheduler, socket, handler, &options.http1, &cancellation).await
483 },
484 )
485 .await
486}
487
488async fn serve_with<S, F, Fut>(
489 scheduler: &S,
490 listener: S::Socket,
491 options: &Options,
492 cancellation: &Cancellation,
493 connection: F,
494) -> Result<Statistics, Error>
495where
496 S: SocketAccept + Clock + SocketSetOption<TcpNoDelay> + SocketClose,
497 F: Fn(S::Socket, Cancellation) -> Fut,
498 Fut: Future<Output = Outcome>,
499{
500 let connections_cancel = cancellation.child();
501 let mut connections = FuturesUnordered::new();
502 let mut statistics = Statistics::default();
503 let mut accept_errors = 0;
505 let result = async {
506 options.http1.validate()?;
507 if options.max_connections == 0 {
508 return Err(Error::Protocol(base_http1::Error::Invalid(
509 "zero connection limit",
510 )));
511 }
512 loop {
513 if connections.len() == options.max_connections {
514 record(
515 &mut statistics,
516 cancellable(connections.next(), cancellation)
517 .await?
518 .unwrap(),
519 );
520 continue;
521 }
522 let accepted = {
526 let mut accept = pin!(accept(
527 scheduler,
528 &listener,
529 &mut accept_errors,
530 cancellation
531 ));
532 loop {
533 let completed = pin!(async {
534 if connections.is_empty() {
535 std::future::pending().await
536 } else {
537 connections.next().await.unwrap()
538 }
539 });
540 match select(accept.as_mut(), completed).await {
541 Either::Left((result, _)) => break result,
542 Either::Right((result, _)) => record(&mut statistics, result),
543 }
544 }
545 };
546 let socket = accepted.map_err(base_http1::Error::from)?;
547 statistics.connections += 1;
548 let cancellation = connections_cancel.clone();
549 connections.push(async {
550 if options.tcp_nodelay {
551 let _ = scheduler
553 .socket_set_option(&socket, TcpNoDelay, true, &cancellation)
554 .await;
555 }
556 connection(socket, cancellation).await
557 });
558 }
559 }
560 .await;
561 statistics.accept_errors = accept_errors;
562 let closed = scheduler.socket_close(listener).await;
565 connections_cancel.cancel();
566 while let Some(result) = connections.next().await {
567 record(&mut statistics, result);
568 }
569 match result {
571 Ok(()) | Err(Error::Cancelled) => {
572 closed?;
573 Ok(statistics)
574 }
575 Err(error) => Err(error),
576 }
577}
578
579fn record(statistics: &mut Statistics, outcome: Outcome) {
580 statistics.requests += outcome.requests;
581 match outcome.result {
582 Ok(()) => {}
583 Err(Error::Cancelled) => {}
584 Err(_) => statistics.failed_connections += 1,
585 }
586}
587
588#[cfg(feature = "rustls")]
591pub async fn serve_tls_connection<S, H>(
592 scheduler: &S,
593 connection: base_endpoint::tls::rustls::RustlsConnection<S::Socket>,
594 handler: &H,
595 options: &Options,
596 cancellation: &Cancellation,
597) -> Result<u64, Error>
598where
599 S: SocketStreamReading + SocketStreamWriting + SocketClose,
600 S::Socket: 'static,
601 H: Handler,
602{
603 let outcome =
604 tls_connection_outcome(scheduler, connection, handler, options, cancellation).await;
605 outcome.result.map(|()| outcome.requests)
606}
607
608#[cfg(feature = "rustls")]
609async fn tls_connection_outcome<S, H>(
610 scheduler: &S,
611 connection: base_endpoint::tls::rustls::RustlsConnection<S::Socket>,
612 handler: &H,
613 options: &Options,
614 cancellation: &Cancellation,
615) -> Outcome
616where
617 S: SocketStreamReading + SocketStreamWriting + SocketClose,
618 S::Socket: 'static,
619 H: Handler,
620{
621 use base_endpoint::tls::TlsSession;
622 let selected = connection.negotiated_protocol().map(<[u8]>::to_vec);
623 let mut requests = 0;
624 let result = connection
625 .with_duplex(scheduler, cancellation, |reader, writer| async {
626 let protocol = options.protocols.negotiated(selected.as_deref())?;
627 let (input, output) = protocol.server(reader, writer, options.http1.clone())?;
628 serve_codecs(
629 input,
630 output,
631 handler,
632 cancellation,
633 &mut requests,
634 &Scheme::HTTPS,
635 )
636 .await
637 })
638 .await;
639 Outcome { requests, result }
640}
641
642#[cfg(feature = "rustls")]
645pub async fn serve_tls<S, H>(
646 scheduler: &S,
647 listener: S::Socket,
648 config: std::sync::Arc<rustls::ServerConfig>,
649 handler: &H,
650 options: &Options,
651 cancellation: &Cancellation,
652) -> Result<Statistics, Error>
653where
654 S: SocketAccept
655 + Clock
656 + SocketSetOption<TcpNoDelay>
657 + SocketStreamReading
658 + SocketStreamWriting
659 + SocketClose,
660 S::Socket: 'static,
661 H: Handler,
662{
663 use base_endpoint::tls::{TlsAcceptor, rustls::RustlsAcceptor};
664 let mut config = (*config).clone();
665 config.alpn_protocols = options.protocols.alpn_protocols();
666 let acceptor = RustlsAcceptor::new(std::sync::Arc::new(config));
667 serve_with(
668 scheduler,
669 listener,
670 options,
671 cancellation,
672 |socket, cancellation| {
673 let acceptor = &acceptor;
674 async move {
675 match acceptor.accept(scheduler, socket, &cancellation).await {
676 Ok(connection) => {
677 tls_connection_outcome(
678 scheduler,
679 connection,
680 handler,
681 options,
682 &cancellation,
683 )
684 .await
685 }
686 Err(error) => Outcome {
687 requests: 0,
688 result: Err(error.into()),
689 },
690 }
691 }
692 },
693 )
694 .await
695}
696
697impl From<base_http_protocol::NegotiationError> for Error {
698 fn from(error: base_http_protocol::NegotiationError) -> Self {
699 Self::Negotiation(error)
700 }
701}
702
703#[cfg(test)]
704mod tests {
705 use super::*;
706 use std::{error::Error as _, io};
707
708 #[test]
709 fn protocol_errors_are_transparent_and_io_errors_have_context() {
710 let error = Error::from(base_http1::Error::Io(io::Error::from(
711 io::ErrorKind::ConnectionReset,
712 )));
713 assert_eq!(error.to_string(), "HTTP/1 connection I/O failed");
714 assert_eq!(error.source().unwrap().to_string(), "connection reset");
715
716 let error = Error::from(io::Error::from(io::ErrorKind::ConnectionReset));
717 assert_eq!(error.to_string(), "HTTP server I/O failed");
718 assert_eq!(error.source().unwrap().to_string(), "connection reset");
719 }
720
721 #[test]
722 fn handler_errors_are_reported_as_the_source() {
723 let error = Error::Handler(Box::new(io::Error::other("database unavailable")));
724 assert_eq!(error.to_string(), "request handler failed");
725 assert_eq!(error.source().unwrap().to_string(), "database unavailable");
726 }
727
728 #[test]
729 fn invalid_responses_report_the_refusal_as_the_source() {
730 let error = Error::Response(base_http1::Error::Invalid("final response required"));
731 assert_eq!(
732 error.to_string(),
733 "request handler returned an invalid response"
734 );
735 assert_eq!(
736 error.source().unwrap().to_string(),
737 "invalid HTTP/1 message: final response required"
738 );
739 }
740
741 #[test]
742 fn scheduler_operation_errors_keep_cancellation_distinct() {
743 let error = Error::from(base_scheduler::OperationError::from(
744 base_scheduler::Cancelled,
745 ));
746 assert_eq!(format!("{error:?}"), "Cancelled");
747 assert_eq!(error.to_string(), "HTTP server cancelled");
748 assert!(error.source().is_none());
749
750 let error = Error::from(base_scheduler::OperationError::from(io::Error::from(
751 io::ErrorKind::ConnectionReset,
752 )));
753 let expected = Error::Protocol(base_http1::Error::Io(io::Error::from(
754 io::ErrorKind::ConnectionReset,
755 )));
756 assert_eq!(format!("{error:?}"), format!("{expected:?}"));
757 assert_eq!(error.source().unwrap().to_string(), "connection reset");
758 }
759
760 #[test]
761 fn accept_failures_are_classified_by_their_effect_on_the_listener() {
762 for kind in [
763 io::ErrorKind::ConnectionAborted,
764 io::ErrorKind::ConnectionReset,
765 io::ErrorKind::ConnectionRefused,
766 io::ErrorKind::Interrupted,
767 io::ErrorKind::WouldBlock,
768 io::ErrorKind::NetworkDown,
769 io::ErrorKind::NetworkUnreachable,
770 io::ErrorKind::HostUnreachable,
771 ] {
772 assert_eq!(
773 accept_failure(&kind.into()),
774 AcceptFailure::Retry,
775 "{kind:?}"
776 );
777 }
778 assert_eq!(
779 accept_failure(&io::ErrorKind::OutOfMemory.into()),
780 AcceptFailure::Backoff
781 );
782 for kind in [
783 io::ErrorKind::InvalidInput,
784 io::ErrorKind::BrokenPipe,
785 io::ErrorKind::PermissionDenied,
786 io::ErrorKind::Other,
787 ] {
788 assert_eq!(
789 accept_failure(&kind.into()),
790 AcceptFailure::Fatal,
791 "{kind:?}"
792 );
793 }
794 for &code in os::RETRY {
795 let error = io::Error::from_raw_os_error(code);
796 assert_eq!(accept_failure(&error), AcceptFailure::Retry, "{error}");
797 }
798 for &code in os::BACKOFF {
799 let error = io::Error::from_raw_os_error(code);
800 assert_eq!(accept_failure(&error), AcceptFailure::Backoff, "{error}");
801 }
802 #[cfg(unix)]
803 for code in [
804 libc::EBADF,
805 libc::EINVAL,
806 libc::ENOTSOCK,
807 libc::EOPNOTSUPP,
808 libc::EACCES,
809 ] {
810 let error = io::Error::from_raw_os_error(code);
811 assert_eq!(accept_failure(&error), AcceptFailure::Fatal, "{error}");
812 }
813 }
814
815 #[test]
816 fn negotiation_errors_are_transparent() {
817 let error = Error::from(base_http_protocol::NegotiationError::Missing);
818 assert_eq!(format!("{error:?}"), "Negotiation(Missing)");
819 assert_eq!(
820 error.to_string(),
821 "HTTP application protocol was not negotiated"
822 );
823 assert!(error.source().is_none());
824 }
825}