Skip to content

Commit ff2ad32

Browse files
author
bors-servo
authored
Auto merge of #13750 - servo:fetch-blob, r=<try>
Implement blob url support in the fetch stack. <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13750) <!-- Reviewable:end -->
2 parents 41df705 + 699287e commit ff2ad32

File tree

6 files changed

+226
-67
lines changed

6 files changed

+226
-67
lines changed

components/net/blob_loader.rs

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ use resource_thread::{send_error, start_sending_sniffed_opt};
1818
use resource_thread::CancellationListener;
1919
use std::boxed::FnBox;
2020
use std::sync::Arc;
21+
use url::Url;
2122
use util::thread::spawn_named;
2223

2324
// TODO: Check on GET
2425
// https://w3c.github.io/FileAPI/#requestResponseModel
2526

26-
pub fn factory<UI: 'static + UIProvider>(filemanager: Arc<FileManager<UI>>)
27+
pub fn factory<UI: 'static + UIProvider>(filemanager: FileManager<UI>)
2728
-> Box<FnBox(LoadData, LoadConsumer, Arc<MimeClassifier>, CancellationListener) + Send> {
2829
box move |load_data: LoadData, start_chan, classifier, cancel_listener| {
2930
spawn_named(format!("blob loader for {}", load_data.url), move || {
@@ -35,7 +36,7 @@ pub fn factory<UI: 'static + UIProvider>(filemanager: Arc<FileManager<UI>>)
3536
fn load_blob<UI: 'static + UIProvider>
3637
(load_data: LoadData, start_chan: LoadConsumer,
3738
classifier: Arc<MimeClassifier>,
38-
filemanager: Arc<FileManager<UI>>,
39+
filemanager: FileManager<UI>,
3940
cancel_listener: CancellationListener) {
4041
let (chan, recv) = ipc::channel().unwrap();
4142
if let Ok((id, origin, _fragment)) = parse_blob_url(&load_data.url.clone()) {
@@ -119,3 +120,71 @@ fn load_blob<UI: 'static + UIProvider>
119120
send_error(load_data.url.clone(), format_err, start_chan);
120121
}
121122
}
123+
124+
// TODO: make async.
125+
pub fn load_blob_sync<UI: 'static + UIProvider>
126+
(url: Url,
127+
filemanager: FileManager<UI>)
128+
-> Result<(Headers, Vec<u8>), NetworkError> {
129+
let (id, origin) = match parse_blob_url(&url) {
130+
Ok((id, origin, _fragment)) => (id, origin),
131+
Err(()) => {
132+
let e = format!("Invalid blob URL format {:?}", url);
133+
return Err(NetworkError::Internal(e));
134+
}
135+
};
136+
137+
let (sender, receiver) = ipc::channel().unwrap();
138+
let check_url_validity = true;
139+
let msg = FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin);
140+
let _ = filemanager.handle(msg, None);
141+
142+
let blob_buf = match receiver.recv().unwrap() {
143+
Ok(ReadFileProgress::Meta(blob_buf)) => blob_buf,
144+
Ok(_) => {
145+
return Err(NetworkError::Internal("Invalid filemanager reply".to_string()));
146+
}
147+
Err(e) => {
148+
return Err(NetworkError::Internal(format!("{:?}", e)));
149+
}
150+
};
151+
152+
let content_type: Mime = blob_buf.type_string.parse().unwrap_or(mime!(Text / Plain));
153+
let charset = content_type.get_param(Attr::Charset);
154+
155+
let mut headers = Headers::new();
156+
157+
if let Some(name) = blob_buf.filename {
158+
let charset = charset.and_then(|c| c.as_str().parse().ok());
159+
headers.set(ContentDisposition {
160+
disposition: DispositionType::Inline,
161+
parameters: vec![
162+
DispositionParam::Filename(charset.unwrap_or(Charset::Us_Ascii),
163+
None, name.as_bytes().to_vec())
164+
]
165+
});
166+
}
167+
168+
// Basic fetch, Step 4.
169+
headers.set(ContentLength(blob_buf.size as u64));
170+
// Basic fetch, Step 5.
171+
headers.set(ContentType(content_type.clone()));
172+
173+
let mut bytes = blob_buf.bytes;
174+
loop {
175+
match receiver.recv().unwrap() {
176+
Ok(ReadFileProgress::Partial(ref mut new_bytes)) => {
177+
bytes.append(new_bytes);
178+
}
179+
Ok(ReadFileProgress::EOF) => {
180+
return Ok((headers, bytes));
181+
}
182+
Ok(_) => {
183+
return Err(NetworkError::Internal("Invalid filemanager reply".to_string()));
184+
}
185+
Err(e) => {
186+
return Err(NetworkError::Internal(format!("{:?}", e)));
187+
}
188+
}
189+
}
190+
}

components/net/fetch/methods.rs

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5+
use blob_loader::load_blob_sync;
56
use connector::create_http_connector;
67
use data_loader::decode;
78
use devtools_traits::DevtoolsControlMsg;
89
use fetch::cors_cache::CORSCache;
10+
use filemanager_thread::{FileManager, UIProvider};
911
use http_loader::{HttpState, set_default_accept_encoding, set_request_cookies};
1012
use http_loader::{NetworkHttpRequestFactory, ReadResult, StreamedResponse, obtain_response, read_block};
1113
use http_loader::{auth_from_cache, determine_request_referrer};
@@ -50,23 +52,28 @@ enum Data {
5052
Done,
5153
}
5254

53-
pub struct FetchContext {
55+
pub struct FetchContext<UI: 'static + UIProvider> {
5456
pub state: HttpState,
5557
pub user_agent: Cow<'static, str>,
5658
pub devtools_chan: Option<Sender<DevtoolsControlMsg>>,
59+
pub filemanager: FileManager<UI>,
5760
}
5861

5962
type DoneChannel = Option<(Sender<Data>, Receiver<Data>)>;
6063

6164
/// [Fetch](https://fetch.spec.whatwg.org#concept-fetch)
62-
pub fn fetch(request: Rc<Request>, target: &mut Target, context: FetchContext) -> Response {
65+
pub fn fetch<UI: 'static + UIProvider>(request: Rc<Request>,
66+
target: &mut Target,
67+
context: FetchContext<UI>)
68+
-> Response {
6369
fetch_with_cors_cache(request, &mut CORSCache::new(), target, context)
6470
}
6571

66-
pub fn fetch_with_cors_cache(request: Rc<Request>,
67-
cache: &mut CORSCache,
68-
target: &mut Target,
69-
context: FetchContext) -> Response {
72+
pub fn fetch_with_cors_cache<UI: 'static + UIProvider>(request: Rc<Request>,
73+
cache: &mut CORSCache,
74+
target: &mut Target,
75+
context: FetchContext<UI>)
76+
-> Response {
7077
// Step 1
7178
if request.window.get() == Window::Client {
7279
// TODO: Set window to request's client object if client is a Window object
@@ -131,9 +138,14 @@ pub fn fetch_with_cors_cache(request: Rc<Request>,
131138
}
132139

133140
/// [Main fetch](https://fetch.spec.whatwg.org/#concept-main-fetch)
134-
fn main_fetch(request: Rc<Request>, cache: &mut CORSCache, cors_flag: bool,
135-
recursive_flag: bool, target: &mut Target, done_chan: &mut DoneChannel,
136-
context: &FetchContext) -> Response {
141+
fn main_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
142+
cache: &mut CORSCache,
143+
cors_flag: bool,
144+
recursive_flag: bool,
145+
target: &mut Target,
146+
done_chan: &mut DoneChannel,
147+
context: &FetchContext<UI>)
148+
-> Response {
137149
// TODO: Implement main fetch spec
138150

139151
// Step 1
@@ -389,9 +401,12 @@ fn main_fetch(request: Rc<Request>, cache: &mut CORSCache, cors_flag: bool,
389401
}
390402

391403
/// [Basic fetch](https://fetch.spec.whatwg.org#basic-fetch)
392-
fn basic_fetch(request: Rc<Request>, cache: &mut CORSCache,
393-
target: &mut Target, done_chan: &mut DoneChannel,
394-
context: &FetchContext) -> Response {
404+
fn basic_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
405+
cache: &mut CORSCache,
406+
target: &mut Target,
407+
done_chan: &mut DoneChannel,
408+
context: &FetchContext<UI>)
409+
-> Response {
395410
let url = request.current_url();
396411

397412
match url.scheme() {
@@ -450,7 +465,29 @@ fn basic_fetch(request: Rc<Request>, cache: &mut CORSCache,
450465
}
451466
},
452467

453-
"blob" | "ftp" => {
468+
"blob" => {
469+
println!("Loading blob {}", url.as_str());
470+
// Step 2.
471+
if *request.method.borrow() != Method::Get {
472+
return Response::network_error();
473+
}
474+
475+
match load_blob_sync(url.clone(), context.filemanager.clone()) {
476+
Ok((headers, bytes)) => {
477+
let mut response = Response::new();
478+
response.url = Some(url.clone());
479+
response.headers = headers;
480+
*response.body.lock().unwrap() = ResponseBody::Done(bytes);
481+
response
482+
},
483+
Err(e) => {
484+
debug!("Failed to load {}: {:?}", url, e);
485+
Response::network_error()
486+
},
487+
}
488+
},
489+
490+
"ftp" => {
454491
// XXXManishearth handle these
455492
panic!("Unimplemented scheme for Fetch")
456493
},
@@ -460,14 +497,15 @@ fn basic_fetch(request: Rc<Request>, cache: &mut CORSCache,
460497
}
461498

462499
/// [HTTP fetch](https://fetch.spec.whatwg.org#http-fetch)
463-
fn http_fetch(request: Rc<Request>,
464-
cache: &mut CORSCache,
465-
cors_flag: bool,
466-
cors_preflight_flag: bool,
467-
authentication_fetch_flag: bool,
468-
target: &mut Target,
469-
done_chan: &mut DoneChannel,
470-
context: &FetchContext) -> Response {
500+
fn http_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
501+
cache: &mut CORSCache,
502+
cors_flag: bool,
503+
cors_preflight_flag: bool,
504+
authentication_fetch_flag: bool,
505+
target: &mut Target,
506+
done_chan: &mut DoneChannel,
507+
context: &FetchContext<UI>)
508+
-> Response {
471509
// This is a new async fetch, reset the channel we are waiting on
472510
*done_chan = None;
473511
// Step 1
@@ -631,13 +669,14 @@ fn http_fetch(request: Rc<Request>,
631669
}
632670

633671
/// [HTTP redirect fetch](https://fetch.spec.whatwg.org#http-redirect-fetch)
634-
fn http_redirect_fetch(request: Rc<Request>,
635-
cache: &mut CORSCache,
636-
response: Rc<Response>,
637-
cors_flag: bool,
638-
target: &mut Target,
639-
done_chan: &mut DoneChannel,
640-
context: &FetchContext) -> Response {
672+
fn http_redirect_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
673+
cache: &mut CORSCache,
674+
response: Rc<Response>,
675+
cors_flag: bool,
676+
target: &mut Target,
677+
done_chan: &mut DoneChannel,
678+
context: &FetchContext<UI>)
679+
-> Response {
641680
// Step 1
642681
assert_eq!(response.return_internal.get(), true);
643682

@@ -711,11 +750,12 @@ fn http_redirect_fetch(request: Rc<Request>,
711750
}
712751

713752
/// [HTTP network or cache fetch](https://fetch.spec.whatwg.org#http-network-or-cache-fetch)
714-
fn http_network_or_cache_fetch(request: Rc<Request>,
715-
credentials_flag: bool,
716-
authentication_fetch_flag: bool,
717-
done_chan: &mut DoneChannel,
718-
context: &FetchContext) -> Response {
753+
fn http_network_or_cache_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
754+
credentials_flag: bool,
755+
authentication_fetch_flag: bool,
756+
done_chan: &mut DoneChannel,
757+
context: &FetchContext<UI>)
758+
-> Response {
719759
// TODO: Implement Window enum for Request
720760
let request_has_no_window = true;
721761

@@ -1108,8 +1148,10 @@ fn http_network_fetch(request: Rc<Request>,
11081148
}
11091149

11101150
/// [CORS preflight fetch](https://fetch.spec.whatwg.org#cors-preflight-fetch)
1111-
fn cors_preflight_fetch(request: Rc<Request>, cache: &mut CORSCache,
1112-
context: &FetchContext) -> Response {
1151+
fn cors_preflight_fetch<UI: 'static + UIProvider>(request: Rc<Request>,
1152+
cache: &mut CORSCache,
1153+
context: &FetchContext<UI>)
1154+
-> Response {
11131155
// Step 1
11141156
let mut preflight = Request::new(request.current_url(), Some(request.origin.borrow().clone()),
11151157
request.is_service_worker_global_scope, request.pipeline_id.get());

components/net/filemanager_thread.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ pub struct FileManager<UI: 'static + UIProvider> {
116116
store: Arc<FileManagerStore<UI>>,
117117
}
118118

119+
// Not derived to avoid an unnecessary `UI: Clone` bound.
120+
impl<UI: 'static + UIProvider> Clone for FileManager<UI> {
121+
fn clone(&self) -> Self {
122+
FileManager {
123+
store: self.store.clone(),
124+
}
125+
}
126+
}
127+
119128
impl<UI: 'static + UIProvider> FileManager<UI> {
120129
pub fn new(ui: &'static UI) -> FileManager<UI> {
121130
FileManager {
@@ -138,37 +147,25 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
138147
})
139148
}
140149
FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin) => {
141-
spawn_named("read file".to_owned(), move || {
142-
if let Err(e) = store.try_read_file(sender.clone(), id, check_url_validity,
143-
origin, cancel_listener) {
144-
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
145-
}
146-
})
150+
if let Err(e) = store.try_read_file(&sender, id, check_url_validity,
151+
origin, cancel_listener) {
152+
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
153+
}
147154
}
148155
FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, sender, origin) => {
149-
spawn_named("transfer memory".to_owned(), move || {
150-
store.promote_memory(blob_buf, set_valid, sender, origin);
151-
})
156+
store.promote_memory(blob_buf, set_valid, sender, origin);
152157
}
153158
FileManagerThreadMsg::AddSlicedURLEntry(id, rel_pos, sender, origin) =>{
154-
spawn_named("add sliced URL entry".to_owned(), move || {
155-
store.add_sliced_url_entry(id, rel_pos, sender, origin);
156-
})
159+
store.add_sliced_url_entry(id, rel_pos, sender, origin);
157160
}
158161
FileManagerThreadMsg::DecRef(id, origin, sender) => {
159-
spawn_named("dec ref".to_owned(), move || {
160-
let _ = sender.send(store.dec_ref(&id, &origin));
161-
})
162+
let _ = sender.send(store.dec_ref(&id, &origin));
162163
}
163164
FileManagerThreadMsg::RevokeBlobURL(id, origin, sender) => {
164-
spawn_named("revoke blob url".to_owned(), move || {
165-
let _ = sender.send(store.set_blob_url_validity(false, &id, &origin));
166-
})
165+
let _ = sender.send(store.set_blob_url_validity(false, &id, &origin));
167166
}
168167
FileManagerThreadMsg::ActivateBlobURL(id, sender, origin) => {
169-
spawn_named("activate blob url".to_owned(), move || {
170-
let _ = sender.send(store.set_blob_url_validity(true, &id, &origin));
171-
});
168+
let _ = sender.send(store.set_blob_url_validity(true, &id, &origin));
172169
}
173170
}
174171
}
@@ -365,7 +362,7 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
365362
})
366363
}
367364

368-
fn get_blob_buf(&self, sender: IpcSender<FileManagerResult<ReadFileProgress>>,
365+
fn get_blob_buf(&self, sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
369366
id: &Uuid, origin_in: &FileOrigin, rel_pos: RelativePos,
370367
check_url_validity: bool,
371368
cancel_listener: Option<CancellationListener>) -> Result<(), BlobURLStoreError> {
@@ -428,7 +425,7 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
428425
}
429426

430427
// Convenient wrapper over get_blob_buf
431-
fn try_read_file(&self, sender: IpcSender<FileManagerResult<ReadFileProgress>>,
428+
fn try_read_file(&self, sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
432429
id: Uuid, check_url_validity: bool, origin_in: FileOrigin,
433430
cancel_listener: Option<CancellationListener>) -> Result<(), BlobURLStoreError> {
434431
self.get_blob_buf(sender, &id, &origin_in, RelativePos::full_range(), check_url_validity, cancel_listener)
@@ -541,7 +538,7 @@ fn select_files_pref_enabled() -> bool {
541538

542539
const CHUNK_SIZE: usize = 8192;
543540

544-
fn chunked_read(sender: IpcSender<FileManagerResult<ReadFileProgress>>,
541+
fn chunked_read(sender: &IpcSender<FileManagerResult<ReadFileProgress>>,
545542
file: &mut File, size: usize, opt_filename: Option<String>,
546543
type_string: String, cancel_listener: Option<CancellationListener>) {
547544
// First chunk

0 commit comments

Comments
 (0)