Upgrade to axum 0.7, drop 3rd party adapters
This commit is contained in:
53 files changed
+643
-3260
No files matched your search
-5
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"rust-analyzer.cargo.features": [
|
||||
"warp"
|
||||
]
|
||||
}
|
||||
@@ -31,42 +31,19 @@ dioxus-hot-reload = { workspace = true, optional = true }
|
||||
dioxus-cli-config = { workspace = true }
|
||||
generational-box = { workspace = true }
|
||||
|
||||
# warp
|
||||
warp = { version = "0.3.3", optional = true }
|
||||
|
||||
# axum
|
||||
axum = { version = "0.6.1", optional = true, features = ["ws"] }
|
||||
|
||||
# salvo
|
||||
salvo = { version = "0.63.0", optional = true, features = ["websocket", "affix"] }
|
||||
once_cell = "1.17.1"
|
||||
async-trait = "0.1.71"
|
||||
|
||||
# rocket
|
||||
rocket = { version = "0.5.0", optional = true }
|
||||
rocket_ws = { version = "0.1.0", optional = true }
|
||||
|
||||
# actix is ... complicated?
|
||||
# actix-files = { version = "0.6.2", optional = true }
|
||||
# actix-web = { version = "4.2.1", optional = true }
|
||||
# actix-ws = { version = "0.2.5", optional = true }
|
||||
axum = { workspace = true, optional = true, features = ["ws"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_env_logger = { version = "0.5.0" }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
warp = "0.3.3"
|
||||
axum = { version = "0.6.1", features = ["ws"] }
|
||||
salvo = { version = "0.63.0", features = ["affix", "websocket"] }
|
||||
rocket = "0.5.0"
|
||||
rocket_ws = "0.1.0"
|
||||
tower = "0.4.13"
|
||||
axum = { workspace = true, features = ["ws"] }
|
||||
tower = { workspace = true }
|
||||
dioxus = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["hot-reload"]
|
||||
# actix = ["actix-files", "actix-web", "actix-ws"]
|
||||
hot-reload = ["dioxus-hot-reload"]
|
||||
rocket = ["dep:rocket", "dep:rocket_ws"]
|
||||
|
||||
[[example]]
|
||||
name = "axum"
|
||||
@@ -75,15 +52,3 @@ required-features = ["axum"]
|
||||
[[example]]
|
||||
name = "axum_stress"
|
||||
required-features = ["axum"]
|
||||
|
||||
[[example]]
|
||||
name = "salvo"
|
||||
required-features = ["salvo"]
|
||||
|
||||
[[example]]
|
||||
name = "warp"
|
||||
required-features = ["warp"]
|
||||
|
||||
[[example]]
|
||||
name = "rocket"
|
||||
required-features = ["rocket"]
|
||||
@@ -26,9 +26,6 @@
|
||||
The current backend frameworks supported include:
|
||||
|
||||
- Axum
|
||||
- Warp
|
||||
- Salvo
|
||||
- Rocket
|
||||
|
||||
Dioxus-LiveView exports some primitives to wire up an app into an existing backend framework.
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_liveview::LiveViewPool;
|
||||
use rocket::response::content::RawHtml;
|
||||
use rocket::{Config, Rocket, State};
|
||||
use rocket_ws::{Channel, WebSocket};
|
||||
|
||||
fn app() -> Element {
|
||||
let mut num = use_signal(|| 0);
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
"hello Rocket! {num}"
|
||||
button { onclick: move |_| num += 1, "Increment" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn index_page_with_glue(glue: &str) -> RawHtml<String> {
|
||||
RawHtml(format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>Dioxus LiveView with Rocket</title> </head>
|
||||
<body> <div id="main"></div> </body>
|
||||
{glue}
|
||||
</html>
|
||||
"#,
|
||||
glue = glue
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(config: &Config) -> RawHtml<String> {
|
||||
index_page_with_glue(&dioxus_liveview::interpreter_glue(&format!(
|
||||
"ws://{addr}:{port}/ws",
|
||||
addr = config.address,
|
||||
port = config.port,
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/as-path")]
|
||||
async fn as_path() -> RawHtml<String> {
|
||||
index_page_with_glue(&dioxus_liveview::interpreter_glue("/ws"))
|
||||
}
|
||||
|
||||
#[get("/ws")]
|
||||
fn ws(ws: WebSocket, pool: &State<LiveViewPool>) -> Channel<'static> {
|
||||
let pool = pool.inner().to_owned();
|
||||
|
||||
ws.channel(move |stream| {
|
||||
Box::pin(async move {
|
||||
let _ = pool
|
||||
.launch(dioxus_liveview::rocket_socket(stream), app)
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let view = dioxus_liveview::LiveViewPool::new();
|
||||
|
||||
Rocket::build()
|
||||
.manage(view)
|
||||
.mount("/", routes![index, as_path, ws])
|
||||
.ignite()
|
||||
.await
|
||||
.expect("Failed to ignite rocket")
|
||||
.launch()
|
||||
.await
|
||||
.expect("Failed to launch rocket");
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_liveview::LiveViewPool;
|
||||
use salvo::affix;
|
||||
use salvo::prelude::*;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn app() -> Element {
|
||||
let mut num = use_signal(|| 0);
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
"hello salvo! {num}"
|
||||
button { onclick: move |_| num += 1, "Increment" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
let addr = "127.0.0.1:3030";
|
||||
let acceptor = TcpListener::new(addr).bind().await;
|
||||
|
||||
let view = LiveViewPool::new();
|
||||
|
||||
let router = Router::new()
|
||||
.hoop(affix::inject(Arc::new(view)))
|
||||
.get(index)
|
||||
.push(Router::with_path("ws").get(connect));
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
Server::new(acceptor).serve(router).await;
|
||||
}
|
||||
|
||||
#[handler]
|
||||
fn index(res: &mut Response) {
|
||||
let addr: SocketAddr = ([127, 0, 0, 1], 3030).into();
|
||||
res.render(Text::Html(format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>Dioxus LiveView with Salvo</title> </head>
|
||||
<body> <div id="main"></div> </body>
|
||||
{glue}
|
||||
</html>
|
||||
"#,
|
||||
glue = dioxus_liveview::interpreter_glue(&format!("ws://{addr}/ws"))
|
||||
)));
|
||||
}
|
||||
|
||||
#[handler]
|
||||
async fn connect(
|
||||
req: &mut Request,
|
||||
depot: &mut Depot,
|
||||
res: &mut Response,
|
||||
) -> Result<(), StatusError> {
|
||||
let view = depot.obtain::<Arc<LiveViewPool>>().unwrap().clone();
|
||||
|
||||
WebSocketUpgrade::new()
|
||||
.upgrade(req, res, |ws| async move {
|
||||
_ = view.launch(dioxus_liveview::salvo_socket(ws), app).await;
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_liveview::warp_adapter::warp_socket;
|
||||
use dioxus_liveview::LiveViewPool;
|
||||
use std::net::SocketAddr;
|
||||
use warp::ws::Ws;
|
||||
use warp::Filter;
|
||||
|
||||
fn app() -> Element {
|
||||
let mut num = use_signal(|| 0);
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
"hello warp! {num}"
|
||||
button {
|
||||
onclick: move |_| num += 1,
|
||||
"Increment"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
let addr: SocketAddr = ([127, 0, 0, 1], 3030).into();
|
||||
|
||||
let index = warp::path::end().map(move || {
|
||||
warp::reply::html(format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>Dioxus LiveView with Warp</title> </head>
|
||||
<body> <div id="main"></div> </body>
|
||||
{glue}
|
||||
</html>
|
||||
"#,
|
||||
glue = dioxus_liveview::interpreter_glue(&format!("ws://{addr}/ws/"))
|
||||
))
|
||||
});
|
||||
|
||||
let pool = LiveViewPool::new();
|
||||
|
||||
let ws = warp::path("ws")
|
||||
.and(warp::ws())
|
||||
.and(warp::any().map(move || pool.clone()))
|
||||
.map(move |ws: Ws, pool: LiveViewPool| {
|
||||
ws.on_upgrade(|ws| async move {
|
||||
let _ = pool.launch(warp_socket(ws), app).await;
|
||||
})
|
||||
});
|
||||
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
warp::serve(index.or(ws)).run(addr).await;
|
||||
}
|
||||
@@ -81,10 +81,8 @@ impl LiveviewRouter for Router {
|
||||
}
|
||||
|
||||
async fn start(self, address: impl Into<std::net::SocketAddr>) {
|
||||
if let Err(err) = axum::Server::bind(&address.into())
|
||||
.serve(self.into_make_service())
|
||||
.await
|
||||
{
|
||||
let listener = tokio::net::TcpListener::bind(address.into()).await.unwrap();
|
||||
if let Err(err) = axum::serve(listener, self.into_make_service()).await {
|
||||
eprintln!("Failed to start axum server: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,11 @@ use std::future::Future;
|
||||
|
||||
use dioxus_core::{Element, VirtualDom};
|
||||
|
||||
#[cfg(feature = "warp")]
|
||||
pub mod warp_adapter;
|
||||
#[cfg(feature = "warp")]
|
||||
pub use warp_adapter::*;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
pub mod axum_adapter;
|
||||
#[cfg(feature = "axum")]
|
||||
pub use axum_adapter::*;
|
||||
|
||||
#[cfg(feature = "salvo")]
|
||||
pub mod salvo_adapter;
|
||||
#[cfg(feature = "salvo")]
|
||||
pub use salvo_adapter::*;
|
||||
|
||||
#[cfg(feature = "rocket")]
|
||||
pub mod rocket_adapter;
|
||||
#[cfg(feature = "rocket")]
|
||||
pub use rocket_adapter::*;
|
||||
|
||||
/// A trait for servers that can be used to host a LiveView app.
|
||||
pub trait LiveviewRouter {
|
||||
/// Create a new router.
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
use crate::LiveViewPool;
|
||||
use crate::LiveviewRouter;
|
||||
use crate::{LiveViewError, LiveViewSocket};
|
||||
use rocket::futures::{SinkExt, StreamExt};
|
||||
use rocket::response::content::RawHtml;
|
||||
use rocket::{get, routes, State};
|
||||
use rocket_ws::Channel;
|
||||
use rocket_ws::WebSocket;
|
||||
use rocket_ws::{result::Error, stream::DuplexStream, Message};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Convert a Rocket WebSocket into a `LiveViewSocket`.
|
||||
///
|
||||
/// This is required to launch a LiveView app using the Rocket web framework.
|
||||
pub fn rocket_socket(stream: DuplexStream) -> impl LiveViewSocket {
|
||||
stream
|
||||
.map(transform_rx)
|
||||
.with(transform_tx)
|
||||
.sink_map_err(|_| LiveViewError::SendingFailed)
|
||||
}
|
||||
|
||||
fn transform_rx(message: Result<Message, Error>) -> Result<Vec<u8>, LiveViewError> {
|
||||
message
|
||||
.map_err(|_| LiveViewError::SendingFailed)?
|
||||
.into_text()
|
||||
.map(|s| s.into_bytes())
|
||||
.map_err(|_| LiveViewError::SendingFailed)
|
||||
}
|
||||
|
||||
async fn transform_tx(message: Vec<u8>) -> Result<Message, Error> {
|
||||
Ok(Message::Binary(message))
|
||||
}
|
||||
|
||||
impl LiveviewRouter for rocket::Rocket<rocket::Build> {
|
||||
fn create_default_liveview_router() -> Self {
|
||||
Self::build()
|
||||
}
|
||||
|
||||
fn with_virtual_dom(
|
||||
self,
|
||||
route: &str,
|
||||
app: impl Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
#[get("/")]
|
||||
async fn index(request: &rocket::route::Route) -> RawHtml<String> {
|
||||
let route = request.uri.base();
|
||||
|
||||
let glue = crate::interpreter_glue(&format!("{route}/ws",));
|
||||
|
||||
let title = crate::app_title();
|
||||
|
||||
RawHtml(format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>{title}</title> </head>
|
||||
<body> <div id="main"></div> </body>
|
||||
{glue}
|
||||
</html>
|
||||
"#
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/ws")]
|
||||
fn ws(ws: WebSocket, app: &State<LiveviewApp>) -> Channel<'static> {
|
||||
let app = app.inner();
|
||||
let pool = app.pool.clone();
|
||||
let app = app.app.clone();
|
||||
|
||||
ws.channel(move |stream| {
|
||||
Box::pin(async move {
|
||||
let _ = pool
|
||||
.launch_virtualdom(crate::rocket_socket(stream), move || app())
|
||||
.await;
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
struct LiveviewApp {
|
||||
app: Arc<dyn Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static>,
|
||||
pool: LiveViewPool,
|
||||
}
|
||||
|
||||
let app = Arc::new(app);
|
||||
|
||||
let view = crate::LiveViewPool::new();
|
||||
|
||||
self.manage(LiveviewApp {
|
||||
app: app,
|
||||
pool: view,
|
||||
})
|
||||
.mount(route, routes![index, ws])
|
||||
}
|
||||
|
||||
async fn start(self, address: impl Into<std::net::SocketAddr>) {
|
||||
let address = address.into();
|
||||
|
||||
let figment = self
|
||||
.figment()
|
||||
.clone()
|
||||
.merge((rocket::Config::PORT, address.port()))
|
||||
.merge((rocket::Config::ADDRESS, address.ip()));
|
||||
|
||||
self.configure(figment)
|
||||
.ignite()
|
||||
.await
|
||||
.expect("Failed to ignite rocket")
|
||||
.launch()
|
||||
.await
|
||||
.expect("Failed to launch rocket");
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
use crate::LiveViewPool;
|
||||
use crate::LiveviewRouter;
|
||||
use crate::{LiveViewError, LiveViewSocket};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use salvo::conn::TcpListener;
|
||||
use salvo::http::StatusError;
|
||||
use salvo::websocket::WebSocketUpgrade;
|
||||
use salvo::websocket::{Message, WebSocket};
|
||||
use salvo::writing::Text;
|
||||
use salvo::Listener;
|
||||
use salvo::Server;
|
||||
use salvo::{handler, Depot, Request, Response, Router};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Convert a Salvo WebSocket into a `LiveViewSocket`.
|
||||
///
|
||||
/// This is required to launch a LiveView app using the Salvo web framework.
|
||||
pub fn salvo_socket(ws: WebSocket) -> impl LiveViewSocket {
|
||||
ws.map(transform_rx)
|
||||
.with(transform_tx)
|
||||
.sink_map_err(|_| LiveViewError::SendingFailed)
|
||||
}
|
||||
|
||||
fn transform_rx(message: Result<Message, salvo::Error>) -> Result<Vec<u8>, LiveViewError> {
|
||||
let as_bytes = message.map_err(|_| LiveViewError::SendingFailed)?;
|
||||
|
||||
Ok(as_bytes.into())
|
||||
}
|
||||
|
||||
async fn transform_tx(message: Vec<u8>) -> Result<Message, salvo::Error> {
|
||||
Ok(Message::binary(message))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LiveviewApp {
|
||||
app: Arc<dyn Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static>,
|
||||
pool: Arc<LiveViewPool>,
|
||||
}
|
||||
|
||||
impl LiveviewRouter for Router {
|
||||
fn create_default_liveview_router() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
fn with_virtual_dom(
|
||||
self,
|
||||
route: &str,
|
||||
app: impl Fn() -> dioxus_core::prelude::VirtualDom + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
let app = Arc::new(app);
|
||||
|
||||
let view = crate::LiveViewPool::new();
|
||||
|
||||
self.push(
|
||||
Router::with_path(route)
|
||||
.hoop(salvo::affix::inject(LiveviewApp {
|
||||
app: app,
|
||||
pool: Arc::new(view),
|
||||
}))
|
||||
.get(index)
|
||||
.push(Router::with_path("ws").get(connect)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn start(self, address: impl Into<std::net::SocketAddr>) {
|
||||
let address = address.into();
|
||||
|
||||
let acceptor = TcpListener::new(address).bind().await;
|
||||
Server::new(acceptor).serve(self).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[handler]
|
||||
fn index(req: &mut Request, res: &mut Response) {
|
||||
let base = req.uri().path();
|
||||
let title = crate::app_title();
|
||||
|
||||
res.render(Text::Html(format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>{title}</title> </head>
|
||||
<body> <div id="main"></div> </body>
|
||||
{glue}
|
||||
</html>
|
||||
"#,
|
||||
glue = crate::interpreter_glue(&format!("{base}/ws"))
|
||||
)));
|
||||
}
|
||||
|
||||
#[handler]
|
||||
async fn connect(
|
||||
req: &mut Request,
|
||||
depot: &mut Depot,
|
||||
res: &mut Response,
|
||||
) -> Result<(), StatusError> {
|
||||
let app = depot.obtain::<LiveviewApp>().unwrap().clone();
|
||||
let view = app.pool.clone();
|
||||
let app = app.app.clone();
|
||||
|
||||
WebSocketUpgrade::new()
|
||||
.upgrade(req, res, |ws| async move {
|
||||
_ = view
|
||||
.launch_virtualdom(crate::salvo_socket(ws), move || app())
|
||||
.await;
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
use crate::{LiveViewError, LiveViewSocket};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use warp::ws::{Message, WebSocket};
|
||||
|
||||
/// Convert a warp WebSocket into a `LiveViewSocket`.
|
||||
///
|
||||
/// This is required to launch a LiveView app using the warp web framework.
|
||||
pub fn warp_socket(ws: WebSocket) -> impl LiveViewSocket {
|
||||
ws.map(transform_rx)
|
||||
.with(transform_tx)
|
||||
.sink_map_err(|_| LiveViewError::SendingFailed)
|
||||
}
|
||||
|
||||
fn transform_rx(message: Result<Message, warp::Error>) -> Result<Vec<u8>, LiveViewError> {
|
||||
// Destructure the `message` into the buffer we got from warp.
|
||||
let msg = message
|
||||
.map_err(|_| LiveViewError::SendingFailed)?
|
||||
.into_bytes();
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
async fn transform_tx(message: Vec<u8>) -> Result<Message, warp::Error> {
|
||||
Ok(Message::binary(message))
|
||||
}
|
||||
@@ -3,10 +3,6 @@ use std::any::Any;
|
||||
|
||||
#[cfg(feature = "axum")]
|
||||
pub type Config = crate::Config<axum::Router>;
|
||||
#[cfg(all(feature = "salvo", not(feature = "axum")))]
|
||||
pub type Config = crate::Config<salvo::Router>;
|
||||
#[cfg(all(feature = "rocket", not(any(feature = "axum", feature = "salvo"))))]
|
||||
pub type Config = crate::Config<rocket::Rocket<rocket::Build>>;
|
||||
|
||||
/// Launches the WebView and runs the event loop, with configuration and root props.
|
||||
pub fn launch(
|
||||
|
||||
@@ -25,7 +25,7 @@ impl<T> WebsocketRx for T where T: StreamExt<Item = Result<String, LiveViewError
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum LiveViewError {
|
||||
#[error("warp error")]
|
||||
#[error("Sending to client error")]
|
||||
SendingFailed,
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ pub fn interpreter_glue(url_or_path: &str) -> String {
|
||||
// If the url starts with a `/`, generate glue which reuses current host
|
||||
let get_ws_url = if url_or_path.starts_with('/') {
|
||||
r#"
|
||||
let loc = window.location;
|
||||
let loc = window.location;
|
||||
let new_url = "";
|
||||
if (loc.protocol === "https:") {{
|
||||
new_url = "wss:";
|
||||
@@ -135,7 +135,7 @@ pub fn interpreter_glue(url_or_path: &str) -> String {
|
||||
function __dioxusGetWsUrl(path) {{
|
||||
{get_ws_url}
|
||||
}}
|
||||
|
||||
|
||||
var WS_ADDR = __dioxusGetWsUrl("{url_or_path}");
|
||||
{handle_edits}
|
||||
</script>
|
||||
|
||||
Reference in new issue
Block a user