diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/http_util.rs | 84 |
1 files changed, 72 insertions, 12 deletions
diff --git a/src/http_util.rs b/src/http_util.rs index 3c375b999..89ac43527 100644 --- a/src/http_util.rs +++ b/src/http_util.rs @@ -26,6 +26,33 @@ pub fn get_client() -> Client<Connector, hyper::Body> { Client::builder().build(c) } +/// Construct the next uri based on base uri and location header fragment +/// See https://tools.ietf.org/html/rfc3986#section-4.2 +fn resolve_uri_from_location(base_uri: &Uri, location: &str) -> Uri { + if location.starts_with("http://") || location.starts_with("https://") { + // absolute uri + return location + .parse::<Uri>() + .expect("provided redirect url should be a valid url"); + } else if location.starts_with("//") { + // "//" authority path-abempty + return format!("{}:{}", base_uri.scheme_part().unwrap().as_str(), location) + .parse::<Uri>() + .expect("provided redirect url should be a valid url"); + } else if location.starts_with("/") { + // path-absolute + let mut new_uri_parts = base_uri.clone().into_parts(); + new_uri_parts.path_and_query = Some(location.parse().unwrap()); + return Uri::from_parts(new_uri_parts).unwrap(); + } else { + // assuming path-noscheme | path-empty + let mut new_uri_parts = base_uri.clone().into_parts(); + new_uri_parts.path_and_query = + Some(format!("{}/{}", base_uri.path(), location).parse().unwrap()); + return Uri::from_parts(new_uri_parts).unwrap(); + } +} + // The CodeFetch message is used to load HTTP javascript resources and expects a // synchronous response, this utility method supports that. pub fn fetch_sync_string(module_name: &str) -> DenoResult<(String, String)> { @@ -33,27 +60,22 @@ pub fn fetch_sync_string(module_name: &str) -> DenoResult<(String, String)> { let client = get_client(); // TODO(kevinkassimo): consider set a max redirection counter // to avoid bouncing between 2 or more urls - let fetch_future = loop_fn((client, Some(url)), |(client, maybe_url)| { - let url = maybe_url.expect("target url should not be None"); + let fetch_future = loop_fn((client, url), |(client, url)| { client - .get(url) + .get(url.clone()) .map_err(DenoError::from) - .and_then(|response| { + .and_then(move |response| { if response.status().is_redirection() { - let new_url_string = response + let location_string = response .headers() .get("location") .expect("url redirection should provide 'location' header") .to_str() .unwrap() .to_string(); - debug!("Redirecting to {}...", &new_url_string); - let maybe_new_url = Some( - new_url_string - .parse::<Uri>() - .expect("provided redirect url should be a valid url"), - ); - return Ok(Loop::Continue((client, maybe_new_url))); + debug!("Redirecting to {}...", &location_string); + let new_url = resolve_uri_from_location(&url, &location_string); + return Ok(Loop::Continue((client, new_url))); } if !response.status().is_success() { return Err(errors::new( @@ -104,3 +126,41 @@ fn test_fetch_sync_string_with_redirect() { assert!(m == "application/json") }); } + +#[test] +fn test_resolve_uri_from_location_full_1() { + let url = "http://deno.land".parse::<Uri>().unwrap(); + let new_uri = resolve_uri_from_location(&url, "http://golang.org"); + assert_eq!(new_uri.host().unwrap(), "golang.org"); +} + +#[test] +fn test_resolve_uri_from_location_full_2() { + let url = "https://deno.land".parse::<Uri>().unwrap(); + let new_uri = resolve_uri_from_location(&url, "https://golang.org"); + assert_eq!(new_uri.host().unwrap(), "golang.org"); +} + +#[test] +fn test_resolve_uri_from_location_relative_1() { + let url = "http://deno.land/x".parse::<Uri>().unwrap(); + let new_uri = resolve_uri_from_location(&url, "//rust-lang.org/en-US"); + assert_eq!(new_uri.host().unwrap(), "rust-lang.org"); + assert_eq!(new_uri.path(), "/en-US"); +} + +#[test] +fn test_resolve_uri_from_location_relative_2() { + let url = "http://deno.land/x".parse::<Uri>().unwrap(); + let new_uri = resolve_uri_from_location(&url, "/y"); + assert_eq!(new_uri.host().unwrap(), "deno.land"); + assert_eq!(new_uri.path(), "/y"); +} + +#[test] +fn test_resolve_uri_from_location_relative_3() { + let url = "http://deno.land/x".parse::<Uri>().unwrap(); + let new_uri = resolve_uri_from_location(&url, "z"); + assert_eq!(new_uri.host().unwrap(), "deno.land"); + assert_eq!(new_uri.path(), "/x/z"); +} |