mirror of https://github.com/tokio-rs/axum
Add tokio feature & make tokio optional for WASM support (#1382)
* add server feature and make tokio and hyper/server and tcp optional * address review comments * don't mention any specific runtimes in the example * sort deps * add `tokio` feature when adding `ws` * don't always pull in tower feature that pulls in tokio io stuff * remove usage of `tokio_cr` * changelog * depend on tokio version that supports wasm * don't make it sound like tokio doesn't support wasm * call out new default feature Co-authored-by: Fisher Darling <fdarlingco@gmail.com> Co-authored-by: David Pedersen <david.pdrsn@gmail.com>
This commit is contained in:
parent
83ba8c3876
commit
31638a2b22
|
@ -209,6 +209,26 @@ jobs:
|
|||
-p axum-macros
|
||||
--target armv5te-unknown-linux-musleabi
|
||||
|
||||
wasm32-unknown-unknown:
|
||||
needs: check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
target: wasm32-unknown-unknown
|
||||
override: true
|
||||
profile: minimal
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: >
|
||||
--manifest-path ./examples/simple-router-wasm/Cargo.toml
|
||||
--target wasm32-unknown-unknown
|
||||
|
||||
dependencies-are-sorted:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
|
|
@ -33,9 +33,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
can be nested or merged into a `Router` with the same state type ([#1368])
|
||||
- **changed:** `Router::nest` now only accepts `Router`s, the general-purpose
|
||||
`Service` nesting method has been renamed to `nest_service` ([#1368])
|
||||
- **added:** Support compiling to WASM. See the `simple-router-wasm` example
|
||||
for more details ([#1382])
|
||||
- **breaking:** New `tokio` default feature needed for WASM support. If you
|
||||
don't need WASM support but have `default_features = false` for other reasons
|
||||
you likely need to re-enable the `tokio` feature ([#1382])
|
||||
|
||||
[#1368]: https://github.com/tokio-rs/axum/pull/1368
|
||||
[#1371]: https://github.com/tokio-rs/axum/pull/1371
|
||||
[#1382]: https://github.com/tokio-rs/axum/pull/1382
|
||||
[#1387]: https://github.com/tokio-rs/axum/pull/1387
|
||||
[#1389]: https://github.com/tokio-rs/axum/pull/1389
|
||||
[#1396]: https://github.com/tokio-rs/axum/pull/1396
|
||||
|
|
|
@ -12,7 +12,7 @@ readme = "README.md"
|
|||
repository = "https://github.com/tokio-rs/axum"
|
||||
|
||||
[features]
|
||||
default = ["form", "http1", "json", "matched-path", "original-uri", "query", "tower-log"]
|
||||
default = ["form", "http1", "json", "matched-path", "original-uri", "query", "tokio", "tower-log"]
|
||||
form = ["dep:serde_urlencoded"]
|
||||
http1 = ["hyper/http1"]
|
||||
http2 = ["hyper/http2"]
|
||||
|
@ -22,8 +22,9 @@ matched-path = []
|
|||
multipart = ["dep:multer"]
|
||||
original-uri = []
|
||||
query = ["dep:serde_urlencoded"]
|
||||
tokio = ["dep:tokio", "hyper/server", "hyper/tcp", "tower/make"]
|
||||
tower-log = ["tower/log"]
|
||||
ws = ["dep:tokio-tungstenite", "dep:sha-1", "dep:base64"]
|
||||
ws = ["tokio", "dep:tokio-tungstenite", "dep:sha-1", "dep:base64"]
|
||||
|
||||
# Required for intra-doc links to resolve correctly
|
||||
__private_docs = ["tower/full", "tower-http/full"]
|
||||
|
@ -36,7 +37,7 @@ bytes = "1.0"
|
|||
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||
http = "0.2.5"
|
||||
http-body = "0.4.4"
|
||||
hyper = { version = "0.14.14", features = ["server", "tcp", "stream"] }
|
||||
hyper = { version = "0.14.14", features = ["stream"] }
|
||||
itoa = "1.0.1"
|
||||
matchit = "0.6"
|
||||
memchr = "2.4.1"
|
||||
|
@ -45,8 +46,7 @@ percent-encoding = "2.1"
|
|||
pin-project-lite = "0.2.7"
|
||||
serde = "1.0"
|
||||
sync_wrapper = "0.1.1"
|
||||
tokio = { version = "1", features = ["time"] }
|
||||
tower = { version = "0.4.13", default-features = false, features = ["util", "make"] }
|
||||
tower = { version = "0.4.13", default-features = false, features = ["util"] }
|
||||
tower-http = { version = "0.3.0", features = ["util", "map-response-body"] }
|
||||
tower-layer = "0.3"
|
||||
tower-service = "0.3"
|
||||
|
@ -60,6 +60,7 @@ serde_json = { version = "1.0", features = ["raw_value"], optional = true }
|
|||
serde_path_to_error = { version = "0.1.8", optional = true }
|
||||
serde_urlencoded = { version = "0.7", optional = true }
|
||||
sha-1 = { version = "0.10", optional = true }
|
||||
tokio = { package = "tokio", version = "1.21", features = ["time"], optional = true }
|
||||
tokio-tungstenite = { version = "0.17.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -70,7 +71,7 @@ quickcheck_macros = "1.0"
|
|||
reqwest = { version = "0.11.11", default-features = false, features = ["json", "stream", "multipart"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.6.1", features = ["macros", "rt", "rt-multi-thread", "net", "test-util"] }
|
||||
tokio = { package = "tokio", version = "1.21", features = ["macros", "rt", "rt-multi-thread", "net", "test-util"] }
|
||||
tokio-stream = "0.1"
|
||||
tracing = "0.1"
|
||||
uuid = { version = "1.0", features = ["serde", "v4"] }
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::{extract::connect_info::IntoMakeServiceWithConnectInfo, routing::IntoMakeService};
|
||||
#[cfg(feature = "tokio")]
|
||||
use crate::extract::connect_info::IntoMakeServiceWithConnectInfo;
|
||||
use crate::routing::IntoMakeService;
|
||||
use tower_service::Service;
|
||||
|
||||
/// Extension trait that adds additional methods to any [`Service`].
|
||||
|
@ -26,6 +28,7 @@ pub trait ServiceExt<R>: Service<R> + Sized {
|
|||
/// ["Rewriting request URI in middleware"]: crate::middleware#rewriting-request-uri-in-middleware
|
||||
/// [`Router`]: crate::Router
|
||||
/// [`ConnectInfo`]: crate::extract::connect_info::ConnectInfo
|
||||
#[cfg(feature = "tokio")]
|
||||
fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C>;
|
||||
}
|
||||
|
||||
|
@ -37,6 +40,7 @@ where
|
|||
IntoMakeService::new(self)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> {
|
||||
IntoMakeServiceWithConnectInfo::new(self)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use http::header::{self, HeaderMap};
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub mod connect_info;
|
||||
pub mod path;
|
||||
pub mod rejection;
|
||||
|
@ -23,7 +24,6 @@ pub use axum_macros::{FromRequest, FromRequestParts};
|
|||
#[doc(inline)]
|
||||
#[allow(deprecated)]
|
||||
pub use self::{
|
||||
connect_info::ConnectInfo,
|
||||
host::Host,
|
||||
path::Path,
|
||||
raw_query::RawQuery,
|
||||
|
@ -31,6 +31,10 @@ pub use self::{
|
|||
state::State,
|
||||
};
|
||||
|
||||
#[doc(inline)]
|
||||
#[cfg(feature = "tokio")]
|
||||
pub use self::connect_info::ConnectInfo;
|
||||
|
||||
#[doc(no_inline)]
|
||||
#[cfg(feature = "json")]
|
||||
pub use crate::Json;
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
//! If you need to read and write concurrently from a [`WebSocket`] you can use
|
||||
//! [`StreamExt::split`]:
|
||||
//!
|
||||
//! ```
|
||||
//! ```rust,no_run
|
||||
//! use axum::{Error, extract::ws::{WebSocket, Message}};
|
||||
//! use futures::{sink::SinkExt, stream::{StreamExt, SplitSink, SplitStream}};
|
||||
//!
|
||||
|
|
|
@ -35,9 +35,11 @@
|
|||
//!
|
||||
#![doc = include_str!("../docs/debugging_handler_type_errors.md")]
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
use crate::extract::connect_info::IntoMakeServiceWithConnectInfo;
|
||||
use crate::{
|
||||
body::Body,
|
||||
extract::{connect_info::IntoMakeServiceWithConnectInfo, FromRequest, FromRequestParts},
|
||||
extract::{FromRequest, FromRequestParts},
|
||||
response::{IntoResponse, Response},
|
||||
routing::IntoMakeService,
|
||||
};
|
||||
|
@ -318,6 +320,7 @@ pub trait HandlerWithoutStateExt<T, B>: Handler<T, (), B> {
|
|||
/// See [`WithState::into_make_service_with_connect_info`] for more details.
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
#[cfg(feature = "tokio")]
|
||||
fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, (), B>, C>;
|
||||
|
@ -335,6 +338,7 @@ where
|
|||
self.with_state(()).into_make_service()
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<IntoService<Self, T, (), B>, C> {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use super::{Handler, IntoService};
|
||||
use crate::{extract::connect_info::IntoMakeServiceWithConnectInfo, routing::IntoMakeService};
|
||||
#[cfg(feature = "tokio")]
|
||||
use crate::extract::connect_info::IntoMakeServiceWithConnectInfo;
|
||||
use crate::routing::IntoMakeService;
|
||||
use http::Request;
|
||||
use std::task::{Context, Poll};
|
||||
use tower_service::Service;
|
||||
|
@ -95,6 +97,7 @@ impl<H, T, S, B> WithState<H, T, S, B> {
|
|||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
|
||||
#[cfg(feature = "tokio")]
|
||||
pub fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<IntoService<H, T, S, B>, C> {
|
||||
|
|
|
@ -350,6 +350,7 @@
|
|||
//! `matched-path` | Enables capturing of every request's router path and the [`MatchedPath`] extractor | Yes
|
||||
//! `multipart` | Enables parsing `multipart/form-data` requests with [`Multipart`] | No
|
||||
//! `original-uri` | Enables capturing of every request's original URI and the [`OriginalUri`] extractor | Yes
|
||||
//! `tokio` | Enables `tokio` as a dependency and `axum::Server`, `SSE` and `extract::connect_info` types. | Yes
|
||||
//! `tower-log` | Enables `tower`'s `log` feature | Yes
|
||||
//! `ws` | Enables WebSockets support via [`extract::ws`] | No
|
||||
//! `form` | Enables the `Form` extractor | Yes
|
||||
|
@ -461,6 +462,7 @@ pub use async_trait::async_trait;
|
|||
pub use headers;
|
||||
#[doc(no_inline)]
|
||||
pub use http;
|
||||
#[cfg(feature = "tokio")]
|
||||
#[doc(no_inline)]
|
||||
pub use hyper::Server;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use http::{header, HeaderValue};
|
|||
|
||||
mod redirect;
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub mod sse;
|
||||
|
||||
#[doc(no_inline)]
|
||||
|
@ -28,7 +29,11 @@ pub use axum_core::response::{
|
|||
};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::{redirect::Redirect, sse::Sse};
|
||||
pub use self::redirect::Redirect;
|
||||
|
||||
#[doc(inline)]
|
||||
#[cfg(feature = "tokio")]
|
||||
pub use sse::Sse;
|
||||
|
||||
/// An HTML response.
|
||||
///
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! Route to services and handlers based on HTTP methods.
|
||||
|
||||
use super::IntoMakeService;
|
||||
#[cfg(feature = "tokio")]
|
||||
use crate::extract::connect_info::IntoMakeServiceWithConnectInfo;
|
||||
use crate::{
|
||||
body::{Body, Bytes, HttpBody},
|
||||
error_handling::{HandleError, HandleErrorLayer},
|
||||
extract::connect_info::IntoMakeServiceWithConnectInfo,
|
||||
handler::{Handler, IntoServiceStateInExtension},
|
||||
http::{Method, Request, StatusCode},
|
||||
response::Response,
|
||||
|
@ -689,6 +690,7 @@ where
|
|||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
/// [`Router::into_make_service_with_connect_info`]: crate::routing::Router::into_make_service_with_connect_info
|
||||
#[cfg(feature = "tokio")]
|
||||
pub fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> {
|
||||
IntoMakeServiceWithConnectInfo::new(self)
|
||||
}
|
||||
|
@ -1186,6 +1188,7 @@ impl<S, B, E> WithState<S, B, E> {
|
|||
/// See [`MethodRouter::into_make_service_with_connect_info`] for more details.
|
||||
///
|
||||
/// [`MakeService`]: tower::make::MakeService
|
||||
#[cfg(feature = "tokio")]
|
||||
pub fn into_make_service_with_connect_info<C>(self) -> IntoMakeServiceWithConnectInfo<Self, C> {
|
||||
IntoMakeServiceWithConnectInfo::new(self)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Routing between [`Service`]s and handlers.
|
||||
|
||||
use self::not_found::NotFound;
|
||||
#[cfg(feature = "tokio")]
|
||||
use crate::extract::connect_info::IntoMakeServiceWithConnectInfo;
|
||||
use crate::{
|
||||
body::{Body, HttpBody},
|
||||
extract::connect_info::IntoMakeServiceWithConnectInfo,
|
||||
handler::{BoxedHandler, Handler},
|
||||
util::try_downcast,
|
||||
Extension,
|
||||
|
@ -547,6 +548,7 @@ where
|
|||
}
|
||||
|
||||
#[doc = include_str!("../docs/routing/into_make_service_with_connect_info.md")]
|
||||
#[cfg(feature = "tokio")]
|
||||
pub fn into_make_service_with_connect_info<C>(
|
||||
self,
|
||||
) -> IntoMakeServiceWithConnectInfo<RouterService<B>, C> {
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "example-simple-router-wasm"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
# `default-features = false` to not depend on tokio features which don't support wasm
|
||||
# you can still pull in tokio manually and only add features that tokio supports for wasm
|
||||
axum = { path = "../../axum", default-features = false }
|
||||
futures-executor = "0.3.21"
|
||||
http = "0.2.7"
|
||||
tower-service = "0.3.1"
|
|
@ -0,0 +1,51 @@
|
|||
//! Run with
|
||||
//!
|
||||
//! ```not_rust
|
||||
//! cd examples && cargo run -p example-simple-router-wasm
|
||||
//! ```
|
||||
//!
|
||||
//! This example shows what using axum in a wasm context might look like. This example should
|
||||
//! always compile with `--target wasm32-unknown-unknown`.
|
||||
//!
|
||||
//! [`mio`](https://docs.rs/mio/latest/mio/index.html), tokio's IO layer, does not support the
|
||||
//! `wasm32-unknown-unknown` target which is why this crate requires `default-features = false`
|
||||
//! for axum.
|
||||
//!
|
||||
//! Most serverless runtimes expect an exported function that takes in a single request and returns
|
||||
//! a single response, much like axum's `Handler` trait. In this example, the handler function is
|
||||
//! `app` with `main` acting as the serverless runtime which originally receives the request and
|
||||
//! calls the app function.
|
||||
//!
|
||||
//! We can use axum's routing, extractors, tower services, and everything else to implement
|
||||
//! our serverless function, even though we are running axum in a wasm context.
|
||||
|
||||
use axum::{
|
||||
response::{Html, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use futures_executor::block_on;
|
||||
use http::Request;
|
||||
use tower_service::Service;
|
||||
|
||||
fn main() {
|
||||
let request: Request<String> = Request::builder()
|
||||
.uri("https://serverless.example/api/")
|
||||
.body("Some Body Data".into())
|
||||
.unwrap();
|
||||
|
||||
let response: Response = block_on(app(request));
|
||||
assert_eq!(200, response.status());
|
||||
}
|
||||
|
||||
#[allow(clippy::let_and_return)]
|
||||
async fn app(request: Request<String>) -> Response {
|
||||
let mut router = Router::new().route("/api/", get(index)).into_service();
|
||||
|
||||
let response = router.call(request).await.unwrap();
|
||||
response
|
||||
}
|
||||
|
||||
async fn index() -> Html<&'static str> {
|
||||
Html("<h1>Hello, World!</h1>")
|
||||
}
|
Loading…
Reference in New Issue