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();
}
}
|