web-static-pack is a set of tools for embedding static resources (GUI, assets, images, styles, html) inside your app, to be later served with a http server of your choice (like hyper).
It consists of two parts:
- web-static-pack-packer (aka "packer") - a standalone application (can be used as a library) used to serialize your assets into single file, called
pack. It will usually be used before you build your target application (eg. in build script / CI / build.rs). During creation of apackall heavy computations are done, eg. calculatingETag, compressed (gzip,brotli) versions, mime guessing etc. As a result apackfile is created, to be used by the next part. - web-static-pack (aka "loader") - a library to include in your target application that will read the
pack(preferably included in the application with https://docs.rs/include_bytes_aligned/latest/include_bytes_aligned/). Thenpackcan be used to form ahttpservice(a function taking a request (parts) and returning response) serving files from thepack.
- Precomputed (in "packer")
ETag(usingsha3), compressed bodies (ingzipandbrotliformats),content-type, etc. This reduces both runtime overhead and dependencies of your target application. - Zero-copy deserialization of a
packthanks to rkyv, allows the pack to be read directly from program memory, without allocating additional ram for pack contents. GET/HEADhttp methods support,ETag/if-none-matchsupport,accept-encoding/content-encodingnegotiation,cache-control,content-lengthetc.
- Directory listings.
- index.html resolving.
packis not portable across crate versions / architectures.- Text files
text/*are assumed to be utf-8 encoded.
For this example lets assume you are building api + gui application in rust. Your gui is pre-built (like with npm build or similar) in ./vcard-personal-portfolio directory (available for real in tests/data/) and you want to serve it from / of your app.
Refer to web-static-pack-packer for full documentation.
To pack whole ./vcard-personal-portfolio directory into ./vcard-personal-portfolio.pack execute the following command:
$ web-static-pack-packer \
directory-single \
./vcard-personal-portfolio \
./vcard-personal-portfolio.packThis will create a ./vcard-personal-portfolio.pack file, containing all your files combined, ready to be included in your target application.
Refer to web-static-pack for full example.
You will need to include the pack in your executable with https://docs.rs/include_bytes_aligned/latest/include_bytes_aligned/. Then pack needs to be loaded from this binary slice. At the end we construct a http service, that will serve our requests.
use include_bytes_aligned::include_bytes_aligned;
static PACK_ARCHIVED_SERIALIZED: &[u8] =
include_bytes_aligned!(16, "vcard-personal-portfolio.pack");
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Error> {
// load (map / cast) [common::pack::PackArchived] from included bytes
let pack_archived = unsafe { load(PACK_ARCHIVED_SERIALIZED).unwrap() };
// create a responder (http service) from `pack`
let responder = Responder::new(pack_archived);
// hyper requires service to be static
// we use graceful, no connections will outlive server function
let responder = unsafe {
transmute::<
&Responder<'_, _>,
&Responder<'static, _>,
>(&responder)
};
// make hyper service function
let service_fn = service_fn(|request| async {
// you can probably filter your /api requests here
let (parts, _body) = request.into_parts();
let response = responder.respond_flatten(
&parts.method,
parts.uri.path(),
&parts.headers,
);
Ok::<_, Infallible>(response)
});
// run hyper server using service_fn, as in:
// https://hyper.rs/guides/1/server/graceful-shutdown/
todo!();
Ok(())
}The 0.5.0 is almost a complete rewrite, however the general idea remains the same.
- We still have two parts - packer and loader. There is also a
commoncrate andtestscrate, however they are not meant to be used directly. - Lots of internals were changed, including rkyv for serialization / zero-copy deserialization. This of course makes packs built with previous versions incompatible with current loader and vice versa.
- We are now built around http crate, which makes web-static-pack compatible with hyper 1.0 without depending on it directly.
- Packer is now built around subcommands. The previous behavior was moved to
directory-singlesubcommand, androot_pathparameter was dropped. See examples. - Since we no longer depend on
hyperin any way (thehttpcrate is common interface),hyper_loaderfeature is no longer present in loader. let loader = loader::Loader::new(...)is nowlet pack_archived = loader::load(...). This value is still used forResponder::new.hyper_loader::Responderis now justresponder::Responder, and it's now built aroundhttpcrate, compatible withhyper1.0.Responderwas rewritten. It now accepts (method, path, headers) not whole request.request_respond_or_errorandparts_respond_or_errorare squashed torespond.request_respondandparts_respondare squashed torespond_flatten.
- True zero-copy deserialization with
rkyv. brotlicompression support.cache-controlsupport.- Packer is now a lib + bin, making it usable in build.rs. Multiple useful methods were exposed.
- Good test coverage, including integration tests.
- Lots of internal improvements.