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#[non_exhaustive]
61#[derive(Debug, Clone)]
62/// A request type loosely representing <https://fetch.spec.whatwg.org/#requests>
63pub struct Request {
64    pub url: Url,
65    pub method: Method,
66    pub content_type: Option<String>,
67    pub headers: HeaderMap,
68    pub body: Body,
69    pub signal: Option<AbortSignal>,
70}
71impl Request {
72    /// A get request to the specified Url and an empty body
73    pub fn get(url: Url) -> Self {
74        Self {
75            url,
76            method: Method::GET,
77            content_type: None,
78            headers: HeaderMap::new(),
79            body: Body::Empty,
80            signal: None,
81        }
82    }
83
84    pub fn signal(mut self, signal: AbortSignal) -> Self {
85        self.signal = Some(signal);
86        self
87    }
88}
89
90#[derive(Debug, Clone)]
91pub enum Body {
92    Bytes(Bytes),
93    Form(FormData),
94    Empty,
95}
96
97/// A list of form entries used for form submission
98#[derive(Debug, Clone, PartialEq, Default)]
99pub struct FormData(pub Vec<Entry>);
100impl FormData {
101    /// Creates a new empty FormData
102    pub fn new() -> Self {
103        FormData(Vec::new())
104    }
105}
106impl Serialize for FormData {
107    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: serde::Serializer,
110    {
111        let mut seq_serializer = serializer.serialize_seq(Some(self.len()))?;
112        for entry in &self.0 {
113            seq_serializer.serialize_element(entry)?;
114        }
115        seq_serializer.end()
116    }
117}
118impl Deref for FormData {
119    type Target = Vec<Entry>;
120
121    fn deref(&self) -> &Self::Target {
122        &self.0
123    }
124}
125
126/// A single form entry consisting of a name and value
127#[derive(Debug, Clone, PartialEq)]
128pub struct Entry {
129    pub name: String,
130    pub value: EntryValue,
131}
132impl Serialize for Entry {
133    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
134    where
135        S: serde::Serializer,
136    {
137        let mut serializer = serializer.serialize_tuple(2)?;
138        serializer.serialize_element(&self.name)?;
139        match &self.value {
140            EntryValue::String(s) => serializer.serialize_element(s)?,
141            EntryValue::File(p) => serializer.serialize_element(p.to_str().unwrap_or_default())?,
142            EntryValue::EmptyFile => serializer.serialize_element("")?,
143        }
144        serializer.end()
145    }
146}
147
148#[derive(Debug, Clone, PartialEq)]
149pub enum EntryValue {
150    String(String),
151    File(PathBuf),
152    EmptyFile,
153}
154impl AsRef<str> for EntryValue {
155    fn as_ref(&self) -> &str {
156        match self {
157            EntryValue::String(s) => s,
158            EntryValue::File(p) => p.to_str().unwrap_or_default(),
159            EntryValue::EmptyFile => "",
160        }
161    }
162}
163
164impl From<&str> for EntryValue {
165    fn from(value: &str) -> Self {
166        EntryValue::String(value.to_string())
167    }
168}
169impl From<PathBuf> for EntryValue {
170    fn from(value: PathBuf) -> Self {
171        EntryValue::File(value)
172    }
173}
174
175/// A default noop NetProvider
176#[derive(Default)]
177pub struct DummyNetProvider;
178impl NetProvider for DummyNetProvider {
179    fn fetch(&self, _doc_id: usize, _request: Request, _handler: Box<dyn NetHandler>) {}
180    fn is_noop(&self) -> bool {
181        true
182    }
183}
184
185/// The AbortController interface represents a controller object that
186/// allows you to abort one or more Web requests as and when desired via
187/// `AbortController`.
188///
189/// <https://developer.mozilla.org/en-US/docs/Web/API/AbortController>
190#[derive(Debug, Default)]
191pub struct AbortController {
192    pub signal: AbortSignal,
193}
194
195impl AbortController {
196    /// The abort() method of the AbortController interface aborts
197    /// an asynchronous operation before it has completed.
198    /// This is able to abort fetch requests.
199    ///
200    /// <https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort>
201    pub fn abort(self) {
202        self.signal.0.store(true, Ordering::SeqCst);
203    }
204}
205
206/// The AbortSignal interface represents a signal object that allows you to
207/// communicate that an operation should be aborted via `AbortController`.
208///
209/// <https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal>
210#[derive(Debug, Default, Clone)]
211pub struct AbortSignal(Arc<AtomicBool>);
212
213impl AbortSignal {
214    /// The aborted read-only property returns a value that indicates whether
215    /// the asynchronous operations the signal is communicating with has been aborted.
216    pub fn aborted(&self) -> bool {
217        self.0.load(Ordering::SeqCst)
218    }
219}
220