summaryrefslogtreecommitdiff
path: root/light-client/src/network.rs
blob: 47eb66abc939c30cd42024ebe26d48e01d559d80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
    Hurry Curry! - a game about cooking
    Copyright 2024 metamuffin

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

*/
use anyhow::Result;
use hurrycurry_protocol::{PacketC, PacketS, BINCODE_CONFIG};
use log::{debug, warn};
use std::{collections::VecDeque, net::TcpStream};
use tungstenite::{
    client::{uri_mode, IntoClientRequest},
    client_tls_with_config,
    handshake::client::Request,
    stream::{MaybeTlsStream, Mode},
    util::NonBlockingError,
    Message, WebSocket,
};

pub struct Network {
    sock: WebSocket<MaybeTlsStream<TcpStream>>,
    pub queue_in: VecDeque<PacketC>,
    pub queue_out: VecDeque<PacketS>,
}

impl Network {
    pub fn connect(addr: &str) -> Result<Self> {
        let (parts, _) = addr.into_client_request().unwrap().into_parts();
        let mut builder = Request::builder()
            .uri(parts.uri.clone().clone())
            .method(parts.method.clone())
            .version(parts.version);
        *builder.headers_mut().unwrap() = parts.headers.clone();
        let request = builder.body(()).unwrap();

        let host = request.uri().host().unwrap();
        let host = if host.starts_with('[') {
            &host[1..host.len() - 1]
        } else {
            host
        };
        let port = request
            .uri()
            .port_u16()
            .unwrap_or(match uri_mode(request.uri())? {
                Mode::Plain => 27032,
                Mode::Tls => 443,
            });
        let stream = TcpStream::connect((host, port))?;
        stream.set_nodelay(true).unwrap();

        let (mut sock, _) = client_tls_with_config(request, stream, None, None).unwrap();

        match sock.get_mut() {
            MaybeTlsStream::Plain(s) => s.set_nonblocking(true).unwrap(),
            MaybeTlsStream::Rustls(s) => s.sock.set_nonblocking(true).unwrap(),
            _ => todo!(),
        };

        Ok(Self {
            sock,
            queue_in: VecDeque::new(),
            queue_out: VecDeque::new(),
        })
    }
    pub fn poll(&mut self) {
        self.queue_in.extend(match self.sock.read() {
            Ok(Message::Text(packet)) => match serde_json::from_str(&packet) {
                Ok(packet) => {
                    debug!("<- {packet:?}");
                    Some(packet)
                }
                Err(e) => {
                    warn!("invalid json packet: {e:?}");
                    None
                }
            },
            Ok(Message::Binary(packet)) => {
                match bincode::decode_from_slice(&packet, BINCODE_CONFIG) {
                    Ok((packet, _)) => {
                        debug!("<- {packet:?}");
                        Some(packet)
                    }
                    Err(e) => {
                        warn!("invalid bincode packet: {e:?}");
                        None
                    }
                }
            }
            Ok(_) => None,
            Err(e) => {
                if let Some(e) = e.into_non_blocking() {
                    warn!("{e:?}");
                }
                None
            }
        });

        for packet in self.queue_out.drain(..) {
            debug!("-> {packet:?}");
            self.sock
                .write(Message::Text(serde_json::to_string(&packet).unwrap()))
                .unwrap();
        }

        self.sock.flush().unwrap();
    }
}