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
9use crate::RuntimeSetup;
10
11/// The WebSocket subprotocol for runtime connections.
12///
13/// Request this subprotocol at a page's URL to open a connection through
14/// [`RuntimeLayer`]. The browser can then request renders and receive the
15/// content as frames. WebSocket connections require a native server.
16pub const RUNTIME_PROTOCOL: &str = "topcoat-runtime";
17
18/// A [`Layer`] that handles runtime requests at each page's URL.
19///
20/// The browser can request a render in two ways:
21///
22/// - Send a `POST` with `X-Topcoat-Runtime: true` and the document's signal values as JSON. The
23/// layer rewrites this to a `GET` at the same path and query. The page, layouts, and guards run
24/// with the supplied signal values. The rewritten request has an empty body and no
25/// [`RUNTIME_HEADER`], `Content-Type`, or `Content-Length` headers. To read the original method,
26/// use [`original_method`](topcoat_router::request::original_method).
27/// - Open a WebSocket with the [`RUNTIME_PROTOCOL`] subprotocol. Each render request on this
28/// connection describes an HTTP request: a method, a path, a body, and a few runtime headers,
29/// such as [`RUNTIME_HEADER`] for a page rerun. The layer dispatches it with the handshake's
30/// other headers as a connected render. Renders run side by side, and each message sent back
31/// names the render it belongs to. A connection may have at most
32/// [`max_runs_per_connection`](Self::max_runs_per_connection) renders at once.
33///
34/// WebSocket connections are supported on native servers. HTTP page reruns
35/// are also available on WebAssembly.
36///
37/// Other requests pass through unchanged, including form submissions and
38/// WebSocket requests that do not use the runtime subprotocol. The layer adds
39/// [`RuntimeSetup`] to their context.
40///
41/// Add it after your application's pathless layers so they receive the
42/// rewritten `GET`.
43///
44/// ```rust
45/// use topcoat_router::Router;
46/// use topcoat_runtime::RuntimeLayer;
47///
48/// let router = Router::builder().layer(RuntimeLayer::new()).build();
49/// ```
50#[derive(Debug, Clone, Copy)]
51pub struct RuntimeLayer {
52 // Read only by the WebSocket handler, which native servers compile.
53 #[cfg_attr(target_family = "wasm", allow(dead_code))]
54 max_runs_per_connection: usize,
55}
56
57impl RuntimeLayer {
58 /// Creates a runtime layer that allows 64 renders at once on each
59 /// connection.
60 #[must_use]
61 pub const fn new() -> Self {
62 Self {
63 max_runs_per_connection: 64,
64 }
65 }
66
67 /// Limits the number of renders running at once on a runtime connection.
68 /// The default limit is 64.
69 ///
70 /// Each connected page or shard has a render, unless it is inside another
71 /// connected page or shard. At the limit, new render requests receive
72 /// `429 Too Many Requests`. They can be accepted again once a render
73 /// finishes or stops.
74 ///
75 /// ```rust
76 /// use topcoat_router::Router;
77 /// use topcoat_runtime::RuntimeLayer;
78 ///
79 /// let router = Router::builder()
80 /// .layer(RuntimeLayer::new().max_runs_per_connection(128))
81 /// .build();
82 /// ```
83 #[must_use]
84 pub const fn max_runs_per_connection(mut self, max: usize) -> Self {
85 self.max_runs_per_connection = max;
86 self
87 }
88}
89
90impl Default for RuntimeLayer {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl Layer for RuntimeLayer {
97 fn path(&self) -> Option<&Path> {
98 None
99 }
100
101 fn handle<'a>(&'a self, cx: &'a Cx, body: Body, next: Next<'a>) -> LayerFuture<'a> {
102 #[cfg(not(target_family = "wasm"))]
103 if socket::requested(cx) {
104 return Box::pin(socket::accept(cx, body, self.max_runs_per_connection));
105 }
106 if rerun::requested(cx) {
107 return Box::pin(rerun::dispatch(cx, body));
108 }
109 let cx = cx.with(RuntimeSetup);
110 Box::pin(async move { next.run(&cx, body).await })
111 }
112}