| 90 |
90 |
|
//! // `tauri.conf.json` and so is not part of this example.
|
| 91 |
91 |
|
//! ```
|
| 92 |
92 |
|
|
| 93 |
|
- |
use std::sync::Arc;
|
|
93 |
+ |
use std::sync::{Arc, OnceLock};
|
| 94 |
94 |
|
|
| 95 |
95 |
|
use quasi_http::Refusal;
|
| 96 |
96 |
|
use quasi_router::{RouteError, Router};
|
| 136 |
136 |
|
/// means a passthrough cannot quietly become a second router.
|
| 137 |
137 |
|
type Passthrough = Box<dyn Fn(&str) -> Option<Served> + Send + Sync + 'static>;
|
| 138 |
138 |
|
|
|
139 |
+ |
/// State a host cannot build until it has already started.
|
|
140 |
+ |
///
|
|
141 |
+ |
/// Tauri is the case this exists for, and it is not an edge one. An app whose
|
|
142 |
+ |
/// state needs a data directory needs an `AppHandle` to resolve it, and the
|
|
143 |
+ |
/// handle does not exist until `Builder::build` runs — which is after every
|
|
144 |
+ |
/// scheme is registered. So the protocol has to be handed to the builder
|
|
145 |
+ |
/// before the thing it routes over can be made.
|
|
146 |
+ |
///
|
|
147 |
+ |
/// [`Protocol::pending`] gives the builder its protocol now and the setup
|
|
148 |
+ |
/// closure this, to fill in once. Requests that arrive before it is filled are
|
|
149 |
+ |
/// answered 503 rather than blocked on: the window is not up yet, so there is
|
|
150 |
+ |
/// nobody to keep waiting.
|
|
151 |
+ |
pub struct Late<S>(Arc<OnceLock<Arc<S>>>);
|
|
152 |
+ |
|
|
153 |
+ |
impl<S> Late<S> {
|
|
154 |
+ |
/// Hand over the state. The first call wins.
|
|
155 |
+ |
///
|
|
156 |
+ |
/// Returns whether this call was the one that set it, so a second caller
|
|
157 |
+ |
/// can say so rather than silently doing nothing.
|
|
158 |
+ |
pub fn set(&self, state: Arc<S>) -> bool {
|
|
159 |
+ |
self.0.set(state).is_ok()
|
|
160 |
+ |
}
|
|
161 |
+ |
|
|
162 |
+ |
/// Whether the state has arrived.
|
|
163 |
+ |
#[must_use]
|
|
164 |
+ |
pub fn is_set(&self) -> bool {
|
|
165 |
+ |
self.0.get().is_some()
|
|
166 |
+ |
}
|
|
167 |
+ |
}
|
|
168 |
+ |
|
|
169 |
+ |
impl<S> Clone for Late<S> {
|
|
170 |
+ |
fn clone(&self) -> Self {
|
|
171 |
+ |
Self(Arc::clone(&self.0))
|
|
172 |
+ |
}
|
|
173 |
+ |
}
|
|
174 |
+ |
|
| 139 |
175 |
|
/// A scheme name, a router, the app's state and a renderer.
|
| 140 |
176 |
|
pub struct Protocol<S, R> {
|
| 141 |
177 |
|
scheme: String,
|
| 142 |
178 |
|
router: Router<S>,
|
| 143 |
|
- |
state: Arc<S>,
|
|
179 |
+ |
state: Arc<OnceLock<Arc<S>>>,
|
| 144 |
180 |
|
render: Arc<R>,
|
| 145 |
181 |
|
body_limit: usize,
|
| 146 |
182 |
|
passthrough: Option<Passthrough>,
|
| 149 |
185 |
|
/// What a request needs, once, behind one `Arc`.
|
| 150 |
186 |
|
struct Context<S, R> {
|
| 151 |
187 |
|
router: Router<S>,
|
| 152 |
|
- |
state: Arc<S>,
|
|
188 |
+ |
state: Arc<OnceLock<Arc<S>>>,
|
| 153 |
189 |
|
render: Arc<R>,
|
| 154 |
190 |
|
body_limit: usize,
|
| 155 |
191 |
|
passthrough: Option<Passthrough>,
|
| 184 |
220 |
|
is_scheme(&scheme),
|
| 185 |
221 |
|
"`{scheme}` is not a legal URL scheme: a letter, then letters, digits, `+`, `-` or `.`"
|
| 186 |
222 |
|
);
|
|
223 |
+ |
let cell = OnceLock::new();
|
|
224 |
+ |
let _ = cell.set(state);
|
| 187 |
225 |
|
Self {
|
| 188 |
226 |
|
scheme,
|
| 189 |
227 |
|
router,
|
| 190 |
|
- |
state,
|
|
228 |
+ |
state: Arc::new(cell),
|
| 191 |
229 |
|
render,
|
| 192 |
230 |
|
body_limit: DEFAULT_BODY_LIMIT,
|
| 193 |
231 |
|
passthrough: None,
|
| 194 |
232 |
|
}
|
| 195 |
233 |
|
}
|
| 196 |
234 |
|
|
|
235 |
+ |
/// The same, for a host whose state does not exist yet.
|
|
236 |
+ |
///
|
|
237 |
+ |
/// Returns the protocol to register and the [`Late`] to fill in once the
|
|
238 |
+ |
/// state can be built. See [`Late`] for why Tauri needs this at all.
|
|
239 |
+ |
///
|
|
240 |
+ |
/// # Panics
|
|
241 |
+ |
///
|
|
242 |
+ |
/// On the same illegal scheme [`Protocol::new`] rejects.
|
|
243 |
+ |
#[must_use]
|
|
244 |
+ |
pub fn pending(scheme: impl Into<String>, router: Router<S>, render: Arc<R>) -> (Self, Late<S>)
|
|
245 |
+ |
where
|
|
246 |
+ |
R: Serves,
|
|
247 |
+ |
{
|
|
248 |
+ |
let scheme = scheme.into();
|
|
249 |
+ |
assert!(
|
|
250 |
+ |
is_scheme(&scheme),
|
|
251 |
+ |
"`{scheme}` is not a legal URL scheme: a letter, then letters, digits, `+`, `-` or `.`"
|
|
252 |
+ |
);
|
|
253 |
+ |
let state = Arc::new(OnceLock::new());
|
|
254 |
+ |
let late = Late(Arc::clone(&state));
|
|
255 |
+ |
(
|
|
256 |
+ |
Self {
|
|
257 |
+ |
scheme,
|
|
258 |
+ |
router,
|
|
259 |
+ |
state,
|
|
260 |
+ |
render,
|
|
261 |
+ |
body_limit: DEFAULT_BODY_LIMIT,
|
|
262 |
+ |
passthrough: None,
|
|
263 |
+ |
},
|
|
264 |
+ |
late,
|
|
265 |
+ |
)
|
|
266 |
+ |
}
|
|
267 |
+ |
|
| 197 |
268 |
|
/// Read at most this many bytes of a form body.
|
| 198 |
269 |
|
#[must_use]
|
| 199 |
270 |
|
pub fn body_limit(mut self, bytes: usize) -> Self {
|
| 284 |
355 |
|
{
|
| 285 |
356 |
|
let path = request.uri().path();
|
| 286 |
357 |
|
|
|
358 |
+ |
// Nothing to route over yet. Only reachable on the deferred path, and only
|
|
359 |
+ |
// in the window between registering the scheme and setup filling it in.
|
|
360 |
+ |
let Some(state) = context.state.get() else {
|
|
361 |
+ |
return http::Response::builder()
|
|
362 |
+ |
.status(503)
|
|
363 |
+ |
.header(http::header::CONTENT_TYPE, "text/plain; charset=utf-8")
|
|
364 |
+ |
.body(b"the app is still starting".to_vec())
|
|
365 |
+ |
.unwrap_or_else(|_| quasi_http::refuse(Refusal::Malformed));
|
|
366 |
+ |
};
|
|
367 |
+ |
|
| 287 |
368 |
|
if let Some(passthrough) = context.passthrough.as_ref()
|
| 288 |
369 |
|
&& let Some(served) = passthrough(path)
|
| 289 |
370 |
|
{
|
| 310 |
391 |
|
// A hosted server gets this from its executor; here it has to be caught.
|
| 311 |
392 |
|
let asked = quasi_http::Asked::new(&incoming);
|
| 312 |
393 |
|
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
| 313 |
|
- |
context.router.handle(&context.state, incoming.into())
|
|
394 |
+ |
context.router.handle(state, incoming.into())
|
| 314 |
395 |
|
}))
|
| 315 |
396 |
|
.unwrap_or_else(|_| Err(RouteError::internal("the request could not be completed")));
|
| 316 |
397 |
|
|