Git Product home page Git Product logo

axum's Introduction

axum

axum is a web application framework that focuses on ergonomics and modularity.

Build status Crates.io Documentation

More information about this crate can be found in the crate documentation.

High level features

  • Route requests to handlers with a macro free API.
  • Declaratively parse requests using extractors.
  • Simple and predictable error handling model.
  • Generate responses with minimal boilerplate.
  • Take full advantage of the tower and tower-http ecosystem of middleware, services, and utilities.

In particular the last point is what sets axum apart from other frameworks. axum doesn't have its own middleware system but instead uses tower::Service. This means axum gets timeouts, tracing, compression, authorization, and more, for free. It also enables you to share middleware with applications written using hyper or tonic.

Usage example

use axum::{
    routing::{get, post},
    http::StatusCode,
    Json, Router,
};
use serde::{Deserialize, Serialize};

#[tokio::main]
async fn main() {
    // initialize tracing
    tracing_subscriber::fmt::init();

    // build our application with a route
    let app = Router::new()
        // `GET /` goes to `root`
        .route("/", get(root))
        // `POST /users` goes to `create_user`
        .route("/users", post(create_user));

    // run our app with hyper, listening globally on port 3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

// basic handler that responds with a static string
async fn root() -> &'static str {
    "Hello, World!"
}

async fn create_user(
    // this argument tells axum to parse the request body
    // as JSON into a `CreateUser` type
    Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
    // insert your application logic here
    let user = User {
        id: 1337,
        username: payload.username,
    };

    // this will be converted into a JSON response
    // with a status code of `201 Created`
    (StatusCode::CREATED, Json(user))
}

// the input to our `create_user` handler
#[derive(Deserialize)]
struct CreateUser {
    username: String,
}

// the output to our `create_user` handler
#[derive(Serialize)]
struct User {
    id: u64,
    username: String,
}

You can find this example as well as other example projects in the example directory.

See the crate documentation for way more examples.

Performance

axum is a relatively thin layer on top of hyper and adds very little overhead. So axum's performance is comparable to hyper. You can find benchmarks here and here.

Safety

This crate uses #![forbid(unsafe_code)] to ensure everything is implemented in 100% safe Rust.

Minimum supported Rust version

axum's MSRV is 1.66.

Examples

The examples folder contains various examples of how to use axum. The docs also provide lots of code snippets and examples. For full-fledged examples, check out community-maintained showcases or tutorials.

Getting Help

In the axum's repo we also have a number of examples showing how to put everything together. Community-maintained showcases and tutorials also demonstrate how to use axum for real-world applications. You're also welcome to ask in the Discord channel or open a discussion with your question.

Community projects

See here for a list of community maintained crates and projects built with axum.

Contributing

๐ŸŽˆ Thanks for your help improving the project! We are so happy to have you! We have a contributing guide to help you get involved in the axum project.

License

This project is licensed under the MIT license.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in axum by you, shall be licensed as MIT, without any additional terms or conditions.

axum's People

Contributors

alexheretic avatar alphakeks avatar altair-bueno avatar david-perez avatar davidpdrsn avatar dbofmmbt avatar gbaranski avatar hi-rustin avatar jplatte avatar lz1998 avatar matze avatar maxcountryman avatar mikhailantoshkin avatar mladedav avatar nashley avatar neoeinstein avatar nylonicious avatar paolobarbolini avatar programatik29 avatar ptrskay3 avatar robertwayne avatar sabrinajewson avatar sunli829 avatar takkuumi avatar tottoto avatar ttys3 avatar uckelman avatar weiznich avatar yanns avatar zys864 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

axum's Issues

Only one method of route is accepted.

Bug Report

Version

0.1.1

Description

Only last method is accepted when same route path with different methods, other methods returns 404.

I tried this code:

use axum::prelude::*;
use hyper::server::Server;

async fn index() -> &'static str {
    "Hi"
}

#[tokio::main]
async fn main() {
    let app = route("/", get(index)).route("/", post(index));
    // run it with hyper on localhost:3000
    Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

I expected to see this happen: request / with get method returns 404.

Instead, this happened: request / with get method returns "hi".

Ability to do more complex route matching

Feature Request

Motivation

  • full path match (eg. create http endpoint which will redirect all requests to https endpoint with same path)
  • multiple groups in a single path segment
  • ability to constrain segment with regex (match int only for instance)

Proposal

There could be a lot of ways of doing this, but the idea is basically the same - allow to pass custom regex to each capture group in route string. Imo best would be to allow to specify name of the the passed group (lookup custom converter in flask). These groups would be predefined in some sort of CustomConverterMap<string, Regex> and somehow passed to the route function (wip). Perhaps there could be different route function (like in django case re_path instead of path) or constrains in case of rails.

# how it is now (id matches everything)
route("/greet/:id", handler);

# how it could be
route("/greet/<int:id>", handler);
route("/greet/<int:id>-<str:name>", handler);

# or even to pass regex string directly into route match string
route("/greet/<r`[1-9][0-9]{3}`:id>", handler);  # match into id only ints between 1000 and 9999

How others are doing it:

Alternatives

Probably the only alternative is changing the spec for url patterns of the application I'm afraid. Stll With full path regex match we can employ same strategy as with 404 messages - use tower response middleware (aka MapResponseLayer) to simply handle it there. It is not the best option though since it decreases readibility - best is to have all route matches explicitly.

Implement Deref for extractors

If the extract::* types like Json would implement std::ops::Deref you could write

#[derive(Deserialize, Serialize)]
struct Request {
    foo: u8,
}

async fn handler(req: Json<Request>) {
    println!("{}", req.foo);
}

instead of

async fn handler(Json(req): Json<Request>) {
    println!("{}", req.foo);
}

or

async fn handler(req: Json<Request>) {
    let req = req.0;
    println!("{}", req.foo);
}

which IMO is easier to read.

The disadvantage is that it introduces some "Magic" that might be difficult to understand for newer rust developers.

Prior art

The bevy game engine can write ECS systems similar to how axum's request handlers work (example) and there you can write e.g.

fn system(resource: Res<Foo>) {
  println!("{}", resource.foo);
}

which works really well and leads to very readably systems.

Make routing setup for SPAs easier

For SPAs its often useful to first check if the URI matches some static resource (like javascript or css) and if not fallback to calling a handler. The handler should receive the entire URI, regardless of what it is, so routing can be done client side.

Its might be possible to do such a setup with axum today using some combination of ServeDir (from tower-http) and nest but I wouldn't be very ergonomic. We should investigate ways to make it easier.

I'm not exactly sure what the best and most general solution is. Could be wildcard routes similarly to what tide has. I think looking into how tide handles setting up something like this and then learning from that is a good place to start.

Websockets

Something along the lines of

#![allow(unused_imports)]

use bytes::Bytes;
use futures::prelude::*;
use futures::SinkExt;
use http::{header::HeaderName, HeaderValue, Request, Response, StatusCode};
use http_body::Empty;
use hyper::{
    server::conn::AddrStream,
    upgrade::{OnUpgrade, Upgraded},
    Body,
};
use sha1::{Digest, Sha1};
use std::future::Future;
use std::pin::Pin;
use std::task::Context;
use std::{convert::Infallible, task::Poll};
use tokio_tungstenite::{
    tungstenite::protocol::{self, WebSocketConfig},
    WebSocketStream,
};
use tower::{make::Shared, ServiceBuilder};
use tower::{BoxError, Service};
use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer};
use tower_http::LatencyUnit;

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let svc = ServiceBuilder::new()
        .layer(TraceLayer::new_for_http())
        .service(WebSocketUpgrade::new(handle_socket));

    let addr = std::net::SocketAddr::from(([0, 0, 0, 0], 3000));

    hyper::Server::bind(&addr)
        .serve(Shared::new(svc))
        .await
        .unwrap();
}

async fn handle_socket(mut socket: WebSocket) {
    while let Some(msg) = socket.recv().await {
        println!("received message: {:?}", msg);
    }
}

#[derive(Debug, Clone)]
pub struct WebSocketUpgrade<F> {
    callback: F,
    config: WebSocketConfig,
}

impl<F> WebSocketUpgrade<F> {
    pub fn new(callback: F) -> Self {
        Self {
            callback,
            config: WebSocketConfig::default(),
        }
    }
}

impl<ReqBody, F, Fut> Service<Request<ReqBody>> for WebSocketUpgrade<F>
where
    F: FnOnce(WebSocket) -> Fut + Clone + Send + 'static,
    Fut: Future<Output = ()> + Send + 'static,
{
    type Response = Response<Empty<Bytes>>;
    type Error = BoxError;
    type Future = ResponseFuture;

    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
        // TODO(david): missing `upgrade` should return "bad request"

        if !header_eq(
            &req,
            HeaderName::from_static("upgrade"),
            HeaderValue::from_static("websocket"),
        ) {
            todo!()
        }

        if !header_eq(
            &req,
            HeaderName::from_static("sec-websocket-version"),
            HeaderValue::from_static("13"),
        ) {
            todo!()
        }

        let key = if let Some(key) = req.headers_mut().remove("sec-websocket-key") {
            key
        } else {
            todo!()
        };

        let on_upgrade = req.extensions_mut().remove::<OnUpgrade>().unwrap();

        let config = self.config;
        let callback = self.callback.clone();

        tokio::spawn(async move {
            let upgraded = on_upgrade.await.unwrap();
            let socket =
                WebSocketStream::from_raw_socket(upgraded, protocol::Role::Server, Some(config))
                    .await;
            let socket = WebSocket { inner: socket };
            callback(socket).await;
        });

        ResponseFuture { key: Some(key) }
    }
}

#[derive(Debug)]
pub struct ResponseFuture {
    key: Option<HeaderValue>,
}

impl Future for ResponseFuture {
    type Output = Result<Response<Empty<Bytes>>, BoxError>;

    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
        let res = Response::builder()
            .status(StatusCode::SWITCHING_PROTOCOLS)
            .header(
                http::header::CONNECTION,
                HeaderValue::from_str("upgrade").unwrap(),
            )
            .header(
                http::header::UPGRADE,
                HeaderValue::from_str("websocket").unwrap(),
            )
            .header(
                http::header::SEC_WEBSOCKET_ACCEPT,
                sign(self.as_mut().key.take().unwrap().as_bytes()),
            )
            .body(Empty::new())
            .unwrap();

        Poll::Ready(Ok(res))
    }
}

fn header_eq<B>(req: &Request<B>, key: HeaderName, value: HeaderValue) -> bool {
    let header = if let Some(x) = req.headers().get(&key) {
        x
    } else {
        return false;
    };
    header == value
}

// from https://github.com/hyperium/headers/blob/master/src/common/sec_websocket_accept.rs#L38
fn sign(key: &[u8]) -> HeaderValue {
    let mut sha1 = Sha1::default();
    sha1.update(key);
    sha1.update(&b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"[..]);
    let b64 = Bytes::from(base64::encode(&sha1.finalize()));
    HeaderValue::from_maybe_shared(b64).expect("base64 is a valid value")
}

#[derive(Debug)]
pub struct WebSocket {
    inner: WebSocketStream<Upgraded>,
}

impl WebSocket {
    pub async fn recv(&mut self) -> Option<Result<protocol::Message, BoxError>> {
        self.inner.next().await.map(|opt| opt.map_err(Into::into))
    }
}

// TODO(david): impl Stream<Message>
// TODO(david): WebSocket::close

Remove `take_*` methods from RequestParts for Version, Method, and Uri

Version, Method, and Uri are all cheap to clone so having take_* methods for them on RequestParts probably isn't necessary. Could just have methods like

fn method(&self) -> Method {}
fn method_mut(&mut self) -> &mut Method {}
// same for version and uri...

That would also remove the Options making extractors that use those types easier to write. It might also remove the need for some the types in https://docs.rs/axum/0.1.2/axum/extract/rejection/index.html, namely things like MethodAlreadyExtracted.

For HeaderMap, Extensions, and body we still take_* methods as those aren't cheap to clone, or cannot be cloned at all.

I think we should do this for 0.2.

Error: failed to run custom build command for openssl-sys v0.9.65

All steps to reproduce the problem (including the cargo output):

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://github.com/tokio-rs/axum.git
cd axum/
cargo build --example hello_world
    Updating crates.io index
  Downloaded askama_escape v0.10.1
  Downloaded askama_shared v0.11.1
  Downloaded bb8 v0.7.0
  Downloaded flate2 v1.0.20
  Downloaded md-5 v0.9.1
  Downloaded digest v0.9.0
  Downloaded miniz_oxide v0.4.4
  Downloaded adler v1.0.2
  Downloaded phf v0.8.0
  Downloaded num-integer v0.1.44
  Downloaded futures-macro v0.3.16
  Downloaded futures-channel v0.3.16
  Downloaded lock_api v0.4.4
  Downloaded async-trait v0.1.51
  Downloaded async-compression v0.3.8
  Downloaded static_assertions v1.1.0
  Downloaded num-traits v0.2.14
  Downloaded askama v0.10.5
  Downloaded ansi_term v0.12.1
  Downloaded alloc-no-stdlib v2.0.1
  Downloaded futures-sink v0.3.16
  Downloaded foreign-types-shared v0.1.1
  Downloaded alloc-stdlib v0.2.1
  Downloaded nom v6.1.2
  Downloaded arrayvec v0.5.2
  Downloaded bb8-postgres v0.7.0
  Downloaded foreign-types v0.3.2
  Downloaded funty v1.1.0
  Downloaded futures v0.3.16
  Downloaded matchers v0.0.1
  Downloaded futures-core v0.3.16
  Downloaded proc-macro2 v1.0.28
  Downloaded radium v0.5.3
  Downloaded postgres-types v0.2.1
  Downloaded socket2 v0.4.1
  Downloaded memchr v2.4.0
  Downloaded autocfg v1.0.1
  Downloaded parking_lot v0.11.1
  Downloaded indexmap v1.7.0
  Downloaded rand_core v0.6.3
  Downloaded futures-io v0.3.16
  Downloaded regex-automata v0.1.10
  Downloaded futures-executor v0.3.16
  Downloaded regex v1.5.4
  Downloaded aho-corasick v0.7.18
  Downloaded native-tls v0.2.7
  Downloaded fallible-iterator v0.2.0
  Downloaded ppv-lite86 v0.2.10
  Downloaded tracing-subscriber v0.2.19
  Downloaded phf_shared v0.8.0
  Downloaded openssl-probe v0.1.4
  Downloaded mime_guess v2.0.3
  Downloaded cc v1.0.69
  Downloaded form_urlencoded v1.0.1
  Downloaded humansize v1.1.1
  Downloaded futures-util v0.3.16
  Downloaded serde v1.0.126
  Downloaded scopeguard v1.1.0
  Downloaded cpufeatures v0.1.5
  Downloaded matches v0.1.8
  Downloaded pin-project-internal v1.0.8
  Downloaded hyper-tls v0.5.0
  Downloaded fnv v1.0.7
  Downloaded generic-array v0.14.4
  Downloaded pin-project v1.0.8
  Downloaded ipnet v2.3.1
  Downloaded tokio-postgres v0.7.2
  Downloaded http-body v0.4.2
  Downloaded getrandom v0.2.3
  Downloaded opaque-debug v0.3.0
  Downloaded stringprep v0.1.2
  Downloaded crc32fast v1.2.1
  Downloaded ryu v1.0.5
  Downloaded futures-task v0.3.16
  Downloaded h2 v0.3.3
  Downloaded postgres-protocol v0.6.1
  Downloaded unicode-normalization v0.1.19
  Downloaded thread_local v1.1.3
  Downloaded subtle v2.4.1
  Downloaded wyz v0.2.0
  Downloaded sha2 v0.9.5
  Downloaded log v0.4.14
  Downloaded hmac v0.10.1
  Downloaded openssl v0.10.35
  Downloaded httparse v1.4.1
  Downloaded mio v0.7.13
  Downloaded typenum v1.13.0
  Downloaded slab v0.4.3
  Downloaded tokio-util v0.6.7
  Downloaded unicode-bidi v0.3.5
  Downloaded tracing-attributes v0.1.15
  Downloaded uuid v0.8.2
  Downloaded sharded-slab v0.1.1
  Downloaded http v0.2.4
  Downloaded url v2.2.2
  Downloaded siphasher v0.3.6
  Downloaded once_cell v1.8.0
  Downloaded tinyvec_macros v0.1.0
  Downloaded quote v1.0.9
  Downloaded num_cpus v1.13.0
  Downloaded lazy_static v1.4.0
  Downloaded hashbrown v0.11.2
  Downloaded tap v1.0.1
  Downloaded mime v0.3.16
  Downloaded reqwest v0.11.4
  Downloaded serde_json v1.0.66
  Downloaded chrono v0.4.19
  Downloaded bytes v1.0.1
  Downloaded cfg-if v1.0.0
  Downloaded tokio-macros v1.3.0
  Downloaded brotli-decompressor v2.3.1
  Downloaded tracing v0.1.26
  Downloaded toml v0.5.8
  Downloaded serde_urlencoded v0.7.0
  Downloaded unicase v2.6.0
  Downloaded block-buffer v0.9.0
  Downloaded crypto-mac v0.10.1
  Downloaded tower-service v0.3.1
  Downloaded try-lock v0.2.3
  Downloaded tokio-native-tls v0.3.0
  Downloaded tower-http v0.1.1
  Downloaded hyper v0.14.11
  Downloaded tinyvec v1.3.1
  Downloaded smallvec v1.6.1
  Downloaded httpdate v1.0.1
  Downloaded tracing-serde v0.1.2
  Downloaded tracing-log v0.1.2
  Downloaded byteorder v1.4.3
  Downloaded bitvec v0.19.5
  Downloaded version_check v0.9.3
  Downloaded tracing-core v0.1.18
  Downloaded bitflags v1.2.1
  Downloaded tower v0.4.8
  Downloaded unicode-xid v0.2.2
  Downloaded want v0.3.0
  Downloaded regex-syntax v0.6.25
  Downloaded parking_lot_core v0.8.3
  Downloaded itoa v0.4.7
  Downloaded syn v1.0.74
  Downloaded pkg-config v0.3.19
  Downloaded tokio v1.9.0
  Downloaded tower-layer v0.3.1
  Downloaded iri-string v0.4.0
  Downloaded idna v0.2.3
  Downloaded openssl-sys v0.9.65
  Downloaded instant v0.1.10
  Downloaded rand_chacha v0.3.1
  Downloaded proc-macro-hack v0.5.19
  Downloaded percent-encoding v2.1.0
  Downloaded base64 v0.13.0
  Downloaded serde_derive v1.0.126
  Downloaded proc-macro-nested v0.1.7
  Downloaded pin-utils v0.1.0
  Downloaded pin-project-lite v0.2.7
  Downloaded rand v0.8.4
  Downloaded lexical-core v0.7.6
  Downloaded askama_derive v0.10.5
  Downloaded brotli v3.3.0
  Downloaded libc v0.2.98
  Downloaded encoding_rs v0.8.28
  Downloaded 160 crates (10.9 MB) in 3.86s (largest was `brotli` at 1.4 MB)
   Compiling autocfg v1.0.1
   Compiling cfg-if v1.0.0
   Compiling proc-macro2 v1.0.28
   Compiling unicode-xid v0.2.2
   Compiling syn v1.0.74
   Compiling libc v0.2.98
   Compiling memchr v2.4.0
   Compiling version_check v0.9.3
   Compiling log v0.4.14
   Compiling pin-project-lite v0.2.7
   Compiling bytes v1.0.1
   Compiling futures-core v0.3.16
   Compiling smallvec v1.6.1
   Compiling scopeguard v1.1.0
   Compiling proc-macro-hack v0.5.19
   Compiling futures-sink v0.3.16
   Compiling proc-macro-nested v0.1.7
   Compiling bitflags v1.2.1
   Compiling futures-task v0.3.16
   Compiling futures-channel v0.3.16
   Compiling serde_derive v1.0.126
   Compiling ryu v1.0.5
   Compiling pin-utils v0.1.0
   Compiling slab v0.4.3
   Compiling typenum v1.13.0
   Compiling lazy_static v1.4.0
   Compiling serde v1.0.126
   Compiling futures-io v0.3.16
   Compiling matches v0.1.8
   Compiling itoa v0.4.7
   Compiling pkg-config v0.3.19
   Compiling radium v0.5.3
   Compiling cc v1.0.69
   Compiling tinyvec_macros v0.1.0
   Compiling percent-encoding v2.1.0
   Compiling lexical-core v0.7.6
   Compiling fnv v1.0.7
   Compiling tap v1.0.1
   Compiling static_assertions v1.1.0
   Compiling funty v1.1.0
   Compiling arrayvec v0.5.2
   Compiling wyz v0.2.0
   Compiling base64 v0.13.0
   Compiling once_cell v1.8.0
   Compiling ppv-lite86 v0.2.10
   Compiling crc32fast v1.2.1
   Compiling openssl v0.10.35
   Compiling httparse v1.4.1
   Compiling tower-service v0.3.1
   Compiling hashbrown v0.11.2
   Compiling alloc-no-stdlib v2.0.1
   Compiling async-trait v0.1.51
   Compiling subtle v2.4.1
   Compiling foreign-types-shared v0.1.1
   Compiling opaque-debug v0.3.0
   Compiling adler v1.0.2
   Compiling try-lock v0.2.3
   Compiling serde_json v1.0.66
   Compiling cpufeatures v0.1.5
   Compiling regex-syntax v0.6.25
   Compiling native-tls v0.2.7
   Compiling openssl-probe v0.1.4
   Compiling byteorder v1.4.3
   Compiling fallible-iterator v0.2.0
   Compiling mime v0.3.16
   Compiling httpdate v1.0.1
   Compiling siphasher v0.3.6
   Compiling humansize v1.1.1
   Compiling askama_escape v0.10.1
   Compiling tower-layer v0.3.1
   Compiling encoding_rs v0.8.28
   Compiling ipnet v2.3.1
   Compiling ansi_term v0.12.1
   Compiling instant v0.1.10
   Compiling lock_api v0.4.4
   Compiling generic-array v0.14.4
   Compiling nom v6.1.2
   Compiling unicase v2.6.0
   Compiling tokio v1.9.0
   Compiling futures-macro v0.3.16
   Compiling futures-util v0.3.16
   Compiling num-traits v0.2.14
   Compiling indexmap v1.7.0
   Compiling miniz_oxide v0.4.4
   Compiling num-integer v0.1.44
   Compiling tracing-core v0.1.18
   Compiling sharded-slab v0.1.1
   Compiling unicode-bidi v0.3.5
   Compiling tinyvec v1.3.1
   Compiling http v0.2.4
   Compiling form_urlencoded v1.0.1
   Compiling thread_local v1.1.3
   Compiling alloc-stdlib v0.2.1
   Compiling foreign-types v0.3.2
   Compiling phf_shared v0.8.0
   Compiling openssl-sys v0.9.65
   Compiling brotli-decompressor v2.3.1
   Compiling phf v0.8.0
   Compiling want v0.3.0
   Compiling tracing-log v0.1.2
   Compiling aho-corasick v0.7.18
   Compiling unicode-normalization v0.1.19
   Compiling quote v1.0.9
   Compiling parking_lot_core v0.8.3
   Compiling num_cpus v1.13.0
   Compiling mio v0.7.13
   Compiling getrandom v0.2.3
   Compiling socket2 v0.4.1
   Compiling bitvec v0.19.5
   Compiling regex-automata v0.1.10
error: failed to run custom build command for `openssl-sys v0.9.65`

Caused by:
  process didn't exit successfully: `~/axum/target/debug/build/openssl-sys-95bfa9550bf83440/build-script-main` (exit status: 101)
  --- stdout
  cargo:rustc-cfg=const_fn
  cargo:rerun-if-env-changed=X86_64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR
  X86_64_UNKNOWN_LINUX_GNU_OPENSSL_LIB_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_LIB_DIR
  OPENSSL_LIB_DIR unset
  cargo:rerun-if-env-changed=X86_64_UNKNOWN_LINUX_GNU_OPENSSL_INCLUDE_DIR
  X86_64_UNKNOWN_LINUX_GNU_OPENSSL_INCLUDE_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_INCLUDE_DIR
  OPENSSL_INCLUDE_DIR unset
  cargo:rerun-if-env-changed=X86_64_UNKNOWN_LINUX_GNU_OPENSSL_DIR
  X86_64_UNKNOWN_LINUX_GNU_OPENSSL_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_DIR
  OPENSSL_DIR unset
  cargo:rerun-if-env-changed=OPENSSL_NO_PKG_CONFIG
  cargo:rerun-if-env-changed=PKG_CONFIG
  cargo:rerun-if-env-changed=OPENSSL_STATIC
  cargo:rerun-if-env-changed=OPENSSL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_STATIC
  cargo:rerun-if-env-changed=PKG_CONFIG_ALL_DYNAMIC
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_PATH
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_LIBDIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64-unknown-linux-gnu
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR_x86_64_unknown_linux_gnu
  cargo:rerun-if-env-changed=HOST_PKG_CONFIG_SYSROOT_DIR
  cargo:rerun-if-env-changed=PKG_CONFIG_SYSROOT_DIR
  run pkg_config fail: "`\"pkg-config\" \"--libs\" \"--cflags\" \"openssl\"` did not exit successfully: exit status: 1\n--- stderr\nPackage openssl was not found in the pkg-config search path.\nPerhaps you should add the directory containing `openssl.pc'\nto the PKG_CONFIG_PATH environment variable\nNo package 'openssl' found\n"

  --- stderr
  thread 'main' panicked at '

  Could not find directory of OpenSSL installation, and this `-sys` crate cannot
  proceed without this knowledge. If OpenSSL is installed and this crate had
  trouble finding it,  you can set the `OPENSSL_DIR` environment variable for the
  compilation process.

  Make sure you also have the development packages of openssl installed.
  For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora.

  If you're in a situation where you think the directory *should* be found
  automatically, please open a bug at https://github.com/sfackler/rust-openssl
  and include information about your system as well as this message.

  $HOST = x86_64-unknown-linux-gnu
  $TARGET = x86_64-unknown-linux-gnu
  openssl-sys = 0.9.65

  '~/.cargo/registry/src/github.com-1ecc6299db9ec823/openssl-sys-0.9.65/build/find_normal.rs:174:5
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: build failed

My system:

lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.2 LTS
Release:	20.04
Codename:	focal

Symmetry between methods routers and path routers

Feature Request

Motivation

Path routing is currently handled quite cleanly in Axum: there is a module, routing, which contains all the methods related to path routing requests. However, method routing is a lot more messy: there are two nearly-identical versions of each utility split across two seemingly unrelated modules (handler and service). The service module is especially odd because it contains service utilities and some concrete services, but doesn't contain any of the other concrete services that this crate also contains (contrary to the module's description), which is confusing.

Proposal

Create a new module, method, that is completely dedicated to method routing. This method will contain any, connect, delete, on, OnMethod, etc from axum::service, but will also provide any_to, connect_to, delete_to, etc which will take in a Handler for the same ergonomics that the handler module used to have. MethodFilter can also be placed inside axum::method. It was quite out of place in axum::routing anyway where the rest of the functions were about path routing.

The existing handler and service modules should be stripped of all their method-related functions, since that is now provided by method. The module description of axum::service will become the much more specific and accurate "Utilities for working with Services". routing could also be renamed to route, for the consistency of using nouns for everything.

Alternatives

Move all the method-handling functions to axum::routing, and not have a method module. I would be totally OK with this. It has the advantage of not needing to move MethodFilter or rename routing, but the module could end up a bit large.

Form extractor

Quick little app that has a form

use http::Request;
use hyper::Server;
use std::{collections::HashMap, net::SocketAddr};
use tower::make::Shared;
use tower_web::prelude::*;

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    // build our application with some routes
    let app = route("/", get(show_form).post(accept_form))
        .layer(tower_http::trace::TraceLayer::new_for_http());

    // run it with hyper
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    tracing::debug!("listening on {}", addr);
    let server = Server::bind(&addr).serve(Shared::new(app));
    server.await.unwrap();
}

async fn show_form(_req: Request<Body>) -> response::Html<&'static str> {
    response::Html(
        r#"
        <!doctype html>
        <html>
            <head></head>
            <body>
                <form action="/" method="post">
                    <label for="name">Enter your name: </label>
                    <input type="text" name="name" id="name" required>

                    <label for="email">Enter your email: </label>
                    <input type="email" name="email" id="email" required>

                    <input type="submit" value="Subscribe!">
                </form>
            </body>
        </html>
        "#,
    )
}

async fn accept_form(
    req: Request<Body>,
    query: Option<extract::Query<HashMap<String, String>>>,
    body: String,
) {
    println!("query = {:?}", query);
    println!("body = {}", body);
    println!("headers = {:?}", req.headers());
}

Should also handle file uploads.

`nest("/", handler)` strips leading slash

If you nest a service at "/" like this:

let app = axum::routing::nest(
    "/",
    get(|method: Method, uri: Uri| async move { println!("{} {} called", method, uri) }),
);

and then call GET /foo/bar the URI the handler receives is foo/bar which is wrong. I should be /foo/bar. Basically nest should make sure the URI always has a leading slash.

Common JSON wrapper type for response and request

Currently there is axum::extract::Json and axum::response::Json, this introduces naming conflicts if handler accepts Json and returns Json.

Actix have actix_web::web::Json which can do at the same time.

InternalServerError when Request and Json extractors are used together

The following test returns HTTP status 500:

#[tokio::test]
async fn test_extractors() {

    let app = route("/api", 
    post(|req: Request<Body>, payload: extract::Json<String>| async move {
        println!("CALL RECEIVED");
    }));

    // Act
    let response = app
        .oneshot(Request::builder()
            .method(http::Method::POST)
            .header(http::header::CONTENT_TYPE, "application/json")
            .uri("/api")
            .body(Body::from(
                serde_json::to_vec(&"payload").unwrap(),
            )).unwrap())
        .await
        .unwrap();

    // Assert
    assert_eq!(response.status(), StatusCode::OK);

}

The issue is caused by the fact that the Request and the Json extractors are used at the same time.

I tried to replace the Request with extract::RequestParts, but, by doing that, the compiler complains that

the trait bound `[closure@....]: axum::handler::Handler<_, _>` is not satisfied.

Add helper fn to test a JSON response

Feature Request

Motivation

As of now, to test a REST endpoint that returns a JSON object, two steps are needed; the first one, to get the body of the response using hyper, and the second one to convert it to JSON with serde:

    #[tokio::test]
    async fn json() {
        let app = app();

        let response = app.oneshot(Request::builder()...).await.unwrap();

        let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 
        let body: Value = serde_json::from_slice(&body).unwrap();

        assert_eq!(body, json!({ "data": [1, 2, 3, 4] }));
    }

Proposal

It would be great to have a single fn that does all the job, so one can write, for example:

    #[tokio::test]
    async fn json() {
        let app = app();

        let response = app.oneshot(Request::builder()...).await.unwrap();

        let body: Value = test::read_response_json(response).unwrap();

        assert_eq!(body, json!({ "data": [1, 2, 3, 4] }));
    }

though this would require, probably, the creation of a test module

cargo doc fails without --all-features due to deny(broken_intra_doc_links)

Bug Report

Trying to generate docs while working on axum fails when cargo doc isn't run with --all-features or with --features used to enable the features that the main readme has intradoc links to.

Version

4e9b38d

Suggested fix

I think a reasonable way to fix this would be removing deny(broken_intra_doc_links) from lib.rs and adjusting CI to pass -D broken_intra_doc_links to rustdoc (not sure whether it has to be passed to cargo doc directly, after --, or rather via RUSTDOCFLAGS).

GraphQL example

Would be nice to provide an example of setting up a basic GraphQL server. Don't think it matters if the example uses async-graphql or juniper. I expect both implementations to be fairly similar.

Websockets example does not seem to work (Linux?) (Requester investigation needed)

Bug Report

Version

a0f6dcc (main branch August 2nd)

Platform

popOS! / Linux
Linux pop-os 5.11.0-7620-generic #21~1626191760~21.04~55de9c3-Ubuntu SMP Tue Jul 20 22:18:55 UTC x86_64 x86_64 x86_64 GNU/Linux

It DOES work on macOS M1
20.3.0 Darwin Kernel Version 20.3.0: Thu Jan 21 00:06:51 PST 2021; root:xnu-7195.81.3~1/RELEASE_ARM64_T8101 arm64

Description

git clone ...
cd axum
RUST_LOG=tower_http=debug,key_value_store=trace \
     cargo run \
     --all-features \
     --example websocket

websocat ws://127.0.0.1/ws
...error connecting 400...

Bring websocket handling more inline with handler API

Feature Request

Motivation

Currently handling websockets works quite differently from handling other requests. This has a few downsides:

  • You currently cannot return an error from a ws request. The connection will always be upgraded. This was also highlighted in #73.
  • You cannot customize the connection upgrade response. Users might wanna add additional headers. That is currently not possible.
  • Applying extractors to ws requests is possible but the implementation isn't very clear. It requires code very similar to what is already in handler/mod.rs.

Proposal

Change the websocket API to instead use a regular handler and a special IntoResponse. Something like

// build our application with a route
let app = route("/", get(handler));

async fn ws_handler(
    // this extractor checks for the required headers
    ws: extract::WebSocketUpgrade,
    // you can put other extractors here
) -> impl IntoResponse {
    // here you can do whatever you want to prepare for handling the connection
    // possibly return an error and rejection the connection all together

    // actually upgrade the connection and call the async closure with
    // the websocket connection when we have it
    ws.on_upgrade(|socket: WebSocket| async {
        // do stuff...
    })
}

This is similar to how things work in actix.

This solution was suggest by gbaranski#5119 on Discord.

Alternatives

Leads to slightly more code in the basic case where you don't wanna apply any extractors and just wanna start a ws connection without rejections. I think that tradeoff is worth it though.

handles `HEAD` requests automatically when there exists a `GET` route that would otherwise match. `405 Method Not Allowed` returns for now.

Feature Request

Motivation

Proposal

handles HEAD requests automatically when there exists a GET route that would otherwise match. It does this by stripping the body from the response, if there is one. You can also specialize the handling of a HEAD request by declaring a route for it; won't interfere with HEAD requests your application explicitly handles.

Alternatives

Just like rocket did.

head-requests

Clarify which crates are necessary to get started

We need enhance documentation for the examples. There are a lot of dependencies. Its hard to know what is needed to compile, and makes hard to setup a new project as we dont know from what crates this functions come.

Support generating OpenAPI/Swagger docs

If you are wring a RESTful API service, the OpenAPI/Swagger documentation is very useful. It would be great if axum is able to generate them automatically.

Improve documentation for router

The router has a few slightly surprising behaviors due to its design that should we clearly document:

  • That route("/foo", get(handler)).route("/foo", post(handler)) only matches POST and not GET. This is #62
  • That routes are matched bottom to top. Not top to bottom, or longest path wins.

Cannot mix infallible handlers and fallible services

An app like this wont compile:

let app =
    // this route fails with Infallible
    route("/foo", get(|| async {}))
    // this route fails with hyper::Error
    .route(
        "/",
        axum::service::get(tower::service_fn(|_: Request<Body>| async {
            Ok::<_, hyper::Error>(Response::new(Body::empty()))
        })),
    );

Because the two error types don't match:

error[E0271]: type mismatch resolving `<Route<axum::handler::OnMethod<axum::handler::IntoService<[closure@examples/foo:10:33: 10:44], hyper::Body, ()>, EmptyRouter>, EmptyRouter> as Service<http::Request<hyper::Body>>>::Error == hyper::Error`
  --> examples/foo:20:10
   |
20 |         .serve(app.into_make_service())
   |          ^^^^^ expected enum `Infallible`, found struct `hyper::Error`

The solution currently is to use .handle_error and convert the tower::service_fns error type to Infallible. However that might not be what you want. Or at the very least we should investigate if things could be changed to "just" work.

I believe the error happens because of this trait bound which requires the two services to have the same error types. Nested suffers from the same issue.

Support for CORS preflight requests

Feature Request

Motivation

Communicating with an axum app from the browser is a popular use case and many browsers restrict cross-origin requests by default.

Background

MDN CORS
CORS server flowchart

See Also

CORS PR in tower-http

Related issue on OPTIONS requests #44
Related issue on SPAs #87

Warp "wrapping filter"
Tide middleware
3rd party crate recommended for rocket

Proposal

Add ergonomic support and docs for handling CORS preflight requests.

axum already provides support for setting headers globally or specifically using middleware, however, I'm not sure of an ergonomic way to handle preflight requests. That's an OPTIONS request preceding e.g. a POST request to the same path.

An app could handle this for example by (pseudocode)

let app = route("/user", get(get_user).options(cors_fn));

Since routing precedence (I think?) limits the ability to put a path wildcard OPTIONS at the root of the application, or some part of the application, the developer is required to chain options to every path declaration.

I'm not ready to propose any specific changes or weigh in on alternatives yet as I'm a new user and this an issue I'm encountering.

Project setup

  • CI
  • Changelog
  • Proper readme
  • Contributing guide
  • License
  • Cargo deny
  • Pull request template
  • Issue template
  • Warning about not being production ready
  • Anything else?

Make it possible to merge routes

Being able to merge routes/apps like this would be really cool

let app = route("/foo", get(|| async {}));
let other_route = route("/bar", get(|| async {}));

// this accepts `GET /foo` and `GET /bar`
let merged = app.or(other_route);

Things to consider:

  • It should work with other things than Route like BoxRoute, Nested, basically anything that implements RouteDsl.

`WebSocketHandler::call` should be allowed to fail

I think it makes sense of WebSocketHandler::call to return Result<impl Future, impl IntoResponse>. I can imagine setting some data via an extractor and doing some fallible thing. Currently its not possible to return an error if an error is encountered.

This is a breaking change so has to wait for 0.2.

Consider renaming UrlParams to Path

Feature Request

Motivation

This would be more in line with Body and Query, and be shorter (I think for some apps this type name will be written down a lot).

Proposal

Rename UrlParams to Path. Not sure about UrlParamsMap, I guess PathMap isn't too bad.

Should `FromRequest` have an associated `Output` type?

Feature Request

Motivation

FromRequest is currently requires from_request to return Result<Self, _>.
This means when you implement it for something like extract::Extension you
must return an extract::Extension value. That requires users to pattern match
to pull out the inner value. That can get a bit tedious, especially when using
nested extractors like ContentLengthLimit.

Proposal

If we added an associated type Output to FromRequest it would allow
extractors to return values other than Self and you wouldn't need to pattern
match:

#[async_trait]
pub trait FromRequest<B> {
    type Output;
    type Rejection: IntoResponse;

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self::Output, Self::Rejection>;
}

pub struct Extension<T>(PhantomData<T>);

#[async_trait]
impl<T, B> FromRequest<B> for Extension<T> {
    type Output = T;
    type Rejection = /* ... */;

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self::Output, Self::Rejection> {
        // ...
    }
}

async fn handler(value: Extension<SomeValue>) {
    // `value` is now `SomeValue`
    // no need for matching with `Extension(value): Extension<SomeValue>`
}

Would be a breaking change so something to consider for 0.2

Alternatives

Keep the current API as it works just fine ๐Ÿคท

Support side-by-side precompressed static files

Feature Request

Motivation

The Rust Playground has a number of assets (mainly JavaScript) that rarely get updated. To save bandwidth, these are served in a compressed form. To save CPU time, they are precompressed on disk.

When a request is made for foo.js and the client indicates that they accept GZip-compressed data, we look instead for foo.js.gz. If that's found, we return it. Otherwise, foo.js is attempted.

The current implementation of this (for the old Iron framework) has a fair amount of hand-written code to achieve this. Most web frameworks I've investigated make it very difficult (impossible?) to reuse relevant code such as path traversal protection or file -> HTTP caching information.

Proposal

I don't think that this is something that Axum itself needs to provide, as it's a relatively uncommon requirement. However, I do think that it should be possible to reuse large chunks of Axum code such as serving a static file.

If it seems generally interesting, then I don't see why it couldn't be added to Axum proper, of course.

Alternatives

I believe many people punt this off to a different web server. For example, NGINX plus ngx_http_gzip_static_module. I don't like this path, but I see why many people choose it.

error[E0432]: unresolved import `axum::extract::TypedHeader`

All steps to reproduce the problem (including the cargo output):

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://github.com/tokio-rs/axum.git
cd axum/
cargo build --features ws --example websocket 
    Updating crates.io index
   Compiling autocfg v1.0.1
   Compiling cfg-if v1.0.0
   Compiling proc-macro2 v1.0.28
   Compiling unicode-xid v0.2.2
   Compiling syn v1.0.74
   Compiling libc v0.2.98
   Compiling version_check v0.9.3
   Compiling memchr v2.4.0
   Compiling log v0.4.14
   Compiling pin-project-lite v0.2.7
   Compiling bytes v1.0.1
   Compiling futures-core v0.3.16
   Compiling smallvec v1.6.1
   Compiling typenum v1.13.0
   Compiling proc-macro-hack v0.5.19
   Compiling scopeguard v1.1.0
   Compiling proc-macro-nested v0.1.7
   Compiling futures-channel v0.3.16
   Compiling futures-sink v0.3.16
   Compiling futures-task v0.3.16
   Compiling bitflags v1.2.1
   Compiling ryu v1.0.5
   Compiling futures-io v0.3.16
   Compiling pin-utils v0.1.0
   Compiling matches v0.1.8
   Compiling serde_derive v1.0.126
   Compiling slab v0.4.3
   Compiling tinyvec_macros v0.1.0
   Compiling lazy_static v1.4.0
   Compiling itoa v0.4.7
   Compiling serde v1.0.126
   Compiling percent-encoding v2.1.0
   Compiling radium v0.5.3
   Compiling opaque-debug v0.3.0
   Compiling cc v1.0.69
   Compiling fnv v1.0.7
   Compiling pkg-config v0.3.19
   Compiling ppv-lite86 v0.2.10
   Compiling base64 v0.13.0
   Compiling cpufeatures v0.1.5
   Compiling lexical-core v0.7.6
   Compiling wyz v0.2.0
   Compiling tap v1.0.1
   Compiling httparse v1.4.1
   Compiling arrayvec v0.5.2
   Compiling static_assertions v1.1.0
   Compiling funty v1.1.0
   Compiling byteorder v1.4.3
   Compiling once_cell v1.8.0
   Compiling openssl v0.10.35
   Compiling alloc-no-stdlib v2.0.1
   Compiling subtle v2.4.1
   Compiling tower-service v0.3.1
   Compiling async-trait v0.1.51
   Compiling foreign-types-shared v0.1.1
   Compiling hashbrown v0.11.2
   Compiling crc32fast v1.2.1
   Compiling regex-syntax v0.6.25
   Compiling serde_json v1.0.66
   Compiling try-lock v0.2.3
   Compiling adler v1.0.2
   Compiling native-tls v0.2.7
   Compiling fallible-iterator v0.2.0
   Compiling siphasher v0.3.6
   Compiling httpdate v1.0.1
   Compiling openssl-probe v0.1.4
   Compiling mime v0.3.16
   Compiling tower-layer v0.3.1
   Compiling askama_escape v0.10.1
   Compiling utf-8 v0.7.6
   Compiling humansize v1.1.1
   Compiling encoding_rs v0.8.28
   Compiling ipnet v2.3.1
   Compiling ansi_term v0.12.1
   Compiling instant v0.1.10
   Compiling lock_api v0.4.4
   Compiling input_buffer v0.4.0
   Compiling generic-array v0.14.4
   Compiling nom v6.1.2
   Compiling unicase v2.6.0
   Compiling tokio v1.9.0
   Compiling futures-macro v0.3.16
   Compiling futures-util v0.3.16
   Compiling num-traits v0.2.14
   Compiling indexmap v1.7.0
   Compiling miniz_oxide v0.4.4
   Compiling num-integer v0.1.44
   Compiling unicode-bidi v0.3.5
   Compiling tinyvec v1.3.1
   Compiling tracing-core v0.1.18
   Compiling sharded-slab v0.1.1
   Compiling http v0.2.4
   Compiling form_urlencoded v1.0.1
   Compiling alloc-stdlib v0.2.1
   Compiling thread_local v1.1.3
   Compiling foreign-types v0.3.2
   Compiling phf_shared v0.8.0
   Compiling openssl-sys v0.9.65
   Compiling brotli-decompressor v2.3.1
   Compiling phf v0.8.0
   Compiling want v0.3.0
   Compiling tracing-log v0.1.2
   Compiling quote v1.0.9
   Compiling aho-corasick v0.7.18
   Compiling unicode-normalization v0.1.19
   Compiling regex-automata v0.1.10
   Compiling parking_lot_core v0.8.3
   Compiling mio v0.7.13
   Compiling num_cpus v1.13.0
   Compiling getrandom v0.2.3
   Compiling socket2 v0.4.1
   Compiling bitvec v0.19.5
   Compiling http-body v0.4.2
   Compiling mime_guess v2.0.3
   Compiling parking_lot v0.11.1
   Compiling rand_core v0.6.3
   Compiling regex v1.5.4
   Compiling flate2 v1.0.20
   Compiling brotli v3.3.0
   Compiling idna v0.2.3
   Compiling stringprep v0.1.2
   Compiling matchers v0.0.1
   Compiling digest v0.9.0
   Compiling block-buffer v0.9.0
   Compiling crypto-mac v0.10.1
   Compiling rand_chacha v0.3.1
   Compiling url v2.2.2
   Compiling chrono v0.4.19
   Compiling hmac v0.10.1
   Compiling sha2 v0.9.5
   Compiling md-5 v0.9.1
   Compiling sha-1 v0.9.7
   Compiling rand v0.8.4
   Compiling postgres-protocol v0.6.1
   Compiling postgres-types v0.2.1
   Compiling iri-string v0.4.0
   Compiling tokio-macros v1.3.0
   Compiling tracing-attributes v0.1.15
   Compiling pin-project-internal v1.0.8
   Compiling thiserror-impl v1.0.26
   Compiling thiserror v1.0.26
   Compiling tungstenite v0.13.0
   Compiling pin-project v1.0.8
   Compiling tracing v0.1.26
   Compiling futures-executor v0.3.16
   Compiling futures v0.3.16
   Compiling tokio-util v0.6.7
   Compiling async-compression v0.3.8
   Compiling tokio-native-tls v0.3.0
   Compiling bb8 v0.7.0
   Compiling tokio-tungstenite v0.14.0
   Compiling h2 v0.3.3
   Compiling tower v0.4.8
   Compiling tokio-postgres v0.7.2
   Compiling tower-http v0.1.1
   Compiling bb8-postgres v0.7.0
   Compiling toml v0.5.8
   Compiling serde_urlencoded v0.7.0
   Compiling tracing-serde v0.1.2
   Compiling uuid v0.8.2
   Compiling tracing-subscriber v0.2.19
   Compiling askama_shared v0.11.1
   Compiling hyper v0.14.11
   Compiling askama_derive v0.10.5
   Compiling askama v0.10.5
   Compiling hyper-tls v0.5.0
   Compiling axum v0.1.1 (~/axum)
   Compiling reqwest v0.11.4
error[E0432]: unresolved import `axum::extract::TypedHeader`
  --> examples/websocket.rs:13:5
   |
13 |     extract::TypedHeader,
   |     ^^^^^^^^^^^^^^^^^^^^ no `TypedHeader` in `extract`

error[E0433]: failed to resolve: use of undeclared crate or module `headers`
  --> examples/websocket.rs:64:42
   |
64 |     TypedHeader(user_agent): TypedHeader<headers::UserAgent>,
   |                                          ^^^^^^^ use of undeclared crate or module `headers`

error[E0277]: the trait bound `fn(WebSocket, [type error]) -> impl Future {handle_socket}: WebSocketHandler<_, _>` is not satisfied
  --> examples/websocket.rs:46:22
   |
46 |     .route("/ws", ws(handle_socket))
   |                      ^^^^^^^^^^^^^ the trait `WebSocketHandler<_, _>` is not implemented for `fn(WebSocket, [type error]) -> impl Future {handle_socket}`
   | 
  ::: ~/axum/src/ws/mod.rs:90:8
   |
90 |     F: WebSocketHandler<B, T>,
   |        ---------------------- required by this bound in `ws`

error[E0277]: the trait bound `fn(WebSocket, [type error]) -> impl Future {handle_socket}: WebSocketHandler<_, _>` is not satisfied
  --> examples/websocket.rs:46:19
   |
46 |     .route("/ws", ws(handle_socket))
   |                   ^^^^^^^^^^^^^^^^^ the trait `WebSocketHandler<_, _>` is not implemented for `fn(WebSocket, [type error]) -> impl Future {handle_socket}`
   |
   = note: required because of the requirements on the impl of `Service<Request<_>>` for `WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, _, _>`

error[E0277]: the trait bound `fn(WebSocket, [type error]) -> impl Future {handle_socket}: WebSocketHandler<hyper::Body, _>` is not satisfied
  --> examples/websocket.rs:56:16
   |
56 |         .serve(app.into_make_service())
   |                ^^^^^^^^^^^^^^^^^^^^^^^ the trait `WebSocketHandler<hyper::Body, _>` is not implemented for `fn(WebSocket, [type error]) -> impl Future {handle_socket}`
   |
   = note: required because of the requirements on the impl of `Service<Request<hyper::Body>>` for `WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>`
   = note: 2 redundant requirements hidden
   = note: required because of the requirements on the impl of `Service<Request<hyper::Body>>` for `tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>`
   = note: required because of the requirements on the impl of `hyper::service::http::HttpService<hyper::Body>` for `axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>`
   = note: required because of the requirements on the impl of `hyper::service::make::MakeServiceRef<AddrStream, hyper::Body>` for `tower::make::make_service::shared::Shared<axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>>`

error[E0277]: the trait bound `hyper::common::exec::Exec: hyper::common::exec::ConnStreamExec<tower_http::trace::ResponseFuture<RouteFuture<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>, hyper::Body>, ServerErrorsAsFailures, DefaultOnResponse, DefaultOnBodyChunk, DefaultOnEos, DefaultOnFailure>, tower_http::trace::ResponseBody<http_body::combinators::box_body::BoxBody<hyper::body::Bytes, BoxStdError>, NeverClassifyEos<ServerErrorsFailureClass>, DefaultOnBodyChunk, DefaultOnEos, DefaultOnFailure>>` is not satisfied
  --> examples/websocket.rs:55:5
   |
55 | /     hyper::Server::bind(&addr)
56 | |         .serve(app.into_make_service())
57 | |         .await
   | |______________^ the trait `hyper::common::exec::ConnStreamExec<tower_http::trace::ResponseFuture<RouteFuture<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>, hyper::Body>, ServerErrorsAsFailures, DefaultOnResponse, DefaultOnBodyChunk, DefaultOnEos, DefaultOnFailure>, tower_http::trace::ResponseBody<http_body::combinators::box_body::BoxBody<hyper::body::Bytes, BoxStdError>, NeverClassifyEos<ServerErrorsFailureClass>, DefaultOnBodyChunk, DefaultOnEos, DefaultOnFailure>>` is not implemented for `hyper::common::exec::Exec`
   |
   = help: the following implementations were found:
             <hyper::common::exec::Exec as hyper::common::exec::ConnStreamExec<F, B>>
   = note: required because of the requirements on the impl of `Future` for `Server<AddrIncoming, tower::make::make_service::shared::Shared<axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>>>`
   = note: required by `poll`

error[E0277]: the trait bound `hyper::common::exec::Exec: hyper::common::exec::NewSvcExec<AddrStream, tower::make::make_service::shared::SharedFuture<axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>>, axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>, hyper::common::exec::Exec, hyper::server::conn::spawn_all::NoopWatcher>` is not satisfied
  --> examples/websocket.rs:55:5
   |
55 | /     hyper::Server::bind(&addr)
56 | |         .serve(app.into_make_service())
57 | |         .await
   | |______________^ the trait `hyper::common::exec::NewSvcExec<AddrStream, tower::make::make_service::shared::SharedFuture<axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>>, axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>, hyper::common::exec::Exec, hyper::server::conn::spawn_all::NoopWatcher>` is not implemented for `hyper::common::exec::Exec`
   |
   = help: the following implementations were found:
             <hyper::common::exec::Exec as hyper::common::exec::NewSvcExec<I, N, S, E, W>>
   = note: required because of the requirements on the impl of `Future` for `Server<AddrIncoming, tower::make::make_service::shared::Shared<axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>>>`
   = note: required by `poll`

error[E0277]: the trait bound `fn(WebSocket, [type error]) -> impl Future {handle_socket}: WebSocketHandler<hyper::Body, _>` is not satisfied
  --> examples/websocket.rs:55:5
   |
55 | /     hyper::Server::bind(&addr)
56 | |         .serve(app.into_make_service())
57 | |         .await
   | |______________^ the trait `WebSocketHandler<hyper::Body, _>` is not implemented for `fn(WebSocket, [type error]) -> impl Future {handle_socket}`
   |
   = note: required because of the requirements on the impl of `Service<Request<hyper::Body>>` for `WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>`
   = note: 2 redundant requirements hidden
   = note: required because of the requirements on the impl of `Service<Request<hyper::Body>>` for `tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>`
   = note: required because of the requirements on the impl of `hyper::service::http::HttpService<hyper::Body>` for `axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>`
   = note: required because of the requirements on the impl of `hyper::service::make::MakeServiceRef<AddrStream, hyper::Body>` for `tower::make::make_service::shared::Shared<axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>>`
   = note: required because of the requirements on the impl of `Future` for `Server<AddrIncoming, tower::make::make_service::shared::Shared<axum::routing::Layered<tower_http::trace::Trace<Route<WebSocketUpgrade<fn(WebSocket, [type error]) -> impl Future {handle_socket}, hyper::Body, _>, Nested<axum::service::OnMethod<BoxResponseBody<HandleError<ServeDir, [closure@examples/websocket.rs:36:31: 41:18], hyper::Body>, hyper::Body>, EmptyRouter>, EmptyRouter>>, SharedClassifier<ServerErrorsAsFailures>>>>>`
   = note: required by `poll`

error: aborting due to 8 previous errors

Some errors have detailed explanations: E0277, E0432, E0433.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `axum`

To learn more, run the command again with --verbose.

CI doesn't seems to not actually run for MSRV

When setting up CI for axum I just copied the setup from Tower thinking that would be fine. However I didn't notice it included an MSRV of 1.40 which is tested on CI.

axum requires at least Rust 1.51 due the use of const generics in ContentLengthLimit. So it seems the 1.40 step on CI isn't actually running, since it should be failing ๐Ÿค”

We should fix CI so it actually does perform the checks and bump our MSRV to 1.51. If it turns out we use a feature not available on 1.51 I would be fine with requiring an even newer version, but it depends on the exact case.

Returning `(HeaderMap, T)` removes headers from `T`'s `IntoResponse` implementation

I would expect the responses from these two routes to be identical:

route(
    "/one",
    get(|| async { (HeaderMap::new(), "Hello, World!") }),
)
.route(
    "/two",
    get(|| async { "Hello, World!" }),
);

But since impl<T: IntoResponse> IntoResponse for (HeaderMap, T) replaces all the headers we loose the content-type header set by impl IntoResponse for &'static str.

(HeaderMap, T) should instead merge the headers into the response.

`memchr` version conflict

Bug Report

Version

axum = { version = "0.1.2", optional = true }
http = { version = "0.2.4", optional = true }
hyper = { version = "0.14.11", optional = true, features = ["full"] }
tower = { version = "0.4.8", optional = true }

Platform

Darwin Kernel Version 20.5.0: Sat May  8 05:10:33 PDT 2021; 
root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64 i386

Crates

The axum crate can't be use.

Description

I include axum in my project and try to run cargo update:

error: failed to select a version for `memchr`.
    ... required by package `nom v6.2.1`
    ... which is depended on by `xxx v0.3.0`
versions that meet the requirements `>=2.0, <2.4` are: 2.3.4, 2.3.3, 2.3.2, 2.3.0, 2.2.1, 2.2.0, 2.1.3, 2.1.2, 2.1.1, 2.1.0, 2.0.2, 2.0.1, 2.0.0

all possible versions conflict with previously selected packages.

  previously selected package `memchr v2.4.0`
    ... which is depended on by `aho-corasick v0.7.18`
    ... which is depended on by `regex v1.5.0`
    ... which is depended on by `axum v0.1.2`
    ... which is depended on by `xxx v0.3.0`

failed to select a version for `memchr` which could resolve this conflict

In my project, i have use nom:6.2.1 crate.

Extractor for request remote address

Feature Request

Motivation

I'm playing with axum and I noticed that there doesn't seem to be any way to extract the remote address (or even just IP) of the other end of the connection. As far as I can tell there's no way to do this anywhere in Tower; tower's example logging middleware doesn't even log the remote address.

Proposal

Any of the following perhaps:

  1. Extractor that gives you a SocketAddr of the peer
  2. An attribute on the Request with the peer information
  3. Perhaps some way to get at the underlying hyper::server::AddrStream?

Alternatives

Uh, I guess put another HTTP server in front of axum that copies the remote address into a header?

Implement MethodFilter as bitflags

Feature Request

Motivation

MethodFilter is currently somewhat restricted, it only supports singular methods as well as a catch-all variant. It would be nice if it were more flexible.

Note that I do not have a specific use case for this, I just thought it would be neat and feel more natural than an Any enum variant.

Proposal

Implement the type as bitflags. It will grow to be two instead of one bytes, but that's a minute cost.

Alternatives

Have a MethodFilter trait implemented on Method as well as closures. This would support custom HTTP methods as well as more advanced filters, but I'm not sure how useful that is.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.