mirror of
https://github.com/servo/servo.git
synced 2025-06-20 07:08:59 +01:00
154 lines
4.4 KiB
Rust
154 lines
4.4 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
//! A task that takes a URL and streams back the binary data.
|
|
|
|
use file_loader;
|
|
use http_loader;
|
|
|
|
use std::cell::Cell;
|
|
use std::comm::{Chan, Port, SharedChan};
|
|
use extra::url::Url;
|
|
use util::spawn_listener;
|
|
|
|
pub enum ControlMsg {
|
|
/// Request the data associated with a particular URL
|
|
Load(Url, Chan<ProgressMsg>),
|
|
Exit
|
|
}
|
|
|
|
/// Messages sent in response to a `Load` message
|
|
#[deriving(Eq)]
|
|
pub enum ProgressMsg {
|
|
/// URL changed due to a redirect. There can be zero or more of these,
|
|
/// but they are guaranteed to arrive before messages of any other type.
|
|
UrlChange(Url),
|
|
/// Binary data - there may be multiple of these
|
|
Payload(~[u8]),
|
|
/// Indicates loading is complete, either successfully or not
|
|
Done(Result<(), ()>)
|
|
}
|
|
|
|
/// Handle to a resource task
|
|
pub type ResourceTask = SharedChan<ControlMsg>;
|
|
|
|
pub type LoaderTask = ~fn(url: Url, Chan<ProgressMsg>);
|
|
|
|
/**
|
|
Creates a task to load a specific resource
|
|
|
|
The ResourceManager delegates loading to a different type of loader task for
|
|
each URL scheme
|
|
*/
|
|
type LoaderTaskFactory = extern "Rust" fn() -> LoaderTask;
|
|
|
|
/// Create a ResourceTask with the default loaders
|
|
pub fn ResourceTask() -> ResourceTask {
|
|
let loaders = ~[
|
|
(~"file", file_loader::factory),
|
|
(~"http", http_loader::factory),
|
|
];
|
|
create_resource_task_with_loaders(loaders)
|
|
}
|
|
|
|
fn create_resource_task_with_loaders(loaders: ~[(~str, LoaderTaskFactory)]) -> ResourceTask {
|
|
let loaders_cell = Cell::new(loaders);
|
|
let chan = do spawn_listener |from_client| {
|
|
// TODO: change copy to move once we can move out of closures
|
|
ResourceManager(from_client, loaders_cell.take()).start()
|
|
};
|
|
SharedChan::new(chan)
|
|
}
|
|
|
|
pub struct ResourceManager {
|
|
from_client: Port<ControlMsg>,
|
|
/// Per-scheme resource loaders
|
|
loaders: ~[(~str, LoaderTaskFactory)],
|
|
}
|
|
|
|
|
|
pub fn ResourceManager(from_client: Port<ControlMsg>,
|
|
loaders: ~[(~str, LoaderTaskFactory)]) -> ResourceManager {
|
|
ResourceManager {
|
|
from_client : from_client,
|
|
loaders : loaders,
|
|
}
|
|
}
|
|
|
|
|
|
impl ResourceManager {
|
|
fn start(&self) {
|
|
loop {
|
|
match self.from_client.recv() {
|
|
Load(url, progress_chan) => {
|
|
self.load(url.clone(), progress_chan)
|
|
}
|
|
Exit => {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn load(&self, url: Url, progress_chan: Chan<ProgressMsg>) {
|
|
|
|
match self.get_loader_factory(&url) {
|
|
Some(loader_factory) => {
|
|
debug!("resource_task: loading url: %s", url.to_str());
|
|
loader_factory(url, progress_chan);
|
|
}
|
|
None => {
|
|
debug!("resource_task: no loader for scheme %s", url.scheme);
|
|
progress_chan.send(Done(Err(())));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_loader_factory(&self, url: &Url) -> Option<LoaderTask> {
|
|
for scheme_loader in self.loaders.iter() {
|
|
match *scheme_loader {
|
|
(ref scheme, ref loader_factory) => {
|
|
if (*scheme) == url.scheme {
|
|
return Some((*loader_factory)());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return None;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_exit() {
|
|
let resource_task = ResourceTask();
|
|
resource_task.send(Exit);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bad_scheme() {
|
|
let resource_task = ResourceTask();
|
|
let progress = Port();
|
|
resource_task.send(Load(url::from_str(~"bogus://whatever").get(), progress.chan()));
|
|
match progress.recv() {
|
|
Done(result) => { assert!(result.is_err()) }
|
|
_ => fail
|
|
}
|
|
resource_task.send(Exit);
|
|
}
|
|
|
|
#[test]
|
|
fn should_delegate_to_scheme_loader() {
|
|
let payload = ~[1, 2, 3];
|
|
let loader_factory = |_url: Url, progress_chan: Chan<ProgressMsg>| {
|
|
progress_chan.send(Payload(payload.clone()));
|
|
progress_chan.send(Done(Ok(())));
|
|
};
|
|
let loader_factories = ~[(~"snicklefritz", loader_factory)];
|
|
let resource_task = create_resource_task_with_loaders(loader_factories);
|
|
let progress = Port();
|
|
resource_task.send(Load(url::from_str(~"snicklefritz://heya").get(), progress.chan()));
|
|
assert!(progress.recv() == Payload(payload));
|
|
assert!(progress.recv() == Done(Ok(())));
|
|
resource_task.send(Exit);
|
|
}
|