diff options
author | Kevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com> | 2018-10-09 17:31:06 -0700 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2018-10-09 20:31:06 -0400 |
commit | 888824c61787edb4a1a0d4141f6c08855d87d2b7 (patch) | |
tree | e571448effd72496d1480bc8ec8e04f010f8e0d2 /src | |
parent | 94889aef08909171ac5dc7b7c9c72c38d439bd7d (diff) |
Add redirect follow feature (#934)
Diffstat (limited to 'src')
-rw-r--r-- | src/http.rs | 74 |
1 files changed, 52 insertions, 22 deletions
diff --git a/src/http.rs b/src/http.rs index 86684207d..0d3189d27 100644 --- a/src/http.rs +++ b/src/http.rs @@ -4,12 +4,10 @@ use errors; use errors::{DenoError, DenoResult}; use tokio_util; -use futures; -use futures::future::Either; +use futures::future::{loop_fn, Loop}; use futures::{Future, Stream}; use hyper; -use hyper::client::Client; -use hyper::client::HttpConnector; +use hyper::client::{Client, HttpConnector}; use hyper::Uri; use hyper_rustls; @@ -33,24 +31,46 @@ pub fn get_client() -> Client<Connector, hyper::Body> { pub fn fetch_sync_string(module_name: &str) -> DenoResult<String> { let url = module_name.parse::<Uri>().unwrap(); let client = get_client(); - let fetch_future = client - .get(url) - .map_err(|err| DenoError::from(err)) - .and_then(|response| { - if !response.status().is_success() { - return Either::A(futures::future::err(errors::new( - errors::ErrorKind::NotFound, - "module not found".to_string(), - ))); - } - Either::B( - response - .into_body() - .concat2() - .map(|body| String::from_utf8(body.to_vec()).unwrap()) - .map_err(|err| DenoError::from(err)), - ) - }); + // 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"); + client + .get(url) + .map_err(|err| DenoError::from(err)) + .and_then(|response| { + if response.status().is_redirection() { + let new_url_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))); + } + if !response.status().is_success() { + return Err(errors::new( + errors::ErrorKind::NotFound, + "module not found".to_string(), + )); + } + Ok(Loop::Break(response)) + }) + }).and_then(|response| { + response + .into_body() + .concat2() + .map(|body| String::from_utf8(body.to_vec()).unwrap()) + .map_err(|err| DenoError::from(err)) + }); + tokio_util::block_on(fetch_future) } @@ -63,3 +83,13 @@ fn test_fetch_sync_string() { assert!(p.len() > 1); }); } + +#[test] +fn test_fetch_sync_string_with_redirect() { + // Relies on external http server. See tools/http_server.py + tokio_util::init(|| { + let p = fetch_sync_string("http://127.0.0.1:4546/package.json").unwrap(); + println!("package.json len {}", p.len()); + assert!(p.len() > 1); + }); +} |