@@ -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 \n spacetimedb_token = \" {}\" \n \n [[server_configs]]\n nickname = \" localhost\" \n host = \" {}\" \n protocol = \" http \" \n " ,
1011- token, host
1008+ "default_server = \" localhost\" \n \n spacetimedb_token = \" {}\" \n \n [[server_configs]]\n nickname = \" localhost\" \n host = \" {}\" \n protocol = \" {} \" \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 \n Host: {}\r \n Content-Length: {}\r \n Authorization: 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.
19321907fn normalize_whitespace ( s : & str ) -> String {
19331908 s. lines ( ) . map ( |line| line. trim_end ( ) ) . collect :: < Vec < _ > > ( ) . join ( "\n " )
0 commit comments