@@ -102,3 +102,55 @@ pub async fn connect(
102102 } ,
103103 ) )
104104}
105+
106+ /// Connect to a WebSocket endpoint through an Arti Tor client.
107+ #[ cfg( feature = "tor" ) ]
108+ pub ( crate ) async fn connect_tor (
109+ tor_client : arti_client:: TorClient < tor_rtcompat:: PreferredRuntime > ,
110+ url : & str ,
111+ headers : & [ ( & str , & str ) ] ,
112+ ) -> Result < ( WsSender , WsReceiver ) , WsError > {
113+ let parsed_url =
114+ url:: Url :: parse ( url) . map_err ( |e| WsError :: Connection ( format ! ( "Invalid URL: {e}" ) ) ) ?;
115+
116+ let host = parsed_url
117+ . host_str ( )
118+ . ok_or_else ( || WsError :: Connection ( "WebSocket URL must include a host" . to_string ( ) ) ) ?;
119+ let port = parsed_url
120+ . port_or_known_default ( )
121+ . ok_or_else ( || WsError :: Connection ( "WebSocket URL must include a port" . to_string ( ) ) ) ?;
122+
123+ let mut request = url
124+ . into_client_request ( )
125+ . map_err ( |e| WsError :: Connection ( e. to_string ( ) ) ) ?;
126+
127+ for & ( name, value) in headers {
128+ if let ( Ok ( header_name) , Ok ( header_value) ) = (
129+ name. parse :: < tokio_tungstenite:: tungstenite:: http:: header:: HeaderName > ( ) ,
130+ value. parse :: < tokio_tungstenite:: tungstenite:: http:: header:: HeaderValue > ( ) ,
131+ ) {
132+ request. headers_mut ( ) . insert ( header_name, header_value) ;
133+ }
134+ }
135+
136+ let stream = tor_client
137+ . connect ( ( host, port) )
138+ . await
139+ . map_err ( |e| WsError :: Connection ( e. to_string ( ) ) ) ?;
140+
141+ let ( ws_stream, _) =
142+ tokio_tungstenite:: client_async_tls_with_config ( request, stream, None , None )
143+ . await
144+ . map_err ( |e| WsError :: Connection ( e. to_string ( ) ) ) ?;
145+
146+ let ( sink, stream) = ws_stream. split ( ) ;
147+
148+ Ok ( (
149+ WsSender {
150+ inner : Box :: new ( sink) ,
151+ } ,
152+ WsReceiver {
153+ inner : Box :: new ( stream) ,
154+ } ,
155+ ) )
156+ }
0 commit comments