blitz_platform_api/fetch.rs
1//! The in-flight table: what a request id names, and what states it passes
2//! through.
3
4use std::collections::HashMap;
5
6use blitz_traits::platform::{FetchError, FetchResponse};
7
8/// Names one request for as long as its host tracks it.
9///
10/// **Never reused**, for the reason `blitz-wasm` gives for handles and listener
11/// ids: a stale id must be an error rather than a silent hit on whatever took
12/// its place. A guest that releases a request and then reads it gets
13/// [`FetchState::Unknown`], not another request's body.
14///
15/// A `u64` here. A binding that has to return this to a guest through a
16/// negative-is-error `i32` has a narrower range to work with, and capping it is
17/// that binding's business, not this crate's.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct RequestId(pub u64);
20
21/// Where a request has got to.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum FetchState {
24 /// No request by that id. Either it never existed or it was released.
25 Unknown,
26 /// Started, not yet answered.
27 Pending,
28 /// A response arrived and is readable.
29 ///
30 /// **Including a 404.** A response is a response whatever its status; see
31 /// [`FetchHandler::complete`](blitz_traits::platform::FetchHandler::complete).
32 Response,
33 /// No response will arrive.
34 Failed(FetchError),
35}
36
37/// What the table holds per id.
38///
39/// The answer is boxed because a [`FetchResponse`] carries a `Url`, a
40/// `HeaderMap` and a `Bytes`, and without the box every *pending* entry would
41/// reserve room for all three. A page that starts a hundred requests holds a
42/// hundred of these, and the whole point of the table is that pending is the
43/// cheap state.
44pub(crate) enum Slot {
45 Pending,
46 Done(Box<Result<FetchResponse, FetchError>>),
47}
48
49/// Every request one host knows about.
50///
51/// A `HashMap` rather than a `Vec` indexed by id, because ids are never reused:
52/// a vector would keep growing across a long-lived document even as requests
53/// are released.
54#[derive(Default)]
55pub(crate) struct InFlight {
56 pub(crate) slots: HashMap<RequestId, Slot>,
57 pub(crate) ready: Vec<RequestId>,
58 next: u64,
59}
60
61impl InFlight {
62 /// Allocate an id and mark it pending.
63 pub(crate) fn begin(&mut self) -> RequestId {
64 let id = RequestId(self.next);
65 self.next += 1;
66 self.slots.insert(id, Slot::Pending);
67 id
68 }
69}