diff options
-rw-r--r-- | src/http.rs | 74 | ||||
-rw-r--r-- | tests/017_import_redirect.ts | 5 | ||||
-rw-r--r-- | tests/017_import_redirect.ts.out | 2 | ||||
-rwxr-xr-x | tools/http_server.py | 25 |
4 files changed, 84 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); + }); +} diff --git a/tests/017_import_redirect.ts b/tests/017_import_redirect.ts new file mode 100644 index 000000000..22003e9dc --- /dev/null +++ b/tests/017_import_redirect.ts @@ -0,0 +1,5 @@ +// http -> https redirect would happen: +// tslint:disable-next-line:max-line-length +import { printHello } from "http://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts"; + +printHello(); diff --git a/tests/017_import_redirect.ts.out b/tests/017_import_redirect.ts.out new file mode 100644 index 000000000..d1ec08c42 --- /dev/null +++ b/tests/017_import_redirect.ts.out @@ -0,0 +1,2 @@ +Downloading http://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts +Hello diff --git a/tools/http_server.py b/tools/http_server.py index fdb8686cd..d33f24d5d 100755 --- a/tools/http_server.py +++ b/tools/http_server.py @@ -9,6 +9,7 @@ from util import root_path from time import sleep PORT = 4545 +REDIRECT_PORT = 4546 def server(): @@ -20,11 +21,35 @@ def server(): return s +def redirect_server(): + os.chdir(root_path) + target_host = "http://localhost:%d" % PORT + + class RedirectHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): + def do_GET(self): + self.send_response(301) + self.send_header('Location', target_host + self.path) + self.end_headers() + + Handler = RedirectHandler + SocketServer.TCPServer.allow_reuse_address = True + s = SocketServer.TCPServer(("", REDIRECT_PORT), Handler) + print "Deno redirect server http://localhost:%d/ -> http://localhost:%d/" % ( + REDIRECT_PORT, PORT) + return s + + def spawn(): + # Main http server s = server() thread = Thread(target=s.serve_forever) thread.daemon = True thread.start() + # Redirect server + rs = redirect_server() + r_thread = Thread(target=rs.serve_forever) + r_thread.daemon = True + r_thread.start() sleep(1) # TODO I'm too lazy to figure out how to do this properly. return thread |