mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
Request header and postdata support for XHR
This commit is contained in:
parent
096eea8369
commit
5f860bb612
10 changed files with 236 additions and 63 deletions
|
@ -2,10 +2,9 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use resource_task::{Done, Payload, Metadata, LoadResponse, LoaderTask, start_sending};
|
use resource_task::{Done, Payload, Metadata, LoadData, LoadResponse, LoaderTask, start_sending};
|
||||||
|
|
||||||
use serialize::base64::FromBase64;
|
use serialize::base64::FromBase64;
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use http::headers::test_utils::from_stream_with_str;
|
use http::headers::test_utils::from_stream_with_str;
|
||||||
use http::headers::content_type::MediaType;
|
use http::headers::content_type::MediaType;
|
||||||
|
@ -19,7 +18,8 @@ pub fn factory() -> LoaderTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(url: Url, start_chan: Sender<LoadResponse>) {
|
fn load(load_data: LoadData, start_chan: Sender<LoadResponse>) {
|
||||||
|
let url = load_data.url;
|
||||||
assert!("data" == url.scheme);
|
assert!("data" == url.scheme);
|
||||||
|
|
||||||
let mut metadata = Metadata::default(url.clone());
|
let mut metadata = Metadata::default(url.clone());
|
||||||
|
@ -76,7 +76,7 @@ fn assert_parse(url: &'static str,
|
||||||
use std::comm;
|
use std::comm;
|
||||||
|
|
||||||
let (start_chan, start_port) = comm::channel();
|
let (start_chan, start_port) = comm::channel();
|
||||||
load(FromStr::from_str(url).unwrap(), start_chan);
|
load(LoadData::new(FromStr::from_str(url).unwrap()), start_chan);
|
||||||
|
|
||||||
let response = start_port.recv();
|
let response = start_port.recv();
|
||||||
assert_eq!(&response.metadata.content_type, &content_type);
|
assert_eq!(&response.metadata.content_type, &content_type);
|
||||||
|
|
|
@ -26,7 +26,8 @@ fn read_all(reader: &mut io::Stream, progress_chan: &Sender<ProgressMsg>)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn factory() -> LoaderTask {
|
pub fn factory() -> LoaderTask {
|
||||||
let f: LoaderTask = proc(url, start_chan) {
|
let f: LoaderTask = proc(load_data, start_chan) {
|
||||||
|
let url = load_data.url;
|
||||||
assert!("file" == url.scheme);
|
assert!("file" == url.scheme);
|
||||||
let progress_chan = start_sending(start_chan, Metadata::default(url.clone()));
|
let progress_chan = start_sending(start_chan, Metadata::default(url.clone()));
|
||||||
spawn_named("file_loader", proc() {
|
spawn_named("file_loader", proc() {
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use resource_task::{Metadata, Payload, Done, LoadResponse, LoaderTask, start_sending};
|
use resource_task::{Metadata, Payload, Done, LoadResponse, LoadData, LoaderTask, start_sending};
|
||||||
|
|
||||||
use collections::hashmap::HashSet;
|
use collections::hashmap::HashSet;
|
||||||
use http::client::{RequestWriter, NetworkStream};
|
use http::client::{RequestWriter, NetworkStream};
|
||||||
use http::method::Get;
|
|
||||||
use http::headers::HeaderEnum;
|
use http::headers::HeaderEnum;
|
||||||
use std::io::Reader;
|
use std::io::Reader;
|
||||||
use servo_util::task::spawn_named;
|
use servo_util::task::spawn_named;
|
||||||
|
@ -23,13 +22,13 @@ fn send_error(url: Url, err: ~str, start_chan: Sender<LoadResponse>) {
|
||||||
start_sending(start_chan, Metadata::default(url)).send(Done(Err(err)));
|
start_sending(start_chan, Metadata::default(url)).send(Done(Err(err)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(mut url: Url, start_chan: Sender<LoadResponse>) {
|
fn load(load_data: LoadData, start_chan: Sender<LoadResponse>) {
|
||||||
// FIXME: At the time of writing this FIXME, servo didn't have any central
|
// 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
|
// location for configuration. If you're reading this and such a
|
||||||
// repository DOES exist, please update this constant to use it.
|
// repository DOES exist, please update this constant to use it.
|
||||||
let max_redirects = 50u;
|
let max_redirects = 50u;
|
||||||
let mut iters = 0u;
|
let mut iters = 0u;
|
||||||
|
let mut url = load_data.url.clone();
|
||||||
let mut redirected_to = HashSet::new();
|
let mut redirected_to = HashSet::new();
|
||||||
|
|
||||||
// Loop to handle redirects.
|
// Loop to handle redirects.
|
||||||
|
@ -56,14 +55,28 @@ fn load(mut url: Url, start_chan: Sender<LoadResponse>) {
|
||||||
|
|
||||||
info!("requesting {:s}", url.to_str());
|
info!("requesting {:s}", url.to_str());
|
||||||
|
|
||||||
let request = RequestWriter::<NetworkStream>::new(Get, url.clone());
|
let request = RequestWriter::<NetworkStream>::new(load_data.method.clone(), url.clone());
|
||||||
let writer = match request {
|
let mut writer = match request {
|
||||||
Ok(w) => box w,
|
Ok(w) => box w,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
send_error(url, e.desc.to_owned(), start_chan);
|
send_error(url, e.desc.to_owned(), start_chan);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
writer.headers = box load_data.headers.clone();
|
||||||
|
match load_data.data {
|
||||||
|
Some(ref data) => {
|
||||||
|
writer.headers.content_length = Some(data.len());
|
||||||
|
match writer.write(data.clone().into_bytes().as_slice()) {
|
||||||
|
Err(e) => {
|
||||||
|
send_error(url, e.desc.to_owned(), start_chan);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
let mut response = match writer.read_response() {
|
let mut response = match writer.read_response() {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err((_, e)) => {
|
Err((_, e)) => {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use image::base::{Image, load_from_memory};
|
use image::base::{Image, load_from_memory};
|
||||||
use resource_task;
|
use resource_task;
|
||||||
use resource_task::ResourceTask;
|
use resource_task::{LoadData, ResourceTask};
|
||||||
use servo_util::url::{UrlMap, url_map};
|
use servo_util::url::{UrlMap, url_map};
|
||||||
|
|
||||||
use std::comm::{channel, Receiver, Sender};
|
use std::comm::{channel, Receiver, Sender};
|
||||||
|
@ -458,7 +458,7 @@ impl ImageCacheTask {
|
||||||
|
|
||||||
fn load_image_data(url: Url, resource_task: ResourceTask) -> Result<~[u8], ()> {
|
fn load_image_data(url: Url, resource_task: ResourceTask) -> Result<~[u8], ()> {
|
||||||
let (response_chan, response_port) = channel();
|
let (response_chan, response_port) = channel();
|
||||||
resource_task.send(resource_task::Load(url, response_chan));
|
resource_task.send(resource_task::Load(LoadData::new(url), response_chan));
|
||||||
|
|
||||||
let mut image_data = vec!();
|
let mut image_data = vec!();
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@ use data_loader;
|
||||||
use std::comm::{channel, Receiver, Sender};
|
use std::comm::{channel, Receiver, Sender};
|
||||||
use std::task::TaskBuilder;
|
use std::task::TaskBuilder;
|
||||||
use http::headers::content_type::MediaType;
|
use http::headers::content_type::MediaType;
|
||||||
use http::headers::response::HeaderCollection;
|
use ResponseHeaderCollection = http::headers::response::HeaderCollection;
|
||||||
|
use RequestHeaderCollection = http::headers::request::HeaderCollection;
|
||||||
|
use http::method::{Method, Get};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -19,10 +21,29 @@ use std::from_str::FromStr;
|
||||||
|
|
||||||
pub enum ControlMsg {
|
pub enum ControlMsg {
|
||||||
/// Request the data associated with a particular URL
|
/// Request the data associated with a particular URL
|
||||||
Load(Url, Sender<LoadResponse>),
|
Load(LoadData, Sender<LoadResponse>),
|
||||||
Exit
|
Exit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deriving(Clone)]
|
||||||
|
pub struct LoadData {
|
||||||
|
pub url: Url,
|
||||||
|
pub method: Method,
|
||||||
|
pub headers: RequestHeaderCollection,
|
||||||
|
pub data: Option<~str>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoadData {
|
||||||
|
pub fn new(url: Url) -> LoadData {
|
||||||
|
LoadData {
|
||||||
|
url: url,
|
||||||
|
method: Get,
|
||||||
|
headers: RequestHeaderCollection::new(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Metadata about a loaded resource, such as is obtained from HTTP headers.
|
/// Metadata about a loaded resource, such as is obtained from HTTP headers.
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
/// Final URL after redirects.
|
/// Final URL after redirects.
|
||||||
|
@ -35,7 +56,7 @@ pub struct Metadata {
|
||||||
pub charset: Option<~str>,
|
pub charset: Option<~str>,
|
||||||
|
|
||||||
/// Headers
|
/// Headers
|
||||||
pub headers: Option<HeaderCollection>,
|
pub headers: Option<ResponseHeaderCollection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metadata {
|
impl Metadata {
|
||||||
|
@ -102,7 +123,7 @@ pub fn start_sending(start_chan: Sender<LoadResponse>, metadata: Metadata) -> Se
|
||||||
pub fn load_whole_resource(resource_task: &ResourceTask, url: Url)
|
pub fn load_whole_resource(resource_task: &ResourceTask, url: Url)
|
||||||
-> Result<(Metadata, Vec<u8>), ~str> {
|
-> Result<(Metadata, Vec<u8>), ~str> {
|
||||||
let (start_chan, start_port) = channel();
|
let (start_chan, start_port) = channel();
|
||||||
resource_task.send(Load(url, start_chan));
|
resource_task.send(Load(LoadData::new(url), start_chan));
|
||||||
let response = start_port.recv();
|
let response = start_port.recv();
|
||||||
|
|
||||||
let mut buf = vec!();
|
let mut buf = vec!();
|
||||||
|
@ -118,7 +139,7 @@ pub fn load_whole_resource(resource_task: &ResourceTask, url: Url)
|
||||||
/// Handle to a resource task
|
/// Handle to a resource task
|
||||||
pub type ResourceTask = Sender<ControlMsg>;
|
pub type ResourceTask = Sender<ControlMsg>;
|
||||||
|
|
||||||
pub type LoaderTask = proc(url: Url, Sender<LoadResponse>);
|
pub type LoaderTask = proc(load_data: LoadData, Sender<LoadResponse>);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates a task to load a specific resource
|
Creates a task to load a specific resource
|
||||||
|
@ -169,8 +190,8 @@ impl ResourceManager {
|
||||||
fn start(&self) {
|
fn start(&self) {
|
||||||
loop {
|
loop {
|
||||||
match self.from_client.recv() {
|
match self.from_client.recv() {
|
||||||
Load(url, start_chan) => {
|
Load(load_data, start_chan) => {
|
||||||
self.load(url.clone(), start_chan)
|
self.load(load_data.clone(), start_chan)
|
||||||
}
|
}
|
||||||
Exit => {
|
Exit => {
|
||||||
break
|
break
|
||||||
|
@ -179,24 +200,24 @@ impl ResourceManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(&self, url: Url, start_chan: Sender<LoadResponse>) {
|
fn load(&self, load_data: LoadData, start_chan: Sender<LoadResponse>) {
|
||||||
match self.get_loader_factory(&url) {
|
match self.get_loader_factory(&load_data) {
|
||||||
Some(loader_factory) => {
|
Some(loader_factory) => {
|
||||||
debug!("resource_task: loading url: {:s}", url.to_str());
|
debug!("resource_task: loading url: {:s}", load_data.url.to_str());
|
||||||
loader_factory(url, start_chan);
|
loader_factory(load_data, start_chan);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
debug!("resource_task: no loader for scheme {:s}", url.scheme);
|
debug!("resource_task: no loader for scheme {:s}", load_data.url.scheme);
|
||||||
start_sending(start_chan, Metadata::default(url)).send(Done(Err("no loader for scheme".to_owned())));
|
start_sending(start_chan, Metadata::default(load_data.url)).send(Done(Err("no loader for scheme".to_owned())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_loader_factory(&self, url: &Url) -> Option<LoaderTask> {
|
fn get_loader_factory(&self, load_data: &LoadData) -> Option<LoaderTask> {
|
||||||
for scheme_loader in self.loaders.iter() {
|
for scheme_loader in self.loaders.iter() {
|
||||||
match *scheme_loader {
|
match *scheme_loader {
|
||||||
(ref scheme, ref loader_factory) => {
|
(ref scheme, ref loader_factory) => {
|
||||||
if (*scheme) == url.scheme {
|
if (*scheme) == load_data.url.scheme {
|
||||||
return Some((*loader_factory)());
|
return Some((*loader_factory)());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +237,7 @@ fn test_exit() {
|
||||||
fn test_bad_scheme() {
|
fn test_bad_scheme() {
|
||||||
let resource_task = ResourceTask();
|
let resource_task = ResourceTask();
|
||||||
let (start_chan, start) = channel();
|
let (start_chan, start) = channel();
|
||||||
resource_task.send(Load(FromStr::from_str("bogus://whatever").unwrap(), start_chan));
|
resource_task.send(Load(LoadData::new(FromStr::from_str("bogus://whatever").unwrap()), start_chan));
|
||||||
let response = start.recv();
|
let response = start.recv();
|
||||||
match response.progress_port.recv() {
|
match response.progress_port.recv() {
|
||||||
Done(result) => { assert!(result.is_err()) }
|
Done(result) => { assert!(result.is_err()) }
|
||||||
|
@ -230,8 +251,8 @@ static snicklefritz_payload: [u8, ..3] = [1, 2, 3];
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn snicklefritz_loader_factory() -> LoaderTask {
|
fn snicklefritz_loader_factory() -> LoaderTask {
|
||||||
let f: LoaderTask = proc(url: Url, start_chan: Sender<LoadResponse>) {
|
let f: LoaderTask = proc(load_data: LoadData, start_chan: Sender<LoadResponse>) {
|
||||||
let progress_chan = start_sending(start_chan, Metadata::default(url));
|
let progress_chan = start_sending(start_chan, Metadata::default(load_data.url));
|
||||||
progress_chan.send(Payload(Vec::from_slice(snicklefritz_payload)));
|
progress_chan.send(Payload(Vec::from_slice(snicklefritz_payload)));
|
||||||
progress_chan.send(Done(Ok(())));
|
progress_chan.send(Done(Ok(())));
|
||||||
};
|
};
|
||||||
|
@ -243,7 +264,7 @@ fn should_delegate_to_scheme_loader() {
|
||||||
let loader_factories = vec!(("snicklefritz".to_owned(), snicklefritz_loader_factory));
|
let loader_factories = vec!(("snicklefritz".to_owned(), snicklefritz_loader_factory));
|
||||||
let resource_task = create_resource_task_with_loaders(loader_factories);
|
let resource_task = create_resource_task_with_loaders(loader_factories);
|
||||||
let (start_chan, start) = channel();
|
let (start_chan, start) = channel();
|
||||||
resource_task.send(Load(FromStr::from_str("snicklefritz://heya").unwrap(), start_chan));
|
resource_task.send(Load(LoadData::new(FromStr::from_str("snicklefritz://heya").unwrap()), start_chan));
|
||||||
|
|
||||||
let response = start.recv();
|
let response = start.recv();
|
||||||
let progress = response.progress_port;
|
let progress = response.progress_port;
|
||||||
|
|
|
@ -37,6 +37,7 @@ impl ByteString {
|
||||||
}
|
}
|
||||||
}).collect())
|
}).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_token(&self) -> bool {
|
pub fn is_token(&self) -> bool {
|
||||||
let ByteString(ref vec) = *self;
|
let ByteString(ref vec) = *self;
|
||||||
vec.iter().all(|&x| {
|
vec.iter().all(|&x| {
|
||||||
|
@ -51,6 +52,55 @@ impl ByteString {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_field_value(&self) -> bool {
|
||||||
|
// Classifications of characters necessary for the [CRLF] (SP|HT) rule
|
||||||
|
#[deriving(Eq)]
|
||||||
|
enum PreviousCharacter {
|
||||||
|
Other,
|
||||||
|
CR,
|
||||||
|
LF,
|
||||||
|
SP_HT // SP or HT
|
||||||
|
}
|
||||||
|
let ByteString(ref vec) = *self;
|
||||||
|
let mut prev = Other; // The previous character
|
||||||
|
vec.iter().all(|&x| {
|
||||||
|
// http://tools.ietf.org/html/rfc2616#section-2.2
|
||||||
|
match x {
|
||||||
|
13 => { // CR
|
||||||
|
if prev == Other || prev == SP_HT {
|
||||||
|
prev = CR;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
10 => { // LF
|
||||||
|
if prev == CR {
|
||||||
|
prev = LF;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
32 | 9 => { // SP | HT
|
||||||
|
if prev == LF || prev == SP_HT {
|
||||||
|
prev = SP_HT;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0..31 | 127 => false, // CTLs
|
||||||
|
x if x > 127 => false, // non ASCII
|
||||||
|
_ if prev == Other || prev == SP_HT => {
|
||||||
|
prev = Other;
|
||||||
|
true
|
||||||
|
},
|
||||||
|
_ => false // Previous character was a CR/LF but not part of the [CRLF] (SP|HT) rule
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for ByteString {
|
impl Hash for ByteString {
|
||||||
|
|
|
@ -43,7 +43,8 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget {
|
||||||
[Throws]
|
[Throws]
|
||||||
void open(ByteString method, /* [EnsureUTF16] */ DOMString url, boolean async, optional /* [EnsureUTF16] */ DOMString? username = null, optional /* [EnsureUTF16] */ DOMString? password = null);
|
void open(ByteString method, /* [EnsureUTF16] */ DOMString url, boolean async, optional /* [EnsureUTF16] */ DOMString? username = null, optional /* [EnsureUTF16] */ DOMString? password = null);
|
||||||
|
|
||||||
// void setRequestHeader(ByteString name, ByteString value);
|
[Throws]
|
||||||
|
void setRequestHeader(ByteString name, ByteString value);
|
||||||
attribute unsigned long timeout;
|
attribute unsigned long timeout;
|
||||||
attribute boolean withCredentials;
|
attribute boolean withCredentials;
|
||||||
readonly attribute XMLHttpRequestUpload upload;
|
readonly attribute XMLHttpRequestUpload upload;
|
||||||
|
|
|
@ -21,7 +21,7 @@ use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
|
use dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget;
|
||||||
use dom::xmlhttprequestupload::XMLHttpRequestUpload;
|
use dom::xmlhttprequestupload::XMLHttpRequestUpload;
|
||||||
use net::resource_task::{ResourceTask, Load, Payload, Done};
|
use net::resource_task::{ResourceTask, Load, LoadData, Payload, Done};
|
||||||
use script_task::{ScriptChan, XHRProgressMsg};
|
use script_task::{ScriptChan, XHRProgressMsg};
|
||||||
use servo_util::str::DOMString;
|
use servo_util::str::DOMString;
|
||||||
use servo_util::url::{parse_url, try_parse_url};
|
use servo_util::url::{parse_url, try_parse_url};
|
||||||
|
@ -31,13 +31,19 @@ use libc;
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
|
|
||||||
use std::comm::channel;
|
use std::comm::channel;
|
||||||
use std::io::MemWriter;
|
use std::io::{BufReader, MemWriter};
|
||||||
|
use std::from_str::FromStr;
|
||||||
|
use std::ascii::StrAsciiExt;
|
||||||
use std::task::TaskBuilder;
|
use std::task::TaskBuilder;
|
||||||
|
use std::path::BytesContainer;
|
||||||
|
|
||||||
use ResponseHeaderCollection = http::headers::response::HeaderCollection;
|
use ResponseHeaderCollection = http::headers::response::HeaderCollection;
|
||||||
use RequestHeaderCollection = http::headers::request::HeaderCollection;
|
use RequestHeaderCollection = http::headers::request::HeaderCollection;
|
||||||
|
|
||||||
|
use http::headers::{HeaderEnum, HeaderValueByteIterator};
|
||||||
|
use http::headers::request::Header;
|
||||||
|
use http::method::{Method, Get, Head, Post, Connect, Trace};
|
||||||
|
|
||||||
// As send() start accepting more and more parameter types,
|
// As send() start accepting more and more parameter types,
|
||||||
// change this to the appropriate type from UnionTypes, eg
|
// change this to the appropriate type from UnionTypes, eg
|
||||||
// use SendParam = dom::bindings::codegen::UnionTypes::StringOrFormData;
|
// use SendParam = dom::bindings::codegen::UnionTypes::StringOrFormData;
|
||||||
|
@ -101,7 +107,7 @@ pub struct XMLHttpRequest {
|
||||||
response_headers: Untraceable<ResponseHeaderCollection>,
|
response_headers: Untraceable<ResponseHeaderCollection>,
|
||||||
|
|
||||||
// Associated concepts
|
// Associated concepts
|
||||||
request_method: ByteString,
|
request_method: Untraceable<Method>,
|
||||||
request_url: Untraceable<Url>,
|
request_url: Untraceable<Url>,
|
||||||
request_headers: Untraceable<RequestHeaderCollection>,
|
request_headers: Untraceable<RequestHeaderCollection>,
|
||||||
request_body: SendParam,
|
request_body: SendParam,
|
||||||
|
@ -131,7 +137,7 @@ impl XMLHttpRequest {
|
||||||
response_xml: None,
|
response_xml: None,
|
||||||
response_headers: Untraceable::new(ResponseHeaderCollection::new()),
|
response_headers: Untraceable::new(ResponseHeaderCollection::new()),
|
||||||
|
|
||||||
request_method: ByteString::new(vec!()),
|
request_method: Untraceable::new(Get),
|
||||||
request_url: Untraceable::new(parse_url("", None)),
|
request_url: Untraceable::new(parse_url("", None)),
|
||||||
request_headers: Untraceable::new(RequestHeaderCollection::new()),
|
request_headers: Untraceable::new(RequestHeaderCollection::new()),
|
||||||
request_body: "".to_owned(),
|
request_body: "".to_owned(),
|
||||||
|
@ -163,7 +169,7 @@ impl XMLHttpRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(fetch_type: &mut SyncOrAsync, resource_task: ResourceTask, url: Url) -> ErrorResult {
|
fn fetch(fetch_type: &mut SyncOrAsync, resource_task: ResourceTask, load_data: LoadData) -> ErrorResult {
|
||||||
|
|
||||||
fn notify_partial_progress(fetch_type: &mut SyncOrAsync, msg: XHRProgress) {
|
fn notify_partial_progress(fetch_type: &mut SyncOrAsync, msg: XHRProgress) {
|
||||||
match *fetch_type {
|
match *fetch_type {
|
||||||
|
@ -179,7 +185,7 @@ impl XMLHttpRequest {
|
||||||
|
|
||||||
// Step 10, 13
|
// Step 10, 13
|
||||||
let (start_chan, start_port) = channel();
|
let (start_chan, start_port) = channel();
|
||||||
resource_task.send(Load(url, start_chan));
|
resource_task.send(Load(load_data, start_chan));
|
||||||
let response = start_port.recv();
|
let response = start_port.recv();
|
||||||
notify_partial_progress(fetch_type, HeadersReceivedMsg(response.metadata.headers.clone()));
|
notify_partial_progress(fetch_type, HeadersReceivedMsg(response.metadata.headers.clone()));
|
||||||
let mut buf = vec!();
|
let mut buf = vec!();
|
||||||
|
@ -213,7 +219,7 @@ pub trait XMLHttpRequestMethods<'a> {
|
||||||
fn Open(&mut self, _method: ByteString, _url: DOMString) -> ErrorResult;
|
fn Open(&mut self, _method: ByteString, _url: DOMString) -> ErrorResult;
|
||||||
fn Open_(&mut self, _method: ByteString, _url: DOMString, _async: bool,
|
fn Open_(&mut self, _method: ByteString, _url: DOMString, _async: bool,
|
||||||
_username: Option<DOMString>, _password: Option<DOMString>) -> ErrorResult;
|
_username: Option<DOMString>, _password: Option<DOMString>) -> ErrorResult;
|
||||||
fn SetRequestHeader(&self, _name: ByteString, _value: ByteString);
|
fn SetRequestHeader(&mut self, name: ByteString, mut value: ByteString) -> ErrorResult;
|
||||||
fn Timeout(&self) -> u32;
|
fn Timeout(&self) -> u32;
|
||||||
fn SetTimeout(&mut self, timeout: u32);
|
fn SetTimeout(&mut self, timeout: u32);
|
||||||
fn WithCredentials(&self) -> bool;
|
fn WithCredentials(&self) -> bool;
|
||||||
|
@ -239,15 +245,15 @@ impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> {
|
||||||
self.ready_state as u16
|
self.ready_state as u16
|
||||||
}
|
}
|
||||||
fn Open(&mut self, method: ByteString, url: DOMString) -> ErrorResult {
|
fn Open(&mut self, method: ByteString, url: DOMString) -> ErrorResult {
|
||||||
self.request_method = method;
|
let maybe_method: Option<Method> = method.as_str().and_then(|s| {
|
||||||
|
FromStr::from_str(s.to_ascii_upper()) // rust-http tests against the uppercase versions
|
||||||
|
});
|
||||||
// Step 2
|
// Step 2
|
||||||
let base: Option<Url> = Some(self.global.root().get_url());
|
let base: Option<Url> = Some(self.global.root().get_url());
|
||||||
match self.request_method.to_lower().as_str() {
|
match maybe_method {
|
||||||
Some("get") => {
|
Some(Get) | Some(Post) | Some(Head) => {
|
||||||
|
|
||||||
// Step 5
|
*self.request_method = maybe_method.unwrap();
|
||||||
self.request_method = self.request_method.to_lower();
|
|
||||||
|
|
||||||
// Step 6
|
// Step 6
|
||||||
let parsed_url = match try_parse_url(url, base) {
|
let parsed_url = match try_parse_url(url, base) {
|
||||||
|
@ -257,8 +263,8 @@ impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> {
|
||||||
// XXXManishearth Do some handling of username/passwords, and abort existing requests
|
// XXXManishearth Do some handling of username/passwords, and abort existing requests
|
||||||
|
|
||||||
// Step 12
|
// Step 12
|
||||||
self.request_url = Untraceable::new(parsed_url);
|
*self.request_url = parsed_url;
|
||||||
self.request_headers = Untraceable::new(RequestHeaderCollection::new());
|
*self.request_headers = RequestHeaderCollection::new();
|
||||||
self.send_flag = false;
|
self.send_flag = false;
|
||||||
// XXXManishearth Set response to a NetworkError
|
// XXXManishearth Set response to a NetworkError
|
||||||
|
|
||||||
|
@ -268,12 +274,13 @@ impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> {
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
// XXXManishearth Handle other standard methods
|
// XXXManishearth Handle other standard methods
|
||||||
Some("connect") | Some("trace") | Some("track") => {
|
Some(Connect) | Some(Trace) => {
|
||||||
|
// XXXManishearth Track isn't in rust-http, but it should end up in this match arm.
|
||||||
Err(Security) // Step 4
|
Err(Security) // Step 4
|
||||||
},
|
},
|
||||||
None => Err(Syntax),
|
None => Err(Syntax), // Step 3
|
||||||
_ => {
|
_ => {
|
||||||
if self.request_method.is_token() {
|
if method.is_token() {
|
||||||
Ok(()) // XXXManishearth handle extension methods
|
Ok(()) // XXXManishearth handle extension methods
|
||||||
} else {
|
} else {
|
||||||
Err(Syntax) // Step 3
|
Err(Syntax) // Step 3
|
||||||
|
@ -286,8 +293,69 @@ impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> {
|
||||||
self.sync = !async;
|
self.sync = !async;
|
||||||
self.Open(method, url)
|
self.Open(method, url)
|
||||||
}
|
}
|
||||||
fn SetRequestHeader(&self, _name: ByteString, _value: ByteString) {
|
fn SetRequestHeader(&mut self, name: ByteString, mut value: ByteString) -> ErrorResult {
|
||||||
|
if self.ready_state != Opened || self.send_flag {
|
||||||
|
return Err(InvalidState); // Step 1, 2
|
||||||
|
}
|
||||||
|
if !name.is_token() || !value.is_field_value() {
|
||||||
|
return Err(Syntax); // Step 3, 4
|
||||||
|
}
|
||||||
|
let name_str = match name.to_lower().as_str() {
|
||||||
|
Some(s) => {
|
||||||
|
match s {
|
||||||
|
// Disallowed headers
|
||||||
|
"accept-charset" | "accept-encoding" |
|
||||||
|
"access-control-request-headers" |
|
||||||
|
"access-control-request-method" |
|
||||||
|
"connection" | "content-length" |
|
||||||
|
"cookie" | "cookie2" | "date" |"dnt" |
|
||||||
|
"expect" | "host" | "keep-alive" | "origin" |
|
||||||
|
"referer" | "te" | "trailer" | "transfer-encoding" |
|
||||||
|
"upgrade" | "user-agent" | "via" => {
|
||||||
|
return Ok(()); // Step 5
|
||||||
|
},
|
||||||
|
_ => StrBuf::from_str(s)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => return Err(Syntax)
|
||||||
|
};
|
||||||
|
let collection = self.request_headers.deref_mut();
|
||||||
|
|
||||||
|
|
||||||
|
// Steps 6,7
|
||||||
|
let old_header = collection.iter().find(|ref h| -> bool {
|
||||||
|
// XXXManishearth following line waiting on the rust upgrade:
|
||||||
|
ByteString::new(h.header_name().into_bytes()).eq_ignore_case(&value)
|
||||||
|
});
|
||||||
|
match old_header {
|
||||||
|
Some(h) => {
|
||||||
|
unsafe {
|
||||||
|
// By step 4, the value is a subset of valid utf8
|
||||||
|
// So this unsafe block should never fail
|
||||||
|
|
||||||
|
let mut buf = h.header_value();
|
||||||
|
buf.push_bytes(&[0x2C, 0x20]);
|
||||||
|
buf.push_bytes(value.as_slice());
|
||||||
|
value = ByteString::new(buf.container_into_owned_bytes());
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = BufReader::new(value.as_slice());
|
||||||
|
let maybe_header: Option<Header> = HeaderEnum::value_from_stream(
|
||||||
|
name_str,
|
||||||
|
&mut HeaderValueByteIterator::new(&mut reader));
|
||||||
|
match maybe_header {
|
||||||
|
Some(h) => {
|
||||||
|
// Overwrites existing headers, which we want since we have
|
||||||
|
// prepended the new header value with the old one already
|
||||||
|
collection.insert(h);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
None => Err(Syntax)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn Timeout(&self) -> u32 {
|
fn Timeout(&self) -> u32 {
|
||||||
self.timeout
|
self.timeout
|
||||||
|
@ -305,13 +373,12 @@ impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> {
|
||||||
Temporary::new(self.upload.get_ref().clone())
|
Temporary::new(self.upload.get_ref().clone())
|
||||||
}
|
}
|
||||||
fn Send(&mut self, data: Option<DOMString>) -> ErrorResult {
|
fn Send(&mut self, data: Option<DOMString>) -> ErrorResult {
|
||||||
// XXXManishearth handle POSTdata, and headers
|
|
||||||
if self.ready_state != Opened || self.send_flag {
|
if self.ready_state != Opened || self.send_flag {
|
||||||
return Err(InvalidState); // Step 1, 2
|
return Err(InvalidState); // Step 1, 2
|
||||||
}
|
}
|
||||||
|
|
||||||
let _data = match self.request_method.to_lower().as_str() {
|
let data = match *self.request_method {
|
||||||
Some("get") | Some("head") => None, // Step 3
|
Get | Head => None, // Step 3
|
||||||
_ => data
|
_ => data
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -324,16 +391,23 @@ impl<'a> XMLHttpRequestMethods<'a> for JSRef<'a, XMLHttpRequest> {
|
||||||
self.send_flag = true;
|
self.send_flag = true;
|
||||||
let mut global = self.global.root();
|
let mut global = self.global.root();
|
||||||
let resource_task = global.page().resource_task.deref().clone();
|
let resource_task = global.page().resource_task.deref().clone();
|
||||||
let url = self.request_url.clone();
|
let mut load_data = LoadData::new((*self.request_url).clone());
|
||||||
|
load_data.data = data;
|
||||||
|
|
||||||
|
// XXXManishearth deal with the Origin/Referer/Accept headers
|
||||||
|
// XXXManishearth the below is only valid when content type is not already set by the user.
|
||||||
|
self.insert_trusted_header("content-type".to_owned(), "text/plain;charset=UTF-8".to_owned());
|
||||||
|
load_data.headers = (*self.request_headers).clone();
|
||||||
|
load_data.method = (*self.request_method).clone();
|
||||||
if self.sync {
|
if self.sync {
|
||||||
return XMLHttpRequest::fetch(&mut Sync(self), resource_task, url);
|
return XMLHttpRequest::fetch(&mut Sync(self), resource_task, load_data);
|
||||||
} else {
|
} else {
|
||||||
let builder = TaskBuilder::new().named("XHRTask");
|
let builder = TaskBuilder::new().named("XHRTask");
|
||||||
unsafe {
|
unsafe {
|
||||||
let addr = self.to_trusted();
|
let addr = self.to_trusted();
|
||||||
let script_chan = global.script_chan.clone();
|
let script_chan = global.script_chan.clone();
|
||||||
builder.spawn(proc() {
|
builder.spawn(proc() {
|
||||||
let _ = XMLHttpRequest::fetch(&mut Async(addr, script_chan), resource_task, url);
|
let _ = XMLHttpRequest::fetch(&mut Async(addr, script_chan), resource_task, load_data);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,6 +505,7 @@ trait PrivateXMLHttpRequestHelpers {
|
||||||
fn release(&mut self);
|
fn release(&mut self);
|
||||||
fn change_ready_state(&mut self, XMLHttpRequestState);
|
fn change_ready_state(&mut self, XMLHttpRequestState);
|
||||||
fn process_partial_response(&mut self, progress: XHRProgress);
|
fn process_partial_response(&mut self, progress: XHRProgress);
|
||||||
|
fn insert_trusted_header(&mut self, name: ~str, value: ~str);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
|
impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
|
||||||
|
@ -493,4 +568,16 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn insert_trusted_header(&mut self, name: ~str, value: ~str) {
|
||||||
|
// Insert a header without checking spec-compliance
|
||||||
|
// Use for hardcoded headers
|
||||||
|
let collection = self.request_headers.deref_mut();
|
||||||
|
let value_bytes = value.into_bytes();
|
||||||
|
let mut reader = BufReader::new(value_bytes);
|
||||||
|
let maybe_header: Option<Header> = HeaderEnum::value_from_stream(
|
||||||
|
StrBuf::from_str(name),
|
||||||
|
&mut HeaderValueByteIterator::new(&mut reader));
|
||||||
|
collection.insert(maybe_header.unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::comm::{channel, Receiver};
|
||||||
use encoding::EncodingRef;
|
use encoding::EncodingRef;
|
||||||
use encoding::all::UTF_8;
|
use encoding::all::UTF_8;
|
||||||
use style::Stylesheet;
|
use style::Stylesheet;
|
||||||
use servo_net::resource_task::{Load, LoadResponse, ProgressMsg, Payload, Done, ResourceTask};
|
use servo_net::resource_task::{Load, LoadData, LoadResponse, ProgressMsg, Payload, Done, ResourceTask};
|
||||||
use servo_util::task::spawn_named;
|
use servo_util::task::spawn_named;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ fn parse_css(provenance: StylesheetProvenance) -> Stylesheet {
|
||||||
UrlProvenance(url, resource_task) => {
|
UrlProvenance(url, resource_task) => {
|
||||||
debug!("cssparse: loading style sheet at {:s}", url.to_str());
|
debug!("cssparse: loading style sheet at {:s}", url.to_str());
|
||||||
let (input_chan, input_port) = channel();
|
let (input_chan, input_port) = channel();
|
||||||
resource_task.send(Load(url, input_chan));
|
resource_task.send(Load(LoadData::new(url), input_chan));
|
||||||
let LoadResponse { metadata: metadata, progress_port: progress_port , ..}
|
let LoadResponse { metadata: metadata, progress_port: progress_port , ..}
|
||||||
= input_port.recv();
|
= input_port.recv();
|
||||||
let final_url = &metadata.final_url;
|
let final_url = &metadata.final_url;
|
||||||
|
|
|
@ -18,7 +18,7 @@ use script_task::Page;
|
||||||
|
|
||||||
use hubbub::hubbub;
|
use hubbub::hubbub;
|
||||||
use hubbub::hubbub::{NullNs, XLinkNs, XmlNs, XmlNsNs};
|
use hubbub::hubbub::{NullNs, XLinkNs, XmlNs, XmlNsNs};
|
||||||
use servo_net::resource_task::{Load, Payload, Done, ResourceTask, load_whole_resource};
|
use servo_net::resource_task::{Load, LoadData, Payload, Done, ResourceTask, load_whole_resource};
|
||||||
use servo_util::namespace;
|
use servo_util::namespace;
|
||||||
use servo_util::namespace::Null;
|
use servo_util::namespace::Null;
|
||||||
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
|
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
|
||||||
|
@ -312,7 +312,7 @@ pub fn parse_html(page: &Page,
|
||||||
|
|
||||||
// Wait for the LoadResponse so that the parser knows the final URL.
|
// Wait for the LoadResponse so that the parser knows the final URL.
|
||||||
let (input_chan, input_port) = channel();
|
let (input_chan, input_port) = channel();
|
||||||
resource_task.send(Load(url.clone(), input_chan));
|
resource_task.send(Load(LoadData::new(url.clone()), input_chan));
|
||||||
let load_response = input_port.recv();
|
let load_response = input_port.recv();
|
||||||
|
|
||||||
debug!("Fetched page; metadata is {:?}", load_response.metadata);
|
debug!("Fetched page; metadata is {:?}", load_response.metadata);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue