Rocket/site/news/2023-11-17-version-0.5.md

624 lines
22 KiB
Markdown

# Rocket v0.5: Stable, Async, Sentinels, Streams, SSE, Forms, WebSockets, & So Much More
<p class="metadata"><strong>
Posted by <a href="https://sergio.bz">Sergio Benitez</a> on Nov 17, 2023
</strong></p>
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` <badge>since `rc.1`</badge>
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("/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
format!("Hello, {} year old named {}!", age, name)
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/hello", routes![hello])
}
```
<details>
<summary>See a <code>diff</code> of the changes from v0.4.</summary>
```diff
- #![feature(proc_macro_hygiene, decl_macro)]
-
#[macro_use] extern crate rocket;
#[get("/<name>/<age>")]
- 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])
+ }
```
</details>
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 <badge>since `rc.1`</badge>
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 = "<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 <badge>since `rc.1`</badge>
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<T>`] 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<Ignite>) -> bool {
r.state::<T>().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<T>`]: @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 <badge>since `rc.1`</badge>
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?<n>")]
fn stream(n: Option<u64>) -> 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 <badge>since `rc.4`</badge>
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 <badge>since `rc.1`</badge>
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
<!-- custom routers? -->
## ❤️ 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:
<ul class="columns">
<li>Aaron Leopold</li>
<li>Abdullah Alyan</li>
<li>Aditya</li>
<li>Alex Macleod</li>
<li>Alex Sears</li>
<li>Alexander van Ratingen</li>
<li>ami-GS</li>
<li>Antoine Martin</li>
<li>arctic-alpaca</li>
<li>arlecchino</li>
<li>Arthur Woimbée</li>
<li>atouchet</li>
<li>Aurora</li>
<li>badoken</li>
<li>Beep LIN</li>
<li>Ben Sully</li>
<li>Benedikt Weber</li>
<li>Benjamin B</li>
<li>BlackDex</li>
<li>Bonex</li>
<li>Brenden Matthews</li>
<li>Brendon Federko</li>
<li>Brett Buford</li>
<li>Cedric Hutchings</li>
<li>Cezar Halmagean</li>
<li>Charles-Axel Dein</li>
<li>Compro Prasad</li>
<li>cui fliter</li>
<li>Daniel Wiesenberg</li>
<li>David Venhoek</li>
<li>Dimitri Sabadie</li>
<li>Dinu Blanovschi</li>
<li>Dominik Boehi</li>
<li>Doni Rubiagatra</li>
<li>Edgar Onghena</li>
<li>Edwin Svensson</li>
<li>est31</li>
<li>Felix Suominen</li>
<li>Fenhl</li>
<li>Filip Gospodinov</li>
<li>Flying-Toast</li>
<li>Follpvosten</li>
<li>Francois Stephany</li>
<li>Gabriel Fontes</li>
<li>gcarq</li>
<li>George Cheng</li>
<li>Giles Cope</li>
<li>Gonçalo Ribeiro</li>
<li>hiyoko3m</li>
<li>Howard Su</li>
<li>hpodhaisky</li>
<li>Ian Jackson</li>
<li>IFcoltransG</li>
<li>Indosaram</li>
<li>inyourface34456</li>
<li>J. Cohen</li>
<li>Jacob Pratt</li>
<li>Jacob Sharf</li>
<li>Jacob Simpson</li>
<li>Jakub Dąbek</li>
<li>Jakub Wieczorek</li>
<li>James Tai</li>
<li>Jason Hinch</li>
<li>Jeb Rosen</li>
<li>Jeremy Kaplan</li>
<li>Jieyou Xu</li>
<li>Joakim Soderlund</li>
<li>Johannes Liebermann</li>
<li>John-John Tedro</li>
<li>Jonah Brüchert</li>
<li>Jonas Møller</li>
<li>Jonathan Dickinson</li>
<li>Jonty</li>
<li>Joscha</li>
<li>Joshua Nitschke</li>
<li>JR Heard</li>
<li>Juhasz Sandor</li>
<li>Julian Büttner</li>
<li>Juraj Fiala</li>
<li>Kenneth Allen</li>
<li>Kevin Wang</li>
<li>Kian-Meng Ang</li>
<li>Konrad Borowski</li>
<li>Leonora Tindall</li>
<li>Lev Kokotov</li>
<li>lewis</li>
<li>Lionel G</li>
<li>Lucille Blumire</li>
<li>Mai-Lapyst</li>
<li>Manuel</li>
<li>Manuel Transfeld</li>
<li>Marc Schreiber</li>
<li>Marc-Stefan Cassola</li>
<li>Marshall Bowers</li>
<li>Martin1887</li>
<li>Martinez</li>
<li>Matthew Pomes</li>
<li>Maxime Guerreiro</li>
<li>meltinglava</li>
<li>Michael Howell</li>
<li>Mikail Bagishov</li>
<li>mixio</li>
<li>multisn8</li>
<li>Necmettin Karakaya</li>
<li>Ning Sun</li>
<li>Nya</li>
<li>Paolo Barbolini</li>
<li>Paul Smith</li>
<li>Paul van Tilburg</li>
<li>Paul Weaver</li>
<li>pennae</li>
<li>Petr Portnov</li>
<li>philipp</li>
<li>Pieter Frenssen</li>
<li>PROgrm_JARvis</li>
<li>Razican</li>
<li>Redrield</li>
<li>Riley Patterson</li>
<li>Rodolphe Bréard</li>
<li>Roger Mo</li>
<li>RotesWasser</li>
<li>rotoclone</li>
<li>Ruben Schmidmeister</li>
<li>Rudi Floren</li>
<li>Rémi Lauzier</li>
<li>Samuele Esposito</li>
<li>Scott McMurray</li>
<li>Sergio Benitez</li>
<li>Silas Sewell</li>
<li>Soham Roy</li>
<li>Steven Murdoch</li>
<li>Stuart Hinson</li>
<li>Thibaud Martinez</li>
<li>Thomas Eckert</li>
<li>ThouCheese</li>
<li>Tilen Pintarič</li>
<li>timando</li>
<li>timokoesters</li>
<li>toshokan</li>
<li>TotalKrill</li>
<li>Unpublished</li>
<li>Vasili</li>
<li>Vladimir Ignatev</li>
<li>Wesley Norris</li>
<li>xelivous</li>
<li>YetAnotherMinion</li>
<li>Yohannes Kifle</li>
<li>Yusuke Kominami</li>
</ul>
[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/