Skip to content

Commit

Permalink
Decode the database name in SQL/HTTP connections
Browse files Browse the repository at this point in the history
A url::Url does not hand you back a URL decoded value for path values,
so we must decode it ourselves.

Link: https://docs.rs/url/2.5.2/url/struct.Url.html#method.path
Link: https://docs.rs/url/2.5.2/url/struct.Url.html#method.path_segments
Signed-off-by: Tristan Partin <tristan@neon.tech>
  • Loading branch information
tristan957 committed Aug 13, 2024
1 parent 6d0a629 commit 07a9eee
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 1 deletion.
4 changes: 3 additions & 1 deletion proxy/src/serverless/sql_over_http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use tracing::error;
use tracing::info;
use typed_json::json;
use url::Url;
use urlencoding;
use utils::http::error::ApiError;

use crate::auth::backend::ComputeUserInfo;
Expand Down Expand Up @@ -168,7 +169,8 @@ fn get_conn_info(
.path_segments()
.ok_or(ConnInfoError::MissingDbName)?;

let dbname: DbName = url_path.next().ok_or(ConnInfoError::InvalidDbName)?.into();
let dbname: DbName =
urlencoding::decode(url_path.next().ok_or(ConnInfoError::InvalidDbName)?)?.into();
ctx.set_dbname(dbname.clone());

let username = RoleName::from(urlencoding::decode(connection_url.username())?);
Expand Down
26 changes: 26 additions & 0 deletions test_runner/regress/test_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import subprocess
import time
import urllib.parse
from typing import Any, List, Optional, Tuple

import psycopg2
Expand Down Expand Up @@ -275,6 +276,31 @@ def q(sql: str, params: Optional[List[Any]] = None) -> Any:
assert res["rowCount"] is None


def test_sql_over_http_db_name_with_space(static_proxy: NeonProxy):
db = "db with spaces"
static_proxy.safe_psql_many(
(
f'create database "{db}"',
"create role http with login password 'http' superuser",
)
)

def q(sql: str, params: Optional[List[Any]] = None) -> Any:
params = params or []
connstr = f"postgresql://http:http@{static_proxy.domain}:{static_proxy.proxy_port}/{urllib.parse.quote(db)}"
response = requests.post(
f"https://{static_proxy.domain}:{static_proxy.external_http_port}/sql",
data=json.dumps({"query": sql, "params": params}),
headers={"Content-Type": "application/sql", "Neon-Connection-String": connstr},
verify=str(static_proxy.test_output_dir / "proxy.crt"),
)
assert response.status_code == 200, response.text
return response.json()

rows = q("select 42 as answer")["rows"]
assert rows == [{"answer": 42}]


def test_sql_over_http_output_options(static_proxy: NeonProxy):
static_proxy.safe_psql("create role http2 with login password 'http2' superuser")

Expand Down

0 comments on commit 07a9eee

Please sign in to comment.