web_static_pack/lib.rs
1//! web-static-pack is the "loader" (2nd stage) part of the
2//! [web-static-pack](https://github.com/peku33/web-static-pack)
3//! project. See project page for a general idea how two parts cooperate.
4//!
5//! Once a `pack` is created with build script / CI / build.rs using
6//! [web-static-pack-packer](https://crates.io/crates/web-static-pack-packer)
7//! it will usually be included in your target application with
8//! <https://docs.rs/include_bytes_aligned/latest/include_bytes_aligned/>.
9//! Then it will be loaded with a [loader::load], utilizing zero-copy
10//! deserialization (so file contents will be sent from executable contents
11//! directly). The pack is then possibly wrapped with [responder::Responder]
12//! http service and used with a web server like hyper.
13//!
14//! The main part of this crate is [responder::Responder]. Its
15//! [responder::Responder::respond_flatten] method makes a [http] service - a
16//! function taking [http::Request] parts (method, path, headers) and returning
17//! [http::Response].
18//!
19//! To make a [responder::Responder], a [common::pack::Pack] is needed. It can
20//! be obtained by [loader::load] function by passing (possibly included in
21//! binary) contents of a `pack` created with the packer.
22//!
23//! # Examples
24//!
25//! ## Creating and calling responder
26//! ```ignore
27//! use anyhow::Error;
28//! use include_bytes_aligned::include_bytes_aligned;
29//! use http::{HeaderMap, Method, StatusCode};
30//! use web_static_pack::{loader::load, responder::Responder};
31//!
32//! // assume we have a vcard-personal-portfolio.pack available from packer examples
33//! static PACK_ARCHIVED_SERIALIZED: &[u8] =
34//! include_bytes_aligned!(16, "vcard-personal-portfolio.pack");
35//!
36//! fn main() -> Result<(), Error> {
37//! // load (map / cast) [common::pack::PackArchived] from included bytes
38//! let pack_archived = unsafe { load(PACK_ARCHIVED_SERIALIZED).unwrap() };
39//!
40//! // create a responder from `pack`
41//! let responder = Responder::new(pack_archived);
42//!
43//! // do some checks on the responder
44//! assert_eq!(
45//! responder.respond_flatten(
46//! &Method::GET,
47//! "/present",
48//! &HeaderMap::default(),
49//! ).status(),
50//! StatusCode::OK
51//! );
52//!
53//! Ok(())
54//! }
55//! ```
56//!
57//! ## Adapting to hyper service
58//! This example is based on
59//! <https://hyper.rs/guides/1/server/graceful-shutdown/>
60//! which is a bit complicated.
61//!
62//! You can run full working example from
63//! `tests/examples/vcard_personal_portfolio_server.rs`
64//!
65//! ```ignore
66//! use anyhow::Error;
67//! use web_static_pack::responder::Responder;
68//! use std::{
69//! convert::Infallible,
70//! mem::transmute,
71//! };
72//!
73//! #[tokio::main(flavor = "current_thread")]
74//! async fn main() -> Result<(), Error> {
75//! // lets assume we have a `responder: Responder` object available from previous example
76//! // hyper requires service to be static
77//! // we use graceful, no connections will outlive server function
78//! let responder = unsafe {
79//! transmute::<
80//! &Responder<'_, _>,
81//! &Responder<'static, _>,
82//! >(&responder)
83//! };
84//!
85//! // make hyper service
86//! let service_fn = service_fn(|request| async {
87//! // you can probably filter your /api requests here
88//! let (parts, _body) = request.into_parts();
89//!
90//! let response = responder.respond_flatten(
91//! &parts.method,
92//! parts.uri.path(),
93//! &parts.headers
94//! );
95//!
96//! Ok::<_, Infallible>(response)
97//! });
98//!
99//! // use service_fn like in hyper example
100//! Ok(())
101//! }
102//! ```
103
104#![allow(clippy::new_without_default)]
105#![allow(clippy::let_and_return)]
106#![warn(missing_docs)]
107
108pub use web_static_pack_common as common;
109
110pub mod body;
111pub mod cache_control;
112pub mod content_encoding;
113pub mod file;
114pub mod loader;
115pub mod pack;
116pub mod responder;