1use crate::error::{self, 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) -> Result<Message, error::EIMZOError> {
78 self.socket.send(message)?;
79
80 loop {
81 let msg = self.socket.read()?;
82 match msg {
83 Message::Ping(payload) => {
84 self.socket.send(Message::Pong(payload))?;
85 }
86 Message::Pong(_) => continue,
87 Message::Text(msg) => return Ok(Message::Text(msg)),
89 Message::Close(msg) => {
90 return Err(error::EIMZOError::WebsocketClosed(msg));
91 }
92 _ => {
93 return Err(error::EIMZOError::Unknown);
94 }
95 }
96 }
97 }
98}