Tide v0.12.0 Release Notes
Release Date: 2020-07-17 // almost 4 years ago-
๐ Docs
๐ This release includes 35 PRs merged over the course of the last month. Most
notably we've streamlined how errors are propagated inside middleware,
๐ฏ introduced a newResponseBuilder
type,State
must now beClone
, and we
are introducing an extensible API forServer::listen
.ResponseBuilder
Returning responses from endpoints is often different from operating on
response in middleware. In order to make it easier for people to author
responses we're introducingtide::ResponseBuilder
in this patch!๐ You can create a new response builder by calling
Response::builder
and
passing it a status code. This enables writing some really concise endpoints:app.at("/").get(|\_| async { let res = Response::builder(203) .body(json!({ "hello": "cats!" })) .header("X-Nori", "me-ow") .header("X-Chashu", "meewwww"); Ok(res) })
This sets Tide up really nicely for the future too; once we have async
closures, and a resolution for Ok-wrapping (fingers crossed) this will be
even more concise. We're excited for developments in the language!Server listen
๐ Tide now supports extensions for
App::listen
. This patch introduces a newListener
trait that is
implemented forstd
types such asTcpStream
,SocketAddr
and
UnixStream
. But can also be implemented by users of Tide to provide custom
transports.๐ In particular, what this enables us to do is to start trialing TLS support in
external crates. We'll soon have
tide-rustls
available as an external
๐ crate that will enable building TLS-terminating Tide servers:let mut app = tide::new();let listener = TlsListener::build() .addrs("localhost:4433") .cert(cert) .key(key); app.listen(listener).await?;
In addition we're shipping
tide::listener::ConcurrentListener
, a convenient
constructor to have a single server respond to incoming requests from
multiple transports. For example, some applications may want to listen on
both IPv4 and IPv6. WithConcurrentListener
that's possible:use std::net::{Ipv4Addr, Ipv6Addr};use tide::listener;let mut app = tide::new();let mut listener = listener::ConcurrentListener::new(); listener.add((Ipv4Addr::new(127, 0, 0, 1), 8000)); listener.add((Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8000)); app.listen(listener).await?;
๐ฏ State must be Clone
One problem we had for a while was that when manually nesting or
parallelizing applications,State
would be wrapped in anArc
multiple
times. In this patch we're solving that by providing people with more control
๐ฏ around howState
is shared by requiringState
to implementClone
.๐ฏ In most existing applications
State
can be madeClone
by manually
wrapping it in anArc::new
. But it's also possible to wrap individual
๐ฏ fields in anArc
and deriving `Clone for the whole struct, as we did in one
of our examples:// Example state before this patch.struct State { users: RwLock\<Vec\<User\>\>, }// Example state after this patch.#[derive(Clone)]struct State { users: Arc\<RwLock\<Vec\<User\>\>\>, }
There is no right answer how to structure
State
; but we wanted to enable
people to factor it in the way that makes most sense for their applications.Using of async-trait
We've migrated all of our traits to use
async-trait
. This should make it easier to authorMiddleware
implementations. For convenience Tide re-exports astide::utils::async_trait
.๐ Changes in middleware error handling
Before this patch, calling
next().await?
in middleware would return a
Result<Response>
. The idea was that theErr
variant could freely be
thrown up the middleware stack, and transformed into aResponse
at the top
of the stack. However in practice this didn't turn out great: middleware such
as CORS needed to manipulate theErr
path to ensure the right headers were
set. And we didn't provide an interface for this.So instead this patch changes the way we handle errors in middleware. We
still enable?
to be used inside middleware, but between each middleware we
convertResult<Response, tide::Error>
into aResponse
, and if an error
occurred, we populate the newly introducedResponse::error
field.This means that middleware can always assume there is a valid
Response
coming through, and no longer needs to check bothOk
andErr
branch
returned bynext().await
. An example:/// Before this patch: need to check both branches.async fn my\_middleware\<State\>(req: Request\<State\>, next: Next) -\> Result\<Response\> { println!("before"); match next().await { Err(err) =\> { println!("status code {}", err.status()); Err(err) } Ok(res) =\> { println!("status code {}", res.status()); Ok(res) } } }/// With this patch: there's only a single branch to operate on.async fn my\_middleware\<State\>(req: Request\<State\>, next: Next) -\> Result\<Response\> { println!("before"); let res = next().await; println!("status code {}", res.status()); Ok(res) }
Note: neither of these examples will quite compile until we have async
closures, but it serves to illustrate the point.โ Added
- โ Add a doc example for
Request::body_form
#631 - โ Add a doc example for
Request::query
#630 - โ Add an upload example #619
- โ Add extensible entrypoint for
Server::listen
#610 - โ Add
From<Body> for Response
#584 - โ Add
ResponseBuilder
#580 - โ Add
Response::error
#570 - โ Add CORS headers to error responses #546
๐ Changed
- ๐ Use
async_trait
to simplify async signatures #639 - ๐ฒ Also include port and host in the log string #634
- Don't panic on missing path param #615
- Return a result from
sse::Sender::send
#598 - ๐ Relax the lifetime requirements of
Server::at
#597 - In middleware
Next::run
now returnsResponse
instead ofResult<Response>
#570 - ๐ Rename
tide::middleware
totide::utils
#567 - ๐ฏ Require
State
to beClone
#644
๐ Fixed
- ๐ Make
ServeDir
return 404 if file does not exists #637 - ๐ Remove
#[must_use]
forResponse::set_status()
#612 - Do not await the spawned task in
Server::listen
#606 - ๐ Allow route based function middlewares #600
- ๐ Fix CORS middleware to retain cookies #599
- Enable SSE senders to break out of loops #598
- โ Remove extra unwraps from
insert_header
calls #590 #588 #583 - Don't crash the server when there's a listen error #587
- โ Add CORS headers to error responses #546
Internal
- โ Remove
executable
mode from lib.rs #635 - โก๏ธ Update to async-sse 4.0.0 #632
- ๐ Comment cleanup fixes #622
- โ Add clippy to ci #618
- โช Restore .github docs #616
- ๐ Use route-recognizer 0.2 #607
- โ Introduce an extension trait for testing servers #601
- โก๏ธ Update the readme with the latest versions #594
- ๐ Fix tempfile usage in tests #592
- ๐ Fix CI #589
- โ Remove unnecessary
move
s from route handlers #581
- โ Add a doc example for