1use std::fmt;
6use std::ops::{Deref, DerefMut};
7use std::str::FromStr;
8use std::sync::Arc;
9
10use malloc_size_of_derive::MallocSizeOf;
11use parking_lot::Mutex;
12use serde::{Deserialize, Serialize};
13use servo_base::generic_channel::{self, GenericSend, GenericSender};
14use servo_url::{ImmutableOrigin, ServoUrl};
15use url::Url;
16use uuid::Uuid;
17
18use crate::{
19 BlobTokenRefreshRequest, BlobTokenRevocationRequest, CoreResourceMsg, FileManagerThreadMsg,
20 ResourceThreads,
21};
22
23#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
25pub enum BlobURLStoreError {
26 InvalidFileID,
28 InvalidOrigin,
30 InvalidEntry,
32 InvalidRange,
34 External(String),
36}
37
38#[derive(Clone, Debug, Deserialize, Serialize)]
40pub struct BlobBuf {
41 pub filename: Option<String>,
42 pub type_string: String,
44 pub size: u64,
46 pub bytes: Vec<u8>,
48}
49
50pub fn parse_blob_url(url: &ServoUrl) -> Result<(Uuid, ImmutableOrigin), &'static str> {
59 if url.query().is_some() {
60 return Err("URL should not contain a query");
61 }
62
63 let Some((_, uuid)) = url.path().rsplit_once('/') else {
64 return Err("Failed to split origin from uuid");
65 };
66
67 let origin = Url::parse(url.path())
69 .ok()
70 .filter(|url| matches!(url.scheme(), "http" | "https" | "file"))
71 .map(|url| url.origin())
72 .map(ImmutableOrigin::new)
73 .unwrap_or(ImmutableOrigin::new_opaque());
74
75 let id = Uuid::from_str(uuid).map_err(|_| "Failed to parse UUID from path segment")?;
76
77 Ok((id, origin))
78}
79
80#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
83pub struct UrlWithBlobClaim {
84 url: ServoUrl,
85 token: Option<TokenSerializationGuard>,
86}
87
88impl UrlWithBlobClaim {
89 pub fn new(url: ServoUrl, token: Option<TokenSerializationGuard>) -> Self {
90 Self { url, token }
91 }
92
93 pub fn token(&self) -> Option<&BlobToken> {
94 self.token.as_ref().map(|guard| guard.token.as_ref())
95 }
96
97 pub fn blob_id(&self) -> Option<Uuid> {
98 self.token.as_ref().map(|guard| guard.token.file_id)
99 }
100
101 pub fn origin(&self) -> ImmutableOrigin {
102 if let Some(guard) = self.token.as_ref() {
103 return guard.token.origin.clone();
104 }
105
106 self.url.origin()
107 }
108
109 pub fn for_url(url: ServoUrl) -> Result<Self, ServoUrl> {
115 if url.scheme() == "blob" {
116 return Err(url);
117 }
118
119 Ok(Self { url, token: None })
120 }
121
122 pub fn from_url_without_having_claimed_blob(url: ServoUrl) -> Self {
125 if url.scheme() == "blob" {
126 log::warn!(
128 "Creating blob URL without claiming its associated blob entry. This might cause race conditions if the URL is revoked."
129 );
130 }
131 Self { url, token: None }
132 }
133
134 pub fn url(&self) -> ServoUrl {
135 self.url.clone()
136 }
137}
138
139impl Deref for UrlWithBlobClaim {
140 type Target = ServoUrl;
141
142 fn deref(&self) -> &Self::Target {
143 &self.url
144 }
145}
146
147impl DerefMut for UrlWithBlobClaim {
148 fn deref_mut(&mut self) -> &mut Self::Target {
149 &mut self.url
150 }
151}
152
153#[derive(Clone, Debug, MallocSizeOf)]
156pub struct TokenSerializationGuard {
157 #[conditional_malloc_size_of]
158 token: Arc<BlobToken>,
159}
160
161impl serde::Serialize for TokenSerializationGuard {
162 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
163 where
164 S: serde::Serializer,
165 {
166 let mut new_token = self.token.refresh();
167 let result = new_token.serialize(serializer);
168 if result.is_ok() {
169 new_token.disown();
171 }
172 result
173 }
174}
175
176impl<'a> serde::Deserialize<'a> for TokenSerializationGuard {
177 fn deserialize<D>(de: D) -> Result<Self, <D as serde::Deserializer<'a>>::Error>
178 where
179 D: serde::Deserializer<'a>,
180 {
181 struct TokenGuardVisitor;
182
183 impl<'de> serde::de::Visitor<'de> for TokenGuardVisitor {
184 type Value = TokenSerializationGuard;
185
186 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
187 write!(formatter, "a TokenSerializationGuard")
188 }
189
190 fn visit_newtype_struct<D>(
191 self,
192 deserializer: D,
193 ) -> Result<Self::Value, <D as serde::Deserializer<'de>>::Error>
194 where
195 D: serde::Deserializer<'de>,
196 {
197 Ok(TokenSerializationGuard {
198 token: Arc::new(BlobToken::deserialize(deserializer)?),
199 })
200 }
201 }
202
203 de.deserialize_newtype_struct("TokenSerializationGuard", TokenGuardVisitor)
204 }
205}
206
207#[derive(Clone, MallocSizeOf)]
208pub struct BlobResolver<'a> {
209 pub origin: ImmutableOrigin,
210 pub resource_threads: &'a ResourceThreads,
211}
212
213#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
214pub struct BlobToken {
217 pub token: Uuid,
218 pub file_id: Uuid,
219 pub disowned: bool,
220 pub origin: ImmutableOrigin,
221 #[conditional_malloc_size_of]
226 pub communicator: Arc<Mutex<BlobTokenCommunicator>>,
227}
228
229#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
230pub struct BlobTokenCommunicator {
231 pub revoke_sender: GenericSender<CoreResourceMsg>,
232 pub refresh_token_sender: GenericSender<CoreResourceMsg>,
233}
234
235impl BlobTokenCommunicator {
236 pub fn stub_for_testing() -> Arc<Mutex<Self>> {
237 Arc::new(Mutex::new(Self {
238 revoke_sender: generic_channel::channel().unwrap().0,
239 refresh_token_sender: generic_channel::channel().unwrap().0,
240 }))
241 }
242}
243
244impl BlobToken {
245 fn refresh(&self) -> Self {
246 let (new_token_sender, new_token_receiver) = generic_channel::channel().unwrap();
247 let refresh_request = BlobTokenRefreshRequest {
248 blob_id: self.file_id,
249 new_token_sender,
250 };
251 self.communicator
252 .lock()
253 .refresh_token_sender
254 .send(CoreResourceMsg::RefreshTokenForFile(refresh_request))
255 .unwrap();
256 let new_token = new_token_receiver.recv().unwrap();
257
258 BlobToken {
259 token: new_token,
260 file_id: self.file_id,
261 communicator: self.communicator.clone(),
262 disowned: false,
263 origin: self.origin.clone(),
264 }
265 }
266
267 fn disown(&mut self) {
269 self.disowned = true;
270 }
271}
272
273impl<'a> BlobResolver<'a> {
274 pub fn acquire_blob_token_for(&self, url: &ServoUrl) -> Option<TokenSerializationGuard> {
275 if url.scheme() != "blob" {
276 return None;
277 }
278 let (file_id, origin) = parse_blob_url(url)
279 .inspect_err(|error| log::warn!("Failed to acquire token for {url}: {error}"))
280 .ok()?;
281 let (sender, receiver) = generic_channel::channel().unwrap();
282 self.resource_threads
283 .send(CoreResourceMsg::ToFileManager(
284 FileManagerThreadMsg::GetTokenForFile(file_id, origin, sender),
285 ))
286 .ok()?;
287 let reply = receiver.recv().ok()?;
288 reply.token.map(|token_id| {
289 let token = BlobToken {
290 token: token_id,
291 file_id,
292 communicator: Arc::new(Mutex::new(BlobTokenCommunicator {
293 revoke_sender: reply.revoke_sender,
294 refresh_token_sender: reply.refresh_sender,
295 })),
296 disowned: false,
297 origin: self.origin.clone(),
298 };
299
300 TokenSerializationGuard {
301 token: Arc::new(token),
302 }
303 })
304 }
305}
306
307impl Drop for BlobToken {
308 fn drop(&mut self) {
309 if self.disowned {
310 return;
311 }
312
313 let revocation_request = BlobTokenRevocationRequest {
314 token: self.token,
315 blob_id: self.file_id,
316 };
317 let _ = self
318 .communicator
319 .lock()
320 .revoke_sender
321 .send(CoreResourceMsg::RevokeTokenForFile(revocation_request));
322 }
323}
324
325impl fmt::Debug for BlobToken {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 f.debug_struct("BlobToken")
328 .field("token", &self.token)
329 .field("file_id", &self.file_id)
330 .field("disowned", &self.disowned)
331 .field("origin", &self.origin)
332 .finish()
333 }
334}