I just had a thought: Why should `response::Headers` be generic, but
`body::StreamBody` should not? `StreamBody` previously boxed the stream
to erase the generics. So we had `response::Headers<T>` but
`body::StreamBody`, without generics.
After thinking about it I think it actually makes sense for responses to
remain generic because you're able to use `impl IntoResponse` so you
don't have to name the generics.
Whereas in the case of `BodyStream` (an extractor) you cannot use `impl Trait`
so it makes sense to box the inner body to make the type easier to name. Besides,
`BodyStream` is mostly useful when the request body isn't `hyper::Body`, as
that already implements `Stream`.
This adds `StreamBody` which converts a `Stream` of `Bytes` into a `http_body::Body`.
---
As suggested by Kestrer on Discord it would make sense for axum to provide different kinds of body types other than `Empty`, `Full`, and `hyper::Body`. There is also some talk about [splitting up `hyper::Body`](https://github.com/hyperium/hyper/issues/2345) so this can be seen as getting started on that effort. axum's body types could be moved to hyper or http-body if thats the direction we decide on.
The types I'm thinking about adding are:
- `StreamBody`- added in this PR
- `AsyncReadBody` - similar to [http-body#41](https://github.com/hyperium/http-body/pull/41/files)
- `ChannelBody` - similar to `hyper::Body::channel`
I think `BodyStream` is more useful without being generic over the
request body.
I'm also looking into adding a response body from a stream called
`StreamBody` which will work pretty much opposite to this.
This removes a small foot gun from the routing.
This means matching different HTTP methods for the same route that
aren't defined together now works.
So `Router::new().route("/", get(...)).route("/", post(...))` now
accepts both `GET` and `POST`. Previously only `POST` would be accepted.
This way there is now only one way to create a router:
```rust
use axum::{Router, handler::get};
let app = Router::new()
.route("/foo", get(handler))
.route("/foo", get(handler));
```
`nest` was changed in the same way:
```rust
use axum::Router;
let app = Router::new().nest("/foo", service);
```
* Improve compile times of `handle_error`
This brings the compile time of the example posted [here][example] from
3 seconds down to 0.3 seconds for me.
Having the bounds on the methods does improve UX but not worth
sacrificing 10x compile time for.
[example]: https://github.com/tokio-rs/axum/issues/145#issue-963183256
* Improve compile time of `check_infallible`
* update changelog
* Add `Headers`
Example usage:
```rust
use axum::{
route,
routing::RoutingDsl,
response::{IntoResponse, Headers},
handler::get,
};
use http::header::{HeaderName, HeaderValue};
// It works with any `IntoIterator<Item = (Key, Value)>` where `Key` can be
// turned into a `HeaderName` and `Value` can be turned into a `HeaderValue`
//
// Such as `Vec<(HeaderName, HeaderValue)>`
async fn just_headers() -> impl IntoResponse {
Headers(vec![
(HeaderName::from_static("X-Foo"), HeaderValue::from_static("foo")),
])
}
// Or `[(&str, &str)]`
async fn from_strings() -> impl IntoResponse {
Headers([("X-Foo", "foo")])
}
```
Fixes https://github.com/tokio-rs/axum/issues/187
* Make work on Rust versions without `IntoIterator` for arrays
* format
* changelog
Previously, on `main`, this wouldn't compile:
```rust
let app = route("/", get(handler))
.layer(
ServiceBuilder::new()
.timeout(Duration::from_secs(10))
.into_inner(),
)
.handle_error(...)
.route(...); // <-- doesn't work
```
That is because `handle_error` would be
`axum::service::ServiceExt::handle_error` which returns `HandleError<_,
_, _, HandleErrorFromService>` which does _not_ implement `RoutingDsl`.
So you couldn't call `route`. This was caused by
https://github.com/tokio-rs/axum/pull/120.
Basically `handle_error` when called on a `RoutingDsl`, the resulting
service should also implement `RoutingDsl`, but if called on another
random service it should _not_ implement `RoutingDsl`.
I don't think thats possible by having `handle_error` on `ServiceExt`
which is implemented for any service, since all axum routers are also
services by design.
This resolves the issue by removing `ServiceExt` and moving its methods
to `RoutingDsl`. Then we have more tight control over what has a
`handle_error` method.
`service::OnMethod` now also has a `handle_error` so you can still
handle errors from random services, by doing
`service::any(svc).handle_error(...)`.
* feat(ws): make Message an enum to allow pattern matching
* fix(examples): update to new websockets `Message`
* fix(ws): remove wildcard imports
* fix(examples/chat): apply clippy's never_loop
* style: `cargo fmt`
* docs:add license notes above parts that are copied
* fix(ws): make CloseCode an alias to u16
* fix: move Message from src/ws/mod.rs to src/extract/ws.rs
* docs: add changelog entry about websocket messages
* fix: remove useless convertions to the same type
Adds associated `Body` and `BodyError` types to `IntoResponse`. This is required for returning responses with bodies other than `hyper::Body` from handlers. That wasn't previously possible.
This is a breaking change so should be shipped in 0.2.
With this you'll be able to do:
```rust
let one = route("/foo", get(|| async { "foo" }))
.route("/bar", get(|| async { "bar" }));
let two = route("/baz", get(|| async { "baz" }));
let app = one.or(two);
```
Fixes https://github.com/tokio-rs/axum/issues/101
As described in
https://github.com/tokio-rs/axum/pull/108#issuecomment-892811637, a
`HandleError` created from `axum::ServiceExt::handle_error` should _not_
implement `RoutingDsl` as that leads to confusing routing behavior.
The technique used here of adding another type parameter to
`HandleError` isn't very clean, I think. But the alternative is
duplicating `HandleError` and having two versions, which I think is less
desirable.
These types were moved around in
https://github.com/tokio-rs/axum/pull/130 but re-export from their old
location for backwards compatibility.
This removes the re-exports.
https://github.com/tokio-rs/axum/pull/129 was a breaking change, in part
because I had to remove the `Copy` impl from `OnMethod`.
For the sake of future proofing I think we should remove other `Copy`
impls from services as well. We can always bring them back once things
have matured more.
These types no longer implement `Copy`:
- `EmptyRouter`
- `ExtractorMiddleware`
- `ExtractorMiddlewareLayer`
Example usage:
```rust
use axum::{prelude::*, sse::{sse, Event, KeepAlive}};
use tokio_stream::StreamExt as _;
use futures::stream::{self, Stream};
use std::{
time::Duration,
convert::Infallible,
};
let app = route("/sse", sse(make_stream).keep_alive(KeepAlive::default()));
async fn make_stream(
// you can also put extractors here
) -> Result<impl Stream<Item = Result<Event, Infallible>>, Infallible> {
// A `Stream` that repeats an event every second
let stream = stream::repeat_with(|| Event::default().data("hi!"))
.map(Ok)
.throttle(Duration::from_secs(1));
Ok(stream)
}
```
Implementation is based on [warp's](https://github.com/seanmonstar/warp/blob/master/src/filters/sse.rs)
Axum expected the `Connection` header to be _exactly_ `upgrade`. Turns
out thats a bit too strict as this didn't work in Firefox.
Turns out `Connection` just has to contain `upgrade`. At least that is
what [warp does](https://github.com/seanmonstar/warp/blob/master/src/filters/ws.rs#L46).
Fixes https://github.com/tokio-rs/axum/issues/43
With this you can get the remote address like so:
```rust
use axum::{prelude::*, extract::ConnectInfo};
use std::net::SocketAddr;
let app = route("/", get(handler));
async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> String {
format!("Hello {}", addr)
}
// Starting the app with `into_make_service_with_connect_info` is required
// for `ConnectInfo` to work.
let make_svc = app.into_make_service_with_connect_info::<SocketAddr, _>();
hyper::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(make_svc)
.await
.expect("server failed");
```
This API is fully generic and supports whatever transport layer you're using with Hyper. I've updated the unix domain socket example to extract `peer_creds` and `peer_addr`.