diff options
Diffstat (limited to 'test_util/src/lib.rs')
-rw-r--r-- | test_util/src/lib.rs | 847 |
1 files changed, 564 insertions, 283 deletions
diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 8a47eb139..dbb184fed 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -5,32 +5,47 @@ #[macro_use] extern crate lazy_static; -use futures::future::{self, FutureExt}; +use futures::FutureExt; +use futures::Stream; +use futures::StreamExt; +use futures::TryStreamExt; +use hyper::header::HeaderValue; +use hyper::service::make_service_fn; +use hyper::service::service_fn; +use hyper::Body; +use hyper::Request; +use hyper::Response; +use hyper::Server; +use hyper::StatusCode; use os_pipe::pipe; #[cfg(unix)] pub use pty; use regex::Regex; use std::collections::HashMap; use std::env; +use std::io; use std::io::Read; use std::io::Write; use std::mem::replace; +use std::net::SocketAddr; use std::path::PathBuf; +use std::pin::Pin; use std::process::Child; use std::process::Command; use std::process::Output; use std::process::Stdio; +use std::result::Result; +use std::sync::Arc; use std::sync::Mutex; use std::sync::MutexGuard; +use std::task::Context; +use std::task::Poll; use tempfile::TempDir; -use warp::http::HeaderValue; -use warp::http::Response; -use warp::http::StatusCode; -use warp::http::Uri; -use warp::hyper::Body; -use warp::reply::with_header; -use warp::reply::Reply; -use warp::Filter; +use tokio::net::TcpListener; +use tokio::net::TcpStream; +use tokio_rustls::rustls; +use tokio_rustls::TlsAcceptor; +use tokio_tungstenite::accept_async; const PORT: u16 = 4545; const REDIRECT_PORT: u16 = 4546; @@ -117,132 +132,225 @@ pub fn test_server_path() -> PathBuf { /// Benchmark server that just serves "hello world" responses. async fn hyper_hello(port: u16) { println!("hyper hello"); - let route = warp::any().map(|| "Hello World!"); - warp::serve(route).bind(([127, 0, 0, 1], port)).await; + let addr = SocketAddr::from(([127, 0, 0, 1], port)); + let hello_svc = make_service_fn(|_| async move { + Ok::<_, hyper::error::Error>(service_fn( + move |_: Request<Body>| async move { + Ok::<_, hyper::error::Error>(Response::new(Body::from("Hello World!"))) + }, + )) + }); + + let server = Server::bind(&addr).serve(hello_svc); + if let Err(e) = server.await { + eprintln!("server error: {}", e); + } } -#[tokio::main] -pub async fn run_all_servers() { - if let Some(port) = env::args().nth(1) { - return hyper_hello(port.parse::<u16>().unwrap()).await; +fn redirect_resp(url: String) -> Response<Body> { + let mut redirect_resp = Response::new(Body::empty()); + *redirect_resp.status_mut() = StatusCode::MOVED_PERMANENTLY; + redirect_resp.headers_mut().insert( + hyper::header::LOCATION, + HeaderValue::from_str(&url[..]).unwrap(), + ); + + redirect_resp +} + +async fn redirect(req: Request<Body>) -> hyper::Result<Response<Body>> { + let p = req.uri().path(); + assert_eq!(&p[0..1], "/"); + let url = format!("http://localhost:{}{}", PORT, p); + + Ok(redirect_resp(url)) +} + +async fn double_redirects(req: Request<Body>) -> hyper::Result<Response<Body>> { + let p = req.uri().path(); + assert_eq!(&p[0..1], "/"); + let url = format!("http://localhost:{}{}", REDIRECT_PORT, p); + + Ok(redirect_resp(url)) +} + +async fn inf_redirects(req: Request<Body>) -> hyper::Result<Response<Body>> { + let p = req.uri().path(); + assert_eq!(&p[0..1], "/"); + let url = format!("http://localhost:{}{}", INF_REDIRECTS_PORT, p); + + Ok(redirect_resp(url)) +} + +async fn another_redirect(req: Request<Body>) -> hyper::Result<Response<Body>> { + let p = req.uri().path(); + assert_eq!(&p[0..1], "/"); + let url = format!("http://localhost:{}/cli/tests/subdir{}", PORT, p); + + Ok(redirect_resp(url)) +} + +async fn run_ws_server(addr: &SocketAddr) { + let mut listener = TcpListener::bind(addr).await.unwrap(); + while let Ok((stream, _addr)) = listener.accept().await { + tokio::spawn(async move { + let ws_stream_fut = accept_async(stream); + + let ws_stream = ws_stream_fut.await; + if let Ok(ws_stream) = ws_stream { + let (tx, rx) = ws_stream.split(); + rx.forward(tx) + .map(|result| { + if let Err(e) = result { + println!("websocket server error: {:?}", e); + } + }) + .await; + } + }); } +} - let routes = warp::path::full().map(|path: warp::path::FullPath| { - let p = path.as_str(); - assert_eq!(&p[0..1], "/"); - let url = format!("http://localhost:{}{}", PORT, p); - let u = url.parse::<Uri>().unwrap(); - warp::redirect(u) - }); - let redirect_server_fut = - warp::serve(routes).bind(([127, 0, 0, 1], REDIRECT_PORT)); - - let websocket_route = warp::ws().map(|ws: warp::ws::Ws| { - ws.on_upgrade(|websocket| { - use futures::stream::StreamExt; - let (tx, rx) = websocket.split(); - rx.forward(tx).map(|result| { - if let Err(e) = result { - println!("websocket server error: {:?}", e); - } - }) - }) - }); - let ws_server_fut = - warp::serve(websocket_route).bind(([127, 0, 0, 1], WS_PORT)); - let wss_server_fut = warp::serve(websocket_route) - .tls() - .cert_path("std/http/testdata/tls/localhost.crt") - .key_path("std/http/testdata/tls/localhost.key") - .bind(([127, 0, 0, 1], WSS_PORT)); - - let routes = warp::path::full().map(|path: warp::path::FullPath| { - let p = path.as_str(); - assert_eq!(&p[0..1], "/"); - let url = format!("http://localhost:{}/cli/tests/subdir{}", PORT, p); - let u = url.parse::<Uri>().unwrap(); - warp::redirect(u) - }); - let another_redirect_server_fut = - warp::serve(routes).bind(([127, 0, 0, 1], ANOTHER_REDIRECT_PORT)); - - let routes = warp::path::full().map(|path: warp::path::FullPath| { - let p = path.as_str(); - assert_eq!(&p[0..1], "/"); - let url = format!("http://localhost:{}{}", REDIRECT_PORT, p); - let u = url.parse::<Uri>().unwrap(); - warp::redirect(u) - }); - let double_redirect_server_fut = - warp::serve(routes).bind(([127, 0, 0, 1], DOUBLE_REDIRECTS_PORT)); - - let routes = warp::path::full().map(|path: warp::path::FullPath| { - let p = path.as_str(); - assert_eq!(&p[0..1], "/"); - let url = format!("http://localhost:{}{}", INF_REDIRECTS_PORT, p); - let u = url.parse::<Uri>().unwrap(); - warp::redirect(u) - }); - let inf_redirect_server_fut = - warp::serve(routes).bind(([127, 0, 0, 1], INF_REDIRECTS_PORT)); - - // redirect server that redirect to absolute paths under same host - // redirects /REDIRECT/file_name to /file_name - let routes = warp::path("REDIRECT") - .and(warp::path::peek()) - .map(|path: warp::path::Peek| { - let p = path.as_str(); - let url = format!("/{}", p); - let u = url.parse::<Uri>().unwrap(); - warp::redirect(u) - }) - .or( - warp::path!("a" / "b" / "c") - .and(warp::header::<String>("x-location")) - .map(|token: String| { - let uri: Uri = token.parse().unwrap(); - warp::redirect(uri) - }), - ) - .or( - warp::any() - .and(warp::path::peek()) - .and(warp::fs::dir(root_path())) - .map(custom_headers), - ); - let absolute_redirect_server_fut = - warp::serve(routes).bind(([127, 0, 0, 1], REDIRECT_ABSOLUTE_PORT)); - - let echo_server = warp::path("echo_server") - .and(warp::post()) - .and(warp::body::bytes()) - .and(warp::header::optional::<String>("x-status")) - .and(warp::header::optional::<String>("content-type")) - .and(warp::header::optional::<String>("user-agent")) - .map( - |bytes: bytes::Bytes, - status: Option<String>, - content_type: Option<String>, - user_agent: Option<String>| - -> Box<dyn Reply> { - let mut res = Response::new(Body::from(bytes)); - if let Some(v) = status { - *res.status_mut() = StatusCode::from_bytes(v.as_bytes()).unwrap(); - } - let h = res.headers_mut(); - if let Some(v) = content_type { - h.insert("content-type", HeaderValue::from_str(&v).unwrap()); +async fn get_tls_config( + cert: &str, + key: &str, +) -> io::Result<Arc<rustls::ServerConfig>> { + let mut cert_path = root_path(); + let mut key_path = root_path(); + cert_path.push(cert); + key_path.push(key); + + let cert_file = std::fs::File::open(cert_path)?; + let key_file = std::fs::File::open(key_path)?; + + let mut cert_reader = io::BufReader::new(cert_file); + let cert = rustls::internal::pemfile::certs(&mut cert_reader) + .expect("Cannot load certificate"); + let mut key_reader = io::BufReader::new(key_file); + let key = { + let pkcs8_key = + rustls::internal::pemfile::pkcs8_private_keys(&mut key_reader) + .expect("Cannot load key file"); + let rsa_key = rustls::internal::pemfile::rsa_private_keys(&mut key_reader) + .expect("Cannot load key file"); + if !pkcs8_key.is_empty() { + Some(pkcs8_key[0].clone()) + } else if !rsa_key.is_empty() { + Some(rsa_key[0].clone()) + } else { + None + } + }; + + match key { + Some(key) => { + let mut config = rustls::ServerConfig::new(rustls::NoClientAuth::new()); + config + .set_single_cert(cert, key) + .map_err(|e| { + eprintln!("Error setting cert: {:?}", e); + }) + .unwrap(); + + return Ok(Arc::new(config)); + } + None => { + return Err(io::Error::new(io::ErrorKind::Other, "Cannot find key")); + } + } +} + +async fn run_wss_server(addr: &SocketAddr) { + let cert_file = "std/http/testdata/tls/localhost.crt"; + let key_file = "std/http/testdata/tls/localhost.key"; + + let tls_config = get_tls_config(cert_file, key_file).await.unwrap(); + let tls_acceptor = TlsAcceptor::from(tls_config); + let mut listener = TcpListener::bind(addr).await.unwrap(); + + while let Ok((stream, _addr)) = listener.accept().await { + let acceptor = tls_acceptor.clone(); + tokio::spawn(async move { + match acceptor.accept(stream).await { + Ok(tls_stream) => { + let ws_stream_fut = accept_async(tls_stream); + let ws_stream = ws_stream_fut.await; + if let Ok(ws_stream) = ws_stream { + let (tx, rx) = ws_stream.split(); + rx.forward(tx) + .map(|result| { + if let Err(e) = result { + println!("Websocket server error: {:?}", e); + } + }) + .await; + } } - if let Some(v) = user_agent { - h.insert("user-agent", HeaderValue::from_str(&v).unwrap()); + Err(e) => { + eprintln!("TLS accept error: {:?}", e); } - Box::new(res) - }, - ); - let echo_multipart_file = warp::path("echo_multipart_file") - .and(warp::post()) - .and(warp::body::bytes()) - .map(|bytes: bytes::Bytes| -> Box<dyn Reply> { + } + }); + } +} + +async fn absolute_redirect( + req: Request<Body>, +) -> hyper::Result<Response<Body>> { + let path = req.uri().path(); + + if path.starts_with("/REDIRECT") { + let url = &req.uri().path()[9..]; + println!("URL: {:?}", url); + let redirect = redirect_resp(url.to_string()); + return Ok(redirect); + } + + if path.starts_with("/a/b/c") { + if let Some(x_loc) = req.headers().get("x-location") { + let loc = x_loc.to_str().unwrap(); + return Ok(redirect_resp(loc.to_string())); + } + } + + let mut file_path = root_path(); + file_path.push(&req.uri().path()[1..]); + if file_path.is_dir() || !file_path.exists() { + let mut not_found_resp = Response::new(Body::empty()); + *not_found_resp.status_mut() = StatusCode::NOT_FOUND; + return Ok(not_found_resp); + } + + let file = tokio::fs::read(file_path).await.unwrap(); + let file_resp = custom_headers(req.uri().path(), file); + return Ok(file_resp); +} + +async fn main_server(req: Request<Body>) -> hyper::Result<Response<Body>> { + return match (req.method(), req.uri().path()) { + (&hyper::Method::POST, "/echo_server") => { + let (parts, body) = req.into_parts(); + let mut response = Response::new(body); + + if let Some(status) = parts.headers.get("x-status") { + *response.status_mut() = + StatusCode::from_bytes(status.as_bytes()).unwrap(); + } + if let Some(content_type) = parts.headers.get("content-type") { + response + .headers_mut() + .insert("content-type", content_type.clone()); + } + if let Some(user_agent) = parts.headers.get("user-agent") { + response + .headers_mut() + .insert("user-agent", user_agent.clone()); + } + Ok(response) + } + (&hyper::Method::POST, "/echo_multipart_file") => { + let body = req.into_body(); + let bytes = &hyper::body::to_bytes(body).await.unwrap()[0..]; let start = b"--boundary\t \r\n\ Content-Disposition: form-data; name=\"field_1\"\r\n\ \r\n\ @@ -253,236 +361,383 @@ pub async fn run_all_servers() { Content-Type: application/octet-stream\r\n\ \r\n"; let end = b"\r\n--boundary--\r\n"; - let b = [start as &[u8], &bytes, end].concat(); + let b = [start as &[u8], bytes, end].concat(); - let mut res = Response::new(Body::from(b)); - let h = res.headers_mut(); - h.insert( + let mut response = Response::new(Body::from(b)); + response.headers_mut().insert( "content-type", HeaderValue::from_static("multipart/form-data;boundary=boundary"), ); - Box::new(res) - }); - let multipart_form_data = - warp::path("multipart_form_data.txt").map(|| -> Box<dyn Reply> { + Ok(response) + } + (_, "/multipart_form_data.txt") => { let b = "Preamble\r\n\ - --boundary\t \r\n\ - Content-Disposition: form-data; name=\"field_1\"\r\n\ - \r\n\ - value_1 \r\n\ - \r\n--boundary\r\n\ - Content-Disposition: form-data; name=\"field_2\";\ - filename=\"file.js\"\r\n\ - Content-Type: text/javascript\r\n\ - \r\n\ - console.log(\"Hi\")\ - \r\n--boundary--\r\n\ - Epilogue"; + --boundary\t \r\n\ + Content-Disposition: form-data; name=\"field_1\"\r\n\ + \r\n\ + value_1 \r\n\ + \r\n--boundary\r\n\ + Content-Disposition: form-data; name=\"field_2\";\ + filename=\"file.js\"\r\n\ + Content-Type: text/javascript\r\n\ + \r\n\ + console.log(\"Hi\")\ + \r\n--boundary--\r\n\ + Epilogue"; let mut res = Response::new(Body::from(b)); res.headers_mut().insert( "content-type", HeaderValue::from_static("multipart/form-data;boundary=boundary"), ); - Box::new(res) - }); - let bad_redirect = warp::path("bad_redirect").map(|| -> Box<dyn Reply> { - let mut res = Response::new(Body::empty()); - *res.status_mut() = StatusCode::FOUND; - Box::new(res) - }); - let non_ascii_redirect = - warp::path("non_ascii_redirect").map(|| -> Box<dyn Reply> { + Ok(res) + } + (_, "/bad_redirect") => { + let mut res = Response::new(Body::empty()); + *res.status_mut() = StatusCode::FOUND; + Ok(res) + } + (_, "/non_ascii_redirect") => { let mut res = Response::new(Body::empty()); *res.status_mut() = StatusCode::MOVED_PERMANENTLY; res.headers_mut().insert( "location", HeaderValue::from_bytes(b"/redirect\xae").unwrap(), ); - Box::new(res) - }); + Ok(res) + } + (_, "/etag_script.ts") => { + let if_none_match = req.headers().get("if-none-match"); + if if_none_match == Some(&HeaderValue::from_static("33a64df551425fcc55e")) + { + let mut resp = Response::new(Body::empty()); + *resp.status_mut() = StatusCode::NOT_MODIFIED; + resp.headers_mut().insert( + "Content-type", + HeaderValue::from_static("application/typescript"), + ); + resp + .headers_mut() + .insert("ETag", HeaderValue::from_static("33a64df551425fcc55e")); - let etag_script = warp::path!("etag_script.ts") - .and(warp::header::optional::<String>("if-none-match")) - .map(|if_none_match| -> Box<dyn Reply> { - if if_none_match == Some("33a64df551425fcc55e".to_string()) { - let r = - warp::reply::with_status(warp::reply(), StatusCode::NOT_MODIFIED); - let r = with_header(r, "Content-type", "application/typescript"); - let r = with_header(r, "ETag", "33a64df551425fcc55e"); - Box::new(r) + Ok(resp) } else { - let mut res = Response::new(Body::from("console.log('etag')")); - let h = res.headers_mut(); - h.insert( + let mut resp = Response::new(Body::from("console.log('etag')")); + resp.headers_mut().insert( "Content-type", HeaderValue::from_static("application/typescript"), ); - h.insert("ETag", HeaderValue::from_static("33a64df551425fcc55e")); - Box::new(res) + resp + .headers_mut() + .insert("ETag", HeaderValue::from_static("33a64df551425fcc55e")); + Ok(resp) } - }); - let xtypescripttypes = warp::path!("xTypeScriptTypes.js") - .map(|| { + } + (_, "/xTypeScriptTypes.js") => { let mut res = Response::new(Body::from("export const foo = 'foo';")); - let h = res.headers_mut(); - h.insert( + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/javascript"), ); - h.insert( + res.headers_mut().insert( "X-TypeScript-Types", HeaderValue::from_static("./xTypeScriptTypes.d.ts"), ); - res - }) - .or(warp::path!("xTypeScriptTypes.d.ts").map(|| { + Ok(res) + } + (_, "/xTypeScriptTypes.d.ts") => { let mut res = Response::new(Body::from("export const foo: 'foo';")); res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/typescript"), ); - res - })) - .or(warp::path!("type_directives_redirect.js").map(|| { + Ok(res) + } + (_, "/type_directives_redirect.js") => { let mut res = Response::new(Body::from("export const foo = 'foo';")); - let h = res.headers_mut(); - h.insert( + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/javascript"), ); - h.insert( + res.headers_mut().insert( "X-TypeScript-Types", HeaderValue::from_static( "http://localhost:4547/xTypeScriptTypesRedirect.d.ts", ), ); - res - })) - .or(warp::path!("type_headers_deno_types.foo.js").map(|| { - let mut res = Response::new(Body::from("export function foo(text) { console.log(text); }")); - let h = res.headers_mut(); - h.insert( + Ok(res) + } + (_, "/type_headers_deno_types.foo.js") => { + let mut res = Response::new(Body::from( + "export function foo(text) { console.log(text); }", + )); + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/javascript"), ); - h.insert( + res.headers_mut().insert( "X-TypeScript-Types", HeaderValue::from_static( "http://localhost:4545/type_headers_deno_types.d.ts", ), ); - res - })) - .or(warp::path!("type_headers_deno_types.d.ts").map(|| { - let mut res = Response::new(Body::from("export function foo(text: number): void;")); - let h = res.headers_mut(); - h.insert( + Ok(res) + } + (_, "/type_headers_deno_types.d.ts") => { + let mut res = + Response::new(Body::from("export function foo(text: number): void;")); + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/typescript"), ); - res - })) - .or(warp::path!("type_headers_deno_types.foo.d.ts").map(|| { - let mut res = Response::new(Body::from("export function foo(text: string): void;")); - let h = res.headers_mut(); - h.insert( + Ok(res) + } + (_, "/type_headers_deno_types.foo.d.ts") => { + let mut res = + Response::new(Body::from("export function foo(text: string): void;")); + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/typescript"), ); - res - })) - .or(warp::path!("cli"/"tests"/"subdir"/"xTypeScriptTypesRedirect.d.ts").map(|| { + Ok(res) + } + (_, "/cli/tests/subdir/xTypeScriptTypesRedirect.d.ts") => { let mut res = Response::new(Body::from( "import './xTypeScriptTypesRedirected.d.ts';", )); - let h = res.headers_mut(); - h.insert( + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/typescript"), ); - res - })) - .or(warp::path!("cli"/"tests"/"subdir"/"xTypeScriptTypesRedirected.d.ts").map(|| { + Ok(res) + } + (_, "/cli/tests/subdir/xTypeScriptTypesRedirected.d.ts") => { let mut res = Response::new(Body::from("export const foo: 'foo';")); - let h = res.headers_mut(); - h.insert( + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/typescript"), ); - res - })) - .or(warp::path!("referenceTypes.js").map(|| { + Ok(res) + } + (_, "/referenceTypes.js") => { let mut res = Response::new(Body::from("/// <reference types=\"./xTypeScriptTypes.d.ts\" />\r\nexport const foo = \"foo\";\r\n")); - let h = res.headers_mut(); - h.insert( + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/javascript"), ); - res - })) - .or(warp::path!("cli"/"tests"/"subdir"/"file_with_:_in_name.ts").map(|| { + Ok(res) + } + (_, "/cli/tests/subdir/file_with_:_in_name.ts") => { let mut res = Response::new(Body::from( "console.log('Hello from file_with_:_in_name.ts');", )); - let h = res.headers_mut(); - h.insert( + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/typescript"), ); - res - })) - .or(warp::path!("cli"/"tests"/"subdir"/"no_js_ext@1.0.0").map(|| { + Ok(res) + } + (_, "/cli/tests/subdir/no_js_ext@1.0.0") => { let mut res = Response::new(Body::from( r#"import { printHello } from "./mod2.ts"; printHello(); "#, )); - let h = res.headers_mut(); - h.insert( + res.headers_mut().insert( "Content-type", HeaderValue::from_static("application/javascript"), ); - res - })); - - let content_type_handler = warp::any() - .and(warp::path::peek()) - .and(warp::fs::dir(root_path())) - .map(custom_headers) - .or(etag_script) - .or(xtypescripttypes) - .or(echo_server) - .or(echo_multipart_file) - .or(multipart_form_data) - .or(bad_redirect) - .or(non_ascii_redirect); - - let http_fut = - warp::serve(content_type_handler.clone()).bind(([127, 0, 0, 1], PORT)); - - let https_fut = warp::serve(content_type_handler.clone()) - .tls() - .cert_path("std/http/testdata/tls/localhost.crt") - .key_path("std/http/testdata/tls/localhost.key") - .bind(([127, 0, 0, 1], HTTPS_PORT)); + Ok(res) + } + _ => { + let mut file_path = root_path(); + file_path.push(&req.uri().path()[1..]); + if let Ok(file) = tokio::fs::read(file_path).await { + let file_resp = custom_headers(&req.uri().path()[1..], file); + return Ok(file_resp); + } + + return Ok(Response::new(Body::empty())); + } + }; +} + +/// Taken from example in https://github.com/ctz/hyper-rustls/blob/a02ef72a227dcdf102f86e905baa7415c992e8b3/examples/server.rs +struct HyperAcceptor<'a> { + acceptor: Pin< + Box< + dyn Stream<Item = io::Result<tokio_rustls::server::TlsStream<TcpStream>>> + + 'a, + >, + >, +} + +impl hyper::server::accept::Accept for HyperAcceptor<'_> { + type Conn = tokio_rustls::server::TlsStream<TcpStream>; + type Error = io::Error; + + fn poll_accept( + mut self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll<Option<Result<Self::Conn, Self::Error>>> { + Pin::new(&mut self.acceptor).poll_next(cx) + } +} + +unsafe impl std::marker::Send for HyperAcceptor<'_> {} + +async fn wrap_redirect_server() { + let redirect_svc = + make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(redirect)) }); + let redirect_addr = SocketAddr::from(([127, 0, 0, 1], REDIRECT_PORT)); + let redirect_server = Server::bind(&redirect_addr).serve(redirect_svc); + if let Err(e) = redirect_server.await { + eprintln!("Redirect error: {:?}", e); + } +} + +async fn wrap_double_redirect_server() { + let double_redirects_svc = make_service_fn(|_| async { + Ok::<_, hyper::Error>(service_fn(double_redirects)) + }); + let double_redirects_addr = + SocketAddr::from(([127, 0, 0, 1], DOUBLE_REDIRECTS_PORT)); + let double_redirects_server = + Server::bind(&double_redirects_addr).serve(double_redirects_svc); + if let Err(e) = double_redirects_server.await { + eprintln!("Double redirect error: {:?}", e); + } +} + +async fn wrap_inf_redirect_server() { + let inf_redirects_svc = make_service_fn(|_| async { + Ok::<_, hyper::Error>(service_fn(inf_redirects)) + }); + let inf_redirects_addr = + SocketAddr::from(([127, 0, 0, 1], INF_REDIRECTS_PORT)); + let inf_redirects_server = + Server::bind(&inf_redirects_addr).serve(inf_redirects_svc); + if let Err(e) = inf_redirects_server.await { + eprintln!("Inf redirect error: {:?}", e); + } +} + +async fn wrap_another_redirect_server() { + let another_redirect_svc = make_service_fn(|_| async { + Ok::<_, hyper::Error>(service_fn(another_redirect)) + }); + let another_redirect_addr = + SocketAddr::from(([127, 0, 0, 1], ANOTHER_REDIRECT_PORT)); + let another_redirect_server = + Server::bind(&another_redirect_addr).serve(another_redirect_svc); + if let Err(e) = another_redirect_server.await { + eprintln!("Another redirect error: {:?}", e); + } +} + +async fn wrap_abs_redirect_server() { + let abs_redirect_svc = make_service_fn(|_| async { + Ok::<_, hyper::Error>(service_fn(absolute_redirect)) + }); + let abs_redirect_addr = + SocketAddr::from(([127, 0, 0, 1], REDIRECT_ABSOLUTE_PORT)); + let abs_redirect_server = + Server::bind(&abs_redirect_addr).serve(abs_redirect_svc); + if let Err(e) = abs_redirect_server.await { + eprintln!("Absolute redirect error: {:?}", e); + } +} + +async fn wrap_main_server() { + let main_server_svc = make_service_fn(|_| async { + Ok::<_, hyper::Error>(service_fn(main_server)) + }); + let main_server_addr = SocketAddr::from(([127, 0, 0, 1], PORT)); + let main_server = Server::bind(&main_server_addr).serve(main_server_svc); + if let Err(e) = main_server.await { + eprintln!("HTTP server error: {:?}", e); + } +} + +async fn wrap_main_https_server() { + let main_server_https_addr = SocketAddr::from(([127, 0, 0, 1], HTTPS_PORT)); + let cert_file = "std/http/testdata/tls/localhost.crt"; + let key_file = "std/http/testdata/tls/localhost.key"; + let tls_config = get_tls_config(cert_file, key_file) + .await + .expect("Cannot get TLS config"); + let mut tcp = TcpListener::bind(&main_server_https_addr) + .await + .expect("Cannot bind TCP"); + loop { + let tls_acceptor = TlsAcceptor::from(tls_config.clone()); + // Prepare a long-running future stream to accept and serve cients. + let incoming_tls_stream = tcp + .incoming() + .map_err(|e| { + eprintln!("Error Incoming: {:?}", e); + io::Error::new(io::ErrorKind::Other, e) + }) + .and_then(move |s| { + use futures::TryFutureExt; + tls_acceptor.accept(s).map_err(|e| { + eprintln!("TLS Error {:?}", e); + e + }) + }) + .boxed(); + + let main_server_https_svc = make_service_fn(|_| async { + Ok::<_, hyper::Error>(service_fn(main_server)) + }); + let main_server_https = Server::builder(HyperAcceptor { + acceptor: incoming_tls_stream, + }) + .serve(main_server_https_svc); + + //continue to prevent TLS error stopping the server + if main_server_https.await.is_err() { + continue; + } + } +} + +#[tokio::main] +pub async fn run_all_servers() { + if let Some(port) = env::args().nth(1) { + return hyper_hello(port.parse::<u16>().unwrap()).await; + } + + let redirect_server_fut = wrap_redirect_server(); + let double_redirects_server_fut = wrap_double_redirect_server(); + let inf_redirects_server_fut = wrap_inf_redirect_server(); + let another_redirect_server_fut = wrap_another_redirect_server(); + let abs_redirect_server_fut = wrap_abs_redirect_server(); + + let ws_addr = SocketAddr::from(([127, 0, 0, 1], WS_PORT)); + let ws_server_fut = run_ws_server(&ws_addr); + let wss_addr = SocketAddr::from(([127, 0, 0, 1], WSS_PORT)); + let wss_server_fut = run_wss_server(&wss_addr); + + let main_server_fut = wrap_main_server(); + let main_server_https_fut = wrap_main_https_server(); let mut server_fut = async { futures::join!( - http_fut, - https_fut, redirect_server_fut, ws_server_fut, wss_server_fut, another_redirect_server_fut, - inf_redirect_server_fut, - double_redirect_server_fut, - absolute_redirect_server_fut, + inf_redirects_server_fut, + double_redirects_server_fut, + abs_redirect_server_fut, + main_server_fut, + main_server_https_fut, ) } .boxed(); let mut did_print_ready = false; - future::poll_fn(move |cx| { + futures::future::poll_fn(move |cx| { let poll_result = server_fut.poll_unpin(cx); if !replace(&mut did_print_ready, true) { println!("ready"); @@ -492,38 +747,61 @@ pub async fn run_all_servers() { .await; } -fn custom_headers(path: warp::path::Peek, f: warp::fs::File) -> Box<dyn Reply> { - let p = path.as_str(); +fn custom_headers(p: &str, body: Vec<u8>) -> Response<Body> { + let mut response = Response::new(Body::from(body)); if p.ends_with("cli/tests/x_deno_warning.js") { - let f = with_header(f, "Content-Type", "application/javascript"); - let f = with_header(f, "X-Deno-Warning", "foobar"); - return Box::new(f); + response.headers_mut().insert( + "Content-Type", + HeaderValue::from_static("application/javascript"), + ); + response + .headers_mut() + .insert("X-Deno-Warning", HeaderValue::from_static("foobar")); + return response; } if p.ends_with("cli/tests/053_import_compression/brotli") { - let f = with_header(f, "Content-Encoding", "br"); - let f = with_header(f, "Content-Type", "application/javascript"); - let f = with_header(f, "Content-Length", "26"); - return Box::new(f); + response + .headers_mut() + .insert("Content-Encoding", HeaderValue::from_static("br")); + response.headers_mut().insert( + "Content-Type", + HeaderValue::from_static("application/javascript"), + ); + response + .headers_mut() + .insert("Content-Length", HeaderValue::from_static("26")); + return response; } if p.ends_with("cli/tests/053_import_compression/gziped") { - let f = with_header(f, "Content-Encoding", "gzip"); - let f = with_header(f, "Content-Type", "application/javascript"); - let f = with_header(f, "Content-Length", "39"); - return Box::new(f); + response + .headers_mut() + .insert("Content-Encoding", HeaderValue::from_static("gzip")); + response.headers_mut().insert( + "Content-Type", + HeaderValue::from_static("application/javascript"), + ); + response + .headers_mut() + .insert("Content-Length", HeaderValue::from_static("39")); + return response; } + if p.contains("cli/tests/encoding/") { let charset = p .split_terminator('/') .last() .unwrap() .trim_end_matches(".ts"); - let f = with_header( - f, + + response.headers_mut().insert( "Content-Type", - &format!("application/typescript;charset={}", charset)[..], + HeaderValue::from_str( + &format!("application/typescript;charset={}", charset)[..], + ) + .unwrap(), ); - return Box::new(f); + return response; } let content_type = if p.contains(".t1.") { @@ -559,10 +837,13 @@ fn custom_headers(path: warp::path::Peek, f: warp::fs::File) -> Box<dyn Reply> { }; if let Some(t) = content_type { - Box::new(with_header(f, "Content-Type", t)) - } else { - Box::new(f) + response + .headers_mut() + .insert("Content-Type", HeaderValue::from_str(t).unwrap()); + return response; } + + response } #[derive(Default)] @@ -790,7 +1071,7 @@ pub fn deno_cmd() -> Command { pub fn run_powershell_script_file( script_file_path: &str, args: Vec<&str>, -) -> Result<(), i64> { +) -> std::result::Result<(), i64> { let deno_dir = new_deno_dir(); let mut command = Command::new("powershell.exe"); |