diff --git a/src/lib.rs b/src/lib.rs index 36f95ae..26559df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -211,7 +211,7 @@ async fn proxy_handler( // A non internal request, but the host header has not been defined (_, _, false, false) => { - info!("Host header not defined"); + debug!("Host header not defined"); *response.body_mut() = Body::from("Host header not defined"); *response.status_mut() = StatusCode::NOT_FOUND; } @@ -256,7 +256,7 @@ async fn proxy_handler( } response = proxy_config.client.make_request(req).await; - info!( + debug!( "Proxied response from: {} | Status: {}", uri, response.status() @@ -309,7 +309,10 @@ pub async fn run_server(config_path: String) { .expect("TLS config error"); let app = Router::new() - .route("/*path", get(proxy_handler)) + .route( + "/*path", + get(proxy_handler).post(proxy_handler).put(proxy_handler), + ) .layer(Extension(proxy_config)) .layer(Extension(proxy_state)); diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 9af7561..ba2e40d 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,5 +1,5 @@ use reqwest::header::HOST; -use reqwest::{Error, Response}; +use reqwest::{Error, Method, Response}; use std::net::TcpListener; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; @@ -22,11 +22,25 @@ impl MockBackend { if self.mock_server.is_none() { let listener = TcpListener::bind(address).unwrap(); let mock_server = MockServer::builder().listener(listener).start().await; - let template = ResponseTemplate::new(200).set_body_raw(resp_body, "text/plain"); Mock::given(method("GET")) .and(path("/test")) - .respond_with(template) + .respond_with(ResponseTemplate::new(200).set_body_raw(resp_body, "text/plain")) + .mount(&mock_server) + .await; + Mock::given(method("HEAD")) + .and(path("/test")) + .respond_with(ResponseTemplate::new(200)) + .mount(&mock_server) + .await; + Mock::given(method("POST")) + .and(path("/test")) + .respond_with(ResponseTemplate::new(200)) + .mount(&mock_server) + .await; + Mock::given(method("PUT")) + .and(path("/test")) + .respond_with(ResponseTemplate::new(200)) .mount(&mock_server) .await; Mock::given(method("GET")) @@ -81,12 +95,18 @@ fn start_proxy() -> bool { parent } -async fn assert_response(resp: Result, expected_status: u16, expected_body: &str) { +async fn assert_response( + resp: Result, + expected_status: u16, + expected_body: Option<&str>, +) { let response = resp.unwrap(); let status = response.status(); let body = response.bytes().await.unwrap(); assert_eq!(status, expected_status); - assert_eq!(body, expected_body); + if let Some(expected_body) = expected_body { + assert_eq!(body, expected_body); + } } async fn http_request( @@ -94,6 +114,7 @@ async fn http_request( url: &str, host_header: Option<&str>, no_proxy: Option, + method: Option, ) -> Result { let mut client_builder = reqwest::Client::builder().danger_accept_invalid_certs(true); if protocol == "http1" { @@ -103,21 +124,30 @@ async fn http_request( } let client = client_builder.build().unwrap(); + let client_method; + if method == Some(Method::HEAD) { + client_method = client.head(url); + } else if method == Some(Method::POST) { + client_method = client.post(url); + } else if method == Some(Method::PUT) { + client_method = client.put(url); + } else { + client_method = client.get(url); + } if host_header.is_some() { - client - .get(url) + client_method .header(HOST, host_header.unwrap()) .send() .await } else if no_proxy.is_some() && no_proxy.unwrap() { - client.get(url).header("x-no-proxy", "true").send().await + client_method.header("x-no-proxy", "true").send().await } else { - client.get(url).send().await + client_method.send().await } } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http1_test() { +async fn http1_get() { MOCK_BACKEND .lock() .unwrap() @@ -134,17 +164,18 @@ async fn http1_test() { "https://localhost:4000/test", Some("test.home"), None, + None, ) .await; // In this case the response should be a 200 from the mock backend - assert_response(resp, 200, "This is the mock backend!").await; + assert_response(resp, 200, Some("This is the mock backend!")).await; finish(proxy_parent); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http1_no_host_header_test() { +async fn http1_get_no_host_header() { MOCK_BACKEND .lock() .unwrap() @@ -156,16 +187,16 @@ async fn http1_no_host_header_test() { thread::sleep(time::Duration::from_millis(1000)); // Send a request to the proxy without a host header - let resp = http_request("http1", "https://localhost:4000/test", None, None).await; + let resp = http_request("http1", "https://localhost:4000/test", None, None, None).await; // In this case the proxy should respond with a 404 - assert_response(resp, 404, "Host header not defined").await; + assert_response(resp, 404, Some("Host header not defined")).await; finish(proxy_parent); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http1_no_proxy_header_status() { +async fn http1_get_no_proxy_header_status() { MOCK_BACKEND .lock() .unwrap() @@ -177,16 +208,23 @@ async fn http1_no_proxy_header_status() { thread::sleep(time::Duration::from_millis(1000)); // Send an internal /status request - let resp = http_request("http1", "https://localhost:4000/status", None, Some(true)).await; + let resp = http_request( + "http1", + "https://localhost:4000/status", + None, + Some(true), + None, + ) + .await; // In this case the proxy should respond with a 200 - assert_response(resp, 200, "The proxy is running").await; + assert_response(resp, 200, Some("The proxy is running")).await; finish(proxy_parent); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http1_no_proxy_header_metrics() { +async fn http1_get_no_proxy_header_metrics() { MOCK_BACKEND .lock() .unwrap() @@ -203,11 +241,19 @@ async fn http1_no_proxy_header_metrics() { "https://localhost:4000/test", Some("test.home"), None, + None, ) .await; // Send an internal /metrics request - let resp = http_request("http1", "https://localhost:4000/metrics", None, Some(true)).await; + let resp = http_request( + "http1", + "https://localhost:4000/metrics", + None, + Some(true), + None, + ) + .await; let response = resp.unwrap(); let status = response.status(); let body = response.bytes().await.unwrap(); @@ -218,7 +264,91 @@ async fn http1_no_proxy_header_metrics() { } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http2_test() { +async fn http1_head() { + MOCK_BACKEND + .lock() + .unwrap() + .init("127.0.0.1:8000", "This is the mock backend!") + .await; + let proxy_parent = start_proxy(); + + // Sleep this thread while the server starts up + thread::sleep(time::Duration::from_millis(1000)); + + // Send a request to the proxy, which should be forwarded to the mock server + let resp = http_request( + "http1", + "https://localhost:4000/test", + Some("test.home"), + None, + Some(Method::HEAD), + ) + .await; + + // In this case the response should be a 200 from the mock backend + assert_response(resp, 200, None).await; + + finish(proxy_parent); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn http1_post() { + MOCK_BACKEND + .lock() + .unwrap() + .init("127.0.0.1:8000", "This is the mock backend!") + .await; + let proxy_parent = start_proxy(); + + // Sleep this thread while the server starts up + thread::sleep(time::Duration::from_millis(1000)); + + // Send a request to the proxy, which should be forwarded to the mock server + let resp = http_request( + "http1", + "https://localhost:4000/test", + Some("test.home"), + None, + Some(Method::POST), + ) + .await; + + // In this case the response should be a 200 from the mock backend + assert_response(resp, 200, None).await; + + finish(proxy_parent); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn http1_put() { + MOCK_BACKEND + .lock() + .unwrap() + .init("127.0.0.1:8000", "This is the mock backend!") + .await; + let proxy_parent = start_proxy(); + + // Sleep this thread while the server starts up + thread::sleep(time::Duration::from_millis(1000)); + + // Send a request to the proxy, which should be forwarded to the mock server + let resp = http_request( + "http1", + "https://localhost:4000/test", + Some("test.home"), + None, + Some(Method::PUT), + ) + .await; + + // In this case the response should be a 200 from the mock backend + assert_response(resp, 200, None).await; + + finish(proxy_parent); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn http2_get() { MOCK_BACKEND .lock() .unwrap() @@ -235,17 +365,18 @@ async fn http2_test() { "https://localhost:4000/test", Some("test.home"), None, + None, ) .await; // In this case the response should be a 200 from the mock backend - assert_response(resp, 200, "This is the mock backend!").await; + assert_response(resp, 200, Some("This is the mock backend!")).await; finish(proxy_parent); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http2_no_host_header_test() { +async fn http2_get_no_host_header() { MOCK_BACKEND .lock() .unwrap() @@ -257,16 +388,16 @@ async fn http2_no_host_header_test() { thread::sleep(time::Duration::from_millis(1000)); // Send a request to the proxy without a host header - let resp = http_request("http2", "https://localhost:4000/test", None, None).await; + let resp = http_request("http2", "https://localhost:4000/test", None, None, None).await; // In this case the proxy should respond with a 404 - assert_response(resp, 404, "Host header not defined").await; + assert_response(resp, 404, Some("Host header not defined")).await; finish(proxy_parent); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http2_no_proxy_header_status() { +async fn http2_get_no_proxy_header_status() { MOCK_BACKEND .lock() .unwrap() @@ -278,16 +409,23 @@ async fn http2_no_proxy_header_status() { thread::sleep(time::Duration::from_millis(1000)); // Send an internal /status request - let resp = http_request("http2", "https://localhost:4000/status", None, Some(true)).await; + let resp = http_request( + "http2", + "https://localhost:4000/status", + None, + Some(true), + None, + ) + .await; // In this case the proxy should respond with a 200 - assert_response(resp, 200, "The proxy is running").await; + assert_response(resp, 200, Some("The proxy is running")).await; finish(proxy_parent); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn http2_no_proxy_header_metrics() { +async fn http2_get_no_proxy_header_metrics() { MOCK_BACKEND .lock() .unwrap() @@ -304,11 +442,19 @@ async fn http2_no_proxy_header_metrics() { "https://localhost:4000/test", Some("test.home"), None, + None, ) .await; // Send an internal /metrics request - let resp = http_request("http2", "https://localhost:4000/metrics", None, Some(true)).await; + let resp = http_request( + "http2", + "https://localhost:4000/metrics", + None, + Some(true), + None, + ) + .await; let response = resp.unwrap(); let status = response.status(); let body = response.bytes().await.unwrap(); @@ -319,7 +465,91 @@ async fn http2_no_proxy_header_metrics() { } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn test_load_balancing_round_robin() { +async fn http2_head() { + MOCK_BACKEND + .lock() + .unwrap() + .init("127.0.0.1:8000", "This is the mock backend!") + .await; + let proxy_parent = start_proxy(); + + // Sleep this thread while the server starts up + thread::sleep(time::Duration::from_millis(1000)); + + // Send a request to the proxy, which should be forwarded to the mock server + let resp = http_request( + "http2", + "https://localhost:4000/test", + Some("test.home"), + None, + Some(Method::HEAD), + ) + .await; + + // In this case the response should be a 200 from the mock backend + assert_response(resp, 200, None).await; + + finish(proxy_parent); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn http2_post() { + MOCK_BACKEND + .lock() + .unwrap() + .init("127.0.0.1:8000", "This is the mock backend!") + .await; + let proxy_parent = start_proxy(); + + // Sleep this thread while the server starts up + thread::sleep(time::Duration::from_millis(1000)); + + // Send a request to the proxy, which should be forwarded to the mock server + let resp = http_request( + "http2", + "https://localhost:4000/test", + Some("test.home"), + None, + Some(Method::POST), + ) + .await; + + // In this case the response should be a 200 from the mock backend + assert_response(resp, 200, None).await; + + finish(proxy_parent); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn http2_put() { + MOCK_BACKEND + .lock() + .unwrap() + .init("127.0.0.1:8000", "This is the mock backend!") + .await; + let proxy_parent = start_proxy(); + + // Sleep this thread while the server starts up + thread::sleep(time::Duration::from_millis(1000)); + + // Send a request to the proxy, which should be forwarded to the mock server + let resp = http_request( + "http2", + "https://localhost:4000/test", + Some("test.home"), + None, + Some(Method::PUT), + ) + .await; + + // In this case the response should be a 200 from the mock backend + assert_response(resp, 200, None).await; + + finish(proxy_parent); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn load_balancing_round_robin() { MOCK_BACKEND .lock() .unwrap() @@ -341,10 +571,11 @@ async fn test_load_balancing_round_robin() { "https://localhost:4000/test", Some("test-lb.home"), None, + None, ) .await; - assert_response(resp, 200, "This is the mock backend!").await; + assert_response(resp, 200, Some("This is the mock backend!")).await; // Response from the second mock backend let resp = http_request( @@ -352,15 +583,16 @@ async fn test_load_balancing_round_robin() { "https://localhost:4000/test", Some("test-lb.home"), None, + None, ) .await; - assert_response(resp, 200, "This is the mock backend 2!").await; + assert_response(resp, 200, Some("This is the mock backend 2!")).await; finish(proxy_parent); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn test_proxied_backend_timeout() { +async fn proxied_backend_timeout() { let proxy_parent = start_proxy(); // Sleep this thread while the server starts up @@ -372,10 +604,11 @@ async fn test_proxied_backend_timeout() { "https://localhost:4000/delay", Some("test.home"), None, + None, ) .await; // In this case the proxy should respond with a 504 - assert_response(resp, 504, "Request timeout").await; + assert_response(resp, 504, Some("Request timeout")).await; finish(proxy_parent); }