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, HeaderValue, 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 cookie jar used by network providers to supply request cookies and store
17/// cookies received in response headers.
18pub trait CookieJar: Send + Sync + 'static {
19    /// Returns the `Cookie` header to send with a request to `url`, if present.
20    fn cookies_header(&self, url: &Url) -> Option<HeaderValue>;
21
22    /// Stores cookies from the response headers received from `url`.
23    fn store_from_response(&self, url: &Url, headers: &HeaderMap);
24}
25
26/// A type that fetches resources for a Document.
27///
28/// This may be over the network via http(s), via the filesystem, or some other method.
29pub trait NetProvider: Send + Sync + 'static {
30    fn fetch(&self, doc_id: usize, request: Request, handler: Box<dyn NetHandler>);
31
32    /// Whether this provider is a no-op (e.g. `DummyNetProvider`) that will never
33    /// deliver resources. When true, callers must NOT register resources as
34    /// "pending critical" — doing so blocks painting forever, since the
35    /// completion callback never fires. Used by integrations that feed a
36    /// pre-rendered DOM and perform no sub-fetches (e.g. aginxbrowser).
37    fn is_noop(&self) -> bool {
38        false
39    }
40}
41
42/// A type that parses raw bytes from a network request into a Data and then calls
43/// the NetCallack with the result.
44pub trait NetHandler: Send + Sync + 'static {
45    fn bytes(self: Box<Self>, resolved_url: String, bytes: Bytes);
46}
47
48/// A callback which gets called every time a network request completes
49// Q: Should we use std::task::Waker for this?
50pub trait NetWaker: Send + Sync + 'static {
51    fn wake(&self, client_id: usize);
52}
53
54impl<F: Fn(usize) + Send + Sync + 'static> NetWaker for F {
55    fn wake(&self, doc_id: usize) {
56        self(doc_id)
57    }
58}
59
60/// The destination of a request, as a Fetch metadata token.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
62pub enum RequestDestination {
63    #[default]
64    Empty,
65    Document,
66    Iframe,
67    Style,
68    Script,
69    Image,
70    Font,
71    Audio,
72    Video,
73    Track,
74    Manifest,
75}
76
77impl RequestDestination {
78    pub fn as_str(self) -> &'static str {
79        match self {
80            Self::Empty => "empty",
81            Self::Document => "document",
82            Self::Iframe => "iframe",
83            Self::Style => "style",
84            Self::Script => "script",
85            Self::Image => "image",
86            Self::Font => "font",
87            Self::Audio => "audio",
88            Self::Video => "video",
89            Self::Track => "track",
90            Self::Manifest => "manifest",
91        }
92    }
93}
94
95#[non_exhaustive]
96#[derive(Debug, Clone)]
97/// A request type loosely representing <https://fetch.spec.whatwg.org/#requests>
98pub struct Request {
99    pub url: Url,
100    pub method: Method,
101    pub content_type: Option<String>,
102    pub headers: HeaderMap,
103    pub body: Body,
104    pub signal: Option<AbortSignal>,
105    pub destination: RequestDestination,
106}
107impl Request {
108    /// A get request to the specified Url and an empty body
109    pub fn get(url: Url) -> Self {
110        Self {
111            url,
112            method: Method::GET,
113            content_type: None,
114            headers: HeaderMap::new(),
115            body: Body::Empty,
116            signal: None,
117            destination: RequestDestination::default(),
118        }
119    }
120
121    pub fn destination(mut self, d: RequestDestination) -> Self {
122        self.destination = d;
123        self
124    }
125
126    pub fn signal(mut self, signal: AbortSignal) -> Self {
127        self.signal = Some(signal);
128        self
129    }
130}
131
132#[derive(Debug, Clone)]
133pub enum Body {
134    Bytes(Bytes),
135    Form(FormData),
136    Empty,
137}
138
139/// A list of form entries used for form submission
140#[derive(Debug, Clone, PartialEq, Default)]
141pub struct FormData(pub Vec<Entry>);
142impl FormData {
143    /// Creates a new empty FormData
144    pub fn new() -> Self {
145        FormData(Vec::new())
146    }
147}
148impl Serialize for FormData {
149    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
150    where
151        S: serde::Serializer,
152    {
153        let mut seq_serializer = serializer.serialize_seq(Some(self.len()))?;
154        for entry in &self.0 {
155            seq_serializer.serialize_element(entry)?;
156        }
157        seq_serializer.end()
158    }
159}
160impl Deref for FormData {
161    type Target = Vec<Entry>;
162
163    fn deref(&self) -> &Self::Target {
164        &self.0
165    }
166}
167
168/// A single form entry consisting of a name and value
169#[derive(Debug, Clone, PartialEq)]
170pub struct Entry {
171    pub name: String,
172    pub value: EntryValue,
173}
174impl Serialize for Entry {
175    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
176    where
177        S: serde::Serializer,
178    {
179        let mut serializer = serializer.serialize_tuple(2)?;
180        serializer.serialize_element(&self.name)?;
181        match &self.value {
182            EntryValue::String(s) => serializer.serialize_element(s)?,
183            EntryValue::File(p) => serializer.serialize_element(p.to_str().unwrap_or_default())?,
184            EntryValue::EmptyFile => serializer.serialize_element("")?,
185        }
186        serializer.end()
187    }
188}
189
190#[derive(Debug, Clone, PartialEq)]
191pub enum EntryValue {
192    String(String),
193    File(PathBuf),
194    EmptyFile,
195}
196impl AsRef<str> for EntryValue {
197    fn as_ref(&self) -> &str {
198        match self {
199            EntryValue::String(s) => s,
200            EntryValue::File(p) => p.to_str().unwrap_or_default(),
201            EntryValue::EmptyFile => "",
202        }
203    }
204}
205
206impl From<&str> for EntryValue {
207    fn from(value: &str) -> Self {
208        EntryValue::String(value.to_string())
209    }
210}
211impl From<PathBuf> for EntryValue {
212    fn from(value: PathBuf) -> Self {
213        EntryValue::File(value)
214    }
215}
216
217/// A default noop NetProvider
218#[derive(Default)]
219pub struct DummyNetProvider;
220impl NetProvider for DummyNetProvider {
221    fn fetch(&self, _doc_id: usize, _request: Request, _handler: Box<dyn NetHandler>) {}
222    fn is_noop(&self) -> bool {
223        true
224    }
225}
226
227/// The AbortController interface represents a controller object that
228/// allows you to abort one or more Web requests as and when desired via
229/// `AbortController`.
230///
231/// <https://developer.mozilla.org/en-US/docs/Web/API/AbortController>
232#[derive(Debug, Default)]
233pub struct AbortController {
234    pub signal: AbortSignal,
235}
236
237impl AbortController {
238    /// The abort() method of the AbortController interface aborts
239    /// an asynchronous operation before it has completed.
240    /// This is able to abort fetch requests.
241    ///
242    /// <https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort>
243    pub fn abort(self) {
244        self.signal.0.store(true, Ordering::SeqCst);
245    }
246}
247
248/// The AbortSignal interface represents a signal object that allows you to
249/// communicate that an operation should be aborted via `AbortController`.
250///
251/// <https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal>
252#[derive(Debug, Default, Clone)]
253pub struct AbortSignal(Arc<AtomicBool>);
254
255impl AbortSignal {
256    /// The aborted read-only property returns a value that indicates whether
257    /// the asynchronous operations the signal is communicating with has been aborted.
258    pub fn aborted(&self) -> bool {
259        self.0.load(Ordering::SeqCst)
260    }
261}