tosic_http/server/builder.rs
1//! The [`HttpServerBuilder`] is a builder for configuring and initializing an [`HttpServer`].
2//! It allows for setting up the server address, adding services, and configuring shared state.
3
4use crate::body::BoxBody;
5use crate::error::Error;
6use crate::handlers::Handlers;
7use crate::server::HttpServer;
8use crate::services::HttpService;
9use crate::state::State;
10use crate::traits::from_request::FromRequest;
11use crate::traits::handler::Handler;
12use crate::traits::responder::Responder;
13use http::Method;
14use std::fmt::Debug;
15use std::future::Future;
16use tokio::io;
17use tokio::net::ToSocketAddrs;
18
19use crate::prelude::{HttpPayload, HttpRequest, HttpResponse};
20use crate::resource::RouteBuilder;
21use crate::route::HandlerFn;
22#[allow(unused_imports)]
23use std::any::TypeId;
24#[allow(unused_imports)]
25use std::collections::HashMap;
26use tower::layer::util::{Identity, Stack};
27use tower::{Layer, Service, ServiceBuilder};
28
29#[derive(Debug, Clone)]
30/// [`HttpServerBuilder`] is a builder for configuring and initializing an [`HttpServer`].
31/// It allows for setting up the server address, adding services, and configuring shared state.
32pub struct HttpServerBuilder<T, L>
33where
34 T: ToSocketAddrs + Default + Clone,
35 L: Layer<HandlerFn> + Clone + Send + 'static,
36{
37 addr: Option<T>,
38 handlers: Handlers,
39 app_state: State,
40 service_builder: ServiceBuilder<L>,
41}
42
43impl<T: ToSocketAddrs + Default + Debug + Clone> Default for HttpServerBuilder<T, Identity> {
44 fn default() -> Self {
45 Self {
46 addr: None,
47 handlers: Handlers::new(),
48 app_state: State::new(),
49 service_builder: ServiceBuilder::new(),
50 }
51 }
52}
53
54impl<T: ToSocketAddrs + Default + Debug + Clone> HttpServerBuilder<T, Identity> {
55 pub(crate) fn new() -> HttpServerBuilder<T, Identity> {
56 Self::default()
57 }
58}
59
60impl<T, L> HttpServerBuilder<T, L>
61where
62 T: ToSocketAddrs + Default + Debug + Clone,
63 L: Layer<HandlerFn> + Clone + Send + 'static,
64 L::Service: Service<(HttpRequest, HttpPayload), Response = HttpResponse, Error = Error>
65 + Send
66 + 'static,
67 <L::Service as Service<(HttpRequest, HttpPayload)>>::Future: Send + 'static,
68{
69 /// Adds shared application state to be accessible in request handlers.
70 ///
71 /// State is stored in a [`HashMap`] and keyed based on the [`TypeId`] of the state object.
72 ///
73 /// # Arguments
74 /// - `state`: A state object of type `S` that implements `Send + Sync + 'static`.
75 ///
76 /// # Returns
77 /// The builder instance with the shared state added.
78 ///
79 /// # Examples
80 /// ```
81 /// # use tosic_http::prelude::HttpServer;
82 /// struct MyState {
83 /// state: String
84 /// }
85 ///
86 /// let builder = HttpServer::builder()
87 /// .with_state(MyState { state: "Hello, world!".to_string() })
88 /// .bind("127.0.0.1:8080");
89 /// ```
90 pub fn app_state<S: Send + Sync + 'static>(self, state: S) -> Self {
91 self.app_state.insert(state);
92
93 self
94 }
95
96 /// Adds a service handler to the server.
97 ///
98 /// # Arguments
99 ///
100 /// - `method`: The HTTP method for the service endpoint.
101 /// - `path`: The path of the service endpoint.
102 /// - `handler`: A handler implementing the [`Handler`] trait, defining a service endpoint.
103 ///
104 /// # Returns
105 ///
106 /// The builder instance with the handler added.
107 ///
108 /// # Examples
109 /// ```
110 /// # use tosic_http::prelude::{HttpServer, Responder, HttpResponse, BoxBody, Method};
111 ///
112 /// async fn basic_handler() -> impl Responder<Body = BoxBody> {
113 /// HttpResponse::new(200)
114 /// }
115 ///
116 /// let builder = HttpServer::builder()
117 /// .service_method(Method::GET, "/", basic_handler)
118 /// .bind("127.0.0.1:8080");
119 /// ```
120 pub fn service_method<H, Args>(mut self, method: Method, path: &str, handler: H) -> Self
121 where
122 H: Handler<Args> + Send + Sync + 'static,
123 Args: FromRequest + Send + 'static,
124 Args::Future: Future + Send + 'static,
125 H::Future: Future + Send + 'static,
126 H::Output: Responder<Body = BoxBody> + 'static,
127 Error: From<Args::Error>,
128 {
129 self.handlers.insert(method, path, handler);
130 self
131 }
132
133 /// Adds a service handler to the server.
134 ///
135 /// # Arguments
136 ///
137 /// - `handler`: A handler implementing the [`Handler`] & [`HttpService`] traits.
138 ///
139 /// # Returns
140 ///
141 /// The builder instance with the handler added.
142 ///
143 /// # Examples
144 /// ```
145 /// # use tosic_http::prelude::{HttpServer, Responder, HttpResponse, BoxBody, Method, get};
146 ///
147 /// #[get("/")]
148 /// async fn basic_handler() -> impl Responder<Body = BoxBody> {
149 /// HttpResponse::new(200)
150 /// }
151 ///
152 /// let builder = HttpServer::builder()
153 /// .service(basic_handler)
154 /// .bind("127.0.0.1:8080");
155 /// ```
156 pub fn service<H, Args>(mut self, handler: H) -> Self
157 where
158 H: HttpService<Args> + Handler<Args> + Send + Sync + 'static,
159 Args: FromRequest + Send + 'static,
160 Args::Future: Future + Send + 'static,
161 H::Future: Future + Send + 'static,
162 H::Output: Responder<Body = BoxBody> + 'static,
163 Error: From<Args::Error>,
164 {
165 self.handlers.insert(H::METHOD, H::PATH, handler);
166 self
167 }
168
169 /// Adds a route to the server.
170 ///
171 /// # Arguments
172 ///
173 /// - `route_builder`: A builder for defining the route.
174 ///
175 /// # Returns
176 ///
177 /// The builder instance with the route added.
178 ///
179 pub fn route(mut self, route_builder: RouteBuilder) -> Self {
180 self.handlers.extend(route_builder.handlers());
181
182 self
183 }
184
185 /// Sets the address the server will bind to.
186 ///
187 /// # Arguments
188 ///
189 /// - `addr`: The address for the server to bind, implementing [`ToSocketAddrs`].
190 ///
191 /// # Returns
192 ///
193 /// Returns the builder instance with the binding address set.
194 ///
195 /// # Examples
196 /// ```
197 /// # use tosic_http::prelude::HttpServer;
198 /// let builder = HttpServer::builder()
199 /// .bind("127.0.0.1:8080");
200 /// ```
201 pub fn bind(mut self, addr: T) -> Self {
202 self.addr = Some(addr);
203 self
204 }
205
206 /// Builds and initializes the [`HttpServer`] with the current configuration.
207 ///
208 /// # Errors
209 /// Returns [`io::Error`] if there was an error initializing the server.
210 ///
211 /// # Examples
212 /// ```
213 /// # use tosic_http::prelude::HttpServer;
214 /// # #[tokio::main]
215 /// # async fn main() {
216 /// let server = HttpServer::builder()
217 /// .bind("127.0.0.1:8080")
218 /// .build()
219 /// .await
220 /// .unwrap();
221 /// # }
222 /// ```
223 pub async fn build(self) -> io::Result<HttpServer<L>> {
224 let addr = self.addr.unwrap_or_default();
225
226 HttpServer::new(addr, self.handlers, self.app_state, self.service_builder).await
227 }
228
229 /// Wraps a layer in the stack.
230 ///
231 /// A layer is a middleware that can modify the request and response.
232 pub fn wrap<S>(self, layer: S) -> HttpServerBuilder<T, Stack<S, L>>
233 where
234 S: Layer<HandlerFn> + Clone + Send + 'static,
235 L: Layer<S::Service> + Clone + Send + 'static,
236 {
237 HttpServerBuilder {
238 addr: self.addr,
239 handlers: self.handlers,
240 app_state: self.app_state,
241 service_builder: self.service_builder.layer(layer),
242 }
243 }
244}