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