xitca_web/lib.rs
1//! a web framework focused on memory efficiency, composability, and fast compile time.
2//!
3//! # Quick start
4//! ```no_run
5//! use xitca_web::{handler::handler_service, route::get, App};
6//!
7//! fn main() -> std::io::Result<()> {
8//! App::new()
9//! .at("/", get(handler_service(async || "Hello,World!")))
10//! .serve()
11//! .bind("localhost:8080")?
12//! .run()
13//! .wait()
14//! }
15//! ```
16//!
17//! # Feature flags
18//! Most features are disabled by default and can be enabled selectively in your `Cargo.toml`.
19//!
20//! ## HTTP versions
21//! - **`http1`** *(default)* — HTTP/1.1 server support.
22//! - **`http2`** — HTTP/2 server support.
23//! - **`http3`** — HTTP/3 (QUIC) server support.
24//!
25//! ## IO
26//! - **`io-uring`** — Linux io-uring based async IO for the server runtime.
27//!
28//! ## TLS
29//! - **`openssl`** — TLS via OpenSSL.
30//! - **`rustls`** — TLS via Rustls.
31//!
32//! ## Extractors / Responders
33//! - **`params`** — Path parameters type extractor (enables `serde`).
34//! - **`json`** — JSON type extractor/responder (enables `serde`).
35//! - **`urlencoded`** — URL-encoded form type extractor (enables `serde`).
36//! - **`multipart`** — Multipart form data type extractor.
37//! - **`grpc`** — gRPC type extractor/responder (via `prost`).
38//! - **`websocket`** — WebSocket type extractor/responder.
39//! - **`cookie`** — Cookie handler type.
40//!
41//! ## Compression middlewares
42//! - **`compress-br`** — Brotli (de)compression.
43//! - **`compress-gz`** — Gzip (de)compression.
44//! - **`compress-de`** — Deflate (de)compression.
45//! - **`compress-zs`** — Zstd (de)compression.
46//!
47//! ## Static file serving
48//! - **`file`** — Static file serving with default file system backend.
49//! - **`file-io-uring`** — Static file serving using io-uring (implies `io-uring` and `file`).
50//! - **`file-raw`** — Static file serving without a default file system backend.
51//!
52//! ## Middleware
53//! - **`rate-limit`** — Rate-limiting middleware.
54//! - **`logger`** — Tracing/logging middleware (via `tracing` and `tracing-subscriber`).
55//!
56//! ## Codegen
57//! - **`codegen`** — Proc-macro code generation (`#[route]`, `#[derive(State)]`, etc.).
58//!
59//! ## Interop
60//! - **`tower-http-compat`** — Compatibility layer for using `tower-http` services and layers.
61//!
62//! ## Other
63//! - **`serde`** — Shared (de)serialization support. Automatically enabled by `params`, `json`, and `urlencoded`.
64//! - **`nightly`** — Enables nightly-only Rust features. **Unstable** — breaking changes to nightly-gated
65//! APIs are not considered semver-breaking.
66//!
67//! # Memory efficient
68//! - zero copy magic types
69//! - zero cost service tree
70//!
71//! ## Zero copy
72//! ```rust
73//! # #[cfg(feature = "json")]
74//! # fn _main() -> std::io::Result<()> {
75//! use xitca_web::{
76//! error::Error,
77//! handler::{handler_service, json::LazyJson, path::PathRef},
78//! route::{get, post},
79//! App
80//! };
81//!
82//! // PathRef is able to borrow http request's path string as reference
83//! // without copying it.
84//! async fn url_query(PathRef(path): PathRef<'_>) -> &'static str {
85//! println!("{path}");
86//! "zero copy path"
87//! }
88//!
89//! // deserializable user type.
90//! #[derive(serde::Deserialize)]
91//! struct User<'a> {
92//! name: &'a str
93//! }
94//!
95//! // LazyJson is able to lazily deserialize User type with zero copy &str.
96//! async fn json(lazy: LazyJson<User<'_>>) -> Result<&'static str, Error> {
97//! let User { name } = lazy.deserialize()?;
98//! println!("{name}");
99//! Ok("zero copy json")
100//! }
101//!
102//! // Almost all magic extract types in xitca-web utilize zero copy
103//! // to avoid unnecessary memory copy.
104//! App::new()
105//! // a route handling incoming url query.
106//! .at("/query", get(handler_service(url_query)))
107//! // a route handling incoming json object.
108//! .at("/json", post(handler_service(json)))
109//! .serve()
110//! .bind("localhost:8080")?
111//! .run()
112//! .wait()
113//! # }
114//! ```
115//!
116//! ## Zero cost
117//! ```rust
118//! use xitca_web::{
119//! handler::{handler_service},
120//! http::WebResponse,
121//! route::get,
122//! middleware::Extension,
123//! service::{Service, ServiceExt},
124//! App, WebContext
125//! };
126//! # fn _main() -> std::io::Result<()> {
127//! App::new()
128//! .at("/", get(handler_service(|| async { "hello,world!" })))
129//! // ServiceExt::enclosed_fn combinator enables async function as middleware.
130//! // the async function is unboxed and potentially inlined with other async services
131//! // for efficient binary code with less memory allocation.
132//! .enclosed_fn(middleware_fn)
133//! // ServiceExt::enclosed combinator enables type impl Service trait as middleware.
134//! // the middleware trait method is unboxed and potentially inlined with other async services
135//! // for efficient binary code with less memory allocation.
136//! .enclosed(Extension::new(()))
137//! .serve()
138//! .bind("localhost:8080")?
139//! .run()
140//! .wait()
141//! # }
142//!
143//! // a simple middleware just forward request to inner service logic.
144//! async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
145//! where
146//! S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
147//! {
148//! service.call(ctx).await
149//! }
150//! ```
151//!
152//! # Composable
153//! - Easy mixture of various level of abstractions and less opinionated APIs
154//! - Common types and traits for cross crates integration of majority rust web ecosystem
155//!
156//! ## Abstraction variety
157//! ```rust
158//! use xitca_web::{
159//! body::ResponseBody,
160//! error::Error,
161//! handler::{handler_service, handler_sync_service, Responder},
162//! http::{Method, WebResponse},
163//! route::get,
164//! service::fn_service,
165//! App, WebContext
166//! };
167//!
168//! # fn _main() -> std::io::Result<()> {
169//! App::new()
170//! // high level abstraction. see fn high for detail.
171//! .at("/high", get(handler_service(high)))
172//! // low level abstraction. see fn low for detail.
173//! .at("/low", get(fn_service(low)))
174//! // abstraction for synchronous. see fn sync for detail.
175//! .at("/sync", get(handler_sync_service(sync)))
176//! .serve()
177//! .bind("localhost:8080")?
178//! .run()
179//! .wait()
180//! # }
181//!
182//! // magic function with arbitrary receiver type and output type
183//! // that can be extracted from http requests and packed into http
184//! // response.
185//! async fn high(method: &Method) -> &'static str {
186//! // extract http method from http request.
187//! assert_eq!(method, Method::GET);
188//! // pack string literal into http response.
189//! "high level"
190//! }
191//!
192//! // function with concrete typed input and output where http types
193//! // are handled manually and explicitly.
194//! // it can also be seen as a desugar of previous example of
195//! // handler_service(high)
196//! async fn low(ctx: WebContext<'_>) -> Result<WebResponse, Error> {
197//! // extract method from request context.
198//! let method = ctx.extract().await?;
199//! // execute high level abstraction example function.
200//! let str = high(method).await;
201//! // convert string literal to http response.
202//! str.respond(ctx).await
203//! }
204//!
205//! // high level abstraction but for synchronous function. this function
206//! // is powered by background thread pool so it does not block the async
207//! // code.
208//! fn sync(method: Method) -> &'static str {
209//! assert_eq!(method, Method::GET);
210//! // blocking thread for long period of time does not impact xitca-web
211//! // async internal.
212//! std::thread::sleep(std::time::Duration::from_secs(3));
213//! "sync"
214//! }
215//! ```
216//!
217//! ## Middleware composability
218//! ```rust
219//! use xitca_web::{
220//! error::Error,
221//! handler::{handler_service},
222//! http::WebResponse,
223//! route::get,
224//! service::{Service, ServiceExt},
225//! App, WebContext
226//! };
227//!
228//! # fn _main() -> std::io::Result<()> {
229//! // ServiceExt::enclosed_fn combinator enables async function as middleware.
230//! // in xitca_web almost all service can be enclosed by an middleware.
231//! App::new()
232//! .at("/",
233//! get(
234//! // apply middleware to handler_service
235//! handler_service(|| async { "hello,world!" })
236//! .enclosed_fn(middleware_fn)
237//! )
238//! // apply middleware to route
239//! .enclosed_fn(middleware_fn)
240//! )
241//! // apply middleware to application
242//! .enclosed_fn(middleware_fn)
243//! .serve()
244//! .bind("localhost:8080")?
245//! .run()
246//! .wait()
247//! # }
248//!
249//! // a simple middleware just forward request to inner service logic.
250//! async fn middleware_fn<S, T, E>(service: &S, ctx: WebContext<'_>) -> Result<T, E>
251//! where
252//! S: for<'r> Service<WebContext<'r>, Response = T, Error = E>
253//! {
254//! service.call(ctx).await
255//! }
256//! ```
257//! For more detailed middleware documentation please reference [middleware]
258//!
259//! ## Cross crates integration
260//! use tower-http inside xitca-web application.
261//! ```rust
262//! # #[cfg(feature = "tower-http-compat")]
263//! # fn _main() -> std::io::Result<()> {
264//! use tower_http::services::ServeDir;
265//! use xitca_web::{
266//! service::tower_http_compat::TowerHttpCompat,
267//! App
268//! };
269//!
270//! App::new()
271//! .at("/", TowerHttpCompat::new(ServeDir::new("/some_folder")))
272//! .serve()
273//! .bind("localhost:8080")?
274//! .run()
275//! .wait()
276//! # }
277//! ```
278//!
279//! # Fast compile time
280//! - additive proc macro
281//! - light weight dependency tree
282//!
283//! ## opt-in proc macro
284//! in xitca-web proc macro is opt-in. This result in a fast compile time with zero
285//! public proc macro. That said you still can enable macros for a higher level style
286//! of API.
287//! ```rust
288//! # #[cfg(feature = "codegen")]
289//! # async fn _main() -> std::io::Result<()> {
290//! use xitca_web::{codegen::route, App};
291//!
292//! #[route("/", method = get)]
293//! async fn index() -> &'static str {
294//! "Hello,World!"
295//! }
296//!
297//! App::new()
298//! .at_typed(index)
299//! .serve()
300//! .bind("localhost:8080")?
301//! .run()
302//! .await
303//! # }
304//! ```
305
306#![forbid(unsafe_code)]
307#![cfg_attr(feature = "nightly", feature(async_iterator, error_generic_member_access))]
308
309mod app;
310mod context;
311#[cfg(feature = "__server")]
312mod server;
313
314pub mod body;
315pub mod error;
316pub mod handler;
317pub mod middleware;
318pub mod service;
319pub mod test;
320
321#[cfg(feature = "codegen")]
322pub mod codegen {
323 //! macro code generation module.
324
325 /// Derive macro for individual struct field type extractable through [StateRef](crate::handler::state::StateRef)
326 /// and [StateOwn](crate::handler::state::StateOwn)
327 ///
328 /// # Example:
329 /// ```rust
330 /// # use xitca_web::{codegen::State, handler::{handler_service, state::StateRef}, App, WebContext};
331 ///
332 /// // use derive macro and attribute to mark the field that can be extracted.
333 /// #[derive(State, Clone)]
334 /// struct MyState {
335 /// #[borrow]
336 /// field: u128
337 /// }
338 ///
339 /// # async fn app() {
340 /// // construct App with MyState type.
341 /// App::new()
342 /// .with_state(MyState { field: 996 })
343 /// .at("/", handler_service(index))
344 /// # .at("/nah", handler_service(nah));
345 /// # }
346 ///
347 /// // extract u128 typed field from MyState.
348 /// async fn index(StateRef(num): StateRef<'_, u128>) -> String {
349 /// assert_eq!(*num, 996);
350 /// num.to_string()
351 /// }
352 /// # async fn nah(_: &WebContext<'_, MyState>) -> &'static str {
353 /// # // needed to infer the body type of request
354 /// # ""
355 /// # }
356 /// ```
357 pub use xitca_codegen::State;
358
359 pub use xitca_codegen::route;
360
361 pub use xitca_codegen::error_impl;
362
363 #[doc(hidden)]
364 /// a hidden module for macro to access public types that are not framework user facing.
365 pub mod __private {
366 pub use xitca_http::util::service::router::{IntoObject, RouteObject, RouterMapErr, TypedRoute};
367 }
368}
369
370pub mod http {
371 //! http types
372
373 use super::body::{RequestBody, ResponseBody};
374
375 pub use xitca_http::http::*;
376
377 /// type alias for default request type xitca-web uses.
378 pub type WebRequest<B = RequestBody> = Request<RequestExt<B>>;
379
380 /// type alias for default response type xitca-web uses.
381 pub type WebResponse<B = ResponseBody> = Response<B>;
382}
383
384pub mod route {
385 //! route services.
386 //! # Examples:
387 //! ```
388 //! # use xitca_web::{
389 //! # handler::handler_service,
390 //! # http::Method,
391 //! # route::{get, post, put, Route}
392 //! # };
393 //! # fn _route() -> Result<(), Box<dyn std::error::Error>> {
394 //! // a simple async function service.
395 //! let service = handler_service(|_: ()| async { });
396 //!
397 //! let custom_method = Method::from_bytes(b"huh")?;
398 //!
399 //! // route get http method and a user custom method to handler_service
400 //! Route::new([Method::GET, custom_method]).route(service.clone());
401 //!
402 //! // shortcut for single http method route(s). they can chained multiple times.
403 //! get(service.clone()).post(service.clone()).put(service);
404 //! # Ok(())
405 //! # }
406 //! ```
407 pub use xitca_http::util::service::route::{Route, connect, delete, get, head, options, patch, post, put, trace};
408}
409
410pub use app::{App, AppObject, NestApp};
411pub use body::BodyStream;
412pub use context::WebContext;
413#[cfg(feature = "__server")]
414pub use server::HttpServer;
415
416pub use xitca_http::bytes;