1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

Add redirect follow feature (#934)

This commit is contained in:
Kevin (Kun) "Kassimo" Qian 2018-10-09 17:31:06 -07:00 committed by Ryan Dahl
parent 94889aef08
commit 888824c617
4 changed files with 84 additions and 22 deletions

View file

@ -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);
});
}

View file

@ -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();

View file

@ -0,0 +1,2 @@
Downloading http://gist.githubusercontent.com/ry/f12b2aa3409e6b52645bc346a9e22929/raw/79318f239f51d764384a8bded8d7c6a833610dde/print_hello.ts
Hello

View file

@ -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