diff options
Diffstat (limited to 'src/components/net')
-rw-r--r-- | src/components/net/data_loader.rs | 154 | ||||
-rw-r--r-- | src/components/net/fetch/cors_cache.rs | 316 | ||||
-rw-r--r-- | src/components/net/fetch/request.rs | 149 | ||||
-rw-r--r-- | src/components/net/fetch/response.rs | 144 | ||||
-rw-r--r-- | src/components/net/file_loader.rs | 50 | ||||
-rw-r--r-- | src/components/net/http_loader.rs | 167 | ||||
-rw-r--r-- | src/components/net/image/base.rs | 67 | ||||
-rw-r--r-- | src/components/net/image/holder.rs | 109 | ||||
-rw-r--r-- | src/components/net/image/test.jpeg | bin | 4962 -> 0 bytes | |||
-rw-r--r-- | src/components/net/image_cache_task.rs | 993 | ||||
-rw-r--r-- | src/components/net/local_image_cache.rs | 166 | ||||
-rw-r--r-- | src/components/net/net.rs | 47 | ||||
-rw-r--r-- | src/components/net/resource_task.rs | 267 |
13 files changed, 0 insertions, 2629 deletions
diff --git a/src/components/net/data_loader.rs b/src/components/net/data_loader.rs deleted file mode 100644 index 5d9fb776674..00000000000 --- a/src/components/net/data_loader.rs +++ /dev/null @@ -1,154 +0,0 @@ -/* 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/. */ - -use std::str; - -use resource_task::{Done, Payload, Metadata, LoadData, LoadResponse, LoaderTask, start_sending}; - -use serialize::base64::FromBase64; - -use http::headers::test_utils::from_stream_with_str; -use http::headers::content_type::MediaType; -use url::{percent_decode, NonRelativeSchemeData}; - - -pub fn factory() -> LoaderTask { - proc(url, start_chan) { - // NB: we don't spawn a new task. - // Hypothesis: data URLs are too small for parallel base64 etc. to be worth it. - // Should be tested at some point. - load(url, start_chan) - } -} - -fn load(load_data: LoadData, start_chan: Sender<LoadResponse>) { - let url = load_data.url; - assert!("data" == url.scheme.as_slice()); - - let mut metadata = Metadata::default(url.clone()); - - // Split out content type and data. - let mut scheme_data = match url.scheme_data { - NonRelativeSchemeData(scheme_data) => scheme_data, - _ => fail!("Expected a non-relative scheme URL.") - }; - match url.query { - Some(query) => { - scheme_data.push_str("?"); - scheme_data.push_str(query.as_slice()); - }, - None => () - } - let parts: Vec<&str> = scheme_data.as_slice().splitn(',', 1).collect(); - if parts.len() != 2 { - start_sending(start_chan, metadata).send(Done(Err("invalid data uri".to_string()))); - return; - } - - // ";base64" must come at the end of the content type, per RFC 2397. - // rust-http will fail to parse it because there's no =value part. - let mut is_base64 = false; - let mut ct_str = parts[0]; - if ct_str.ends_with(";base64") { - is_base64 = true; - ct_str = ct_str.slice_to(ct_str.as_bytes().len() - 7); - } - - // Parse the content type using rust-http. - // FIXME: this can go into an infinite loop! (rust-http #25) - let content_type: Option<MediaType> = from_stream_with_str(ct_str); - metadata.set_content_type(&content_type); - - let progress_chan = start_sending(start_chan, metadata); - let bytes = percent_decode(parts[1].as_bytes()); - - if is_base64 { - // FIXME(#2909): It’s unclear what to do with non-alphabet characters, - // but Acid 3 apparently depends on spaces being ignored. - let bytes = bytes.move_iter().filter(|&b| b != ' ' as u8).collect::<Vec<u8>>(); - // FIXME(#2877): use bytes.as_slice().from_base64() when we upgrade to a Rust version - // that includes https://github.com/rust-lang/rust/pull/15810 - let fake_utf8 = unsafe { str::raw::from_utf8(bytes.as_slice()) }; - match fake_utf8.from_base64() { - Err(..) => { - progress_chan.send(Done(Err("non-base64 data uri".to_string()))); - } - Ok(data) => { - progress_chan.send(Payload(data)); - progress_chan.send(Done(Ok(()))); - } - } - } else { - progress_chan.send(Payload(bytes)); - progress_chan.send(Done(Ok(()))); - } -} - -#[cfg(test)] -fn assert_parse(url: &'static str, - content_type: Option<(String, String)>, - charset: Option<String>, - data: Option<Vec<u8>>) { - use std::comm; - use url::Url; - - let (start_chan, start_port) = comm::channel(); - load(LoadData::new(Url::parse(url).unwrap()), start_chan); - - let response = start_port.recv(); - assert_eq!(&response.metadata.content_type, &content_type); - assert_eq!(&response.metadata.charset, &charset); - - let progress = response.progress_port.recv(); - - match data { - None => { - assert_eq!(progress, Done(Err("invalid data uri".to_string()))); - } - Some(dat) => { - assert_eq!(progress, Payload(dat)); - assert_eq!(response.progress_port.recv(), Done(Ok(()))); - } - } -} - -#[test] -fn empty_invalid() { - assert_parse("data:", None, None, None); -} - -#[test] -fn plain() { - assert_parse("data:,hello%20world", None, None, Some(b"hello world".iter().map(|&x| x).collect())); -} - -#[test] -fn plain_ct() { - assert_parse("data:text/plain,hello", - Some(("text".to_string(), "plain".to_string())), None, Some(b"hello".iter().map(|&x| x).collect())); -} - -#[test] -fn plain_charset() { - assert_parse("data:text/plain;charset=latin1,hello", - Some(("text".to_string(), "plain".to_string())), Some("latin1".to_string()), Some(b"hello".iter().map(|&x| x).collect())); -} - -#[test] -fn base64() { - assert_parse("data:;base64,C62+7w==", None, None, Some(vec!(0x0B, 0xAD, 0xBE, 0xEF))); -} - -#[test] -fn base64_ct() { - assert_parse("data:application/octet-stream;base64,C62+7w==", - Some(("application".to_string(), "octet-stream".to_string())), None, Some(vec!(0x0B, 0xAD, 0xBE, 0xEF))); -} - -#[test] -fn base64_charset() { - assert_parse("data:text/plain;charset=koi8-r;base64,8PLl9+XkIO3l5Pfl5A==", - Some(("text".to_string(), "plain".to_string())), Some("koi8-r".to_string()), - Some(vec!(0xF0, 0xF2, 0xE5, 0xF7, 0xE5, 0xE4, 0x20, 0xED, 0xE5, 0xE4, 0xF7, 0xE5, 0xE4))); -} diff --git a/src/components/net/fetch/cors_cache.rs b/src/components/net/fetch/cors_cache.rs deleted file mode 100644 index fb6676e8064..00000000000 --- a/src/components/net/fetch/cors_cache.rs +++ /dev/null @@ -1,316 +0,0 @@ -/* 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/. */ - -//! An implementation of the [CORS preflight cache](http://fetch.spec.whatwg.org/#cors-preflight-cache) -//! For now this library is XHR-specific. -//! For stuff involving `<img>`, `<iframe>`, `<form>`, etc please check what -//! the request mode should be and compare with the fetch spec -//! This library will eventually become the core of the Fetch crate -//! with CORSRequest being expanded into FetchRequest (etc) - -use http::method::Method; -use std::ascii::StrAsciiExt; -use std::comm::{Sender, Receiver, channel}; -use time; -use time::{now, Timespec}; -use url::Url; - -/// Union type for CORS cache entries -/// -/// Each entry might pertain to a header or method -#[deriving(Clone)] -pub enum HeaderOrMethod { - HeaderData(String), - MethodData(Method) -} - -impl HeaderOrMethod { - fn match_header(&self, header_name: &str) -> bool { - match *self { - HeaderData(ref s) => s.as_slice().eq_ignore_ascii_case(header_name), - _ => false - } - } - - fn match_method(&self, method: &Method) -> bool { - match *self { - MethodData(ref m) => m == method, - _ => false - } - } -} - -/// An entry in the CORS cache -#[deriving(Clone)] -pub struct CORSCacheEntry { - pub origin: Url, - pub url: Url, - pub max_age: uint, - pub credentials: bool, - pub header_or_method: HeaderOrMethod, - created: Timespec -} - -impl CORSCacheEntry { - fn new (origin:Url, url: Url, max_age: uint, credentials: bool, header_or_method: HeaderOrMethod) -> CORSCacheEntry { - CORSCacheEntry { - origin: origin, - url: url, - max_age: max_age, - credentials: credentials, - header_or_method: header_or_method, - created: time::now().to_timespec() - } - } -} - -/// Properties of Request required to cache match. -pub struct CacheRequestDetails { - pub origin: Url, - pub destination: Url, - pub credentials: bool -} - -/// Trait for a generic CORS Cache -pub trait CORSCache { - /// [Clear the cache](http://fetch.spec.whatwg.org/#concept-cache-clear) - fn clear (&mut self, request: CacheRequestDetails); - - /// Remove old entries - fn cleanup(&mut self); - - /// Returns true if an entry with a [matching header](http://fetch.spec.whatwg.org/#concept-cache-match-header) is found - fn match_header(&mut self, request: CacheRequestDetails, header_name: &str) -> bool; - - /// Updates max age if an entry for a [matching header](http://fetch.spec.whatwg.org/#concept-cache-match-header) is found. - /// - /// If not, it will insert an equivalent entry - fn match_header_and_update(&mut self, request: CacheRequestDetails, header_name: &str, new_max_age: uint) -> bool; - - /// Returns true if an entry with a [matching method](http://fetch.spec.whatwg.org/#concept-cache-match-method) is found - fn match_method(&mut self, request: CacheRequestDetails, method: Method) -> bool; - - /// Updates max age if an entry for [a matching method](http://fetch.spec.whatwg.org/#concept-cache-match-method) is found. - /// - /// If not, it will insert an equivalent entry - fn match_method_and_update(&mut self, request: CacheRequestDetails, method: Method, new_max_age: uint) -> bool; - /// Insert an entry - fn insert(&mut self, entry: CORSCacheEntry); -} - -/// A simple, vector-based CORS Cache -#[deriving(Clone)] -#[unstable = "This might later be replaced with a HashMap-like entity, though that requires a separate Origin struct"] -pub struct BasicCORSCache(Vec<CORSCacheEntry>); - -impl BasicCORSCache { - fn find_entry_by_header<'a>(&'a mut self, request: &CacheRequestDetails, header_name: &str) -> Option<&'a mut CORSCacheEntry> { - self.cleanup(); - let BasicCORSCache(ref mut buf) = *self; - let entry = buf.mut_iter().find(|e| e.origin.scheme == request.origin.scheme && - e.origin.host() == request.origin.host() && - e.origin.port() == request.origin.port() && - e.url == request.destination && - e.credentials == request.credentials && - e.header_or_method.match_header(header_name)); - entry - } - - fn find_entry_by_method<'a>(&'a mut self, request: &CacheRequestDetails, method: Method) -> Option<&'a mut CORSCacheEntry> { - // we can take the method from CORSRequest itself - self.cleanup(); - let BasicCORSCache(ref mut buf) = *self; - let entry = buf.mut_iter().find(|e| e.origin.scheme == request.origin.scheme && - e.origin.host() == request.origin.host() && - e.origin.port() == request.origin.port() && - e.url == request.destination && - e.credentials == request.credentials && - e.header_or_method.match_method(&method)); - entry - } -} - -impl CORSCache for BasicCORSCache { - /// http://fetch.spec.whatwg.org/#concept-cache-clear - #[allow(dead_code)] - fn clear (&mut self, request: CacheRequestDetails) { - let BasicCORSCache(buf) = self.clone(); - let new_buf: Vec<CORSCacheEntry> = buf.move_iter().filter(|e| e.origin == request.origin && request.destination == e.url).collect(); - *self = BasicCORSCache(new_buf); - } - - // Remove old entries - fn cleanup(&mut self) { - let BasicCORSCache(buf) = self.clone(); - let now = time::now().to_timespec(); - let new_buf: Vec<CORSCacheEntry> = buf.move_iter().filter(|e| now.sec > e.created.sec + e.max_age as i64).collect(); - *self = BasicCORSCache(new_buf); - } - - fn match_header(&mut self, request: CacheRequestDetails, header_name: &str) -> bool { - self.find_entry_by_header(&request, header_name).is_some() - } - - fn match_header_and_update(&mut self, request: CacheRequestDetails, header_name: &str, new_max_age: uint) -> bool { - match self.find_entry_by_header(&request, header_name).map(|e| e.max_age = new_max_age) { - Some(_) => true, - None => { - self.insert(CORSCacheEntry::new(request.origin, request.destination, new_max_age, - request.credentials, HeaderData(header_name.to_string()))); - false - } - } - } - - fn match_method(&mut self, request: CacheRequestDetails, method: Method) -> bool { - self.find_entry_by_method(&request, method).is_some() - } - - fn match_method_and_update(&mut self, request: CacheRequestDetails, method: Method, new_max_age: uint) -> bool { - match self.find_entry_by_method(&request, method.clone()).map(|e| e.max_age = new_max_age) { - Some(_) => true, - None => { - self.insert(CORSCacheEntry::new(request.origin, request.destination, new_max_age, - request.credentials, MethodData(method))); - false - } - } - } - - fn insert(&mut self, entry: CORSCacheEntry) { - self.cleanup(); - let BasicCORSCache(ref mut buf) = *self; - buf.push(entry); - } -} - -/// Various messages that can be sent to a CORSCacheTask -pub enum CORSCacheTaskMsg { - Clear(CacheRequestDetails, Sender<()>), - Cleanup(Sender<()>), - MatchHeader(CacheRequestDetails, String, Sender<bool>), - MatchHeaderUpdate(CacheRequestDetails, String, uint, Sender<bool>), - MatchMethod(CacheRequestDetails, Method, Sender<bool>), - MatchMethodUpdate(CacheRequestDetails, Method, uint, Sender<bool>), - Insert(CORSCacheEntry, Sender<()>), - ExitMsg -} - -/// A Sender to a CORSCacheTask -/// -/// This can be used as a CORS Cache. -/// The methods on this type block until they can run, and it behaves similar to a mutex -pub type CORSCacheSender = Sender<CORSCacheTaskMsg>; - -impl CORSCache for CORSCacheSender { - fn clear (&mut self, request: CacheRequestDetails) { - let (tx, rx) = channel(); - self.send(Clear(request, tx)); - let _ = rx.recv_opt(); - } - - fn cleanup(&mut self) { - let (tx, rx) = channel(); - self.send(Cleanup(tx)); - let _ = rx.recv_opt(); - } - - fn match_header(&mut self, request: CacheRequestDetails, header_name: &str) -> bool { - let (tx, rx) = channel(); - self.send(MatchHeader(request, header_name.to_string(), tx)); - rx.recv_opt().unwrap_or(false) - } - - fn match_header_and_update(&mut self, request: CacheRequestDetails, header_name: &str, new_max_age: uint) -> bool { - let (tx, rx) = channel(); - self.send(MatchHeaderUpdate(request, header_name.to_string(), new_max_age, tx)); - rx.recv_opt().unwrap_or(false) - } - - fn match_method(&mut self, request: CacheRequestDetails, method: Method) -> bool { - let (tx, rx) = channel(); - self.send(MatchMethod(request, method, tx)); - rx.recv_opt().unwrap_or(false) - } - - fn match_method_and_update(&mut self, request: CacheRequestDetails, method: Method, new_max_age: uint) -> bool { - let (tx, rx) = channel(); - self.send(MatchMethodUpdate(request, method, new_max_age, tx)); - rx.recv_opt().unwrap_or(false) - } - - fn insert(&mut self, entry: CORSCacheEntry) { - let (tx, rx) = channel(); - self.send(Insert(entry, tx)); - let _ = rx.recv_opt(); - } -} - -/// A simple task-based CORS Cache that can be sent messages -/// -/// #Example -/// ``` -/// let task = CORSCacheTask::new(); -/// let builder = TaskBuilder::new().named("XHRTask"); -/// let mut sender = task.get_sender(); -/// builder.spawn(proc() { task.run() }); -/// sender.insert(CORSCacheEntry::new(/* parameters here */)); -/// ``` -pub struct CORSCacheTask { - receiver: Receiver<CORSCacheTaskMsg>, - cache: BasicCORSCache, - sender: CORSCacheSender -} - -impl CORSCacheTask { - pub fn new() -> CORSCacheTask { - let (tx, rx) = channel(); - CORSCacheTask { - receiver: rx, - cache: BasicCORSCache(vec![]), - sender: tx - } - } - - /// Provides a sender to the cache task - pub fn get_sender(&self) -> CORSCacheSender { - self.sender.clone() - } - - /// Runs the cache task - /// This blocks the current task, so it is advised - /// to spawn a new task for this - /// Send ExitMsg to the associated Sender to exit - pub fn run(&mut self) { - loop { - match self.receiver.recv() { - Clear(request, tx) => { - self.cache.clear(request); - tx.send(()); - }, - Cleanup(tx) => { - self.cache.cleanup(); - tx.send(()); - }, - MatchHeader(request, header, tx) => { - tx.send(self.cache.match_header(request, header.as_slice())); - }, - MatchHeaderUpdate(request, header, new_max_age, tx) => { - tx.send(self.cache.match_header_and_update(request, header.as_slice(), new_max_age)); - }, - MatchMethod(request, method, tx) => { - tx.send(self.cache.match_method(request, method)); - }, - MatchMethodUpdate(request, method, new_max_age, tx) => { - tx.send(self.cache.match_method_and_update(request, method, new_max_age)); - }, - Insert(entry, tx) => { - self.cache.insert(entry); - tx.send(()); - }, - ExitMsg => break - } - } - } -} diff --git a/src/components/net/fetch/request.rs b/src/components/net/fetch/request.rs deleted file mode 100644 index c14efe9c59e..00000000000 --- a/src/components/net/fetch/request.rs +++ /dev/null @@ -1,149 +0,0 @@ -/* 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/. */ - -use url::Url; -use http::method::{Get, Method}; -use http::headers::request::HeaderCollection; -use fetch::cors_cache::CORSCache; -use fetch::response::Response; - -/// A [request context](http://fetch.spec.whatwg.org/#concept-request-context) -pub enum Context { - Audio, Beacon, CSPreport, Download, Embed, Eventsource, - Favicon, Fetch, Font, Form, Frame, Hyperlink, IFrame, Image, - ImageSet, Import, Internal, Location, Manifest, Object, Ping, - Plugin, Prefetch, Script, ServiceWorker, SharedWorker, Subresource, - Style, Track, Video, Worker, XMLHttpRequest, XSLT -} - -/// A [request context frame type](http://fetch.spec.whatwg.org/#concept-request-context-frame-type) -pub enum ContextFrameType { - Auxiliary, - TopLevel, - Nested, - ContextNone -} - -/// A [referer](http://fetch.spec.whatwg.org/#concept-request-referrer) -pub enum Referer { - RefererNone, - Client, - RefererUrl(Url) -} - -/// A [request mode](http://fetch.spec.whatwg.org/#concept-request-mode) -pub enum RequestMode { - SameOrigin, - NoCORS, - CORSMode, - ForcedPreflightMode -} - -/// Request [credentials mode](http://fetch.spec.whatwg.org/#concept-request-credentials-mode) -pub enum CredentialsMode { - Omit, - CredentialsSameOrigin, - Include -} - -/// [Response tainting](http://fetch.spec.whatwg.org/#concept-request-response-tainting) -pub enum ResponseTainting { - Basic, - CORSTainting, - Opaque -} - -/// A [Request](http://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec -pub struct Request { - pub method: Method, - pub url: Url, - pub headers: HeaderCollection, - pub unsafe_request: bool, - pub body: Option<Vec<u8>>, - pub preserve_content_codings: bool, - // pub client: GlobalRef, // XXXManishearth copy over only the relevant fields of the global scope, - // not the entire scope to avoid the libscript dependency - pub skip_service_worker: bool, - pub context: Context, - pub context_frame_type: ContextFrameType, - pub origin: Option<Url>, - pub force_origin_header: bool, - pub same_origin_data: bool, - pub referer: Referer, - pub authentication: bool, - pub sync: bool, - pub mode: RequestMode, - pub credentials_mode: CredentialsMode, - pub use_url_credentials: bool, - pub manual_redirect: bool, - pub redirect_count: uint, - pub response_tainting: ResponseTainting, - pub cache: Option<Box<CORSCache>> -} - -impl Request { - pub fn new(url: Url, context: Context) -> Request { - Request { - method: Get, - url: url, - headers: HeaderCollection::new(), - unsafe_request: false, - body: None, - preserve_content_codings: false, - skip_service_worker: false, - context: context, - context_frame_type: ContextNone, - origin: None, - force_origin_header: false, - same_origin_data: false, - referer: Client, - authentication: false, - sync: false, - mode: NoCORS, - credentials_mode: Omit, - use_url_credentials: false, - manual_redirect: false, - redirect_count: 0, - response_tainting: Basic, - cache: None - } - } - - /// [Basic fetch](http://fetch.spec.whatwg.org#basic-fetch) - pub fn basic_fetch(&mut self) -> Response { - match self.url.scheme.as_slice() { - "about" => match self.url.non_relative_scheme_data() { - Some(s) if s.as_slice() == "blank" => { - let mut response = Response::new(); - let _ = response.headers.insert_raw("Content-Type".to_string(), b"text/html;charset=utf-8"); - response - }, - _ => Response::network_error() - }, - "http" | "https" => { - self.http_fetch(false, false, false) - }, - "blob" | "data" | "file" | "ftp" => { - // XXXManishearth handle these - fail!("Unimplemented scheme for Fetch") - }, - - _ => Response::network_error() - } - } - - // [HTTP fetch](http://fetch.spec.whatwg.org#http-fetch) - pub fn http_fetch(&mut self, _cors_flag: bool, cors_preflight_flag: bool, _authentication_fetch_flag: bool) -> Response { - let response = Response::new(); - // TODO: Service worker fetch - // Step 3 - // Substep 1 - self.skip_service_worker = true; - // Substep 2 - if cors_preflight_flag { - // XXXManishearth stuff goes here - } - response - } -} diff --git a/src/components/net/fetch/response.rs b/src/components/net/fetch/response.rs deleted file mode 100644 index 359ec6aa394..00000000000 --- a/src/components/net/fetch/response.rs +++ /dev/null @@ -1,144 +0,0 @@ -/* 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/. */ - -use url::Url; -use http::status::{Status, UnregisteredStatus}; -use StatusOk = http::status::Ok; -use http::headers::HeaderEnum; -use http::headers::response::HeaderCollection; -use std::ascii::OwnedStrAsciiExt; -use std::comm::Receiver; - -/// [Response type](http://fetch.spec.whatwg.org/#concept-response-type) -#[deriving(Clone, PartialEq)] -pub enum ResponseType { - Basic, - CORS, - Default, - Error, - Opaque -} - -/// [Response termination reason](http://fetch.spec.whatwg.org/#concept-response-termination-reason) -#[deriving(Clone)] -pub enum TerminationReason { - EndUserAbort, - Fatal, - Timeout -} - -/// The response body can still be pushed to after fetch -/// This provides a way to store unfinished response bodies -#[unstable = "I haven't yet decided exactly how the interface for this will be"] -#[deriving(Clone)] -pub enum ResponseBody { - Empty, // XXXManishearth is this necessary, or is Done(vec![]) enough? - Receiving(Vec<u8>), - Done(Vec<u8>), -} - -#[unstable = "I haven't yet decided exactly how the interface for this will be"] -pub enum ResponseMsg { - Chunk(Vec<u8>), - Finished, - Errored -} - -#[unstable = "I haven't yet decided exactly how the interface for this will be"] -pub struct ResponseLoader { - response: Response, - chan: Receiver<ResponseMsg> -} - -/// A [Response](http://fetch.spec.whatwg.org/#concept-response) as defined by the Fetch spec -#[deriving(Clone)] -pub struct Response { - pub response_type: ResponseType, - pub termination_reason: Option<TerminationReason>, - pub url: Option<Url>, - pub status: Status, - pub headers: HeaderCollection, - pub body: ResponseBody, - /// [Internal response](http://fetch.spec.whatwg.org/#concept-internal-response), only used if the Response is a filtered response - pub internal_response: Option<Box<Response>>, -} - -impl Response { - pub fn new() -> Response { - Response { - response_type: Default, - termination_reason: None, - url: None, - status: StatusOk, - headers: HeaderCollection::new(), - body: Empty, - internal_response: None - } - } - - pub fn network_error() -> Response { - Response { - response_type: Error, - termination_reason: None, - url: None, - status: UnregisteredStatus(0, "".to_string()), - headers: HeaderCollection::new(), - body: Empty, - internal_response: None - } - } - - pub fn is_network_error(&self) -> bool { - match self.response_type { - Error => true, - _ => false - } - } - - /// Convert to a filtered response, of type `filter_type`. - /// Do not use with type Error or Default - pub fn to_filtered(self, filter_type: ResponseType) -> Response { - assert!(filter_type != Error); - assert!(filter_type != Default); - if self.is_network_error() { - return self; - } - let old_headers = self.headers.clone(); - let mut response = self.clone(); - response.internal_response = Some(box self); - match filter_type { - Default | Error => unreachable!(), - Basic => { - let mut headers = HeaderCollection::new(); - for h in old_headers.iter() { - match h.header_name().into_ascii_lower().as_slice() { - "set-cookie" | "set-cookie2" => {}, - _ => headers.insert(h) - } - } - response.headers = headers; - response.response_type = filter_type; - }, - CORS => { - let mut headers = HeaderCollection::new(); - for h in old_headers.iter() { - match h.header_name().into_ascii_lower().as_slice() { - "cache-control" | "content-language" | - "content-type" | "expires" | "last-modified" | "Pragma" => {}, - // XXXManishearth handle Access-Control-Expose-Headers - _ => headers.insert(h) - } - } - response.headers = headers; - response.response_type = filter_type; - }, - Opaque => { - response.headers = HeaderCollection::new(); - response.status = UnregisteredStatus(0, "".to_string()); - response.body = Empty; - } - } - response - } -} diff --git a/src/components/net/file_loader.rs b/src/components/net/file_loader.rs deleted file mode 100644 index 43c3191c600..00000000000 --- a/src/components/net/file_loader.rs +++ /dev/null @@ -1,50 +0,0 @@ -/* 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/. */ - -use resource_task::{ProgressMsg, Metadata, Payload, Done, LoaderTask, start_sending}; - -use std::io; -use std::io::File; -use servo_util::task::spawn_named; - -static READ_SIZE: uint = 8192; - -fn read_all(reader: &mut io::Stream, progress_chan: &Sender<ProgressMsg>) - -> Result<(), String> { - loop { - let mut buf = vec!(); - match reader.push_at_least(READ_SIZE, READ_SIZE, &mut buf) { - Ok(_) => progress_chan.send(Payload(buf)), - Err(e) => match e.kind { - io::EndOfFile => { - if buf.len() > 0 { - progress_chan.send(Payload(buf)); - } - return Ok(()); - } - _ => return Err(e.desc.to_string()), - } - } - } -} - -pub fn factory() -> LoaderTask { - let f: LoaderTask = proc(load_data, start_chan) { - let url = load_data.url; - assert!("file" == url.scheme.as_slice()); - let progress_chan = start_sending(start_chan, Metadata::default(url.clone())); - spawn_named("file_loader", proc() { - match File::open_mode(&Path::new(url.serialize_path().unwrap()), io::Open, io::Read) { - Ok(ref mut reader) => { - let res = read_all(reader as &mut io::Stream, &progress_chan); - progress_chan.send(Done(res)); - } - Err(e) => { - progress_chan.send(Done(Err(e.desc.to_string()))); - } - }; - }); - }; - f -} diff --git a/src/components/net/http_loader.rs b/src/components/net/http_loader.rs deleted file mode 100644 index c7cb56d4231..00000000000 --- a/src/components/net/http_loader.rs +++ /dev/null @@ -1,167 +0,0 @@ -/* 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/. */ - -use resource_task::{Metadata, Payload, Done, LoadResponse, LoadData, LoaderTask, start_sending_opt}; - -use std::collections::hashmap::HashSet; -use http::client::{RequestWriter, NetworkStream}; -use http::headers::HeaderEnum; -use std::io::Reader; -use servo_util::task::spawn_named; -use url::Url; - -pub fn factory() -> LoaderTask { - let f: LoaderTask = proc(url, start_chan) { - spawn_named("http_loader", proc() load(url, start_chan)) - }; - f -} - -fn send_error(url: Url, err: String, start_chan: Sender<LoadResponse>) { - match start_sending_opt(start_chan, Metadata::default(url)) { - Ok(p) => p.send(Done(Err(err))), - _ => {} - }; -} - -fn load(load_data: LoadData, start_chan: Sender<LoadResponse>) { - // FIXME: At the time of writing this FIXME, servo didn't have any central - // location for configuration. If you're reading this and such a - // repository DOES exist, please update this constant to use it. - let max_redirects = 50u; - let mut iters = 0u; - let mut url = load_data.url.clone(); - let mut redirected_to = HashSet::new(); - - // Loop to handle redirects. - loop { - iters = iters + 1; - - if iters > max_redirects { - send_error(url, "too many redirects".to_string(), start_chan); - return; - } - - if redirected_to.contains(&url) { - send_error(url, "redirect loop".to_string(), start_chan); - return; - } - - redirected_to.insert(url.clone()); - - match url.scheme.as_slice() { - "http" | "https" => {} - _ => { - let s = format!("{:s} request, but we don't support that scheme", url.scheme); - send_error(url, s, start_chan); - return; - } - } - - info!("requesting {:s}", url.serialize()); - - let request = RequestWriter::<NetworkStream>::new(load_data.method.clone(), url.clone()); - let mut writer = match request { - Ok(w) => box w, - Err(e) => { - send_error(url, e.desc.to_string(), start_chan); - return; - } - }; - - // Preserve the `host` header set automatically by RequestWriter. - let host = writer.headers.host.clone(); - writer.headers = box load_data.headers.clone(); - writer.headers.host = host; - if writer.headers.accept_encoding.is_none() { - // We currently don't support HTTP Compression (FIXME #2587) - writer.headers.accept_encoding = Some(String::from_str("identity".as_slice())) - } - match load_data.data { - Some(ref data) => { - writer.headers.content_length = Some(data.len()); - match writer.write(data.as_slice()) { - Err(e) => { - send_error(url, e.desc.to_string(), start_chan); - return; - } - _ => {} - } - }, - _ => {} - } - let mut response = match writer.read_response() { - Ok(r) => r, - Err((_, e)) => { - send_error(url, e.desc.to_string(), start_chan); - return; - } - }; - - // Dump headers, but only do the iteration if info!() is enabled. - info!("got HTTP response {:s}, headers:", response.status.to_string()); - info!("{:?}", - for header in response.headers.iter() { - info!(" - {:s}: {:s}", header.header_name(), header.header_value()); - }); - - if 3 == (response.status.code() / 100) { - match response.headers.location { - Some(new_url) => { - // CORS (http://fetch.spec.whatwg.org/#http-fetch, status section, point 9, 10) - match load_data.cors { - Some(ref c) => { - if c.preflight { - // The preflight lied - send_error(url, "Preflight fetch inconsistent with main fetch".to_string(), start_chan); - return; - } else { - // XXXManishearth There are some CORS-related steps here, - // but they don't seem necessary until credentials are implemented - } - } - _ => {} - } - info!("redirecting to {:s}", new_url.serialize()); - url = new_url; - continue; - } - None => () - } - } - - let mut metadata = Metadata::default(url); - metadata.set_content_type(&response.headers.content_type); - metadata.headers = Some(*response.headers.clone()); - metadata.status = response.status.clone(); - - let progress_chan = match start_sending_opt(start_chan, metadata) { - Ok(p) => p, - _ => return - }; - loop { - let mut buf = Vec::with_capacity(1024); - - unsafe { buf.set_len(1024); } - match response.read(buf.as_mut_slice()) { - Ok(len) => { - unsafe { buf.set_len(len); } - if progress_chan.send_opt(Payload(buf)).is_err() { - // The send errors when the receiver is out of scope, - // which will happen if the fetch has timed out (or has been aborted) - // so we don't need to continue with the loading of the file here. - return; - } - } - Err(_) => { - let _ = progress_chan.send_opt(Done(Ok(()))); - break; - } - } - } - - // We didn't get redirected. - break; - } -} diff --git a/src/components/net/image/base.rs b/src/components/net/image/base.rs deleted file mode 100644 index deda4ee8556..00000000000 --- a/src/components/net/image/base.rs +++ /dev/null @@ -1,67 +0,0 @@ -/* 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/. */ - -use std::iter::range_step; -use stb_image = stb_image::image; -use png; - -// FIXME: Images must not be copied every frame. Instead we should atomically -// reference count them. -pub type Image = png::Image; - - -static TEST_IMAGE: &'static [u8] = include_bin!("test.jpeg"); - -pub fn test_image_bin() -> Vec<u8> { - TEST_IMAGE.iter().map(|&x| x).collect() -} - -// TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this. -fn byte_swap(data: &mut [u8]) { - let length = data.len(); - for i in range_step(0, length, 4) { - let r = data[i + 2]; - data[i + 2] = data[i + 0]; - data[i + 0] = r; - } -} - -pub fn load_from_memory(buffer: &[u8]) -> Option<Image> { - if buffer.len() == 0 { - return None; - } - - if png::is_png(buffer) { - match png::load_png_from_memory(buffer) { - Ok(mut png_image) => { - match png_image.pixels { - png::RGB8(ref mut data) | png::RGBA8(ref mut data) => { - byte_swap(data.as_mut_slice()); - } - _ => {} - } - Some(png_image) - } - Err(_err) => None, - } - } else { - // For non-png images, we use stb_image - // Can't remember why we do this. Maybe it's what cairo wants - static FORCE_DEPTH: uint = 4; - - match stb_image::load_from_memory_with_depth(buffer, FORCE_DEPTH, true) { - stb_image::ImageU8(mut image) => { - assert!(image.depth == 4); - byte_swap(image.data.as_mut_slice()); - Some(png::Image { - width: image.width as u32, - height: image.height as u32, - pixels: png::RGBA8(image.data) - }) - } - stb_image::ImageF32(_image) => fail!("HDR images not implemented"), - stb_image::Error(_) => None - } - } -} diff --git a/src/components/net/image/holder.rs b/src/components/net/image/holder.rs deleted file mode 100644 index 11f055aad9d..00000000000 --- a/src/components/net/image/holder.rs +++ /dev/null @@ -1,109 +0,0 @@ -/* 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/. */ - -use image::base::Image; -use image_cache_task::{ImageReady, ImageNotReady, ImageFailed}; -use local_image_cache::LocalImageCache; - -use geom::size::Size2D; -use std::mem; -use sync::{Arc, Mutex}; -use url::Url; - -// FIXME: Nasty coupling here This will be a problem if we want to factor out image handling from -// the network stack. This should probably be factored out into an interface and use dependency -// injection. - -/// A struct to store image data. The image will be loaded once the first time it is requested, -/// and an Arc will be stored. Clones of this Arc are given out on demand. -#[deriving(Clone)] -pub struct ImageHolder { - url: Url, - image: Option<Arc<Box<Image>>>, - cached_size: Size2D<int>, - local_image_cache: Arc<Mutex<LocalImageCache>>, -} - -impl ImageHolder { - pub fn new(url: Url, local_image_cache: Arc<Mutex<LocalImageCache>>) -> ImageHolder { - debug!("ImageHolder::new() {}", url.serialize()); - let holder = ImageHolder { - url: url, - image: None, - cached_size: Size2D(0,0), - local_image_cache: local_image_cache.clone(), - }; - - // Tell the image cache we're going to be interested in this url - // FIXME: These two messages must be sent to prep an image for use - // but they are intended to be spread out in time. Ideally prefetch - // should be done as early as possible and decode only once we - // are sure that the image will be used. - { - let val = holder.local_image_cache.lock(); - let mut local_image_cache = val; - local_image_cache.prefetch(&holder.url); - local_image_cache.decode(&holder.url); - } - - holder - } - - /// This version doesn't perform any computation, but may be stale w.r.t. newly-available image - /// data that determines size. - /// - /// The intent is that the impure version is used during layout when dimensions are used for - /// computing layout. - pub fn size(&self) -> Size2D<int> { - self.cached_size - } - - /// Query and update the current image size. - pub fn get_size(&mut self) -> Option<Size2D<int>> { - debug!("get_size() {}", self.url.serialize()); - self.get_image().map(|img| { - self.cached_size = Size2D(img.width as int, - img.height as int); - self.cached_size.clone() - }) - } - - pub fn get_image_if_present(&self) -> Option<Arc<Box<Image>>> { - debug!("get_image_if_present() {}", self.url.serialize()); - self.image.clone() - } - - pub fn get_image(&mut self) -> Option<Arc<Box<Image>>> { - debug!("get_image() {}", self.url.serialize()); - - // If this is the first time we've called this function, load - // the image and store it for the future - if self.image.is_none() { - let port = { - let val = self.local_image_cache.lock(); - let mut local_image_cache = val; - local_image_cache.get_image(&self.url) - }; - match port.recv() { - ImageReady(image) => { - self.image = Some(image); - } - ImageNotReady => { - debug!("image not ready for {:s}", self.url.serialize()); - } - ImageFailed => { - debug!("image decoding failed for {:s}", self.url.serialize()); - } - } - } - - // Clone isn't pure so we have to swap out the mutable image option - let image = mem::replace(&mut self.image, None); - let result = image.clone(); - mem::replace(&mut self.image, image); - - return result; - } -} - diff --git a/src/components/net/image/test.jpeg b/src/components/net/image/test.jpeg Binary files differdeleted file mode 100644 index 1a0bdb7acd1..00000000000 --- a/src/components/net/image/test.jpeg +++ /dev/null diff --git a/src/components/net/image_cache_task.rs b/src/components/net/image_cache_task.rs deleted file mode 100644 index de0c978c3cf..00000000000 --- a/src/components/net/image_cache_task.rs +++ /dev/null @@ -1,993 +0,0 @@ -/* 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/. */ - -use image::base::{Image, load_from_memory}; -use resource_task; -use resource_task::{LoadData, ResourceTask}; - -use std::comm::{channel, Receiver, Sender}; -use std::collections::hashmap::HashMap; -use std::mem::replace; -use std::task::spawn; -use std::result; -use sync::{Arc, Mutex}; -use serialize::{Encoder, Encodable}; -use url::Url; - -pub enum Msg { - /// Tell the cache that we may need a particular image soon. Must be posted - /// before Decode - Prefetch(Url), - - /// Tell the cache to decode an image. Must be posted before GetImage/WaitForImage - Decode(Url), - - /// Request an Image object for a URL. If the image is not is not immediately - /// available then ImageNotReady is returned. - GetImage(Url, Sender<ImageResponseMsg>), - - /// Wait for an image to become available (or fail to load). - WaitForImage(Url, Sender<ImageResponseMsg>), - - /// Clients must wait for a response before shutting down the ResourceTask - Exit(Sender<()>), - - /// Used by the prefetch tasks to post back image binaries - StorePrefetchedImageData(Url, Result<Vec<u8>, ()>), - - /// Used by the decoder tasks to post decoded images back to the cache - StoreImage(Url, Option<Arc<Box<Image>>>), - - /// For testing - WaitForStore(Sender<()>), - - /// For testing - WaitForStorePrefetched(Sender<()>), -} - -#[deriving(Clone)] -pub enum ImageResponseMsg { - ImageReady(Arc<Box<Image>>), - ImageNotReady, - ImageFailed -} - -impl PartialEq for ImageResponseMsg { - fn eq(&self, other: &ImageResponseMsg) -> bool { - match (self, other) { - (&ImageReady(..), &ImageReady(..)) => fail!("unimplemented comparison"), - (&ImageNotReady, &ImageNotReady) => true, - (&ImageFailed, &ImageFailed) => true, - - (&ImageReady(..), _) | (&ImageNotReady, _) | (&ImageFailed, _) => false - } - } -} - -#[deriving(Clone)] -pub struct ImageCacheTask { - chan: Sender<Msg>, -} - -impl<E, S: Encoder<E>> Encodable<S, E> for ImageCacheTask { - fn encode(&self, _: &mut S) -> Result<(), E> { - Ok(()) - } -} - -type DecoderFactory = fn() -> proc(&[u8]) -> Option<Image>; - -impl ImageCacheTask { - pub fn new(resource_task: ResourceTask) -> ImageCacheTask { - let (chan, port) = channel(); - let chan_clone = chan.clone(); - - spawn(proc() { - let mut cache = ImageCache { - resource_task: resource_task, - port: port, - chan: chan_clone, - state_map: HashMap::new(), - wait_map: HashMap::new(), - need_exit: None - }; - cache.run(); - }); - - ImageCacheTask { - chan: chan, - } - } - - pub fn new_sync(resource_task: ResourceTask) -> ImageCacheTask { - let (chan, port) = channel(); - - spawn(proc() { - let inner_cache = ImageCacheTask::new(resource_task); - - loop { - let msg: Msg = port.recv(); - - match msg { - GetImage(url, response) => { - inner_cache.send(WaitForImage(url, response)); - } - Exit(response) => { - inner_cache.send(Exit(response)); - break; - } - msg => inner_cache.send(msg) - } - } - }); - - ImageCacheTask { - chan: chan, - } - } -} - -struct ImageCache { - /// A handle to the resource task for fetching the image binaries - resource_task: ResourceTask, - /// The port on which we'll receive client requests - port: Receiver<Msg>, - /// A copy of the shared chan to give to child tasks - chan: Sender<Msg>, - /// The state of processsing an image for a URL - state_map: HashMap<Url, ImageState>, - /// List of clients waiting on a WaitForImage response - wait_map: HashMap<Url, Arc<Mutex<Vec<Sender<ImageResponseMsg>>>>>, - need_exit: Option<Sender<()>>, -} - -#[deriving(Clone)] -enum ImageState { - Init, - Prefetching(AfterPrefetch), - Prefetched(Vec<u8>), - Decoding, - Decoded(Arc<Box<Image>>), - Failed -} - -#[deriving(Clone)] -enum AfterPrefetch { - DoDecode, - DoNotDecode -} - -impl ImageCache { - pub fn run(&mut self) { - let mut store_chan: Option<Sender<()>> = None; - let mut store_prefetched_chan: Option<Sender<()>> = None; - - loop { - let msg = self.port.recv(); - - debug!("image_cache_task: received: {:?}", msg); - - match msg { - Prefetch(url) => self.prefetch(url), - StorePrefetchedImageData(url, data) => { - store_prefetched_chan.map(|chan| { - chan.send(()); - }); - store_prefetched_chan = None; - - self.store_prefetched_image_data(url, data); - } - Decode(url) => self.decode(url), - StoreImage(url, image) => { - store_chan.map(|chan| { - chan.send(()); - }); - store_chan = None; - - self.store_image(url, image) - } - GetImage(url, response) => self.get_image(url, response), - WaitForImage(url, response) => { - self.wait_for_image(url, response) - } - WaitForStore(chan) => store_chan = Some(chan), - WaitForStorePrefetched(chan) => store_prefetched_chan = Some(chan), - Exit(response) => { - assert!(self.need_exit.is_none()); - self.need_exit = Some(response); - } - } - - let need_exit = replace(&mut self.need_exit, None); - - match need_exit { - Some(response) => { - // Wait until we have no outstanding requests and subtasks - // before exiting - let mut can_exit = true; - for (_, state) in self.state_map.iter() { - match *state { - Prefetching(..) => can_exit = false, - Decoding => can_exit = false, - - Init | Prefetched(..) | Decoded(..) | Failed => () - } - } - - if can_exit { - response.send(()); - break; - } else { - self.need_exit = Some(response); - } - } - None => () - } - } - } - - fn get_state(&self, url: Url) -> ImageState { - match self.state_map.find(&url) { - Some(state) => state.clone(), - None => Init - } - } - - fn set_state(&mut self, url: Url, state: ImageState) { - self.state_map.insert(url, state); - } - - fn prefetch(&mut self, url: Url) { - match self.get_state(url.clone()) { - Init => { - let to_cache = self.chan.clone(); - let resource_task = self.resource_task.clone(); - let url_clone = url.clone(); - - spawn(proc() { - let url = url_clone; - debug!("image_cache_task: started fetch for {:s}", url.serialize()); - - let image = load_image_data(url.clone(), resource_task.clone()); - - let result = if image.is_ok() { - Ok(image.unwrap()) - } else { - Err(()) - }; - to_cache.send(StorePrefetchedImageData(url.clone(), result)); - debug!("image_cache_task: ended fetch for {:s}", url.serialize()); - }); - - self.set_state(url, Prefetching(DoNotDecode)); - } - - Prefetching(..) | Prefetched(..) | Decoding | Decoded(..) | Failed => { - // We've already begun working on this image - } - } - } - - fn store_prefetched_image_data(&mut self, url: Url, data: Result<Vec<u8>, ()>) { - match self.get_state(url.clone()) { - Prefetching(next_step) => { - match data { - Ok(data) => { - self.set_state(url.clone(), Prefetched(data)); - match next_step { - DoDecode => self.decode(url), - _ => () - } - } - Err(..) => { - self.set_state(url.clone(), Failed); - self.purge_waiters(url, || ImageFailed); - } - } - } - - Init - | Prefetched(..) - | Decoding - | Decoded(..) - | Failed => { - fail!("wrong state for storing prefetched image") - } - } - } - - fn decode(&mut self, url: Url) { - match self.get_state(url.clone()) { - Init => fail!("decoding image before prefetch"), - - Prefetching(DoNotDecode) => { - // We don't have the data yet, queue up the decode - self.set_state(url, Prefetching(DoDecode)) - } - - Prefetching(DoDecode) => { - // We don't have the data yet, but the decode request is queued up - } - - Prefetched(data) => { - let to_cache = self.chan.clone(); - let url_clone = url.clone(); - - spawn(proc() { - let url = url_clone; - debug!("image_cache_task: started image decode for {:s}", url.serialize()); - let image = load_from_memory(data.as_slice()); - let image = if image.is_some() { - Some(Arc::new(box image.unwrap())) - } else { - None - }; - to_cache.send(StoreImage(url.clone(), image)); - debug!("image_cache_task: ended image decode for {:s}", url.serialize()); - }); - - self.set_state(url, Decoding); - } - - Decoding | Decoded(..) | Failed => { - // We've already begun decoding - } - } - } - - fn store_image(&mut self, url: Url, image: Option<Arc<Box<Image>>>) { - - match self.get_state(url.clone()) { - Decoding => { - match image { - Some(image) => { - self.set_state(url.clone(), Decoded(image.clone())); - self.purge_waiters(url, || ImageReady(image.clone()) ); - } - None => { - self.set_state(url.clone(), Failed); - self.purge_waiters(url, || ImageFailed ); - } - } - } - - Init - | Prefetching(..) - | Prefetched(..) - | Decoded(..) - | Failed => { - fail!("incorrect state in store_image") - } - } - - } - - fn purge_waiters(&mut self, url: Url, f: || -> ImageResponseMsg) { - match self.wait_map.pop(&url) { - Some(waiters) => { - let mut items = waiters.lock(); - for response in items.iter() { - response.send(f()); - } - } - None => () - } - } - - fn get_image(&self, url: Url, response: Sender<ImageResponseMsg>) { - match self.get_state(url.clone()) { - Init => fail!("request for image before prefetch"), - Prefetching(DoDecode) => response.send(ImageNotReady), - Prefetching(DoNotDecode) | Prefetched(..) => fail!("request for image before decode"), - Decoding => response.send(ImageNotReady), - Decoded(image) => response.send(ImageReady(image.clone())), - Failed => response.send(ImageFailed), - } - } - - fn wait_for_image(&mut self, url: Url, response: Sender<ImageResponseMsg>) { - match self.get_state(url.clone()) { - Init => fail!("request for image before prefetch"), - - Prefetching(DoNotDecode) | Prefetched(..) => fail!("request for image before decode"), - - Prefetching(DoDecode) | Decoding => { - // We don't have this image yet - if self.wait_map.contains_key(&url) { - let waiters = self.wait_map.find_mut(&url).unwrap(); - let mut response = Some(response); - let mut items = waiters.lock(); - items.push(response.take().unwrap()); - } else { - let response = vec!(response); - let wrapped = Arc::new(Mutex::new(response)); - self.wait_map.insert(url, wrapped); - } - } - - Decoded(image) => { - response.send(ImageReady(image.clone())); - } - - Failed => { - response.send(ImageFailed); - } - } - } - -} - - -pub trait ImageCacheTaskClient { - fn exit(&self); -} - -impl ImageCacheTaskClient for ImageCacheTask { - fn exit(&self) { - let (response_chan, response_port) = channel(); - self.send(Exit(response_chan)); - response_port.recv(); - } -} - -impl ImageCacheTask { - pub fn send(&self, msg: Msg) { - self.chan.send(msg); - } - - #[cfg(test)] - fn wait_for_store(&self) -> Receiver<()> { - let (chan, port) = channel(); - self.send(WaitForStore(chan)); - port - } - - #[cfg(test)] - fn wait_for_store_prefetched(&self) -> Receiver<()> { - let (chan, port) = channel(); - self.send(WaitForStorePrefetched(chan)); - port - } -} - -fn load_image_data(url: Url, resource_task: ResourceTask) -> Result<Vec<u8>, ()> { - let (response_chan, response_port) = channel(); - resource_task.send(resource_task::Load(LoadData::new(url), response_chan)); - - let mut image_data = vec!(); - - let progress_port = response_port.recv().progress_port; - loop { - match progress_port.recv() { - resource_task::Payload(data) => { - image_data.push_all(data.as_slice()); - } - resource_task::Done(result::Ok(..)) => { - return Ok(image_data.move_iter().collect()); - } - resource_task::Done(result::Err(..)) => { - return Err(()); - } - } - } -} - - -pub fn spawn_listener<A: Send>(f: proc(Receiver<A>):Send) -> Sender<A> { - let (setup_chan, setup_port) = channel(); - - spawn(proc() { - let (chan, port) = channel(); - setup_chan.send(chan); - f(port); - }); - setup_port.recv() -} - - -#[cfg(test)] -mod tests { - use super::*; - - use resource_task; - use resource_task::{ResourceTask, Metadata, start_sending}; - use image::base::test_image_bin; - use std::comm; - use url::Url; - - trait Closure { - fn invoke(&self, _response: Sender<resource_task::ProgressMsg>) { } - } - struct DoesNothing; - impl Closure for DoesNothing { } - - struct JustSendOK { - url_requested_chan: Sender<()>, - } - impl Closure for JustSendOK { - fn invoke(&self, response: Sender<resource_task::ProgressMsg>) { - self.url_requested_chan.send(()); - response.send(resource_task::Done(Ok(()))); - } - } - - struct SendTestImage; - impl Closure for SendTestImage { - fn invoke(&self, response: Sender<resource_task::ProgressMsg>) { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(Ok(()))); - } - } - - struct SendBogusImage; - impl Closure for SendBogusImage { - fn invoke(&self, response: Sender<resource_task::ProgressMsg>) { - response.send(resource_task::Payload(vec!())); - response.send(resource_task::Done(Ok(()))); - } - } - - struct SendTestImageErr; - impl Closure for SendTestImageErr { - fn invoke(&self, response: Sender<resource_task::ProgressMsg>) { - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(Err("".to_string()))); - } - } - - struct WaitSendTestImage { - wait_port: Receiver<()>, - } - impl Closure for WaitSendTestImage { - fn invoke(&self, response: Sender<resource_task::ProgressMsg>) { - // Don't send the data until after the client requests - // the image - self.wait_port.recv(); - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(Ok(()))); - } - } - - struct WaitSendTestImageErr { - wait_port: Receiver<()>, - } - impl Closure for WaitSendTestImageErr { - fn invoke(&self, response: Sender<resource_task::ProgressMsg>) { - // Don't send the data until after the client requests - // the image - self.wait_port.recv(); - response.send(resource_task::Payload(test_image_bin())); - response.send(resource_task::Done(Err("".to_string()))); - } - } - - fn mock_resource_task<T: Closure+Send>(on_load: Box<T>) -> ResourceTask { - spawn_listener(proc(port: Receiver<resource_task::ControlMsg>) { - loop { - match port.recv() { - resource_task::Load(_, response) => { - let chan = start_sending(response, Metadata::default( - Url::parse("file:///fake").unwrap())); - on_load.invoke(chan); - } - resource_task::Exit => break - } - } - }) - } - - #[test] - fn should_exit_on_request() { - let mock_resource_task = mock_resource_task(box DoesNothing); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - #[should_fail] - fn should_fail_if_unprefetched_image_is_requested() { - let mock_resource_task = mock_resource_task(box DoesNothing); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - let (chan, port) = channel(); - image_cache_task.send(GetImage(url, chan)); - port.recv(); - } - - #[test] - fn should_request_url_from_resource_task_on_prefetch() { - let (url_requested_chan, url_requested) = channel(); - - let mock_resource_task = mock_resource_task(box JustSendOK { url_requested_chan: url_requested_chan}); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url)); - url_requested.recv(); - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_not_request_url_from_resource_task_on_multiple_prefetches() { - let (url_requested_chan, url_requested) = comm::channel(); - - let mock_resource_task = mock_resource_task(box JustSendOK { url_requested_chan: url_requested_chan}); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Prefetch(url)); - url_requested.recv(); - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - match url_requested.try_recv() { - Err(_) => (), - Ok(_) => fail!(), - }; - } - - #[test] - fn should_return_image_not_ready_if_data_has_not_arrived() { - let (wait_chan, wait_port) = comm::channel(); - - let mock_resource_task = mock_resource_task(box WaitSendTestImage{wait_port: wait_port}); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url, response_chan)); - assert!(response_port.recv() == ImageNotReady); - wait_chan.send(()); - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_return_decoded_image_data_if_data_has_arrived() { - let mock_resource_task = mock_resource_task(box SendTestImage); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - let join_port = image_cache_task.wait_for_store(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - join_port.recv(); - - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url, response_chan)); - match response_port.recv() { - ImageReady(_) => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_return_decoded_image_data_for_multiple_requests() { - let mock_resource_task = mock_resource_task(box SendTestImage); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - let join_port = image_cache_task.wait_for_store(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - join_port.recv(); - - for _ in range(0u32, 2u32) { - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url.clone(), response_chan)); - match response_port.recv() { - ImageReady(_) => (), - _ => fail!("bleh") - } - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_not_request_image_from_resource_task_if_image_is_already_available() { - let (image_bin_sent_chan, image_bin_sent) = comm::channel(); - - let (resource_task_exited_chan, resource_task_exited) = comm::channel(); - - let mock_resource_task = spawn_listener(proc(port: Receiver<resource_task::ControlMsg>) { - loop { - match port.recv() { - resource_task::Load(_, response) => { - let chan = start_sending(response, Metadata::default( - Url::parse("file:///fake").unwrap())); - chan.send(resource_task::Payload(test_image_bin())); - chan.send(resource_task::Done(Ok(()))); - image_bin_sent_chan.send(()); - } - resource_task::Exit => { - resource_task_exited_chan.send(()); - break - } - } - } - }); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - image_bin_sent.recv(); - - image_cache_task.send(Prefetch(url.clone())); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - - resource_task_exited.recv(); - - // Our resource task should not have received another request for the image - // because it's already cached - match image_bin_sent.try_recv() { - Err(_) => (), - Ok(_) => fail!(), - } - } - - #[test] - fn should_not_request_image_from_resource_task_if_image_fetch_already_failed() { - let (image_bin_sent_chan, image_bin_sent) = comm::channel(); - - let (resource_task_exited_chan, resource_task_exited) = comm::channel(); - - let mock_resource_task = spawn_listener(proc(port: Receiver<resource_task::ControlMsg>) { - loop { - match port.recv() { - resource_task::Load(_, response) => { - let chan = start_sending(response, Metadata::default( - Url::parse("file:///fake").unwrap())); - chan.send(resource_task::Payload(test_image_bin())); - chan.send(resource_task::Done(Err("".to_string()))); - image_bin_sent_chan.send(()); - } - resource_task::Exit => { - resource_task_exited_chan.send(()); - break - } - } - } - }); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - image_bin_sent.recv(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - - resource_task_exited.recv(); - - // Our resource task should not have received another request for the image - // because it's already cached - match image_bin_sent.try_recv() { - Err(_) => (), - Ok(_) => fail!(), - } - } - - #[test] - fn should_return_failed_if_image_bin_cannot_be_fetched() { - let mock_resource_task = mock_resource_task(box SendTestImageErr); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - let join_port = image_cache_task.wait_for_store_prefetched(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - join_port.recv(); - - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url, response_chan)); - match response_port.recv() { - ImageFailed => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_return_failed_for_multiple_get_image_requests_if_image_bin_cannot_be_fetched() { - let mock_resource_task = mock_resource_task(box SendTestImageErr); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - let join_port = image_cache_task.wait_for_store_prefetched(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - join_port.recv(); - - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url.clone(), response_chan)); - match response_port.recv() { - ImageFailed => (), - _ => fail!("bleh") - } - - // And ask again, we should get the same response - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url, response_chan)); - match response_port.recv() { - ImageFailed => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_return_failed_if_image_decode_fails() { - let mock_resource_task = mock_resource_task(box SendBogusImage); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - let join_port = image_cache_task.wait_for_store(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - join_port.recv(); - - // Make the request - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url, response_chan)); - - match response_port.recv() { - ImageFailed => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_return_image_on_wait_if_image_is_already_loaded() { - let mock_resource_task = mock_resource_task(box SendTestImage); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - let join_port = image_cache_task.wait_for_store(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - // Wait until our mock resource task has sent the image to the image cache - join_port.recv(); - - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(WaitForImage(url, response_chan)); - match response_port.recv() { - ImageReady(..) => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_return_image_on_wait_if_image_is_not_yet_loaded() { - let (wait_chan, wait_port) = comm::channel(); - - let mock_resource_task = mock_resource_task(box WaitSendTestImage {wait_port: wait_port}); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(WaitForImage(url, response_chan)); - - wait_chan.send(()); - - match response_port.recv() { - ImageReady(..) => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn should_return_image_failed_on_wait_if_image_fails_to_load() { - let (wait_chan, wait_port) = comm::channel(); - - let mock_resource_task = mock_resource_task(box WaitSendTestImageErr{wait_port: wait_port}); - - let image_cache_task = ImageCacheTask::new(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(WaitForImage(url, response_chan)); - - wait_chan.send(()); - - match response_port.recv() { - ImageFailed => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } - - #[test] - fn sync_cache_should_wait_for_images() { - let mock_resource_task = mock_resource_task(box SendTestImage); - - let image_cache_task = ImageCacheTask::new_sync(mock_resource_task.clone()); - let url = Url::parse("file:///").unwrap(); - - image_cache_task.send(Prefetch(url.clone())); - image_cache_task.send(Decode(url.clone())); - - let (response_chan, response_port) = comm::channel(); - image_cache_task.send(GetImage(url, response_chan)); - match response_port.recv() { - ImageReady(_) => (), - _ => fail!("bleh") - } - - image_cache_task.exit(); - mock_resource_task.send(resource_task::Exit); - } -} diff --git a/src/components/net/local_image_cache.rs b/src/components/net/local_image_cache.rs deleted file mode 100644 index 1427c831654..00000000000 --- a/src/components/net/local_image_cache.rs +++ /dev/null @@ -1,166 +0,0 @@ -/* 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/. */ - -/*! -An adapter for ImageCacheTask that does local caching to avoid -extra message traffic, it also avoids waiting on the same image -multiple times and thus triggering reflows multiple times. -*/ - -use image_cache_task::{Decode, GetImage, ImageCacheTask, ImageFailed, ImageNotReady, ImageReady}; -use image_cache_task::{ImageResponseMsg, Prefetch, WaitForImage}; - -use std::comm::{Receiver, channel}; -use std::collections::hashmap::HashMap; -use servo_util::task::spawn_named; -use url::Url; - -pub trait ImageResponder { - fn respond(&self) -> proc(ImageResponseMsg):Send; -} - -pub struct LocalImageCache { - image_cache_task: ImageCacheTask, - round_number: uint, - on_image_available: Option<Box<ImageResponder+Send>>, - state_map: HashMap<Url, ImageState> -} - -impl LocalImageCache { - pub fn new(image_cache_task: ImageCacheTask) -> LocalImageCache { - LocalImageCache { - image_cache_task: image_cache_task, - round_number: 1, - on_image_available: None, - state_map: HashMap::new() - } - } -} - -#[deriving(Clone)] -struct ImageState { - prefetched: bool, - decoded: bool, - last_request_round: uint, - last_response: ImageResponseMsg -} - -impl LocalImageCache { - /// The local cache will only do a single remote request for a given - /// URL in each 'round'. Layout should call this each time it begins - pub fn next_round(&mut self, on_image_available: Box<ImageResponder+Send>) { - self.round_number += 1; - self.on_image_available = Some(on_image_available); - } - - pub fn prefetch(&mut self, url: &Url) { - { - let state = self.get_state(url); - if state.prefetched { - return - } - - state.prefetched = true; - } - - self.image_cache_task.send(Prefetch((*url).clone())); - } - - pub fn decode(&mut self, url: &Url) { - { - let state = self.get_state(url); - if state.decoded { - return - } - state.decoded = true; - } - - self.image_cache_task.send(Decode((*url).clone())); - } - - // FIXME: Should return a Future - pub fn get_image(&mut self, url: &Url) -> Receiver<ImageResponseMsg> { - { - let round_number = self.round_number; - let state = self.get_state(url); - - // Save the previous round number for comparison - let last_round = state.last_request_round; - // Set the current round number for this image - state.last_request_round = round_number; - - match state.last_response { - ImageReady(ref image) => { - let (chan, port) = channel(); - chan.send(ImageReady(image.clone())); - return port; - } - ImageNotReady => { - if last_round == round_number { - let (chan, port) = channel(); - chan.send(ImageNotReady); - return port; - } else { - // We haven't requested the image from the - // remote cache this round - } - } - ImageFailed => { - let (chan, port) = channel(); - chan.send(ImageFailed); - return port; - } - } - } - - let (response_chan, response_port) = channel(); - self.image_cache_task.send(GetImage((*url).clone(), response_chan)); - - let response = response_port.recv(); - match response { - ImageNotReady => { - // Need to reflow when the image is available - // FIXME: Instead we should be just passing a Future - // to the caller, then to the display list. Finally, - // the compositor should be resonsible for waiting - // on the image to load and triggering layout - let image_cache_task = self.image_cache_task.clone(); - assert!(self.on_image_available.is_some()); - let on_image_available: proc(ImageResponseMsg):Send = self.on_image_available.as_ref().unwrap().respond(); - let url = (*url).clone(); - spawn_named("LocalImageCache", proc() { - let (response_chan, response_port) = channel(); - image_cache_task.send(WaitForImage(url.clone(), response_chan)); - on_image_available(response_port.recv()); - }); - } - _ => () - } - - // Put a copy of the response in the cache - let response_copy = match response { - ImageReady(ref image) => ImageReady(image.clone()), - ImageNotReady => ImageNotReady, - ImageFailed => ImageFailed - }; - self.get_state(url).last_response = response_copy; - - let (chan, port) = channel(); - chan.send(response); - return port; - } - - fn get_state<'a>(&'a mut self, url: &Url) -> &'a mut ImageState { - let state = self.state_map.find_or_insert_with(url.clone(), |_| { - let new_state = ImageState { - prefetched: false, - decoded: false, - last_request_round: 0, - last_response: ImageNotReady - }; - new_state - }); - state - } -} diff --git a/src/components/net/net.rs b/src/components/net/net.rs deleted file mode 100644 index 94290bdd7ff..00000000000 --- a/src/components/net/net.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* 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/. */ - -#![crate_name = "net"] -#![crate_type = "rlib"] - -#![feature(default_type_params, globs, managed_boxes, phase)] - -extern crate debug; -extern crate collections; -extern crate geom; -extern crate http; -extern crate png; -#[phase(plugin, link)] -extern crate log; -extern crate serialize; -extern crate servo_util = "util"; -extern crate stb_image; -extern crate sync; -extern crate time; -extern crate url; - -/// Image handling. -/// -/// It may be surprising that this goes in the network crate as opposed to the graphics crate. -/// However, image handling is generally very integrated with the network stack (especially where -/// caching is involved) and as a result it must live in here. -pub mod image { - pub mod base; - pub mod holder; -} - -pub mod file_loader; -pub mod http_loader; -pub mod data_loader; -pub mod image_cache_task; -pub mod local_image_cache; -pub mod resource_task; - -/// An implementation of the [Fetch spec](http://fetch.spec.whatwg.org/) -pub mod fetch { - #![allow(dead_code)] // XXXManishearth this is only temporary until the Fetch mod starts being used - pub mod request; - pub mod response; - pub mod cors_cache; -} diff --git a/src/components/net/resource_task.rs b/src/components/net/resource_task.rs deleted file mode 100644 index bdc1c3f2339..00000000000 --- a/src/components/net/resource_task.rs +++ /dev/null @@ -1,267 +0,0 @@ -/* 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 data_loader; - -use std::comm::{channel, Receiver, Sender}; -use std::task::TaskBuilder; -use std::os; -use http::headers::content_type::MediaType; -use ResponseHeaderCollection = http::headers::response::HeaderCollection; -use RequestHeaderCollection = http::headers::request::HeaderCollection; -use http::method::{Method, Get}; -use url::Url; - -use StatusOk = http::status::Ok; -use http::status::Status; - - -pub enum ControlMsg { - /// Request the data associated with a particular URL - Load(LoadData, Sender<LoadResponse>), - Exit -} - -#[deriving(Clone)] -pub struct LoadData { - pub url: Url, - pub method: Method, - pub headers: RequestHeaderCollection, - pub data: Option<Vec<u8>>, - pub cors: Option<ResourceCORSData> -} - -impl LoadData { - pub fn new(url: Url) -> LoadData { - LoadData { - url: url, - method: Get, - headers: RequestHeaderCollection::new(), - data: None, - cors: None - } - } -} - -#[deriving(Clone)] -pub struct ResourceCORSData { - /// CORS Preflight flag - pub preflight: bool, - /// Origin of CORS Request - pub origin: Url -} - -/// Metadata about a loaded resource, such as is obtained from HTTP headers. -pub struct Metadata { - /// Final URL after redirects. - pub final_url: Url, - - /// MIME type / subtype. - pub content_type: Option<(String, String)>, - - /// Character set. - pub charset: Option<String>, - - /// Headers - pub headers: Option<ResponseHeaderCollection>, - - /// HTTP Status - pub status: Status -} - -impl Metadata { - /// Metadata with defaults for everything optional. - pub fn default(url: Url) -> Metadata { - Metadata { - final_url: url, - content_type: None, - charset: None, - headers: None, - status: StatusOk // http://fetch.spec.whatwg.org/#concept-response-status-message - } - } - - /// Extract the parts of a MediaType that we care about. - pub fn set_content_type(&mut self, content_type: &Option<MediaType>) { - match *content_type { - None => (), - Some(MediaType { type_: ref type_, - subtype: ref subtype, - parameters: ref parameters }) => { - self.content_type = Some((type_.clone(), subtype.clone())); - for &(ref k, ref v) in parameters.iter() { - if "charset" == k.as_slice() { - self.charset = Some(v.clone()); - } - } - } - } - } -} - -/// Message sent in response to `Load`. Contains metadata, and a port -/// for receiving the data. -/// -/// Even if loading fails immediately, we send one of these and the -/// progress_port will provide the error. -pub struct LoadResponse { - /// Metadata, such as from HTTP headers. - pub metadata: Metadata, - /// Port for reading data. - pub progress_port: Receiver<ProgressMsg>, -} - -/// Messages sent in response to a `Load` message -#[deriving(PartialEq,Show)] -pub enum ProgressMsg { - /// Binary data - there may be multiple of these - Payload(Vec<u8>), - /// Indicates loading is complete, either successfully or not - Done(Result<(), String>) -} - -/// For use by loaders in responding to a Load message. -pub fn start_sending(start_chan: Sender<LoadResponse>, metadata: Metadata) -> Sender<ProgressMsg> { - start_sending_opt(start_chan, metadata).ok().unwrap() -} - -/// For use by loaders in responding to a Load message. -pub fn start_sending_opt(start_chan: Sender<LoadResponse>, metadata: Metadata) -> Result<Sender<ProgressMsg>, ()> { - let (progress_chan, progress_port) = channel(); - let result = start_chan.send_opt(LoadResponse { - metadata: metadata, - progress_port: progress_port, - }); - match result { - Ok(_) => Ok(progress_chan), - Err(_) => Err(()) - } -} - -/// Convenience function for synchronously loading a whole resource. -pub fn load_whole_resource(resource_task: &ResourceTask, url: Url) - -> Result<(Metadata, Vec<u8>), String> { - let (start_chan, start_port) = channel(); - resource_task.send(Load(LoadData::new(url), start_chan)); - let response = start_port.recv(); - - let mut buf = vec!(); - loop { - match response.progress_port.recv() { - Payload(data) => buf.push_all(data.as_slice()), - Done(Ok(())) => return Ok((response.metadata, buf)), - Done(Err(e)) => return Err(e) - } - } -} - -/// Handle to a resource task -pub type ResourceTask = Sender<ControlMsg>; - -pub type LoaderTask = proc(load_data: LoadData, Sender<LoadResponse>); - -/** -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 -pub fn new_resource_task() -> ResourceTask { - let (setup_chan, setup_port) = channel(); - let builder = TaskBuilder::new().named("ResourceManager"); - builder.spawn(proc() { - ResourceManager::new(setup_port).start(); - }); - setup_chan -} - -struct ResourceManager { - from_client: Receiver<ControlMsg>, -} - - -impl ResourceManager { - fn new(from_client: Receiver<ControlMsg>) -> ResourceManager { - ResourceManager { - from_client : from_client, - } - } -} - - -impl ResourceManager { - fn start(&self) { - loop { - match self.from_client.recv() { - Load(load_data, start_chan) => { - self.load(load_data, start_chan) - } - Exit => { - break - } - } - } - } - - fn load(&self, mut load_data: LoadData, start_chan: Sender<LoadResponse>) { - let loader = match load_data.url.scheme.as_slice() { - "file" => file_loader::factory(), - "http" | "https" => http_loader::factory(), - "data" => data_loader::factory(), - "about" => { - match load_data.url.non_relative_scheme_data().unwrap() { - "crash" => fail!("Loading the about:crash URL."), - "failure" => { - // FIXME: Find a way to load this without relying on the `../src` directory. - let mut path = os::self_exe_path().expect("can't get exe path"); - path.pop(); - path.push_many(["src", "test", "html", "failure.html"]); - load_data.url = Url::from_file_path(&path).unwrap(); - file_loader::factory() - } - _ => { - start_sending(start_chan, Metadata::default(load_data.url)) - .send(Done(Err("Unknown about: URL.".to_string()))); - return - } - } - }, - _ => { - debug!("resource_task: no loader for scheme {:s}", load_data.url.scheme); - start_sending(start_chan, Metadata::default(load_data.url)) - .send(Done(Err("no loader for scheme".to_string()))); - return - } - }; - debug!("resource_task: loading url: {:s}", load_data.url.serialize()); - loader(load_data, start_chan); - } -} - -#[test] -fn test_exit() { - let resource_task = new_resource_task(); - resource_task.send(Exit); -} - -#[test] -fn test_bad_scheme() { - let resource_task = new_resource_task(); - let (start_chan, start) = channel(); - let url = Url::parse("bogus://whatever").unwrap(); - resource_task.send(Load(LoadData::new(url), start_chan)); - let response = start.recv(); - match response.progress_port.recv() { - Done(result) => { assert!(result.is_err()) } - _ => fail!("bleh") - } - resource_task.send(Exit); -} |