Skip to main content

e_imzo/
client.rs

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
14/// Gateway of connected state for `Client` struct.
15pub struct Connected;
16
17/// Gateway of disconnected state for `Client` struct, which is defaulted.
18pub struct Disconnected;
19
20/// The client instance for transmitting data through WebSocket connection
21pub struct Client<State> {
22    pub socket: WebSocket<TlsStream<TcpStream>>,
23    /// Connected Marker
24    _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        // Establish a TCP connection, then wrap the TCP stream with TLS and connect to the server
40        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}