diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index e9309c7e4a..2d9f3c104d 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -312,7 +312,17 @@ impl SourceFileFetcher { let (mut source_file, headers) = result; if let Some(redirect_to) = headers.get("location") { - let redirect_url = Url::parse(redirect_to).expect("Should be valid URL"); + let redirect_url = match Url::parse(redirect_to) { + Ok(redirect_url) => redirect_url, + Err(url::ParseError::RelativeUrlWithoutBase) => { + let mut url = module_url.clone(); + url.set_path(redirect_to); + url + } + Err(e) => { + return Err(e.into()); + } + }; return self.fetch_cached_remote_source(&redirect_url); } @@ -1089,6 +1099,55 @@ mod tests { drop(http_server_guard); } + #[tokio::test] + async fn test_get_source_code_7() { + let http_server_guard = crate::test_util::http_server(); + let (_temp_dir, fetcher) = test_setup(); + + // Testing redirect with Location set to absolute url. + let redirect_module_url = Url::parse( + "http://localhost:4550/REDIRECT/cli/tests/subdir/redirects/redirect1.js", + ) + .unwrap(); + let redirect_source_filepath = + fetcher.http_cache.get_cache_filename(&redirect_module_url); + let redirect_source_filename = + redirect_source_filepath.to_str().unwrap().to_string(); + let target_module_url = Url::parse( + "http://localhost:4550/cli/tests/subdir/redirects/redirect1.js", + ) + .unwrap(); + let redirect_target_filepath = + fetcher.http_cache.get_cache_filename(&target_module_url); + let redirect_target_filename = + redirect_target_filepath.to_str().unwrap().to_string(); + + // Test basic follow and headers recording + let result = fetcher + .get_source_file(&redirect_module_url, true, false, false) + .await; + assert!(result.is_ok()); + let mod_meta = result.unwrap(); + // File that requires redirection should be empty file. + assert_eq!(fs::read_to_string(&redirect_source_filename).unwrap(), ""); + let (_, headers) = fetcher.http_cache.get(&redirect_module_url).unwrap(); + assert_eq!( + headers.get("location").unwrap(), + "/cli/tests/subdir/redirects/redirect1.js" + ); + // The target of redirection is downloaded instead. + assert_eq!( + fs::read_to_string(&redirect_target_filename).unwrap(), + "export const redirect = 1;\n" + ); + let (_, headers) = fetcher.http_cache.get(&target_module_url).unwrap(); + assert!(headers.get("location").is_none()); + // Examine the meta result. + assert_eq!(mod_meta.url, target_module_url); + + drop(http_server_guard); + } + #[tokio::test] async fn test_get_source_no_remote() { let http_server_guard = crate::test_util::http_server(); diff --git a/tools/http_server.py b/tools/http_server.py index 7cb2e28f9c..0ed61465c2 100755 --- a/tools/http_server.py +++ b/tools/http_server.py @@ -21,6 +21,7 @@ REDIRECT_PORT = 4546 ANOTHER_REDIRECT_PORT = 4547 DOUBLE_REDIRECTS_PORT = 4548 INF_REDIRECTS_PORT = 4549 +REDIRECT_ABSOLUTE_PORT = 4550 HTTPS_PORT = 5545 @@ -284,6 +285,29 @@ def inf_redirects_server(): return base_redirect_server(INF_REDIRECTS_PORT, INF_REDIRECTS_PORT) +# redirect server that redirect to absolute paths under same host +# redirects /REDIRECT/file_name to /file_name +def absolute_redirect_server(): + os.chdir(root_path) + + class AbsoluteRedirectHandler(ContentTypeHandler): + def do_GET(self): + print(self.path) + if (self.path.startswith("/REDIRECT/")): + self.send_response(302) + self.send_header('Location', + self.path.split('/REDIRECT', 1)[1]) + self.end_headers() + else: + ContentTypeHandler.do_GET(self) + + s = get_socket(REDIRECT_ABSOLUTE_PORT, AbsoluteRedirectHandler, False) + if not QUIET: + print("absolute redirect server http://localhost:%d/" % + REDIRECT_ABSOLUTE_PORT) + return RunningServer(s, start(s)) + + def https_server(): os.chdir(root_path) # Hopefully the main thread doesn't also chdir. Handler = ContentTypeHandler @@ -310,7 +334,8 @@ def start(s): @contextmanager def spawn(): servers = (server(), redirect_server(), another_redirect_server(), - double_redirects_server(), https_server()) + double_redirects_server(), https_server(), + absolute_redirect_server()) # In order to wait for each of the servers to be ready, we try connecting to # them with a tcp socket. for running_server in servers: