1use crate::error::Result;
2use native_tls::{TlsConnector, TlsStream};
3use std::marker::{self, PhantomData};
4use std::net::TcpStream;
5use tungstenite::{
6 Message, WebSocket,
7 client::client,
8 handshake::client::{Request, generate_key},
9};
10use url::Url;
11
12static BASE_URL: &str = "wss://127.0.0.1:64443";
13
14pub struct Connected;
16
17pub struct Disconnected;
19
20pub struct Client<State> {
22 pub socket: WebSocket<TlsStream<TcpStream>>,
23 _state: marker::PhantomData<State>,
25}
26
27impl Client<Disconnected> {
28 pub fn connect<T>(url: Option<T>) -> Result<Client<Connected>>
29 where
30 T: ToString,
31 {
32 let url = match url {
33 Some(v) => v.to_string(),
34 None => BASE_URL.to_string(),
35 };
36
37 let url = Url::parse(&format!("{url}/service/cryptapi"))?;
38
39 let tls = TlsConnector::builder()
41 .danger_accept_invalid_certs(true)
42 .build()?;
43
44 let remote_addr = match (url.host(), url.port()) {
45 (Some(host), Some(port)) => Some(format!("{host}:{port}")),
46 _ => None,
47 }
48 .ok_or(())
49 .unwrap();
50
51 let req = Request::builder()
52 .method("GET")
53 .header("Host", "localhost")
54 .header("Connection", "Upgrade")
55 .header("Upgrade", "websocket")
56 .header("Origin", "https://localhost")
57 .header("Sec-WebSocket-Version", "13")
58 .header("Sec-WebSocket-Key", generate_key())
59 .uri(url.to_string())
60 .body(())
61 .unwrap();
62 let tcp_stream = std::net::TcpStream::connect(remote_addr.clone())?;
63 let tls_stream = tls.connect(remote_addr.as_str(), tcp_stream)?;
64
65 let (socket, _) = client(req, tls_stream)?;
66
67 let connection = Client {
68 socket,
69 _state: PhantomData,
70 };
71
72 Ok(connection)
73 }
74}
75
76impl Client<Connected> {
77 pub fn send_and_wait(&mut self, message: Message) -> tungstenite::Result<Message> {
78 self.socket.send(message)?;
79
80 while let Ok(message) = self.socket.read() {
81 return Ok(message);
82 }
83
84 unreachable!();
85 }
86}