# Rocket v0.5: Stable, Async, Sentinels, Streams, SSE, Forms, WebSockets, & So Much More

Posted by Sergio Benitez on Nov 17, 2023

Four years, four release candidates, a thousand commits, and over a thousand issues, discussions, and PRs later, I am ~~relieved~~ thrilled to announce the general availability of Rocket v0.5. > **Rocket** is an async backend web framework for Rust with a focus on > usability, security, extensibility, and speed. Rocket makes it simple to write > secure web applications without sacrificing productivity or performance. We encourage all users to upgrade. For a guided migration from Rocket v0.4 to Rocket v0.5, please consult the newly available [upgrading guide]. Rocket v0.4 will continue to be supported and receive security updates until the time Rocket v0.6 is released. ! note: This is a co-announcement [along with the prelaunch] of [RWF2]. We're addressing the community's concerns regarding the pace of Rocket's development, leadership, and release cadence in a separate announcement. Please see the accompanying [RWF2 prelaunch announcement](../2023-11-17-rwf2-prelaunch/) to learn more and see how you can get involved. [RWF2]: https://rwf2.org [along with the prelaunch]: ../2023-11-17-rwf2-prelaunch/ [upgrading guide]: @guide-v0.5/upgrading ## What's New? Almost every bit has been reevaluated with a focus on usability and developer productivity, security, and consistency across the library and [broader ecosystem]. The changes are numerous, so we focus on the most noteworthy changes here and encourage everyone to read the [CHANGELOG] for a complete list. For answers to frequently asked questions, see the new [FAQ]. [CHANGELOG]: https://github.com/SergioBenitez/Rocket/blob/v0.5.0/CHANGELOG.md [broader ecosystem]: @guide-v0.5/faq/#releases [FAQ]: @guide-v0.5/faq ### ⚓ Support for Stable `rustc` since `rc.1` Rocket v0.5 compiles and builds on Rust stable. You can now compile and build Rocket applications with `rustc` from the stable release channel and remove all `#![feature(..)]` crate attributes. The complete canonical example with a single `hello` route becomes: ```rust #[macro_use] extern crate rocket; #[get("//")] fn hello(name: &str, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) } #[launch] fn rocket() -> _ { rocket::build().mount("/hello", routes![hello]) } ```
See a diff of the changes from v0.4. ```diff - #![feature(proc_macro_hygiene, decl_macro)] - #[macro_use] extern crate rocket; #[get("//")] - fn hello(name: String, age: u8) -> String { + fn hello(name: &str, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) } - fn main() { - rocket::ignite().mount("/hello", routes![hello]).launch(); - } + #[launch] + fn rocket() -> _ { + rocket::build().mount("/hello", routes![hello]) + } ```
Note the new [`launch`] attribute, which simplifies starting an `async` runtime for Rocket applications. See the [migration guide] for more on transitioning to a stable toolchain. [`launch`]: @api-v0.5/rocket/attr.launch.html ### 📥 Async I/O since `rc.1` Rocket's core request handling was rebuilt in v0.5 to take advantage of the latest `async` networking facilities in Rust. Backed by `tokio`, Rocket automatically multiplexes request handling across `async` tasks on all of the available cores on the machine. As a result, route handlers can now be declared `async` and make use of `await` syntax: ```rust use rocket::tokio; use rocket::data::{Data, ToByteUnit}; #[post("/debug", data = "")] async fn debug(data: Data<'_>) -> std::io::Result<()> { // Stream at most 512KiB all of the body data to stdout. data.open(512.kibibytes()) .stream_to(tokio::io::stdout()) .await?; Ok(()) } ``` See the [Blocking I/O](@guide-v0.5/upgrading#blocking-io) section of the upgrading guide for complete details on the `async` I/O transition. ### 💂 Sentinels since `rc.1` Rocket v0.5 introduces [sentinels]. Entirely unique to Rocket, sentinels offer an automatic last line of defense against runtime errors by enabling any type that appears in a route to abort application launch under invalid conditions. For example, the [`&State`] guard in v0.5 is a [`Sentinel`] that aborts launch if the type `T` is not in managed state, thus preventing associated runtime errors. [`Sentinel`]s can be implemented outside of Rocket, too, and you should seek to do so whenever possible. For instance, the [`Template`] type from [`rocket_dyn_templates`] is a sentinel that ensures templates are properly registered. As another example, consider a `MyResponder` that expects: * A specific type `T` to be in managed state. * An catcher to be registered for the `400` status code. Making `MyResponder` a sentinel that guards against these conditions is as simple as: ```rust use rocket::{Rocket, Ignite, Sentinel}; # struct MyResponder; # struct T; impl Sentinel for MyResponder { fn abort(r: &Rocket) -> bool { r.state::().is_none() || !r.catchers().any(|c| c.code == Some(400)) } } ``` [sentinels]: @api-v0.5/rocket/trait.Sentinel.html [`Sentinel`]: @api-v0.5/rocket/trait.Sentinel.html [`&State`]: @api-v0.5/rocket/struct.State.html [`Template`]: @api-v0.5/rocket_dyn_templates/struct.Template.html [`rocket_dyn_templates`]: @api-v0.5/rocket_dyn_templates/index.html ### ☄️ Streams and SSE since `rc.1` Powered by the new asynchronous core, Rocket v0.5 introduces real-time, typed `async` [streams]. The new [async streams] section of the guide contains further details, and we encourage all interested parties to see the new real-time, multi-room [chat example]. As a taste of what's possible, the following `stream` route emits a `"pong"` Server-Sent Event every `n` seconds, defaulting to `1`: ```rust # use rocket::*; use rocket::tokio::time::{interval, Duration}; use rocket::response::stream::{Event, EventStream};; #[get("/ping?")] fn stream(n: Option) -> EventStream![] { EventStream! { let mut timer = interval(Duration::from_secs(n.unwrap_or(1))); loop { yield Event::data("pong"); timer.tick().await; } } } ``` [streams]: @api-v0.5/rocket/response/stream/index.html [async streams]: @guide-v0.5/responses/#async-streams [chat example]: @example/chat ### 🔌 WebSockets since `rc.4` Rocket v0.5 introduces support for HTTP connection upgrades via a new [upgrade API]. The API allows responders to assume control of raw I/O with the client in an existing HTTP connection, thus allowing HTTP connections to be _upgraded_ to any protocol, including WebSockets! The newly introduced [`rocket_ws`] library takes advantage of the new API to implement first-class support for WebSockets entirely outside of Rocket's core. Working with `rocket_ws` to implement an echo server looks like this: ```rust # use rocket::get; use rocket_ws::{WebSocket, Stream}; #[get("/echo")] fn echo_compose(ws: WebSocket) -> Stream!['static] { ws.stream(|io| io) } ``` Just like the newly introduced `async` streams, `rocket_ws` also supports using generator syntax for WebSocket messages: ```rust # use rocket::get; use rocket_ws::{WebSocket, Stream}; #[get("/echo")] fn echo_stream(ws: WebSocket) -> Stream!['static] { Stream! { ws => for await message in ws { yield message?; } } } ``` For complete usage details, see the [`rocket_ws`] documentation. [upgrade API]: @api-v0.5/rocket/response/struct.Response.html#upgrading [`rocket_ws`]: @api-v0.5/rocket_ws ### 📝 Comprehensive Forms since `rc.1` Rocket v0.5 entirely revamps [forms] with support for [multipart uploads], [arbitrary collections] with [arbitrary nesting], [ad-hoc validation], and an improved [`FromForm` derive], obviating the need for nearly all custom implementations of `FromForm` or `FromFormField`. Rocket's new wire protocol for forms allows applications to express _any structure_ with _any level of nesting and collection_ without any custom code, eclipsing what's offered by other web frameworks. As an illustrative example, consider the following structures: ```rust use rocket::form::FromForm; #[derive(FromForm)] struct MyForm<'r> { owner: Person<'r>, pet: Pet<'r>, } #[derive(FromForm)] struct Person<'r> { name: &'r str } #[derive(FromForm)] struct Pet<'r> { name: &'r str, #[field(validate = eq(true))] good_pet: bool, } ``` To parse request data into a `MyForm`, a form with fields of `owner.name`, `pet.name`, and `pet.good_pet` must be submitted. The ad-hoc validation on `good_pet` validates that `good_pet` parses as `true`. Such a form, URL-encoded, may look like: ```rust,ignore "owner.name=Bob&pet.name=Sally&pet.good_pet=yes" ``` Rocket's derived `FromForm` implementation for `MyForm` will automatically parse such a submission into the correct value: ```rust,ignore MyForm { owner: Person { name: "Bob".into() }, pet: Pet { name: "Sally".into(), good_pet: true, } } # }; ``` The rewritten [forms guide] provides complete details on revamped forms support. [forms guide]: @guide-v0.5/requests/#forms [ad-hoc validation]: @guide-v0.5/requests#ad-hoc-validation [arbitrary nesting]: @guide-v0.5/requests#nesting [multipart uploads]: @guide-v0.5/requests#multipart [forms]: @guide-v0.5/requests#forms [`FromFormField`]: @api-v0.5/rocket/form/trait.FromFormField.html [arbitrary collections]: @guide-v0.5/requests#collections [`FromForm` derive]: @api-v0.5/rocket/derive.FromForm.html ### 🚀 And so much more! Rocket v0.5 introduces over **40** new features and major improvements! We encourage everyone to review the [CHANGELOG] to learn about them all. Here are a few more we don't want you to miss: * An automatically enabled [`Shield`]: security and privacy headers for all responses. * [Graceful shutdown] with configurable grace periods, [notification], and [shutdown fairings]. * An entirely new, flexible and robust [configuration system] based on [Figment]. * Type-system enforced [incoming data limits] to mitigate memory-based DoS attacks. * Support for [mutual TLS] and client [`Certificate`]s. * Asynchronous database pooling support via [`rocket_db_pools`]. * Compile-time URI literals via a fully revamped [`uri!`] macro. [`Shield`]: @api-v0.5/rocket/shield/struct.Shield.html [graceful shutdown]: @api-v0.5/rocket/config/struct.Shutdown.html#summary [notification]: @api-v0.5/rocket/struct.Shutdown.html [shutdown fairings]: @api-v0.5/rocket/fairing/trait.Fairing.html#shutdown [configuration system]: @guide-v0.5/configuration/#configuration [Figment]: https://docs.rs/figment/ [incoming data limits]: @guide-v0.5/requests/#streaming [mutual TLS]: @guide-v0.5/configuration/#mutual-tls [`uri!`]: @api-v0.5/rocket/macro.uri.html [`rocket_db_pools`]: @api-v0.5/rocket_db_pools/index.html [`Certificate`]: @api-v0.5/rocket/mtls/struct.Certificate.html [migration guide]: @guide-v0.5/upgrading ## What's Next? We think Rocket provides the most productive and confidence-inspiring web development experience in Rust today, but as always, there's room for improvement. To that end, here's what's on the docket for the next major release: 0. **Migration to RWF2** Discussed further in the [RWF2 prelaunch announcement], Rocket will transition to being managed by the newly formed Rocket Web Framework Foundation: _RWF2_. The net effect is increased development transparency, including public roadmaps and periodic updates, financial support for high-quality contributions, and codified pathways into the project's governance. 0. **Pluggable Connection Listeners** Rocket currently expects and enjoins connection origination via TCP/IP. While sufficient for the common case, it excludes other desirable interfaces such as Unix Domain Sockets (UDS). In the next major release, Rocket will expose [an interface for implementing and plugging-in custom connection listeners]. Rocket itself will make use of this interface to expose more common mediums out-of-the-box, such as the aforementioned UDS. 0. **Native `async` Traits** Given the [stabilization of `async fn` in traits], the next major release will seek to eliminate Rocket's dependence on `#[async_trait]` opting instead for native `async` traits. This will greatly improve our documentation, which currently calls out the attribute for each affected trait, as well as offer modest performance improvements. 0. [**Typed Catchers**](https://github.com/SergioBenitez/Rocket/issues/749) Today's catchers cannot receive strictly typed error data. This results in workarounds where error data is queried for well-typedness at runtime. While it _has_ been possible to implement a form of typed error catching prior, doing so necessitated limiting error data to `'static` values, as other Rust web frameworks do, a concession we're unwilling to make. After much experimentation, we have an approach that is ergonomic to use, safe, and correct, all without the `'static` limitation. This will allow error catchers to "pattern match" against error types at compile-time. At runtime, Rocket will match emerging error types against the declared catchers and call the appropriate catcher with the fully-typed value. 0. **Short-Circuitable Request Processing** Whether with success or failure, fairings and guards cannot presently terminate request processing early. The rationale for forbidding this functionality was that it would allow third-party crates and plugins to dictate responses without offering any recourse to the top-level application. With the advent of typed catchers, however, we now have a mechanism by which a top-level application can intercept early responses via their type, resolving the prior concern. As such, in the next major release, fairings and guards will be able to respond to requests early, and catchers will be able to intercept those early responses at will. 0. **Associated Resources** Often a set of routes will share a set requirements. For example, they may share a URI prefix, subset of guards, and some managed state. In today's Rocket, these common requirements must be repeatedly specified for each route. While this is by design (we _want_ a route's requirements to be obvious), the repetition is arduous and potentially error prone. In an upcoming major release, Rocket will introduce new mechanisms by which a set of routes can share an explicitly declared set of requirements. Their _explicit_ and _declarative_ nature results in requirements that are simultaneously obvious _and_ declared once. We're really excited about this upcoming change and will be announcing more in the near future. 0. **Performance Improvements** Rocket appears to lag behind other Rust web frameworks in benchmarks. This is partly due to [poor benchmarking], partly due to security-minded design decisions, and partially due to unexploited opportunities. In the next release, we'll be addressing the latter points. Specifically: - _Explore making work stealing optional._ Rocket currently defaults to using tokio's multithreaded, work-stealing scheduler. This avoids tail latency issues when faced with irregular and heterogeneous tasks at the expense of throughput due to higher bookkeeping costs associated with work stealing. Other Rust web frameworks instead opt to use tokio's single-threaded scheduler, which while theoretically suboptimal, may yield better performance results in practice, especially when benchmarking homogeneous workloads. While we believe work-stealing schedulers are the right choice for the majority of applications desireing robust performance characteristics, we also believe the choice should be the user's. We'll seek to make this choice easier in the next release. - _Reduce conversions from external to internal HTTP types._ Rocket revalidates and sometimes copies incoming HTTP request data. In Rocket v0.5, we began transitioning to a model where we revalidate security insensitive data in debug mode only, allowing for bugs to be caught and reported while reducing performance impacts in production. In the next release, we seek to extend this approach. [an interface for implementing and plugging-in custom connection listeners]: https://github.com/SergioBenitez/Rocket/issues/1070#issuecomment-1491101952 [stabilization of `async fn` in traits]: https://github.com/rust-lang/rust/pull/115822 [poor benchmarking]: @guide-v0.5/faq/#performance ## ❤️ Thank You A very special thank you to [Jeb Rosen], Rocket's maintainer from v0.4 to v0.5-rc.1, without whom Rocket v0.5 wouldn't exist. Jeb is responsible for leading the migration to `async` and Rust stable along with tireless efforts to improve Rocket's documentation and address the community. Rocket is better for having had Jeb along for the ride. Thank you, Jeb. [Jeb Rosen]: https://github.com/SergioBenitez/Rocket/commits?author=jebrosen A special thank you to all of Rocket's users, especially those who diligently waded through all four release candidates, raised issues, and participated on [GitHub] and the [Matrix channel]. You all are an awesome, kind, and thoughtful bunch. Thank you. A heartfelt _thank you_ as well to _all_ **148** who contributed to Rocket v0.5:
  • Aaron Leopold
  • Abdullah Alyan
  • Aditya
  • Alex Macleod
  • Alex Sears
  • Alexander van Ratingen
  • ami-GS
  • Antoine Martin
  • arctic-alpaca
  • arlecchino
  • Arthur Woimbée
  • atouchet
  • Aurora
  • badoken
  • Beep LIN
  • Ben Sully
  • Benedikt Weber
  • Benjamin B
  • BlackDex
  • Bonex
  • Brenden Matthews
  • Brendon Federko
  • Brett Buford
  • Cedric Hutchings
  • Cezar Halmagean
  • Charles-Axel Dein
  • Compro Prasad
  • cui fliter
  • Daniel Wiesenberg
  • David Venhoek
  • Dimitri Sabadie
  • Dinu Blanovschi
  • Dominik Boehi
  • Doni Rubiagatra
  • Edgar Onghena
  • Edwin Svensson
  • est31
  • Felix Suominen
  • Fenhl
  • Filip Gospodinov
  • Flying-Toast
  • Follpvosten
  • Francois Stephany
  • Gabriel Fontes
  • gcarq
  • George Cheng
  • Giles Cope
  • Gonçalo Ribeiro
  • hiyoko3m
  • Howard Su
  • hpodhaisky
  • Ian Jackson
  • IFcoltransG
  • Indosaram
  • inyourface34456
  • J. Cohen
  • Jacob Pratt
  • Jacob Sharf
  • Jacob Simpson
  • Jakub Dąbek
  • Jakub Wieczorek
  • James Tai
  • Jason Hinch
  • Jeb Rosen
  • Jeremy Kaplan
  • Jieyou Xu
  • Joakim Soderlund
  • Johannes Liebermann
  • John-John Tedro
  • Jonah Brüchert
  • Jonas Møller
  • Jonathan Dickinson
  • Jonty
  • Joscha
  • Joshua Nitschke
  • JR Heard
  • Juhasz Sandor
  • Julian Büttner
  • Juraj Fiala
  • Kenneth Allen
  • Kevin Wang
  • Kian-Meng Ang
  • Konrad Borowski
  • Leonora Tindall
  • Lev Kokotov
  • lewis
  • Lionel G
  • Lucille Blumire
  • Mai-Lapyst
  • Manuel
  • Manuel Transfeld
  • Marc Schreiber
  • Marc-Stefan Cassola
  • Marshall Bowers
  • Martin1887
  • Martinez
  • Matthew Pomes
  • Maxime Guerreiro
  • meltinglava
  • Michael Howell
  • Mikail Bagishov
  • mixio
  • multisn8
  • Necmettin Karakaya
  • Ning Sun
  • Nya
  • Paolo Barbolini
  • Paul Smith
  • Paul van Tilburg
  • Paul Weaver
  • pennae
  • Petr Portnov
  • philipp
  • Pieter Frenssen
  • PROgrm_JARvis
  • Razican
  • Redrield
  • Riley Patterson
  • Rodolphe Bréard
  • Roger Mo
  • RotesWasser
  • rotoclone
  • Ruben Schmidmeister
  • Rudi Floren
  • Rémi Lauzier
  • Samuele Esposito
  • Scott McMurray
  • Sergio Benitez
  • Silas Sewell
  • Soham Roy
  • Steven Murdoch
  • Stuart Hinson
  • Thibaud Martinez
  • Thomas Eckert
  • ThouCheese
  • Tilen Pintarič
  • timando
  • timokoesters
  • toshokan
  • TotalKrill
  • Unpublished
  • Vasili
  • Vladimir Ignatev
  • Wesley Norris
  • xelivous
  • YetAnotherMinion
  • Yohannes Kifle
  • Yusuke Kominami
[GitHub discussions]: https://github.com/SergioBenitez/Rocket/discussions [Matrix channel]: https://chat.mozilla.org/#/room/#rocket:mozilla.org ## Get Involved Looking to help with Rocket? To contribute code, head over to [GitHub]. To get involved with the project, see the [RWF2 prelaunch announcement]. We'd love to have you. [GitHub]: https://github.com/SergioBenitez/Rocket [RWF2 prelaunch announcement]: ../2023-11-17-rwf2-prelaunch/