Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamed SSR Response #2697

Merged
merged 59 commits into from Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
199ca5e
yew::platform?
futursolo May 11, 2022
e6fd176
Stream Response.
futursolo May 22, 2022
a652578
Migrate example
futursolo May 22, 2022
bb41baa
Remove old implementation.
futursolo May 22, 2022
5c5f9e0
Remove extra implementation.
futursolo May 22, 2022
fca682d
Prefer String instead of Cow.
futursolo May 22, 2022
9f82906
Fix MSRV.
futursolo May 22, 2022
3eb9f3b
Fix trybuild.
futursolo May 22, 2022
89cce2f
Merge branch 'master' into platform
futursolo May 22, 2022
bcf4182
Optimise Memory Allocation.
futursolo May 22, 2022
ff0ad94
More optimisation.
futursolo May 22, 2022
e5b76a7
BufWriter.
futursolo May 22, 2022
d4b15f6
Fix tests.
futursolo May 22, 2022
39f1878
Optimise BufWriter.
futursolo May 24, 2022
511b1c8
Remove more allocations.
futursolo May 24, 2022
7f2f46a
Merge branch 'master' into platform
futursolo May 24, 2022
13827e9
Allow setting of buffer capacity.
futursolo May 24, 2022
8b159fc
Fix capacity size.
futursolo May 24, 2022
65a4c20
Fix capacity size.
futursolo May 24, 2022
03e10a6
Remove unneeded const notation.
futursolo May 24, 2022
f8f198b
Fix macro tests.
futursolo May 24, 2022
7c5c7bd
Merge branch 'master' into platform
futursolo May 27, 2022
45d218c
Slightly optimises BufWriter committing logic.
futursolo May 27, 2022
12c0e38
Optimise Implementation.
futursolo Jun 12, 2022
49bbd55
Move BufWriter to a separate file.
futursolo Jun 12, 2022
c01fced
Merge branch 'master' into platform
futursolo Jun 12, 2022
c878adb
Additional Implementation Note.
futursolo Jun 12, 2022
6811275
merge branch 'master' into platform
futursolo Jun 20, 2022
52ee6cc
Merge branch 'master' into platform
futursolo Jun 23, 2022
8694cc9
Merge branch 'master' into platform
futursolo Jun 24, 2022
b784c60
Merge branch 'master' into platform
futursolo Jun 24, 2022
88085ac
Adjust API so it matches `std::channel::mpsc::channel`.
futursolo Jun 24, 2022
08e3bad
Fix feature soundness.
futursolo Jun 24, 2022
05d97da
Make a compatibility layer on channels.
futursolo Jun 25, 2022
6904da4
Fix clippy.
futursolo Jun 25, 2022
848f8f7
Fix feature soundness.
futursolo Jun 25, 2022
64d2ee4
Fix CI.
futursolo Jun 25, 2022
47caa52
Inlining.
futursolo Jun 25, 2022
f277d41
Add documentation.
futursolo Jun 25, 2022
704f2a8
Punctuation.
futursolo Jun 25, 2022
dacdce8
Switch to tokio channel.
futursolo Jun 25, 2022
901cffd
Remvoe pin-project.
futursolo Jun 25, 2022
4511ffd
Fix feature soundness.
futursolo Jun 25, 2022
318f1ef
Typo.
futursolo Jun 25, 2022
d656fc7
Move io to platform.
futursolo Jun 25, 2022
6b4f31f
Tokio does not compile.
futursolo Jun 25, 2022
8cc2a57
Fix workflow.
futursolo Jun 25, 2022
4e03969
Restore wrongly removed docs.
futursolo Jun 25, 2022
5f90233
Does tokio work?
futursolo Jun 25, 2022
a6a3423
Switch back to tokio.
futursolo Jun 25, 2022
ecbcaca
Remove pin-project.
futursolo Jun 25, 2022
01494ce
Use cargo resolver 2.
futursolo Jun 25, 2022
466bed4
Add panic notice.
futursolo Jun 26, 2022
4855da4
Update documentation.
futursolo Jun 26, 2022
e5cd82c
Merge branch 'master' into platform
futursolo Jun 28, 2022
0d477f4
Properties does not have to be send.
futursolo Jun 28, 2022
9c36639
Fix capacity checking as pointed in the review.
futursolo Jun 29, 2022
ba23671
Implementation order.
futursolo Jun 29, 2022
8462ec4
Update note.
futursolo Jun 29, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 6 additions & 16 deletions .github/workflows/main-checks.yml
Expand Up @@ -25,15 +25,10 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --features "csr,ssr,hydration" -- -D warnings
args: --all-targets --features "csr,ssr,hydration,tokio" -- -D warnings

- name: Lint feature soundness
run: |
cargo clippy -- --deny=warnings
cargo clippy --features=ssr -- --deny=warnings
cargo clippy --features=csr -- --deny=warnings
cargo clippy --features=hydration -- --deny=warnings
cargo clippy --features "csr,ssr,hydration,tokio" --all-targets -- --deny=warnings
run: bash ../../ci/feature-soundness.sh
working-directory: packages/yew


Expand All @@ -55,15 +50,10 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets --features "csr,ssr,hydration" --release -- -D warnings
args: --all-targets --features "csr,ssr,hydration,tokio" --release -- -D warnings

- name: Lint feature soundness
run: |
cargo clippy --release -- --deny=warnings
cargo clippy --release --features=ssr -- --deny=warnings
cargo clippy --release --features=csr -- --deny=warnings
cargo clippy --release --features=hydration -- --deny=warnings
cargo clippy --release --features "csr,ssr,hydration,tokio" --all-targets -- --deny=warnings
run: bash ../../ci/feature-soundness-release.sh
working-directory: packages/yew

spell_check:
Expand Down Expand Up @@ -129,7 +119,7 @@ jobs:
matrix:
toolchain:
# anyway to dynamically grep the MSRV from Cargo.toml?
- 1.56.1 # MSRV
- 1.60.0 # MSRV
- stable

steps:
Expand Down Expand Up @@ -180,7 +170,7 @@ jobs:
matrix:
toolchain:
# anyway to dynamically grep the MSRV from Cargo.toml?
- 1.56.1 # MSRV
- 1.60.0 # MSRV
- stable
- nightly

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -45,3 +45,4 @@ members = [
"tools/process-benchmark-results",
"tools/website-test",
]
resolver = "2"
32 changes: 32 additions & 0 deletions ci/feature-soundness-release.sh
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -xe

# You can extract the feature list with the following command:
# cargo hack check --feature-powerset --exclude-features nightly

# You need to run this script in packages/yew

cargo clippy --release --no-default-features -- --deny=warnings
cargo clippy --release --no-default-features --features csr -- --deny=warnings
cargo clippy --release --no-default-features --features default -- --deny=warnings
cargo clippy --release --no-default-features --features csr,default -- --deny=warnings
cargo clippy --release --no-default-features --features hydration -- --deny=warnings
cargo clippy --release --no-default-features --features default,hydration -- --deny=warnings
cargo clippy --release --no-default-features --features ssr -- --deny=warnings
cargo clippy --release --no-default-features --features csr,ssr -- --deny=warnings
cargo clippy --release --no-default-features --features default,ssr -- --deny=warnings
cargo clippy --release --no-default-features --features csr,default,ssr -- --deny=warnings
cargo clippy --release --no-default-features --features hydration,ssr -- --deny=warnings
cargo clippy --release --no-default-features --features default,hydration,ssr -- --deny=warnings
cargo clippy --release --no-default-features --features tokio -- --deny=warnings
cargo clippy --release --no-default-features --features csr,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features default,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features csr,default,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features hydration,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features default,hydration,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features ssr,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features csr,ssr,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features default,ssr,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features csr,default,ssr,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features hydration,ssr,tokio -- --deny=warnings
cargo clippy --release --no-default-features --features default,hydration,ssr,tokio -- --deny=warnings
32 changes: 32 additions & 0 deletions ci/feature-soundness.sh
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -xe

# You can extract the feature list with the following command:
# cargo hack check --feature-powerset --exclude-features nightly

# You need to run this script in packages/yew

cargo clippy --no-default-features -- --deny=warnings
cargo clippy --no-default-features --features csr -- --deny=warnings
cargo clippy --no-default-features --features default -- --deny=warnings
cargo clippy --no-default-features --features csr,default -- --deny=warnings
cargo clippy --no-default-features --features hydration -- --deny=warnings
cargo clippy --no-default-features --features default,hydration -- --deny=warnings
cargo clippy --no-default-features --features ssr -- --deny=warnings
cargo clippy --no-default-features --features csr,ssr -- --deny=warnings
cargo clippy --no-default-features --features default,ssr -- --deny=warnings
cargo clippy --no-default-features --features csr,default,ssr -- --deny=warnings
cargo clippy --no-default-features --features hydration,ssr -- --deny=warnings
cargo clippy --no-default-features --features default,hydration,ssr -- --deny=warnings
cargo clippy --no-default-features --features tokio -- --deny=warnings
cargo clippy --no-default-features --features csr,tokio -- --deny=warnings
cargo clippy --no-default-features --features default,tokio -- --deny=warnings
cargo clippy --no-default-features --features csr,default,tokio -- --deny=warnings
cargo clippy --no-default-features --features hydration,tokio -- --deny=warnings
cargo clippy --no-default-features --features default,hydration,tokio -- --deny=warnings
cargo clippy --no-default-features --features ssr,tokio -- --deny=warnings
cargo clippy --no-default-features --features csr,ssr,tokio -- --deny=warnings
cargo clippy --no-default-features --features default,ssr,tokio -- --deny=warnings
cargo clippy --no-default-features --features csr,default,ssr,tokio -- --deny=warnings
cargo clippy --no-default-features --features hydration,ssr,tokio -- --deny=warnings
cargo clippy --no-default-features --features default,hydration,ssr,tokio -- --deny=warnings
1 change: 0 additions & 1 deletion examples/function_router/src/app.rs
@@ -1,7 +1,6 @@
use std::collections::HashMap;

use yew::prelude::*;
use yew::virtual_dom::AttrValue;
use yew_router::history::{AnyHistory, History, MemoryHistory};
use yew_router::prelude::*;

Expand Down
5 changes: 2 additions & 3 deletions examples/simple_ssr/Cargo.toml
Expand Up @@ -10,6 +10,8 @@ yew = { path = "../../packages/yew" }
reqwest = { version = "0.11.8", features = ["json"] }
serde = { version = "1.0.132", features = ["derive"] }
uuid = { version = "1.0.0", features = ["serde"] }
futures = "0.3"
bytes = "1.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = "0.4"
Expand All @@ -19,9 +21,6 @@ log = "0.4"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.15.0", features = ["full"] }
warp = "0.3"
num_cpus = "1.13"
tokio-util = { version = "0.7", features = ["rt"] }
once_cell = "1.5"
clap = { version = "3.1.7", features = ["derive"] }

[features]
Expand Down
43 changes: 24 additions & 19 deletions examples/simple_ssr/src/bin/simple_ssr_server.rs
@@ -1,13 +1,13 @@
use std::error::Error;
use std::path::PathBuf;

use bytes::Bytes;
use clap::Parser;
use once_cell::sync::Lazy;
use futures::stream::{self, Stream, StreamExt};
use simple_ssr::App;
use tokio_util::task::LocalPoolHandle;
use warp::Filter;

// We spawn a local pool that is as big as the number of cpu threads.
static LOCAL_POOL: Lazy<LocalPoolHandle> = Lazy::new(|| LocalPoolHandle::new(num_cpus::get()));
type BoxedError = Box<dyn Error + Send + Sync + 'static>;

/// A basic example
#[derive(Parser, Debug)]
Expand All @@ -17,19 +17,18 @@ struct Opt {
dir: PathBuf,
}

async fn render(index_html_s: &str) -> String {
let content = LOCAL_POOL
.spawn_pinned(move || async move {
let renderer = yew::ServerRenderer::<App>::new();

renderer.render().await
})
.await
.expect("the task has failed.");

// Good enough for an example, but developers should avoid the replace and extra allocation
// here in an actual app.
index_html_s.replace("<body>", &format!("<body>{}", content))
async fn render(
index_html_before: String,
index_html_after: String,
) -> Box<dyn Stream<Item = Result<Bytes, BoxedError>> + Send> {
let renderer = yew::ServerRenderer::<App>::new();

Box::new(
stream::once(async move { index_html_before })
.chain(renderer.render_stream().await)
.chain(stream::once(async move { index_html_after }))
.map(|m| Result::<_, BoxedError>::Ok(m.into())),
)
}

#[tokio::main]
Expand All @@ -40,10 +39,16 @@ async fn main() {
.await
.expect("failed to read index.html");

let (index_html_before, index_html_after) = index_html_s.split_once("<body>").unwrap();
let mut index_html_before = index_html_before.to_owned();
index_html_before.push_str("<body>");
let index_html_after = index_html_after.to_owned();

let html = warp::path::end().then(move || {
let index_html_s = index_html_s.clone();
let index_html_before = index_html_before.clone();
let index_html_after = index_html_after.clone();

async move { warp::reply::html(render(&index_html_s).await) }
async move { warp::reply::html(render(index_html_before, index_html_after).await) }
});

let routes = html.or(warp::fs::dir(opts.dir));
Expand Down
4 changes: 1 addition & 3 deletions examples/ssr_router/Cargo.toml
Expand Up @@ -9,6 +9,7 @@ edition = "2021"
yew = { path = "../../packages/yew" }
function_router = { path = "../function_router" }
log = "0.4"
futures = "0.3"

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = "0.4"
Expand All @@ -20,9 +21,6 @@ axum = "0.5"
tower = { version = "0.4", features = ["make"] }
tower-http = { version = "0.3", features = ["fs"] }
env_logger = "0.9"
num_cpus = "1.13"
tokio-util = { version = "0.7", features = ["rt"] }
once_cell = "1.5"
clap = { version = "3.1.7", features = ["derive"] }

[features]
Expand Down
52 changes: 26 additions & 26 deletions examples/ssr_router/src/bin/ssr_router_server.rs
@@ -1,24 +1,21 @@
use std::collections::HashMap;
use std::convert::Infallible;
use std::path::PathBuf;

use axum::body::Body;
use axum::body::{Body, StreamBody};
use axum::error_handling::HandleError;
use axum::extract::Query;
use axum::handler::Handler;
use axum::http::{Request, StatusCode};
use axum::response::Html;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::{Extension, Router};
use clap::Parser;
use function_router::{ServerApp, ServerAppProps};
use once_cell::sync::Lazy;
use tokio_util::task::LocalPoolHandle;
use futures::stream::{self, StreamExt};
use tower::ServiceExt;
use tower_http::services::ServeDir;

// We spawn a local pool that is as big as the number of cpu threads.
static LOCAL_POOL: Lazy<LocalPoolHandle> = Lazy::new(|| LocalPoolHandle::new(num_cpus::get()));

/// A basic example
#[derive(Parser, Debug)]
struct Opt {
Expand All @@ -28,29 +25,23 @@ struct Opt {
}

async fn render(
Extension(index_html_s): Extension<String>,
Extension((index_html_before, index_html_after)): Extension<(String, String)>,
url: Request<Body>,
Query(queries): Query<HashMap<String, String>>,
) -> Html<String> {
) -> impl IntoResponse {
let url = url.uri().to_string();

let content = LOCAL_POOL
.spawn_pinned(move || async move {
let server_app_props = ServerAppProps {
url: url.into(),
queries,
};

let renderer = yew::ServerRenderer::<ServerApp>::with_props(server_app_props);

renderer.render().await
})
.await
.expect("the task has failed.");
let renderer = yew::ServerRenderer::<ServerApp>::with_props(move || ServerAppProps {
url: url.into(),
queries,
});

// Good enough for an example, but developers should avoid the replace and extra allocation
// here in an actual app.
Html(index_html_s.replace("<body>", &format!("<body>{}", content)))
StreamBody::new(
stream::once(async move { index_html_before })
.chain(renderer.render_stream().await)
.chain(stream::once(async move { index_html_after }))
.map(Result::<_, Infallible>::Ok),
)
}

#[tokio::main]
Expand All @@ -63,6 +54,12 @@ async fn main() {
.await
.expect("failed to read index.html");

let (index_html_before, index_html_after) = index_html_s.split_once("<body>").unwrap();
let mut index_html_before = index_html_before.to_owned();
index_html_before.push_str("<body>");

let index_html_after = index_html_after.to_owned();

let handle_error = |e| async move {
(
StatusCode::INTERNAL_SERVER_ERROR,
Expand All @@ -77,7 +74,10 @@ async fn main() {
.append_index_html_on_directories(false)
.fallback(
render
.layer(Extension(index_html_s))
.layer(Extension((
index_html_before.clone(),
index_html_after.clone(),
)))
.into_service()
.map_err(|err| -> std::io::Error { match err {} }),
),
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-macro/Cargo.toml
Expand Up @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0"
keywords = ["web", "wasm", "frontend", "webasm", "webassembly"]
categories = ["gui", "web-programming", "wasm"]
description = "A framework for making client-side single-page apps"
rust-version = "1.56.1"
rust-version = "1.60.0"

[lib]
proc-macro = true
Expand Down
2 changes: 1 addition & 1 deletion packages/yew-macro/Makefile.toml
@@ -1,6 +1,6 @@
[tasks.test]
clear = true
toolchain = "1.56.1"
toolchain = "1.60.0"
command = "cargo"
# test target can be optionally specified like `cargo make test html_macro`,
args = ["test", "${@}"]
Expand Down