Skip to content

Commit 77ed3b1

Browse files
Smoketests - Use reqwest for HTTP API calls (#5456)
# Description of Changes Use `reqwest` for smoketest HTTP API helper calls instead of hand-writing HTTP over a raw `TcpStream`. This lets the shared `api_call` helpers work against HTTPS remote servers as well as local HTTP servers. It also preserves the server URL protocol when `login_with_token` rewrites a test config. # API and ABI breaking changes None. # Expected complexity level and risk 1. This is a focused smoketest helper change. # Testing - [x] `cargo fmt --all --check` - [x] `git diff --check` - [x] `cargo check -p spacetimedb-smoketests` --------- Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
1 parent 478967a commit 77ed3b1

2 files changed

Lines changed: 31 additions & 56 deletions

File tree

crates/smoketests/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ toml.workspace = true
1313
regex.workspace = true
1414
anyhow.workspace = true
1515
fs_extra.workspace = true
16+
reqwest = { workspace = true, features = ["blocking"] }
1617
which = "8.0.0"
1718

1819
[dev-dependencies]
@@ -22,7 +23,6 @@ assert_cmd = "2"
2223
predicates = "3"
2324
tokio.workspace = true
2425
tokio-postgres.workspace = true
25-
reqwest = { workspace = true, features = ["blocking"] }
2626
xmltree.workspace = true
2727

2828
[lints]

crates/smoketests/src/lib.rs

Lines changed: 30 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -976,10 +976,8 @@ impl Smoketest {
976976

977977
/// Returns the server host (without protocol), e.g., "127.0.0.1:3000".
978978
pub fn server_host(&self) -> &str {
979-
self.server_url
980-
.strip_prefix("http://")
981-
.or_else(|| self.server_url.strip_prefix("https://"))
982-
.unwrap_or(&self.server_url)
979+
let (_, host) = split_server_url(&self.server_url);
980+
host
983981
}
984982

985983
/// Returns the PostgreSQL wire protocol port, if enabled.
@@ -1005,10 +1003,10 @@ impl Smoketest {
10051003
}
10061004

10071005
pub fn login_with_token(&self, token: &str) -> Result<()> {
1008-
let host = self.server_host();
1006+
let (protocol, host) = split_server_url(&self.server_url);
10091007
let config_str = format!(
1010-
"default_server = \"localhost\"\n\nspacetimedb_token = \"{}\"\n\n[[server_configs]]\nnickname = \"localhost\"\nhost = \"{}\"\nprotocol = \"http\"\n",
1011-
token, host
1008+
"default_server = \"localhost\"\n\nspacetimedb_token = \"{}\"\n\n[[server_configs]]\nnickname = \"localhost\"\nhost = \"{}\"\nprotocol = \"{}\"\n",
1009+
token, host, protocol
10121010
);
10131011
fs::write(&self.config_path, config_str).context("Failed to write config.toml")?;
10141012
Ok(())
@@ -1701,58 +1699,25 @@ log = "0.4"
17011699
body: Option<&[u8]>,
17021700
extra_headers: &str,
17031701
) -> Result<ApiResponse> {
1704-
use std::io::{Read, Write};
1705-
use std::net::TcpStream;
1706-
1707-
// Parse server URL to get host and port
1708-
let url = &self.server_url;
1709-
let host_port = url
1710-
.strip_prefix("http://")
1711-
.or_else(|| url.strip_prefix("https://"))
1712-
.unwrap_or(url);
1713-
1714-
let mut stream = TcpStream::connect(host_port).context("Failed to connect to server")?;
1715-
stream.set_read_timeout(Some(std::time::Duration::from_secs(30))).ok();
1716-
1717-
// Get auth token
17181702
let token = self.read_token()?;
1719-
1720-
// Build HTTP request
1721-
let content_length = body.map(|b| b.len()).unwrap_or(0);
1722-
let request = format!(
1723-
"{} {} HTTP/1.1\r\nHost: {}\r\nContent-Length: {}\r\nAuthorization: Bearer {}\r\n{}Connection: close\r\n\r\n",
1724-
method, path, host_port, content_length, token, extra_headers
1725-
);
1726-
1727-
stream.write_all(request.as_bytes())?;
1703+
let method = reqwest::Method::from_bytes(method.as_bytes()).context("invalid HTTP method")?;
1704+
let url = format!("{}{}", self.server_url.trim_end_matches('/'), path);
1705+
1706+
let client = reqwest::blocking::Client::builder()
1707+
.timeout(std::time::Duration::from_secs(30))
1708+
.build()
1709+
.context("failed to build HTTP client")?;
1710+
let mut request = client.request(method, url).bearer_auth(token);
1711+
if extra_headers.contains("Content-Type: application/json") {
1712+
request = request.header(reqwest::header::CONTENT_TYPE, "application/json");
1713+
}
17281714
if let Some(body) = body {
1729-
stream.write_all(body)?;
1715+
request = request.body(body.to_vec());
17301716
}
17311717

1732-
// Read response
1733-
let mut response = Vec::new();
1734-
stream.read_to_end(&mut response)?;
1735-
1736-
// Parse HTTP response
1737-
let response_str = String::from_utf8_lossy(&response);
1738-
let mut lines = response_str.lines();
1739-
1740-
// Parse status line
1741-
let status_line = lines.next().context("Empty response")?;
1742-
let status_code: u16 = status_line
1743-
.split_whitespace()
1744-
.nth(1)
1745-
.and_then(|s| s.parse().ok())
1746-
.context("Failed to parse status code")?;
1747-
1748-
// Find body (after empty line)
1749-
let header_end = response_str.find("\r\n\r\n").unwrap_or(response_str.len());
1750-
let body_start = header_end + 4;
1751-
let body = if body_start < response.len() {
1752-
response[body_start..].to_vec()
1753-
} else {
1754-
Vec::new()
1755-
};
1718+
let response = request.send().context("HTTP request failed")?;
1719+
let status_code = response.status().as_u16();
1720+
let body = response.bytes().context("failed to read HTTP response body")?.to_vec();
17561721

17571722
Ok(ApiResponse { status_code, body })
17581723
}
@@ -1928,6 +1893,16 @@ impl Drop for SubscriptionHandle {
19281893
}
19291894
}
19301895

1896+
fn split_server_url(server_url: &str) -> (&str, &str) {
1897+
if let Some(host) = server_url.strip_prefix("http://") {
1898+
("http", host)
1899+
} else if let Some(host) = server_url.strip_prefix("https://") {
1900+
("https", host)
1901+
} else {
1902+
("http", server_url)
1903+
}
1904+
}
1905+
19311906
/// Normalizes whitespace by trimming trailing whitespace from each line.
19321907
fn normalize_whitespace(s: &str) -> String {
19331908
s.lines().map(|line| line.trim_end()).collect::<Vec<_>>().join("\n")

0 commit comments

Comments
 (0)