From 28d07218697593b891d096c34ed84e1b2fc523f4 Mon Sep 17 00:00:00 2001 From: Paul Butler Date: Mon, 23 Sep 2024 20:54:27 -0400 Subject: [PATCH] pass test with no connection token --- Cargo.lock | 3 + plane/plane-tests/Cargo.toml | 5 ++ plane/plane-tests/tests/common/mod.rs | 4 +- plane/plane-tests/tests/common/proxy_mock.rs | 66 ++++++++++++++++ .../tests/common/simple_axum_server.rs | 76 +++++++++++++++++++ plane/plane-tests/tests/proxy.rs | 18 +++++ plane/src/proxy/mod.rs | 7 +- plane/src/proxy/proxy_connection.rs | 3 - plane/src/proxy/proxy_server.rs | 29 +++---- plane/src/proxy/request.rs | 4 + 10 files changed, 187 insertions(+), 28 deletions(-) create mode 100644 plane/plane-tests/tests/common/proxy_mock.rs create mode 100644 plane/plane-tests/tests/common/simple_axum_server.rs create mode 100644 plane/plane-tests/tests/proxy.rs diff --git a/Cargo.lock b/Cargo.lock index 439a63f25..f35e65e72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2083,10 +2083,13 @@ dependencies = [ "chrono", "dynamic-proxy", "futures-util", + "http 1.1.0", + "http-body-util", "hyper 0.14.28", "plane-dynamic", "plane-test-macro", "reqwest 0.11.27", + "serde", "serde_json", "thiserror", "tokio", diff --git a/plane/plane-tests/Cargo.toml b/plane/plane-tests/Cargo.toml index 68af9e5c8..535b185d3 100644 --- a/plane/plane-tests/Cargo.toml +++ b/plane/plane-tests/Cargo.toml @@ -22,3 +22,8 @@ tracing = "0.1.40" tracing-appender = "0.2.2" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } url = "2.4.1" + +[dev-dependencies] +http = "1.1.0" +http-body-util = "0.1.2" +serde = "1.0.210" diff --git a/plane/plane-tests/tests/common/mod.rs b/plane/plane-tests/tests/common/mod.rs index f452b0b47..c77f0766f 100644 --- a/plane/plane-tests/tests/common/mod.rs +++ b/plane/plane-tests/tests/common/mod.rs @@ -7,9 +7,11 @@ use tokio::time::timeout; pub mod async_drop; pub mod auth_mock; pub mod docker; +pub mod proxy_mock; pub mod resources; +pub mod simple_axum_server; pub mod test_env; -pub mod timeout; +pub mod timeout; // NOTE: copied from dynamic-proxy pub fn run_test(name: &str, time_limit: Duration, test_function: F) where diff --git a/plane/plane-tests/tests/common/proxy_mock.rs b/plane/plane-tests/tests/common/proxy_mock.rs new file mode 100644 index 000000000..18be0c843 --- /dev/null +++ b/plane/plane-tests/tests/common/proxy_mock.rs @@ -0,0 +1,66 @@ +use dynamic_proxy::server::{HttpsConfig, SimpleHttpServer}; +use plane::{ + protocol::{RouteInfoRequest, RouteInfoResponse}, + proxy::proxy_server::ProxyState, +}; +use std::net::SocketAddr; +use tokio::{net::TcpListener, sync::mpsc}; + +pub struct MockProxy { + proxy_state: ProxyState, + route_info_request_receiver: mpsc::Receiver, + addr: SocketAddr, + server: SimpleHttpServer, +} + +impl MockProxy { + pub async fn new() -> Self { + let proxy_state = ProxyState::new(); + let (route_info_request_sender, route_info_request_receiver) = mpsc::channel(1); + + proxy_state.inner.route_map.set_sender(move |m| { + route_info_request_sender + .try_send(m) + .expect("Failed to send route info request"); + }); + + let listener = TcpListener::bind("127.0.0.1:0") + .await + .expect("Failed to bind listener"); + let addr = listener.local_addr().expect("Failed to get local address"); + + let server = SimpleHttpServer::new(proxy_state.clone(), listener, HttpsConfig::http()) + .expect("Failed to create server"); + + Self { + proxy_state, + route_info_request_receiver, + addr, + server, + } + } + + pub fn addr(&self) -> SocketAddr { + self.addr + } + + pub async fn recv_route_info_request(&mut self) -> RouteInfoRequest { + self.route_info_request_receiver + .recv() + .await + .expect("Failed to receive route info request") + } + + pub async fn expect_no_route_info_request(&mut self) { + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + assert!( + self.route_info_request_receiver.is_empty(), + "Expected no route info request, but got: {}", + self.route_info_request_receiver.len() + ); + } + + pub async fn send_route_info_response(&mut self, response: RouteInfoResponse) { + self.proxy_state.inner.route_map.receive(response); + } +} diff --git a/plane/plane-tests/tests/common/simple_axum_server.rs b/plane/plane-tests/tests/common/simple_axum_server.rs new file mode 100644 index 000000000..999e0ca74 --- /dev/null +++ b/plane/plane-tests/tests/common/simple_axum_server.rs @@ -0,0 +1,76 @@ +use axum::{body::Body, extract::Request, routing::any, Json, Router}; +use http::Method; +use http_body_util::BodyExt; +use serde::{Deserialize, Serialize}; +use std::{collections::HashMap, net::SocketAddr}; +use tokio::net::TcpListener; + +pub struct SimpleAxumServer { + handle: tokio::task::JoinHandle<()>, + addr: SocketAddr, +} + +#[allow(unused)] +impl SimpleAxumServer { + pub async fn new() -> Self { + let app = Router::new() + .route("/*path", any(return_request_info)) + .route("/", any(return_request_info)); + + let addr = SocketAddr::from(([127, 0, 0, 1], 0)); + let tcp_listener = TcpListener::bind(addr).await.unwrap(); + let addr = tcp_listener.local_addr().unwrap(); + + let handle = tokio::spawn(async { + axum::serve(tcp_listener, app.into_make_service()) + .await + .unwrap(); + }); + + Self { handle, addr } + } + + pub fn addr(&self) -> SocketAddr { + self.addr + } +} + +impl Drop for SimpleAxumServer { + fn drop(&mut self) { + self.handle.abort(); + } +} + +// Handler function for the root route +async fn return_request_info(method: Method, request: Request) -> Json { + let method = method.to_string(); + + let path = request.uri().path().to_string(); + let query = request.uri().query().unwrap_or("").to_string(); + + let headers: HashMap = request + .headers() + .iter() + .map(|(k, v)| (k.to_string(), v.to_str().unwrap().to_string())) + .collect(); + + let body = request.into_body().collect().await.unwrap().to_bytes(); + let body = String::from_utf8(body.to_vec()).unwrap(); + + Json(RequestInfo { + path, + query, + method, + headers, + body, + }) +} + +#[derive(Serialize, Deserialize)] +pub struct RequestInfo { + pub path: String, + pub query: String, + pub method: String, + pub headers: HashMap, + pub body: String, +} diff --git a/plane/plane-tests/tests/proxy.rs b/plane/plane-tests/tests/proxy.rs new file mode 100644 index 000000000..6eec7631d --- /dev/null +++ b/plane/plane-tests/tests/proxy.rs @@ -0,0 +1,18 @@ +use common::{proxy_mock::MockProxy, test_env::TestEnvironment}; +use plane_test_macro::plane_test; +use reqwest::StatusCode; + +mod common; + +#[plane_test] +async fn proxy_no_bearer_token(env: TestEnvironment) { + let mut proxy = MockProxy::new().await; + let url = format!("http://{}", proxy.addr()); + let handle = tokio::spawn(async { reqwest::get(url).await.expect("Failed to send request") }); + + proxy.expect_no_route_info_request().await; + + let response = handle.await.unwrap(); + + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); +} diff --git a/plane/src/proxy/mod.rs b/plane/src/proxy/mod.rs index 7f2159ef1..b0476e67e 100644 --- a/plane/src/proxy/mod.rs +++ b/plane/src/proxy/mod.rs @@ -4,15 +4,12 @@ use crate::proxy::cert_manager::watcher_manager_pair; use crate::{client::PlaneClient, signals::wait_for_shutdown_signal, types::ClusterName}; use anyhow::Result; use dynamic_proxy::server::{ - HttpsConfig, ServerWithHttpRedirect, ServerWithHttpRedirectConfig, - ServerWithHttpRedirectHttpsConfig, SimpleHttpServer, + ServerWithHttpRedirect, ServerWithHttpRedirectConfig, ServerWithHttpRedirectHttpsConfig, }; use serde::{Deserialize, Serialize}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; -use tokio::net::TcpListener; use url::Url; pub mod cert_manager; @@ -21,7 +18,7 @@ pub mod command; mod connection_monitor; pub mod proxy_connection; // mod proxy_service; -mod proxy_server; +pub mod proxy_server; // mod rewriter; mod request; mod route_map; diff --git a/plane/src/proxy/proxy_connection.rs b/plane/src/proxy/proxy_connection.rs index 827f279f1..22b9e32fb 100644 --- a/plane/src/proxy/proxy_connection.rs +++ b/plane/src/proxy/proxy_connection.rs @@ -31,7 +31,6 @@ impl ProxyConnection { loop { let mut conn = proxy_connection.connect_with_retry(&name).await; - state.set_connected(true); let sender = conn.sender(MessageFromProxy::CertManagerRequest); cert_manager.set_request_sender(move |m| { @@ -73,8 +72,6 @@ impl ProxyConnection { } } } - - state.set_connected(false); } }) }; diff --git a/plane/src/proxy/proxy_server.rs b/plane/src/proxy/proxy_server.rs index 2fb297461..4a5c06102 100644 --- a/plane/src/proxy/proxy_server.rs +++ b/plane/src/proxy/proxy_server.rs @@ -3,21 +3,21 @@ use super::{ route_map::RouteMap, }; use dynamic_proxy::{ - body::SimpleBody, - hyper::{body::Incoming, service::Service, Request, Response, Uri}, + body::{simple_empty_body, SimpleBody}, + hyper::{body::Incoming, service::Service, Request, Response, StatusCode, Uri}, proxy::ProxyClient, request::MutableRequest, }; -use std::{future::Future, sync::atomic::AtomicBool}; +use std::future::{ready, Future}; use std::{pin::Pin, sync::Arc}; pub struct ProxyStateInner { pub route_map: RouteMap, pub proxy_client: ProxyClient, pub monitor: ConnectionMonitorHandle, - pub connected: AtomicBool, } +#[derive(Clone)] pub struct ProxyState { pub inner: Arc, } @@ -34,25 +34,12 @@ impl ProxyState { route_map: RouteMap::new(), proxy_client: ProxyClient::new(), monitor: ConnectionMonitorHandle::new(), - connected: AtomicBool::new(false), }; Self { inner: Arc::new(inner), } } - - pub fn set_connected(&self, connected: bool) { - self.inner - .connected - .store(connected, std::sync::atomic::Ordering::Relaxed); - } - - pub fn connected(&self) -> bool { - self.inner - .connected - .load(std::sync::atomic::Ordering::Relaxed) - } } impl Service> for ProxyState { @@ -73,8 +60,12 @@ impl Service> for ProxyState { let mut uri_parts = request.parts.uri.clone().into_parts(); let bearer_token = get_and_maybe_remove_bearer_token(&mut uri_parts); - // TODO - let bearer_token = bearer_token.unwrap(); + let Some(bearer_token) = bearer_token else { + return Box::pin(ready(Ok(Response::builder() + .status(StatusCode::UNAUTHORIZED) + .body(simple_empty_body()) + .unwrap()))); + }; request.parts.uri = Uri::from_parts(uri_parts).unwrap(); diff --git a/plane/src/proxy/request.rs b/plane/src/proxy/request.rs index e7bf2afcb..e3c051fdf 100644 --- a/plane/src/proxy/request.rs +++ b/plane/src/proxy/request.rs @@ -50,6 +50,10 @@ pub fn get_and_maybe_remove_bearer_token(parts: &mut uri::Parts) -> Option (full_path, "/"), }; + if token.is_empty() { + return None; + } + let token = BearerToken::from(token.to_string()); if token.is_static() {