topcoat_runtime/layer.rs
1mod rerun;
2#[cfg(not(target_family = "wasm"))]
3mod socket;
4
5pub use rerun::*;
6use topcoat_core::context::Cx;
7use topcoat_router::{Body, Layer, LayerFuture, Next, Path};
8
9/// The WebSocket subprotocol for runtime connections.
10///
11/// Request this subprotocol at a page's or shard's URL to open a connection
12/// through [`RuntimeLayer`]. The browser can then request renders and
13/// receive the content as frames. WebSocket connections require a native
14/// server.
15pub const RUNTIME_PROTOCOL: &str = "topcoat-runtime";
16
17/// A [`Layer`] that handles runtime requests at each page's URL.
18///
19/// The browser can request a render in two ways:
20///
21/// - Send a `POST` with `X-Topcoat-Runtime: true` and the document's signal values as JSON. The
22/// layer rewrites this to a `GET` at the same path and query. The page, layouts, and guards run
23/// with the supplied signal values. The rewritten request has an empty body and no
24/// [`RUNTIME_HEADER`], `Content-Type`, or `Content-Length` headers. To read the original method,
25/// use [`original_method`](topcoat_router::request::original_method).
26/// - Open a WebSocket with the [`RUNTIME_PROTOCOL`] subprotocol. Each render request on this
27/// connection runs the page as a `GET`, using headers from the handshake and the signal values
28/// sent by the browser. A render request naming a shard identity instead runs the shard endpoint
29/// at that URL with the arguments and signal values it carries.
30///
31/// WebSocket connections are supported on native servers. HTTP page reruns
32/// are also available on WebAssembly.
33///
34/// Other requests pass through unchanged, including form submissions and
35/// WebSocket requests that do not use the runtime subprotocol.
36///
37/// Register this layer with
38/// [`RouterBuilderRuntimeExt::runtime`](crate::RouterBuilderRuntimeExt::runtime).
39/// Call it after adding your application's pathless layers so they receive
40/// the rewritten `GET`.
41#[derive(Debug, Clone, Copy, Default)]
42pub struct RuntimeLayer;
43
44impl Layer for RuntimeLayer {
45 fn path(&self) -> Option<&Path> {
46 None
47 }
48
49 fn handle<'a>(&'a self, cx: &'a Cx, body: Body, next: Next<'a>) -> LayerFuture<'a> {
50 #[cfg(not(target_family = "wasm"))]
51 if socket::requested(cx) {
52 return Box::pin(socket::accept(cx, body));
53 }
54 if rerun::requested(cx) {
55 return Box::pin(rerun::dispatch(cx, body));
56 }
57 next.run(cx, body)
58 }
59}