Update integration with support for axum 0.7 (#2082)

* chore: update to axum 0.7

Removed http, since it's included in axum, and replaced hyper by http-body-util, which is a smaller.

* chore: update samples to work with nre axum

Missing sessions_axum_auth, pending PR merge.

* chore: all dependencies update to axum 0.7

* chore: cargo fmt

* chore: fix doctests

* chore: Fix example that in reality doesn't use axum.

Fixed anyway.

* chore: more examples support for axum 0.7

* Small tweak
This commit is contained in:
Daniel Santana 2023-12-31 00:05:17 +00:00 committed by Greg Johnston
parent 0c4cf5471d
commit cadd217078
32 changed files with 233 additions and 265 deletions

View file

@ -7,22 +7,22 @@ edition = "2021"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
console_log = "1.0.0" console_log = "1.0"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
cfg-if = "1.0.0" cfg-if = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] } leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true } leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta" } leptos_meta = { path = "../../meta" }
leptos_router = { path = "../../router" } leptos_router = { path = "../../router" }
log = "0.4.17" log = "0.4"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
simple_logger = "4.0.0" simple_logger = "4.0"
axum = { version = "0.6.1", optional = true } axum = { version = "0.7", optional = true }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }
tokio = { version = "1.22.0", features = ["full"], optional = true } tokio = { version = "1", features = ["full"], optional = true }
http = { version = "0.2.8" } http = { version = "1.0" }
thiserror = "1.0.38" thiserror = "1.0"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
[features] [features]

View file

@ -2,7 +2,7 @@ use cfg_if::cfg_if;
cfg_if! { if #[cfg(feature = "ssr")] { cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::{Body},
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -28,12 +28,12 @@ cfg_if! { if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"), format!("Something went wrong: {err}"),

View file

@ -61,8 +61,8 @@ async fn main() {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr); log!("listening on http://{}", &addr);
axum::Server::bind(&addr) let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
.serve(app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -11,24 +11,24 @@ codegen-units = 1
lto = true lto = true
[dependencies] [dependencies]
console_log = "1.0.0" console_log = "1.0"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
cfg-if = "1.0.0" cfg-if = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] } leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true } leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] } leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_router = { path = "../../router", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4.17" log = "0.4"
simple_logger = "4.0.0" simple_logger = "4.0"
serde = { version = "1.0.148", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tracing = "0.1" tracing = "0.1"
gloo-net = { version = "0.2.5", features = ["http"] } gloo-net = { version = "0.4", features = ["http"] }
reqwest = { version = "0.11.13", features = ["json"] } reqwest = { version = "0.11", features = ["json"] }
axum = { version = "0.6.1", optional = true } axum = { version = "0.7", optional = true }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }
tokio = { version = "1.22.0", features = ["full"], optional = true } tokio = { version = "1", features = ["full"], optional = true }
http = { version = "0.2.11", optional = true } http = { version = "1.0", optional = true }
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] } web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
wasm-bindgen = "0.2" wasm-bindgen = "0.2"

View file

@ -3,7 +3,7 @@ use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -26,12 +26,12 @@ if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err), format!("Something went wrong: {}", err),

View file

@ -3,13 +3,14 @@ use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
response::IntoResponse,
}; };
use tower::ServiceExt; use tower::ServiceExt;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
pub async fn file_handler(uri: Uri) -> Result<Response<BoxBody>, (StatusCode, String)> { pub async fn file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/pkg").await?; let res = get_static_file(uri.clone(), "/pkg").await?;
if res.status() == StatusCode::NOT_FOUND { if res.status() == StatusCode::NOT_FOUND {
@ -24,7 +25,7 @@ if #[cfg(feature = "ssr")] {
} }
} }
pub async fn get_static_file_handler(uri: Uri) -> Result<Response<BoxBody>, (StatusCode, String)> { pub async fn get_static_file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/static").await?; let res = get_static_file(uri.clone(), "/static").await?;
if res.status() == StatusCode::NOT_FOUND { if res.status() == StatusCode::NOT_FOUND {
@ -34,14 +35,14 @@ if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, base: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, base: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(&uri).body(Body::empty()).unwrap(); let req = Request::builder().uri(&uri).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// When run normally, the root should be the crate root // When run normally, the root should be the crate root
if base == "/static" { if base == "/static" {
match ServeDir::new("./static").oneshot(req).await { match ServeDir::new("./static").oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err), format!("Something went wrong: {}", err),
@ -49,7 +50,7 @@ if #[cfg(feature = "ssr")] {
} }
} else if base == "/pkg" { } else if base == "/pkg" {
match ServeDir::new("./pkg").oneshot(req).await { match ServeDir::new("./pkg").oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err), format!("Something went wrong: {}", err),

View file

@ -32,8 +32,8 @@ if #[cfg(feature = "ssr")] {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
log!("listening on {}", addr); log!("listening on {}", addr);
axum::Server::bind(&addr) let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
.serve(app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -11,9 +11,9 @@ codegen-units = 1
lto = true lto = true
[dependencies] [dependencies]
console_log = "1.0.0" console_log = "1.0"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
cfg-if = "1.0.0" cfg-if = "1.0"
leptos = { path = "../../leptos", features = [ leptos = { path = "../../leptos", features = [
"nightly", "nightly",
"experimental-islands", "experimental-islands",
@ -23,20 +23,20 @@ leptos_axum = { path = "../../integrations/axum", optional = true, features = [
] } ] }
leptos_meta = { path = "../../meta", features = ["nightly"] } leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_router = { path = "../../router", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4.17" log = "0.4"
simple_logger = "4.0.0" simple_logger = "4.0"
serde = { version = "1.0.148", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tracing = "0.1" tracing = "0.1"
gloo-net = { version = "0.2.5", features = ["http"] } gloo-net = { version = "0.4", features = ["http"] }
reqwest = { version = "0.11.13", features = ["json"] } reqwest = { version = "0.11", features = ["json"] }
axum = { version = "0.6.1", optional = true, features = ["http2"] } axum = { version = "0.7", optional = true, features = ["http2"] }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = [ tower-http = { version = "0.5", features = [
"fs", "fs",
"compression-br", "compression-br",
], optional = true } ], optional = true }
tokio = { version = "1.22.0", features = ["full"], optional = true } tokio = { version = "1", features = ["full"], optional = true }
http = { version = "0.2.11", optional = true } http = { version = "1.0", optional = true }
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] } web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"

View file

@ -3,7 +3,7 @@ use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -26,12 +26,12 @@ if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err), format!("Something went wrong: {}", err),

View file

@ -3,13 +3,13 @@ use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
}; };
use tower::ServiceExt; use tower::ServiceExt;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
pub async fn file_handler(uri: Uri) -> Result<Response<BoxBody>, (StatusCode, String)> { pub async fn file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/pkg").await?; let res = get_static_file(uri.clone(), "/pkg").await?;
if res.status() == StatusCode::NOT_FOUND { if res.status() == StatusCode::NOT_FOUND {
@ -24,7 +24,7 @@ if #[cfg(feature = "ssr")] {
} }
} }
pub async fn get_static_file_handler(uri: Uri) -> Result<Response<BoxBody>, (StatusCode, String)> { pub async fn get_static_file_handler(uri: Uri) -> Result<Response<Body>, (StatusCode, String)> {
let res = get_static_file(uri.clone(), "/static").await?; let res = get_static_file(uri.clone(), "/static").await?;
if res.status() == StatusCode::NOT_FOUND { if res.status() == StatusCode::NOT_FOUND {
@ -34,14 +34,14 @@ if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, base: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, base: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(&uri).body(Body::empty()).unwrap(); let req = Request::builder().uri(&uri).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// When run normally, the root should be the crate root // When run normally, the root should be the crate root
if base == "/static" { if base == "/static" {
match ServeDir::new("./static").oneshot(req).await { match ServeDir::new("./static").oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err), format!("Something went wrong: {}", err),
@ -49,7 +49,7 @@ if #[cfg(feature = "ssr")] {
} }
} else if base == "/pkg" { } else if base == "/pkg" {
match ServeDir::new("./pkg").oneshot(req).await { match ServeDir::new("./pkg").oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {}", err), format!("Something went wrong: {}", err),

View file

@ -27,8 +27,8 @@ async fn main() {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
logging::log!("listening on {}", addr); logging::log!("listening on {}", addr);
axum::Server::bind(&addr) let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
.serve(app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -11,22 +11,22 @@ codegen-units = 1
lto = true lto = true
[dependencies] [dependencies]
console_log = "1.0.0" console_log = "1.0"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
cfg-if = "1.0.0" cfg-if = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] } leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true } leptos_axum = { path = "../../integrations/axum", default-features = false, optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] } leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_router = { path = "../../router", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4.17" log = "0.4"
simple_logger = "4.0.0" simple_logger = "4.0"
serde = { version = "1.0.148", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tracing = "0.1" tracing = "0.1"
gloo-net = { version = "0.4.0", features = ["http"] } gloo-net = { version = "0.4", features = ["http"] }
reqwest = { version = "0.11.13", features = ["json"] } reqwest = { version = "0.11", features = ["json"] }
axum = { version = "0.6", default-features = false, optional = true } axum = { version = "0.7", default-features = false, optional = true }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
http = { version = "0.2.11", optional = true } http = { version = "1.0", optional = true }
web-sys = { version = "0.3", features = [ web-sys = { version = "0.3", features = [
"AbortController", "AbortController",
"AbortSignal", "AbortSignal",
@ -34,10 +34,10 @@ web-sys = { version = "0.3", features = [
"Response", "Response",
] } ] }
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
wasm-bindgen-futures = { version = "0.4.37", features = [ wasm-bindgen-futures = { version = "0.4", features = [
"futures-core-03-stream", "futures-core-03-stream",
], optional = true } ], optional = true }
axum-js-fetch = { version = "0.2.1", optional = true } axum-js-fetch = { version = "0.2", optional = true }
lazy_static = "1.4.0" lazy_static = "1.4.0"
[features] [features]

View file

@ -3,7 +3,7 @@ use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{Body, BoxBody}, body::Body,
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -25,7 +25,7 @@ if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root

View file

@ -62,7 +62,7 @@ cfg_if! {
let routes = generate_route_list(App); let routes = generate_route_list(App);
// build our application with a route // build our application with a route
let app: axum::Router<(), axum::body::Body> = Router::new() let app: axum::Router = Router::new()
.leptos_routes(&leptos_options, routes, || view! { <App/> } ) .leptos_routes(&leptos_options, routes, || view! { <App/> } )
.route("/api/*fn_name", post(leptos_axum::handle_server_fns)) .route("/api/*fn_name", post(leptos_axum::handle_server_fns))
.with_state(leptos_options); .with_state(leptos_options);

View file

@ -7,40 +7,41 @@ edition = "2021"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
anyhow = "1.0.66" anyhow = "1.0"
console_log = "1.0.0" console_log = "1.0"
rand = { version = "0.8.5", features = ["min_const_gen"], optional = true } rand = { version = "0.8", features = ["min_const_gen"], optional = true }
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
futures = "0.3.25" futures = "0.3"
cfg-if = "1.0.0" cfg-if = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] } leptos = { path = "../../leptos", features = ["nightly"] }
leptos_meta = { path = "../../meta", features = ["nightly"] } leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true } leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_router = { path = "../../router", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4.17" log = "0.4"
simple_logger = "4.0.0" simple_logger = "4.0"
serde = { version = "1.0.148", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
axum = { version = "0.6.1", optional = true, features=["macros"] } axum = { version = "0.7", optional = true, features=["macros"] }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }
tokio = { version = "1.22.0", features = ["full"], optional = true } tokio = { version = "1", features = ["full"], optional = true }
http = { version = "0.2.11" } http = { version = "1.0" }
sqlx = { version = "0.7.2", features = [ sqlx = { version = "0.7.2", features = [
"runtime-tokio-rustls", "runtime-tokio-rustls",
"sqlite", "sqlite",
], optional = true } ], optional = true }
thiserror = "1.0.38" thiserror = "1.0"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
axum_session_auth = { version = "0.9.0", features = [ axum_session_auth = { version = "0.10", features = [
"sqlite-rustls", "sqlite-rustls",
], optional = true } ], optional = true }
axum_session = { version = "0.9.0", features = [ axum_session = { version = "0.10", features = [
"sqlite-rustls", "sqlite-rustls",
], optional = true } ], optional = true }
bcrypt = { version = "0.14", optional = true } bcrypt = { version = "0.15", optional = true }
async-trait = { version = "0.1.64", optional = true } async-trait = { version = "0.1", optional = true }
[features] [features]
default = ["ssr"]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
ssr = [ ssr = [
"dep:axum", "dep:axum",

View file

@ -3,7 +3,7 @@ use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -29,12 +29,12 @@ if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"), format!("Something went wrong: {err}"),

View file

@ -100,8 +100,8 @@ if #[cfg(feature = "ssr")] {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr); log!("listening on http://{}", &addr);
axum::Server::bind(&addr) let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
.serve(app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -18,9 +18,9 @@ leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4" log = "0.4"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
thiserror = "1" thiserror = "1"
axum = { version = "0.6.1", optional = true } axum = { version = "0.7", optional = true }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }
tokio = { version = "1", features = ["time"], optional = true } tokio = { version = "1", features = ["time"], optional = true }
wasm-bindgen = "0.2" wasm-bindgen = "0.2"

View file

@ -2,7 +2,7 @@ use cfg_if::cfg_if;
cfg_if! { if #[cfg(feature = "ssr")] { cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -28,12 +28,12 @@ cfg_if! { if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"), format!("Something went wrong: {err}"),

View file

@ -27,8 +27,8 @@ async fn main() {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
log!("listening on http://{}", &addr); log!("listening on http://{}", &addr);
axum::Server::bind(&addr) let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
.serve(app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -7,8 +7,8 @@ edition = "2021"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
axum = { version = "0.6.18", optional = true } axum = { version = "0.7", optional = true }
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
console_log = "1" console_log = "1"
cfg-if = "1" cfg-if = "1"
leptos = { path = "../../leptos", features = ["nightly"] } leptos = { path = "../../leptos", features = ["nightly"] }
@ -17,13 +17,13 @@ leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_router = { path = "../../router", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4.17" log = "0.4.17"
simple_logger = "4" simple_logger = "4"
tokio = { version = "1.28.1", optional = true } tokio = { version = "1", optional = true }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }
wasm-bindgen = "0.2.84" wasm-bindgen = "0.2"
thiserror = "1.0.40" thiserror = "1.0"
tracing = { version = "0.1.37", optional = true } tracing = { version = "0.1", optional = true }
http = "0.2.11" http = "1.0"
[features] [features]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"] hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]

View file

@ -2,7 +2,7 @@ use cfg_if::cfg_if;
cfg_if! { if #[cfg(feature = "ssr")] { cfg_if! { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -28,12 +28,12 @@ cfg_if! { if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"), format!("Something went wrong: {err}"),

View file

@ -31,8 +31,8 @@ async fn main() {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
info!("listening on http://{}", &addr); info!("listening on http://{}", &addr);
axum::Server::bind(&addr) let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
.serve(app.into_make_service()) axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -7,27 +7,27 @@ edition = "2021"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
console_log = "1.0.0" console_log = "1.0"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
futures = "0.3.25" futures = "0.3"
cfg-if = "1.0.0" cfg-if = "1.0"
http = "1.0"
leptos = { path = "../../leptos", features = ["nightly"] } leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true } leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] } leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_router = { path = "../../router", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] }
log = "0.4.17" log = "0.4"
simple_logger = "4.0.0" simple_logger = "4.0"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
axum = { version = "0.6.1", optional = true } axum = { version = "0.7", optional = true }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }
tokio = { version = "1.22.0", features = ["full"], optional = true } tokio = { version = "1", features = ["full"], optional = true }
http = { version = "0.2.11" } sqlx = { version = "0.7", features = [
sqlx = { version = "0.6.2", features = [
"runtime-tokio-rustls", "runtime-tokio-rustls",
"sqlite", "sqlite",
], optional = true } ], optional = true }
thiserror = "1.0.38" thiserror = "1.0"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
[features] [features]

View file

@ -3,7 +3,7 @@ use cfg_if::cfg_if;
cfg_if! { cfg_if! {
if #[cfg(feature = "ssr")] { if #[cfg(feature = "ssr")] {
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
extract::State, extract::State,
response::IntoResponse, response::IntoResponse,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
@ -29,12 +29,12 @@ if #[cfg(feature = "ssr")] {
} }
} }
async fn get_static_file(uri: Uri, root: &str) -> Result<Response<BoxBody>, (StatusCode, String)> { async fn get_static_file(uri: Uri, root: &str) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap(); let req = Request::builder().uri(uri.clone()).body(Body::empty()).unwrap();
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"), format!("Something went wrong: {err}"),

View file

@ -10,14 +10,14 @@ cfg_if! {
response::{IntoResponse, Response}, response::{IntoResponse, Response},
Router, Router,
}; };
use axum::body::Body as AxumBody; use axum::body::Body;
use crate::todo::*; use crate::todo::*;
use todo_app_sqlite_axum::*; use todo_app_sqlite_axum::*;
use crate::fallback::file_and_error_handler; use crate::fallback::file_and_error_handler;
use leptos_axum::{generate_route_list, LeptosRoutes}; use leptos_axum::{generate_route_list, LeptosRoutes};
//Define a handler to test extractor with state //Define a handler to test extractor with state
async fn custom_handler(Path(id): Path<String>, State(options): State<LeptosOptions>, req: Request<AxumBody>) -> Response{ async fn custom_handler(Path(id): Path<String>, State(options): State<LeptosOptions>, req: Request<Body>) -> Response{
let handler = leptos_axum::render_app_to_stream_with_context(options, let handler = leptos_axum::render_app_to_stream_with_context(options,
move || { move || {
provide_context(id.clone()); provide_context(id.clone());
@ -60,9 +60,9 @@ cfg_if! {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
logging::log!("listening on http://{}", &addr); logging::log!("listening on http://{}", &addr);
axum::Server::bind(&addr) axum::serve(listener, app.into_make_service())
.serve(app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -7,27 +7,27 @@ edition = "2021"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
console_log = "1.0.0" console_log = "1.0"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1"
futures = "0.3.25" futures = "0.3"
leptos = { path = "../../leptos", features = ["nightly"] } leptos = { path = "../../leptos", features = ["nightly"] }
leptos_axum = { path = "../../integrations/axum", optional = true } leptos_axum = { path = "../../integrations/axum", optional = true }
leptos_meta = { path = "../../meta", features = ["nightly"] } leptos_meta = { path = "../../meta", features = ["nightly"] }
leptos_router = { path = "../../router", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] }
leptos_integration_utils = { path = "../../integrations/utils", optional = true } leptos_integration_utils = { path = "../../integrations/utils", optional = true }
log = "0.4.17" log = "0.4"
simple_logger = "4.0.0" simple_logger = "4.0"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
axum = { version = "0.6.1", optional = true } axum = { version = "0.7", optional = true }
tower = { version = "0.4.13", optional = true } tower = { version = "0.4", optional = true }
tower-http = { version = "0.4", features = ["fs"], optional = true } tower-http = { version = "0.5", features = ["fs"], optional = true }
tokio = { version = "1.22.0", features = ["full"], optional = true } tokio = { version = "1", features = ["full"], optional = true }
http = { version = "0.2.11" } http = { version = "1.0" }
sqlx = { version = "0.6.2", features = [ sqlx = { version = "0.7", features = [
"runtime-tokio-rustls", "runtime-tokio-rustls",
"sqlite", "sqlite",
], optional = true } ], optional = true }
thiserror = "1.0.38" thiserror = "1.0"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
[features] [features]

View file

@ -1,5 +1,5 @@
use axum::{ use axum::{
body::{boxed, Body, BoxBody}, body::Body,
extract::State, extract::State,
http::{Request, Response, StatusCode, Uri}, http::{Request, Response, StatusCode, Uri},
response::{Html, IntoResponse, Response as AxumResponse}, response::{Html, IntoResponse, Response as AxumResponse},
@ -28,7 +28,7 @@ pub async fn file_or_index_handler(
async fn get_static_file( async fn get_static_file(
uri: Uri, uri: Uri,
root: &str, root: &str,
) -> Result<Response<BoxBody>, (StatusCode, String)> { ) -> Result<Response<Body>, (StatusCode, String)> {
let req = Request::builder() let req = Request::builder()
.uri(uri.clone()) .uri(uri.clone())
.body(Body::empty()) .body(Body::empty())
@ -36,7 +36,7 @@ async fn get_static_file(
// `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot` // `ServeDir` implements `tower::Service` so we can call it with `tower::ServiceExt::oneshot`
// This path is relative to the cargo root // This path is relative to the cargo root
match ServeDir::new(root).oneshot(req).await { match ServeDir::new(root).oneshot(req).await {
Ok(res) => Ok(res.map(boxed)), Ok(res) => Ok(res.into_response()),
Err(err) => Err(( Err(err) => Err((
StatusCode::INTERNAL_SERVER_ERROR, StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {err}"), format!("Something went wrong: {err}"),

View file

@ -39,8 +39,10 @@ async fn main() {
// run our app with hyper // run our app with hyper
// `axum::Server` is a re-export of `hyper::Server` // `axum::Server` is a re-export of `hyper::Server`
logging::log!("listening on http://{}", &addr); logging::log!("listening on http://{}", &addr);
axum::Server::bind(&addr) let listener = tokio::net::TcpListener::bind(&addr)
.serve(app.into_make_service()) .await
.expect("couldn't bind to address");
axum::serve(listener, app.into_make_service())
.await .await
.unwrap(); .unwrap();
} }

View file

@ -8,23 +8,26 @@ repository = "https://github.com/leptos-rs/leptos"
description = "Axum integrations for the Leptos web framework." description = "Axum integrations for the Leptos web framework."
[dependencies] [dependencies]
axum = { version = "0.6", default-features = false, features = [ axum = { version = "0.7", default-features = false, features = [
"matched-path", "matched-path",
] } ] }
futures = "0.3" futures = "0.3"
http = "0.2.11" http-body-util = "0.1"
hyper = "0.14.23"
leptos = { workspace = true, features = ["ssr"] } leptos = { workspace = true, features = ["ssr"] }
leptos_meta = { workspace = true, features = ["ssr"] } leptos_meta = { workspace = true, features = ["ssr"] }
leptos_router = { workspace = true, features = ["ssr"] } leptos_router = { workspace = true, features = ["ssr"] }
leptos_integration_utils = { workspace = true } leptos_integration_utils = { workspace = true }
parking_lot = "0.12"
serde_json = "1" serde_json = "1"
tokio = { version = "1", default-features = false } tokio = { version = "1", default-features = false }
parking_lot = "0.12.1" tokio-util = { version = "0.7", features = ["rt"] }
tokio-util = { version = "0.7.7", features = ["rt"] } tracing = "0.1"
tracing = "0.1.37" once_cell = "1.18"
once_cell = "1.17" cfg-if = "1.0"
cfg-if = "1.0.0"
[dev-dependencies]
axum = "0.7"
tokio = { version = "1", features = ["net"] }
[features] [features]
nonce = ["leptos/nonce"] nonce = ["leptos/nonce"]

View file

@ -34,11 +34,15 @@
//! directory in the Leptos repository. //! directory in the Leptos repository.
use axum::{ use axum::{
body::{Body, Bytes, Full, StreamBody}, body::{Body, Bytes},
extract::{FromRef, FromRequestParts, MatchedPath, Path, RawQuery}, extract::{FromRef, FromRequestParts, MatchedPath, Path, RawQuery},
http::{ http::{
header::{HeaderName, HeaderValue}, header::{self, HeaderName, HeaderValue},
HeaderMap, Request, StatusCode, method::Method,
request::Parts,
uri::Uri,
version::Version,
HeaderMap, Request, Response, StatusCode,
}, },
response::IntoResponse, response::IntoResponse,
routing::{delete, get, patch, post, put}, routing::{delete, get, patch, post, put},
@ -47,11 +51,7 @@ use futures::{
channel::mpsc::{Receiver, Sender}, channel::mpsc::{Receiver, Sender},
Future, SinkExt, Stream, StreamExt, Future, SinkExt, Stream, StreamExt,
}; };
use http::{ use http_body_util::BodyExt;
header, method::Method, request::Parts, uri::Uri, version::Version,
Response,
};
use hyper::body;
use leptos::{ use leptos::{
leptos_server::{server_fn_by_path, Payload}, leptos_server::{server_fn_by_path, Payload},
server_fn::Encoding, server_fn::Encoding,
@ -162,7 +162,7 @@ pub async fn generate_request_and_parts(
) -> (Request<Body>, RequestParts) { ) -> (Request<Body>, RequestParts) {
// provide request headers as context in server scope // provide request headers as context in server scope
let (parts, body) = req.into_parts(); let (parts, body) = req.into_parts();
let body = body::to_bytes(body).await.unwrap_or_default(); let body = body.collect().await.unwrap_or_default().to_bytes();
let request_parts = RequestParts { let request_parts = RequestParts {
method: parts.method.clone(), method: parts.method.clone(),
uri: parts.uri.clone(), uri: parts.uri.clone(),
@ -196,9 +196,8 @@ pub async fn generate_request_and_parts(
/// .route("/api/*fn_name", post(leptos_axum::handle_server_fns)); /// .route("/api/*fn_name", post(leptos_axum::handle_server_fns));
/// ///
/// // run our app with hyper /// // run our app with hyper
/// // `axum::Server` is a re-export of `hyper::Server` /// let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
/// axum::Server::bind(&addr) /// axum::serve(listener, app.into_make_service())
/// .serve(app.into_make_service())
/// .await /// .await
/// .unwrap(); /// .unwrap();
/// } /// }
@ -358,21 +357,21 @@ async fn handle_server_fns_inner(
match serialized { match serialized {
Payload::Binary(data) => res Payload::Binary(data) => res
.header("Content-Type", "application/cbor") .header("Content-Type", "application/cbor")
.body(Full::from(data)), .body(Body::from(data)),
Payload::Url(data) => res Payload::Url(data) => res
.header( .header(
"Content-Type", "Content-Type",
"application/x-www-form-urlencoded", "application/x-www-form-urlencoded",
) )
.body(Full::from(data)), .body(Body::from(data)),
Payload::Json(data) => res Payload::Json(data) => res
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(Full::from(data)), .body(Body::from(data)),
} }
} }
Err(e) => Response::builder() Err(e) => Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR) .status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Full::from( .body(Body::from(
serde_json::to_string(&e) serde_json::to_string(&e)
.unwrap_or_else(|_| e.to_string()), .unwrap_or_else(|_| e.to_string()),
)), )),
@ -382,7 +381,7 @@ async fn handle_server_fns_inner(
res res
} else { } else {
Response::builder().status(StatusCode::BAD_REQUEST).body( Response::builder().status(StatusCode::BAD_REQUEST).body(
Full::from(format!( Body::from(format!(
"Could not find a server function at the route \ "Could not find a server function at the route \
{fn_name}. \n\nIt's likely that either {fn_name}. \n\nIt's likely that either
1. The API prefix you specify in the `#[server]` \ 1. The API prefix you specify in the `#[server]` \
@ -442,9 +441,8 @@ pub type PinnedHtmlStream =
/// )); /// ));
/// ///
/// // run our app with hyper /// // run our app with hyper
/// // `axum::Server` is a re-export of `hyper::Server` /// let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
/// axum::Server::bind(&addr) /// axum::serve(listener, app.into_make_service())
/// .serve(app.into_make_service())
/// .await /// .await
/// .unwrap(); /// .unwrap();
/// } /// }
@ -463,13 +461,8 @@ pub fn render_app_to_stream<IV>(
app_fn: impl Fn() -> IV + Clone + Send + 'static, app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -490,13 +483,8 @@ pub fn render_route<IV>(
app_fn: impl Fn() -> IV + Clone + Send + 'static, app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -504,6 +492,7 @@ where
{ {
render_route_with_context(options, paths, || {}, app_fn) render_route_with_context(options, paths, || {}, app_fn)
} }
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries /// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries
/// to route it using [leptos_router], serving an in-order HTML stream of your application. /// to route it using [leptos_router], serving an in-order HTML stream of your application.
/// This stream will pause at each `<Suspense/>` node and wait for it to resolve before /// This stream will pause at each `<Suspense/>` node and wait for it to resolve before
@ -543,9 +532,8 @@ where
/// )); /// ));
/// ///
/// // run our app with hyper /// // run our app with hyper
/// // `axum::Server` is a re-export of `hyper::Server` /// let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
/// axum::Server::bind(&addr) /// axum::serve(listener, app.into_make_service())
/// .serve(app.into_make_service())
/// .await /// .await
/// .unwrap(); /// .unwrap();
/// } /// }
@ -564,13 +552,8 @@ pub fn render_app_to_stream_in_order<IV>(
app_fn: impl Fn() -> IV + Clone + Send + 'static, app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -611,13 +594,8 @@ pub fn render_app_to_stream_with_context<IV>(
app_fn: impl Fn() -> IV + Clone + Send + 'static, app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -644,13 +622,8 @@ pub fn render_route_with_context<IV>(
app_fn: impl Fn() -> IV + Clone + Send + 'static, app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -704,6 +677,7 @@ where
} }
} }
} }
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries /// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries
/// to route it using [leptos_router], serving an HTML stream of your application. /// to route it using [leptos_router], serving an HTML stream of your application.
/// ///
@ -732,13 +706,8 @@ pub fn render_app_to_stream_with_context_and_replace_blocks<IV>(
replace_blocks: bool, replace_blocks: bool,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -791,7 +760,7 @@ where
async fn generate_response( async fn generate_response(
res_options: ResponseOptions, res_options: ResponseOptions,
rx: Receiver<String>, rx: Receiver<String>,
) -> Response<StreamBody<PinnedHtmlStream>> { ) -> Response<Body> {
let mut stream = Box::pin(rx.map(|html| Ok(Bytes::from(html)))); let mut stream = Box::pin(rx.map(|html| Ok(Bytes::from(html))));
// Get the first and second chunks in the stream, which renders the app shell, and thus allows Resources to run // Get the first and second chunks in the stream, which renders the app shell, and thus allows Resources to run
@ -806,9 +775,9 @@ async fn generate_response(
futures::stream::iter([first_chunk.unwrap(), second_chunk.unwrap()]) futures::stream::iter([first_chunk.unwrap(), second_chunk.unwrap()])
.chain(stream); .chain(stream);
let mut res = Response::new(StreamBody::new( let mut res =
Box::pin(complete_stream) as PinnedHtmlStream Body::from_stream(Box::pin(complete_stream) as PinnedHtmlStream)
)); .into_response();
if let Some(status) = res_options.status { if let Some(status) = res_options.status {
*res.status_mut() = status *res.status_mut() = status
@ -819,11 +788,11 @@ async fn generate_response(
let mut res_headers = res_options.headers.clone(); let mut res_headers = res_options.headers.clone();
headers.extend(res_headers.drain()); headers.extend(res_headers.drain());
if !headers.contains_key(http::header::CONTENT_TYPE) { if !headers.contains_key(header::CONTENT_TYPE) {
// Set the Content Type headers on all responses. This makes Firefox show the page source // Set the Content Type headers on all responses. This makes Firefox show the page source
// without complaining // without complaining
headers.insert( headers.insert(
http::header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_str("text/html; charset=utf-8").unwrap(), HeaderValue::from_str("text/html; charset=utf-8").unwrap(),
); );
} }
@ -897,13 +866,8 @@ pub fn render_app_to_stream_in_order_with_context<IV>(
app_fn: impl Fn() -> IV + Clone + Send + 'static, app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -1012,8 +976,9 @@ fn provide_contexts(
/// ///
/// // run our app with hyper /// // run our app with hyper
/// // `axum::Server` is a re-export of `hyper::Server` /// // `axum::Server` is a re-export of `hyper::Server`
/// axum::Server::bind(&addr) /// let listener =
/// .serve(app.into_make_service()) /// tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
/// axum::serve(listener, app.into_make_service())
/// .await /// .await
/// .unwrap(); /// .unwrap();
/// } /// }
@ -1075,13 +1040,8 @@ pub fn render_app_async_stream_with_context<IV>(
app_fn: impl Fn() -> IV + Clone + Send + 'static, app_fn: impl Fn() -> IV + Clone + Send + 'static,
) -> impl Fn( ) -> impl Fn(
Request<Body>, Request<Body>,
) -> Pin< ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send + 'static>>
Box< + Clone
dyn Future<Output = Response<StreamBody<PinnedHtmlStream>>>
+ Send
+ 'static,
>,
> + Clone
+ Send + Send
+ 'static + 'static
where where
@ -1149,10 +1109,10 @@ where
let complete_stream = let complete_stream =
futures::stream::iter([Ok(Bytes::from(html))]); futures::stream::iter([Ok(Bytes::from(html))]);
let mut res = Response::new(StreamBody::new(Box::pin( let mut res = Body::from_stream(
complete_stream, Box::pin(complete_stream) as PinnedHtmlStream
) )
as PinnedHtmlStream)); .into_response();
if let Some(status) = res_options.status { if let Some(status) = res_options.status {
*res.status_mut() = status *res.status_mut() = status
} }
@ -1162,11 +1122,11 @@ where
headers.extend(res_headers.drain()); headers.extend(res_headers.drain());
// This one doesn't use generate_response(), so we need to do this seperately // This one doesn't use generate_response(), so we need to do this seperately
if !headers.contains_key(http::header::CONTENT_TYPE) { if !headers.contains_key(header::CONTENT_TYPE) {
// Set the Content Type headers on all responses. This makes Firefox show the page source // Set the Content Type headers on all responses. This makes Firefox show the page source
// without complaining // without complaining
headers.insert( headers.insert(
http::header::CONTENT_TYPE, header::CONTENT_TYPE,
HeaderValue::from_str("text/html; charset=utf-8") HeaderValue::from_str("text/html; charset=utf-8")
.unwrap(), .unwrap(),
); );
@ -1291,6 +1251,7 @@ where
}) })
} }
} }
/// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically /// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically
/// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element /// create routes in Axum's Router without having to use wildcard matching or fallbacks. Takes in your root app Element
/// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths. /// as an argument so it can walk you app tree. This version is tailored to generate Axum compatible paths.
@ -1464,7 +1425,7 @@ where
handler: H, handler: H,
) -> Self ) -> Self
where where
H: axum::handler::Handler<T, S, axum::body::Body>, H: axum::handler::Handler<T, S>,
T: 'static; T: 'static;
} }
@ -1803,7 +1764,7 @@ where
handler: H, handler: H,
) -> Self ) -> Self
where where
H: axum::handler::Handler<T, S, axum::body::Body>, H: axum::handler::Handler<T, S>,
T: 'static, T: 'static,
{ {
let mut router = self; let mut router = self;