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, &'static str> {
54 if url.query().is_some() {
55 return Err("URL should not contain a query");
56 }
57
58 let Some((_, uuid)) = url.path().rsplit_once('/') else {
59 return Err("Failed to split origin from uuid");
60 };
61
62 Uuid::from_str(uuid).map_err(|_| "Failed to parse UUID from path segment")
63}
64
65#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
68pub struct UrlWithBlobClaim {
69 url: ServoUrl,
70 token: Option<TokenSerializationGuard>,
71}
72
73impl UrlWithBlobClaim {
74 pub fn new(url: ServoUrl, token: Option<TokenSerializationGuard>) -> Self {
75 Self { url, token }
76 }
77
78 pub fn token(&self) -> Option<&BlobToken> {
79 self.token.as_ref().map(|guard| guard.token.as_ref())
80 }
81
82 pub fn blob_id(&self) -> Option<Uuid> {
83 self.token.as_ref().map(|guard| guard.token.file_id)
84 }
85
86 pub fn origin(&self) -> ImmutableOrigin {
88 let url = &self.url;
91 if url.scheme() == "blob" {
92 if let Some(guard) = self.token.as_ref() {
95 return guard.token.origin.clone();
96 }
97
98 Url::parse(url.path())
100 .ok()
101 .filter(|url| matches!(url.scheme(), "http" | "https" | "file"))
104 .map(|url| ImmutableOrigin::new(&url))
105 .unwrap_or(ImmutableOrigin::new_opaque())
108 } else {
109 url.origin()
111 }
112 }
113
114 pub fn for_url(url: ServoUrl) -> Result<Self, ServoUrl> {
120 if url.scheme() == "blob" {
121 return Err(url);
122 }
123
124 Ok(Self { url, token: None })
125 }
126
127 pub fn from_url_without_having_claimed_blob(url: ServoUrl) -> Self {
130 if url.scheme() == "blob" {
131 log::warn!(
133 "Creating blob URL without claiming its associated blob entry. This might cause race conditions if the URL is revoked."
134 );
135 }
136 Self { url, token: None }
137 }
138
139 pub fn url(&self) -> ServoUrl {
140 self.url.clone()
141 }
142}
143
144impl Deref for UrlWithBlobClaim {
145 type Target = ServoUrl;
146
147 fn deref(&self) -> &Self::Target {
148 &self.url
149 }
150}
151
152impl DerefMut for UrlWithBlobClaim {
153 fn deref_mut(&mut self) -> &mut Self::Target {
154 &mut self.url
155 }
156}
157
158#[derive(Clone, Debug, MallocSizeOf)]
161pub struct TokenSerializationGuard {
162 #[conditional_malloc_size_of]
163 token: Arc<BlobToken>,
164}
165
166impl serde::Serialize for TokenSerializationGuard {
167 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
168 where
169 S: serde::Serializer,
170 {
171 let mut new_token = self.token.refresh();
172 let result = new_token.serialize(serializer);
173 if result.is_ok() {
174 new_token.disown();
176 }
177 result
178 }
179}
180
181impl<'a> serde::Deserialize<'a> for TokenSerializationGuard {
182 fn deserialize<D>(de: D) -> Result<Self, <D as serde::Deserializer<'a>>::Error>
183 where
184 D: serde::Deserializer<'a>,
185 {
186 struct TokenGuardVisitor;
187
188 impl<'de> serde::de::Visitor<'de> for TokenGuardVisitor {
189 type Value = TokenSerializationGuard;
190
191 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
192 write!(formatter, "a TokenSerializationGuard")
193 }
194
195 fn visit_newtype_struct<D>(
196 self,
197 deserializer: D,
198 ) -> Result<Self::Value, <D as serde::Deserializer<'de>>::Error>
199 where
200 D: serde::Deserializer<'de>,
201 {
202 Ok(TokenSerializationGuard {
203 token: Arc::new(BlobToken::deserialize(deserializer)?),
204 })
205 }
206 }
207
208 de.deserialize_newtype_struct("TokenSerializationGuard", TokenGuardVisitor)
209 }
210}
211
212#[derive(Clone, MallocSizeOf)]
213pub struct BlobResolver<'a> {
214 pub origin: ImmutableOrigin,
215 pub resource_threads: &'a ResourceThreads,
216}
217
218#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
219pub struct BlobToken {
222 pub token: Uuid,
223 pub file_id: Uuid,
224 pub disowned: bool,
225 pub origin: ImmutableOrigin,
226 #[conditional_malloc_size_of]
231 pub communicator: Arc<Mutex<BlobTokenCommunicator>>,
232}
233
234#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
235pub struct BlobTokenCommunicator {
236 pub revoke_sender: GenericSender<CoreResourceMsg>,
237 pub refresh_token_sender: GenericSender<CoreResourceMsg>,
238}
239
240impl BlobTokenCommunicator {
241 pub fn stub_for_testing() -> Arc<Mutex<Self>> {
242 Arc::new(Mutex::new(Self {
243 revoke_sender: generic_channel::channel().unwrap().0,
244 refresh_token_sender: generic_channel::channel().unwrap().0,
245 }))
246 }
247}
248
249impl BlobToken {
250 fn refresh(&self) -> Self {
251 let (new_token_sender, new_token_receiver) = generic_channel::channel().unwrap();
252 let refresh_request = BlobTokenRefreshRequest {
253 blob_id: self.file_id,
254 new_token_sender,
255 };
256 self.communicator
257 .lock()
258 .refresh_token_sender
259 .send(CoreResourceMsg::RefreshTokenForFile(refresh_request))
260 .unwrap();
261 let new_token = new_token_receiver.recv().unwrap();
262
263 BlobToken {
264 token: new_token,
265 file_id: self.file_id,
266 communicator: self.communicator.clone(),
267 disowned: false,
268 origin: self.origin.clone(),
269 }
270 }
271
272 fn disown(&mut self) {
274 self.disowned = true;
275 }
276}
277
278impl<'a> BlobResolver<'a> {
279 pub fn acquire_blob_token_for(&self, url: &ServoUrl) -> Option<TokenSerializationGuard> {
280 if url.scheme() != "blob" {
281 return None;
282 }
283 let file_id = parse_blob_url(url)
284 .inspect_err(|error| log::warn!("Failed to acquire token for {url}: {error}"))
285 .ok()?;
286 let (sender, receiver) = generic_channel::channel().unwrap();
287 self.resource_threads
288 .send(CoreResourceMsg::ToFileManager(
289 FileManagerThreadMsg::GetTokenForFile(file_id, sender),
290 ))
291 .ok()?;
292 let reply = receiver.recv().ok()?;
293 reply.token.map(|token_id| {
294 let token = BlobToken {
295 token: token_id,
296 file_id,
297 communicator: Arc::new(Mutex::new(BlobTokenCommunicator {
298 revoke_sender: reply.revoke_sender,
299 refresh_token_sender: reply.refresh_sender,
300 })),
301 disowned: false,
302 origin: self.origin.clone(),
303 };
304
305 TokenSerializationGuard {
306 token: Arc::new(token),
307 }
308 })
309 }
310}
311
312impl Drop for BlobToken {
313 fn drop(&mut self) {
314 if self.disowned {
315 return;
316 }
317
318 let revocation_request = BlobTokenRevocationRequest {
319 token: self.token,
320 blob_id: self.file_id,
321 };
322 let _ = self
323 .communicator
324 .lock()
325 .revoke_sender
326 .send(CoreResourceMsg::RevokeTokenForFile(revocation_request));
327 }
328}
329
330impl fmt::Debug for BlobToken {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 f.debug_struct("BlobToken")
333 .field("token", &self.token)
334 .field("file_id", &self.file_id)
335 .field("disowned", &self.disowned)
336 .field("origin", &self.origin)
337 .finish()
338 }
339}