summaryrefslogtreecommitdiffstats
path: root/src/bin
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-28 22:38:54 +0100
committersalmonik <salmonik@tuta.io>2026-10-28 22:38:54 +0100
commitef2e13df274aaf0f2e50c03c63db33d65d9a0275 (patch)
tree991fa12e70ec11e84e8f6c6e7bc0a60b0a150759 /src/bin
parentv0.2.5: fix terminal width, ping timing and output formatting (diff)
downloadzagan-bet-ef2e13df274aaf0f2e50c03c63db33d65d9a0275.tar.gz
zagan-bet-ef2e13df274aaf0f2e50c03c63db33d65d9a0275.zip
v0.2.6: preserve buffered lines and add request read timeouts
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/client/connection.rs8
-rw-r--r--src/bin/client/repl.rs5
-rw-r--r--src/bin/client/tls.rs36
-rw-r--r--src/bin/server/tls.rs26
4 files changed, 53 insertions, 22 deletions
diff --git a/src/bin/client/connection.rs b/src/bin/client/connection.rs
index 93a7829..49695dd 100644
--- a/src/bin/client/connection.rs
+++ b/src/bin/client/connection.rs
@@ -66,6 +66,7 @@ pub fn connect(
66 } 66 }
67 67
68 let mut tls = tls; 68 let mut tls = tls;
69 tls.set_read_timeout(Some(Duration::from_secs(10)))?;
69 70
70 let start = Instant::now(); 71 let start = Instant::now();
71 tls.write_line("PING")?; 72 tls.write_line("PING")?;
@@ -124,6 +125,13 @@ impl TlsStream {
124 self.inner.write_line(msg) 125 self.inner.write_line(msg)
125 } 126 }
126 127
128 pub fn set_read_timeout(
129 &self,
130 duration: Option<std::time::Duration>,
131 ) -> std::io::Result<()> {
132 self.inner.set_read_timeout(duration)
133 }
134
127 pub fn try_clone(&self) -> std::io::Result<TlsStream> { 135 pub fn try_clone(&self) -> std::io::Result<TlsStream> {
128 Ok(TlsStream { 136 Ok(TlsStream {
129 inner: self.inner.try_clone()?, 137 inner: self.inner.try_clone()?,
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs
index 76b149b..033fb1a 100644
--- a/src/bin/client/repl.rs
+++ b/src/bin/client/repl.rs
@@ -18,6 +18,7 @@ use zagan_bet::client::database;
18use zagan_bet::client::ui::{ 18use zagan_bet::client::ui::{
19 display_width, format_pong, host_lines, input_cursor_column, lines_to_move_up, 19 display_width, format_pong, host_lines, input_cursor_column, lines_to_move_up,
20}; 20};
21use zagan_bet::protocol::io_failure_message;
21 22
22use crate::connection::TlsStream; 23use crate::connection::TlsStream;
23 24
@@ -299,9 +300,9 @@ impl ClientState {
299 self.stream = Some(stream); 300 self.stream = Some(stream);
300 response 301 response
301 } 302 }
302 Err(_) => { 303 Err(error) => {
303 self.stream = None; 304 self.stream = None;
304 "ERROR Connection lost".to_string() 305 io_failure_message(&error).to_string()
305 } 306 }
306 } 307 }
307 } 308 }
diff --git a/src/bin/client/tls.rs b/src/bin/client/tls.rs
index ca99838..b118f63 100644
--- a/src/bin/client/tls.rs
+++ b/src/bin/client/tls.rs
@@ -19,6 +19,7 @@ use std::sync::Arc;
19use parking_lot::Mutex; 19use parking_lot::Mutex;
20use rustls::client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; 20use rustls::client::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
21use rustls::DigitallySignedStruct; 21use rustls::DigitallySignedStruct;
22use zagan_bet::protocol::LineBuffer;
22use zagan_bet::tofu::{decide, spki_fingerprint, TrustDecision}; 23use zagan_bet::tofu::{decide, spki_fingerprint, TrustDecision};
23 24
24pub struct TlsConnector { 25pub struct TlsConnector {
@@ -59,7 +60,11 @@ impl TlsConnector {
59 conn.complete_io(&mut sock)?; 60 conn.complete_io(&mut sock)?;
60 61
61 Ok(TlsStream { 62 Ok(TlsStream {
62 session: Arc::new(Mutex::new(Session { sock, tls: conn })), 63 session: Arc::new(Mutex::new(Session {
64 sock,
65 tls: conn,
66 lines: LineBuffer::new(),
67 })),
63 }) 68 })
64 } 69 }
65 70
@@ -144,6 +149,7 @@ impl ServerCertVerifier for TofuVerifier {
144struct Session { 149struct Session {
145 sock: TcpStream, 150 sock: TcpStream,
146 tls: rustls::ClientConnection, 151 tls: rustls::ClientConnection,
152 lines: LineBuffer,
147} 153}
148 154
149impl Session { 155impl Session {
@@ -159,17 +165,25 @@ pub struct TlsStream {
159 165
160impl TlsStream { 166impl TlsStream {
161 pub fn read_line(&mut self, buf: &mut String) -> std::io::Result<usize> { 167 pub fn read_line(&mut self, buf: &mut String) -> std::io::Result<usize> {
162 let mut byte_buf = [0u8; 1024];
163 let mut s = self.session.lock(); 168 let mut s = self.session.lock();
164 loop { 169 loop {
170 if let Some(line) = s.lines.next_line() {
171 buf.push_str(&line);
172 buf.push('\n');
173 return Ok(line.len() + 1);
174 }
175
176 let mut byte_buf = [0u8; 4096];
165 match s.tls.reader().read(&mut byte_buf) { 177 match s.tls.reader().read(&mut byte_buf) {
166 Ok(0) => return Ok(0), 178 Ok(0) => {
167 Ok(n) => { 179 if let Some(rest) = s.lines.take_rest() {
168 buf.push_str(std::str::from_utf8(&byte_buf[..n]).unwrap_or("")); 180 let read = rest.len();
169 if buf.contains('\n') { 181 buf.push_str(&rest);
170 return Ok(n); 182 return Ok(read);
171 } 183 }
184 return Ok(0);
172 } 185 }
186 Ok(n) => s.lines.push(&byte_buf[..n]),
173 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => s.drive()?, 187 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => s.drive()?,
174 Err(e) => return Err(e), 188 Err(e) => return Err(e),
175 } 189 }
@@ -183,6 +197,14 @@ impl TlsStream {
183 s.drive() 197 s.drive()
184 } 198 }
185 199
200 /// Apply a read timeout to the underlying TCP socket.
201 pub fn set_read_timeout(
202 &self,
203 duration: Option<std::time::Duration>,
204 ) -> std::io::Result<()> {
205 self.session.lock().sock.set_read_timeout(duration)
206 }
207
186 /// Clones share the same underlying TLS session. 208 /// Clones share the same underlying TLS session.
187 pub fn try_clone(&self) -> std::io::Result<TlsStream> { 209 pub fn try_clone(&self) -> std::io::Result<TlsStream> {
188 Ok(self.clone()) 210 Ok(self.clone())
diff --git a/src/bin/server/tls.rs b/src/bin/server/tls.rs
index acf3315..62a25db 100644
--- a/src/bin/server/tls.rs
+++ b/src/bin/server/tls.rs
@@ -1,6 +1,8 @@
1use std::io::{Read, Write}; 1use std::io::{Read, Write};
2use std::net::TcpStream; 2use std::net::TcpStream;
3 3
4use zagan_bet::protocol::LineBuffer;
5
4#[derive(Clone)] 6#[derive(Clone)]
5pub struct TlsServer { 7pub struct TlsServer {
6 acceptor: native_tls::TlsAcceptor, 8 acceptor: native_tls::TlsAcceptor,
@@ -42,27 +44,25 @@ impl TlsServer {
42 44
43pub struct TlsStream { 45pub struct TlsStream {
44 stream: native_tls::TlsStream<TcpStream>, 46 stream: native_tls::TlsStream<TcpStream>,
47 lines: LineBuffer,
45} 48}
46 49
47impl TlsStream { 50impl TlsStream {
48 pub fn new(stream: native_tls::TlsStream<TcpStream>) -> Self { 51 pub fn new(stream: native_tls::TlsStream<TcpStream>) -> Self {
49 Self { stream } 52 Self {
53 stream,
54 lines: LineBuffer::new(),
55 }
50 } 56 }
51 57
52 pub fn read_line(&mut self, s: &mut String) -> std::io::Result<usize> { 58 pub fn read_line(&mut self, s: &mut String) -> std::io::Result<usize> {
53 let mut buf = [0u8; 1024]; 59 match self.lines.read_line(&mut self.stream)? {
54 loop { 60 Some(line) => {
55 match self.stream.read(&mut buf) { 61 s.push_str(&line);
56 Ok(0) => return Ok(0), 62 s.push('\n');
57 Ok(n) => { 63 Ok(line.len() + 1)
58 let chunk = std::str::from_utf8(&buf[..n]).unwrap_or("");
59 s.push_str(chunk);
60 if s.contains('\n') {
61 return Ok(n);
62 }
63 }
64 Err(e) => return Err(e),
65 } 64 }
65 None => Ok(0),
66 } 66 }
67 } 67 }
68 68