diff --git a/libs/proxy/postgres-protocol2/src/message/frontend.rs b/libs/proxy/postgres-protocol2/src/message/frontend.rs index d49b4addf2f8..bc6168f33732 100644 --- a/libs/proxy/postgres-protocol2/src/message/frontend.rs +++ b/libs/proxy/postgres-protocol2/src/message/frontend.rs @@ -273,12 +273,12 @@ pub struct StartupMessageParams { impl StartupMessageParams { /// Set parameter's value by its name. pub fn insert(&mut self, name: &str, value: &str) { - if name.contains('\0') | value.contains('\0') { + if name.contains('\0') || value.contains('\0') { panic!("startup parameter name or value contained a null") } - self.params.put(name.as_bytes()); + self.params.put_slice(name.as_bytes()); self.params.put_u8(0); - self.params.put(value.as_bytes()); + self.params.put_slice(value.as_bytes()); self.params.put_u8(0); } } diff --git a/proxy/src/proxy/mod.rs b/proxy/src/proxy/mod.rs index 9416310c7bdd..f74eb5940fb2 100644 --- a/proxy/src/proxy/mod.rs +++ b/proxy/src/proxy/mod.rs @@ -340,7 +340,7 @@ pub(crate) async fn handle_client( let params_compat = match &user_info { auth::Backend::ControlPlane(_, info) => { - info.info.options.get("proxy_params_compat").is_some() + info.info.options.get(NeonOptions::PARAMS_COMPAT).is_some() } auth::Backend::Local(_) => false, }; @@ -417,6 +417,19 @@ pub(crate) async fn prepare_client_connection

( pub(crate) struct NeonOptions(Vec<(SmolStr, SmolStr)>); impl NeonOptions { + // proxy options: + + /// `PARAMS_COMPAT` allows opting in to forwarding all startup parameters from client to compute. + const PARAMS_COMPAT: &str = "proxy_params_compat"; + + // cplane options: + + /// `LSN` allows provisioning an ephemeral compute with time-travel to the provided LSN. + const LSN: &str = "lsn"; + + /// `ENDPOINT_TYPE` allows configuring an ephemeral compute to be read_only or read_write. + const ENDPOINT_TYPE: &str = "endpoint_type"; + pub(crate) fn parse_params(params: &StartupMessageParams) -> Self { params .options_raw() @@ -436,15 +449,15 @@ impl NeonOptions { } pub(crate) fn is_ephemeral(&self) -> bool { - fn parameter_is_not_ephemeral(p: &str) -> bool { - p == "proxy_params_compat" - } - - self.0 - .iter() - .filter(|(k, _)| !parameter_is_not_ephemeral(k)) - .count() - > 0 + self.0.iter().any(|(k, _)| match &**k { + // This is not a cplane option, we know it does not create ephemeral computes. + Self::PARAMS_COMPAT => false, + Self::LSN => true, + Self::ENDPOINT_TYPE => true, + // err on the side of caution. any cplane options we don't know about + // might lead to ephemeral computes. + _ => true, + }) } fn parse_from_iter<'a>(options: impl Iterator) -> Self {