Skip to main content

gotham/
lib.rs

1//! Gotham – A flexible web framework that promotes stability, safety, security and speed.
2//!
3//! You can find out more about Gotham, including where to get help, at
4//! <https://gotham.rs/>.
5//!
6//! We look forward to welcoming you into the Gotham community!
7#![doc(html_root_url = "https://docs.rs/gotham/0.8.0")]
8// Update when changed in Cargo.toml
9// Stricter requirements once we get to pull request stage, all warnings must be resolved.
10#![cfg_attr(feature = "ci", deny(warnings))]
11#![allow(
12    clippy::needless_lifetimes,
13    clippy::should_implement_trait,
14    clippy::unit_arg,
15    clippy::match_wild_err_arm,
16    clippy::new_without_default,
17    clippy::wrong_self_convention,
18    clippy::mutex_atomic,
19    clippy::borrowed_box,
20    clippy::get_unwrap
21)]
22#![warn(missing_docs, rust_2018_idioms, unreachable_pub)]
23#![deny(elided_lifetimes_in_paths, unsafe_code)]
24#![doc(test(no_crate_inject, attr(deny(warnings))))]
25
26pub mod extractor;
27pub mod handler;
28pub mod helpers;
29pub mod middleware;
30pub mod pipeline;
31pub mod prelude;
32pub mod router;
33pub mod service;
34pub mod state;
35
36/// Test utilities for Gotham and Gotham consumer apps.
37#[cfg(feature = "testing")]
38pub mod test;
39
40/// Functions for creating a Gotham service using HTTP.
41pub mod plain;
42
43/// Functions for creating a Gotham service using HTTPS.
44#[cfg(feature = "__tls")]
45pub mod tls;
46
47/// Re-export anyhow
48pub use anyhow;
49/// Re-export bytes
50pub use bytes;
51/// Re-export cookie
52pub use cookie;
53/// Re-export http
54pub use http;
55/// Re-export http-body
56pub use http_body;
57/// Re-export http-body-util
58pub use http_body_util;
59/// Re-export hyper
60pub use hyper;
61/// Re-export hyper-util
62pub use hyper_util;
63/// Re-export mime
64pub use mime;
65/// Re-export tower-service
66pub use tower_service;
67
68/// Re-export rustls
69#[cfg(feature = "__tls")]
70pub use tokio_rustls::rustls;
71
72use futures_util::TryFutureExt;
73use hyper_util::rt::{TokioExecutor, TokioIo};
74use std::future::Future;
75use std::io;
76use std::net::ToSocketAddrs;
77use std::sync::Arc;
78use thiserror::Error;
79use tokio::io::{AsyncRead, AsyncWrite};
80use tokio::net::{TcpListener, TcpStream};
81use tokio::runtime::{self, Runtime};
82
83use crate::handler::NewHandler;
84use crate::service::GothamService;
85
86pub use plain::*;
87#[cfg(feature = "__tls")]
88pub use tls::start as start_with_tls;
89
90/// The error that can occur when starting the gotham server.
91#[derive(Debug, Error)]
92#[non_exhaustive]
93pub enum StartError {
94    /// I/O error.
95    #[error("I/O Error: {0}")]
96    IoError(#[from] io::Error),
97}
98
99fn new_runtime(threads: usize) -> Runtime {
100    runtime::Builder::new_multi_thread()
101        .worker_threads(threads)
102        .thread_name("gotham-worker")
103        .enable_all()
104        .build()
105        .unwrap()
106}
107
108async fn tcp_listener<A>(addr: A) -> io::Result<TcpListener>
109where
110    A: ToSocketAddrs + 'static,
111{
112    let addr = addr
113        .to_socket_addrs()?
114        .next()
115        .ok_or_else(|| io::Error::other("unable to resolve listener address"))?;
116    TcpListener::bind(addr).await
117}
118
119/// Returns a `Future` used to spawn a Gotham application.
120///
121/// This is used internally, but it's exposed for clients that want to set up their own TLS
122/// support. The wrap argument is a function that will receive a tokio-io TcpStream and should wrap
123/// the socket as necessary. Errors returned by this function will be ignored and the connection
124/// will be dropped if the future returned by the wrapper resolves to an error.
125pub async fn bind_server<NH, F, Wrapped, Wrap>(
126    listener: TcpListener,
127    new_handler: NH,
128    wrap: Wrap,
129) -> !
130where
131    NH: NewHandler + 'static,
132    F: Future<Output = Result<Wrapped, ()>> + Unpin + Send + 'static,
133    Wrapped: Unpin + AsyncRead + AsyncWrite + Send + 'static,
134    Wrap: Fn(TcpStream) -> F,
135{
136    let protocol = Arc::new(hyper_util::server::conn::auto::Builder::new(
137        TokioExecutor::new(),
138    ));
139    let gotham_service = GothamService::new(new_handler);
140
141    loop {
142        let (socket, addr) = match listener.accept().await {
143            Ok(ok) => ok,
144            Err(err) => {
145                log::error!("Socket Error: {}", err);
146                continue;
147            }
148        };
149
150        let service = gotham_service.connect(addr);
151        let accepted_protocol = Arc::clone(&protocol);
152        let wrapper = wrap(socket);
153
154        // NOTE: HTTP protocol errors and handshake errors are ignored here (i.e. so the socket
155        // will be dropped).
156        let task = async move {
157            let socket = wrapper.await?;
158
159            accepted_protocol
160                .serve_connection_with_upgrades(TokioIo::new(socket), service)
161                .map_err(drop)
162                .await?;
163
164            Result::<_, ()>::Ok(())
165        };
166
167        tokio::spawn(task);
168    }
169}