SolvedRocket Tracking Issue for Async I/O migration

Status

Rocket's master branch is currently fully asynchronous and supports async/await!

Pending

  • #1067 - Remove or replace typed headers (PR: #1535)
  • #1329 - Documentation fixes, additions, improvements.
  • #1070 - Expose APIs for customizing the underlying I/O, instead of being restricted to TCP with optional TLS.
  • Detect blocking I/O in async: it seems the best we can do for now is to document the issues.
    A "blocking-in-async" lint in clippy or rustc would be much nicer, if feasible, but would occur upstream...someday.
  • Miscellaneous small TODOs (tagged as TODO.async) throughout code.
  • Response bodies are passed back to hyper using a channel.

Done

  • Async I/O everything!
  • #1325 - Data reading and connection limits.
  • Keep-alive and idle client timeouts
  • Handling of blocking database connections.
  • Gracefully handle panics with 500. (PR #1491)
  • #1066 - Add a Write-like interface for responses, and consideration of AsyncRead vs Stream<Item=Result<Chunk, Error>>
  • #180, #1374 - fixing graceful shutdown when using "infinite" responders
  • #33 - Server-Sent Events
  • #1117 - Asynchronous database pools, or at the very least forward compatibility with them.

Design

The async APIs are based on rust's async/await feature and std::future::Future, running on the tokio (0.2) runtime.

  • High-level APIs (routes and catchers):
    • Routes can now be defined as #[get|post|route(...)] async fn(params...) -> R where R is any Responder type. Routes that do not await anything can remain unchanged. #[catch]ers can also be defined as an async fn.
    • Methods on Data now return Futures that must be .awaited. As a consequence, any route that uses Data directly must be async. Routes that use data guards such as Json are not affected by this change.
    • Routes that make their own network requests or other I/O should be async, as it will allow other requests to continue while waiting for the operation.
  • Mid and low-level APIs (FromTransformedData and FromData, Fairing, Responder)
    • Several traits are now defined using async-trait, re-exported as #[rocket::async_trait].
    • Data is read asynchronously: DataStream implements tokio::io::AsyncRead instead of Read. Thus, FromTransformedData::transform and From(Transformed)Data::from_data are async fn or return Futures.
    • Response bodies are streamed to the client asynchronously: set_body and related methods expect tokio::io::AsyncRead instead of Read.
      • Consequently, Fairing::on_response and Responder::respond_to are now async fn. This allows Responders and Fairings to access and/or replace the body data.
  • The testing APIs have been moved to rocket::local::asynchronous and rocket::local::blocking. The blocking test API is simpler to use, and recommended when possible.
  • #[rocket::main], #[rocket::launch], and #[rocket::async_test] have been added to make async fn easily accessible for main() and test().
  • Many long-standing bugs with keep-alive, graceful shutdown (#180), performance, and more are fixed or will be easier to fix by using actively-supported versions of libraries.
  • Some new functionality will be possible to implement, such as SSE and WebSockets. These will likely not make it into Rocket 0.5. At this point, Rocket should not be missing any APIs necessary to implement SSE "by hand". WebSockets is a bit more complicated, and tracked in #90 .
46 Answers

✔️Accepted Answer

Since my last update comment above, these were also done:

  • async_trait - it works for enough traits we went ahead and used it
  • Switching to AsyncSeek for bodies
  • Miscellaneous cleanups and fixes to code and documentation

I am currently working on the following, in #1242 (and forgot to link it back here):

  • Asynchronous on_attach fairings. This would require some pretty big API changes.

My next PR after that will likely be a wrapper making it safe(r) to use the currently supported blocking databases.

I will look into any async-specific issues that could use assistance. One that comes to mind now is typed headers - e.g. hyperium/headers#48 (comment), or an investigation into which typed header crate(s) would be suitable for built-in support in Rocket would be helpful.

Other Answers:

Hi all,

I've switched over fully to the latest Rocket with full async for a couple of my latest projects, but was running into the fact that compression support doesn't exist yet. This was inflating responses to my users and bringing up the cost of my bill for network egress, so I hacked up a placeholder solution that adds back gzip support: Ameobea@eab53cf

The API is exactly the same as before; you just add features = ["compression"] for rocket-contrib and add the Compression::fairing(). The implementation is not optimal in any way; the compression itself isn't async but it really shouldn't be a big deal for almost all workloads and the huge benefit of having gzip compression outweighs any increase in response time by a huge margin.

I figured this may be useful to other people who wanted to gain the benefits of compression and the latest async Rocket without having to re-write any of their code. Once someone gets around to doing the job properly, it should just be a change to Cargo.toml to fix it.

EDIT 2021-07-17:

I've created a library that performs response compression using the async-compression library under the hood:

https://github.com/Ameobea/rocket_async_compression

With the new rocket_db_pools, I'm happy to close this issue. The remaining issues tracked in the parent post are not specific to async I/O (typed headers, more docs, and connection APIs) and have independent issues or have no viable solutions outside of rustc (lints for async safety).

@jebrosen whats the status on getting the async branch merged? Are there any issues we can help with to push it over the finish line?

It sounds like you should be implementing a custom Handler.

More Issues: