Skip to main content

blitz_traits/
net.rs

1//! Abstractions of networking so that custom networking implementations can be provided
2
3pub use bytes::Bytes;
4pub use http::{self, HeaderMap, Method};
5use serde::{
6    Serialize,
7    ser::{SerializeSeq, SerializeTuple},
8};
9use std::sync::{
10    Arc,
11    atomic::{AtomicBool, Ordering},
12};
13use std::{ops::Deref, path::PathBuf};
14pub use url::Url;
15
16/// A type that fetches resources for a Document.
17///
18/// This may be over the network via http(s), via the filesystem, or some other method.
19pub trait NetProvider: Send + Sync + 'static {
20    fn fetch(&self, doc_id: usize, request: Request, handler: Box<dyn NetHandler>);
21
22    /// Whether this provider is a no-op (e.g. `DummyNetProvider`) that will never
23    /// deliver resources. When true, callers must NOT register resources as
24    /// "pending critical" — doing so blocks painting forever, since the
25    /// completion callback never fires. Used by integrations that feed a
26    /// pre-rendered DOM and perform no sub-fetches (e.g. aginxbrowser).
27    fn is_noop(&self) -> bool {
28        false
29    }
30}
31
32/// A type that parses raw bytes from a network request into a Data and then calls
33/// the NetCallack with the result.
34pub trait NetHandler: Send + Sync + 'static {
35    fn bytes(self: Box<Self>, resolved_url: String, bytes: Bytes);
36}
37
38/// A callback which gets called every time a network request completes
39// Q: Should we use std::task::Waker for this?
40pub trait NetWaker: Send + Sync + 'static {
41    fn wake(&self, client_id: usize);
42}
43
44impl<F: Fn(usize) + Send + Sync + 'static> NetWaker for F {
45    fn wake(&self, doc_id: usize) {
46        self(doc_id)
47    }
48}
49
50#[non_exhaustive]
51#[derive(Debug, Clone)]
52/// A request type loosely representing <https://fetch.spec.whatwg.org/#requests>
53pub struct Request {
54    pub url: Url,
55    pub method: Method,
56    pub content_type: Option<String>,
57    pub headers: HeaderMap,
58    pub body: Body,
59    pub signal: Option<AbortSignal>,
60}
61impl Request {
62    /// A get request to the specified Url and an empty body
63    pub fn get(url: Url) -> Self {
64        Self {
65            url,
66            method: Method::GET,
67            content_type: None,
68            headers: HeaderMap::new(),
69            body: Body::Empty,
70            signal: None,
71        }
72    }
73
74    pub fn signal(mut self, signal: AbortSignal) -> Self {
75        self.signal = Some(signal);
76        self
77    }
78}
79
80#[derive(Debug, Clone)]
81pub enum Body {
82    Bytes(Bytes),
83    Form(FormData),
84    Empty,
85}
86
87/// A list of form entries used for form submission
88#[derive(Debug, Clone, PartialEq, Default)]
89pub struct FormData(pub Vec<Entry>);
90impl FormData {
91    /// Creates a new empty FormData
92    pub fn new() -> Self {
93        FormData(Vec::new())
94    }
95}
96impl Serialize for FormData {
97    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98    where
99        S: serde::Serializer,
100    {
101        let mut seq_serializer = serializer.serialize_seq(Some(self.len()))?;
102        for entry in &self.0 {
103            seq_serializer.serialize_element(entry)?;
104        }
105        seq_serializer.end()
106    }
107}
108impl Deref for FormData {
109    type Target = Vec<Entry>;
110
111    fn deref(&self) -> &Self::Target {
112        &self.0
113    }
114}
115
116/// A single form entry consisting of a name and value
117#[derive(Debug, Clone, PartialEq)]
118pub struct Entry {
119    pub name: String,
120    pub value: EntryValue,
121}
122impl Serialize for Entry {
123    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
124    where
125        S: serde::Serializer,
126    {
127        let mut serializer = serializer.serialize_tuple(2)?;
128        serializer.serialize_element(&self.name)?;
129        match &self.value {
130            EntryValue::String(s) => serializer.serialize_element(s)?,
131            EntryValue::File(p) => serializer.serialize_element(p.to_str().unwrap_or_default())?,
132            EntryValue::EmptyFile => serializer.serialize_element("")?,
133        }
134        serializer.end()
135    }
136}
137
138#[derive(Debug, Clone, PartialEq)]
139pub enum EntryValue {
140    String(String),
141    File(PathBuf),
142    EmptyFile,
143}
144impl AsRef<str> for EntryValue {
145    fn as_ref(&self) -> &str {
146        match self {
147            EntryValue::String(s) => s,
148            EntryValue::File(p) => p.to_str().unwrap_or_default(),
149            EntryValue::EmptyFile => "",
150        }
151    }
152}
153
154impl From<&str> for EntryValue {
155    fn from(value: &str) -> Self {
156        EntryValue::String(value.to_string())
157    }
158}
159impl From<PathBuf> for EntryValue {
160    fn from(value: PathBuf) -> Self {
161        EntryValue::File(value)
162    }
163}
164
165/// A default noop NetProvider
166#[derive(Default)]
167pub struct DummyNetProvider;
168impl NetProvider for DummyNetProvider {
169    fn fetch(&self, _doc_id: usize, _request: Request, _handler: Box<dyn NetHandler>) {}
170    fn is_noop(&self) -> bool {
171        true
172    }
173}
174
175/// The AbortController interface represents a controller object that
176/// allows you to abort one or more Web requests as and when desired.
177///
178/// <https://developer.mozilla.org/en-US/docs/Web/API/AbortController>
179#[derive(Debug, Default)]
180pub struct AbortController {
181    pub signal: AbortSignal,
182}
183
184impl AbortController {
185    /// The abort() method of the AbortController interface aborts
186    /// an asynchronous operation before it has completed.
187    /// This is able to abort fetch requests.
188    ///
189    /// <https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort>
190    pub fn abort(self) {
191        self.signal.0.store(true, Ordering::SeqCst);
192    }
193}
194
195/// The AbortSignal interface represents a signal object that allows you to
196/// communicate with an asynchronous operation (such as a fetch request) and
197/// abort it if required via an AbortController object.
198///
199/// <https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal>
200#[derive(Debug, Default, Clone)]
201pub struct AbortSignal(Arc<AtomicBool>);
202
203impl AbortSignal {
204    /// The aborted read-only property returns a value that indicates whether
205    /// the asynchronous operations the signal is communicating with are
206    /// aborted (true) or not (false).
207    ///
208    /// <https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/aborted>
209    pub fn aborted(&self) -> bool {
210        self.0.load(Ordering::SeqCst)
211    }
212}