diff --git a/proxy/src/serverless/error.rs b/proxy/src/serverless/error.rs new file mode 100644 index 0000000000..323c91baa5 --- /dev/null +++ b/proxy/src/serverless/error.rs @@ -0,0 +1,5 @@ +use http::StatusCode; + +pub trait HttpCodeError { + fn get_http_status_code(&self) -> StatusCode; +} diff --git a/proxy/src/serverless/mod.rs b/proxy/src/serverless/mod.rs index 8fb7a771d9..edbb0347d3 100644 --- a/proxy/src/serverless/mod.rs +++ b/proxy/src/serverless/mod.rs @@ -6,6 +6,7 @@ mod backend; pub mod cancel_set; mod conn_pool; mod conn_pool_lib; +mod error; mod http_conn_pool; mod http_util; mod json; diff --git a/proxy/src/serverless/sql_over_http.rs b/proxy/src/serverless/sql_over_http.rs index 1f3eec6d19..0713c27d65 100644 --- a/proxy/src/serverless/sql_over_http.rs +++ b/proxy/src/serverless/sql_over_http.rs @@ -28,6 +28,7 @@ use uuid::Uuid; use super::backend::{LocalProxyConnError, PoolingBackend}; use super::conn_pool::{AuthData, ConnInfoWithAuth}; use super::conn_pool_lib::{self, ConnInfo}; +use super::error::HttpCodeError; use super::http_util::json_response; use super::json::{json_to_pg_text, pg_text_row_to_json, JsonConversionError}; use super::local_conn_pool; @@ -238,7 +239,6 @@ fn get_conn_info( Ok(ConnInfoWithAuth { conn_info, auth }) } -// TODO: return different http error codes pub(crate) async fn handle( config: &'static ProxyConfig, ctx: RequestMonitoring, @@ -319,9 +319,8 @@ pub(crate) async fn handle( "forwarding error to user" ); - // TODO: this shouldn't always be bad request. json_response( - StatusCode::BAD_REQUEST, + e.get_http_status_code(), json!({ "message": message, "code": code, @@ -405,6 +404,25 @@ impl UserFacingError for SqlOverHttpError { } } +impl HttpCodeError for SqlOverHttpError { + fn get_http_status_code(&self) -> StatusCode { + match self { + SqlOverHttpError::ReadPayload(_) => StatusCode::BAD_REQUEST, + SqlOverHttpError::ConnectCompute(h) => match h.get_error_kind() { + ErrorKind::User => StatusCode::BAD_REQUEST, + _ => StatusCode::INTERNAL_SERVER_ERROR, + }, + SqlOverHttpError::ConnInfo(_) => StatusCode::BAD_REQUEST, + SqlOverHttpError::RequestTooLarge(_) => StatusCode::PAYLOAD_TOO_LARGE, + SqlOverHttpError::ResponseTooLarge(_) => StatusCode::INSUFFICIENT_STORAGE, + SqlOverHttpError::InvalidIsolationLevel => StatusCode::BAD_REQUEST, + SqlOverHttpError::Postgres(_) => StatusCode::BAD_REQUEST, + SqlOverHttpError::JsonConversion(_) => StatusCode::INTERNAL_SERVER_ERROR, + SqlOverHttpError::Cancelled(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } +} + #[derive(Debug, thiserror::Error)] pub(crate) enum ReadPayloadError { #[error("could not read the HTTP request body: {0}")] diff --git a/test_runner/regress/test_proxy.py b/test_runner/regress/test_proxy.py index f598900af9..e59d46e352 100644 --- a/test_runner/regress/test_proxy.py +++ b/test_runner/regress/test_proxy.py @@ -561,7 +561,7 @@ def test_sql_over_http_pool_dos(static_proxy: NeonProxy): # query generates a million rows - should hit the 10MB reponse limit quickly response = query( - 400, + 507, "select * from generate_series(1, 5000) a cross join generate_series(1, 5000) b cross join (select 'foo'::foo) c;", ) assert "response is too large (max is 10485760 bytes)" in response["message"]