Skip to content

Commit

Permalink
Add redirect follow feature (denoland#934)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkassimo authored and ry committed Oct 10, 2018
1 parent 94889ae commit 888824c
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 22 deletions.
74 changes: 52 additions & 22 deletions src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)
}

Expand All @@ -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("https://127.0.0.1:4546/package.json").unwrap();
println!("package.json len {}", p.len());
assert!(p.len() > 1);
});
}
5 changes: 5 additions & 0 deletions tests/017_import_redirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// http -> https redirect would happen:
// tslint:disable-next-line:max-line-length
import { printHello } from "https://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts";

printHello();
2 changes: 2 additions & 0 deletions tests/017_import_redirect.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Downloading https://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts
Hello
25 changes: 25 additions & 0 deletions tools/http_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from time import sleep

PORT = 4545
REDIRECT_PORT = 4546


def server():
Expand All @@ -20,11 +21,35 @@ def server():
return s


def redirect_server():
os.chdir(root_path)
target_host = "https://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 https://localhost:%d/ -> https://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

Expand Down

0 comments on commit 888824c

Please sign in to comment.