1use super::noise::{NoiseConn, NoiseReader, NoiseWriter};
5use super::wire::Io;
6use anyhow::{bail, ensure, Result};
7use std::collections::{HashMap, VecDeque};
8use std::future::Future;
9use std::sync::atomic::{AtomicI64, AtomicU32, Ordering};
10use std::sync::{Arc, Mutex};
11use tokio::sync::mpsc;
12
13pub const INITIAL_WINDOW: u32 = 256 * 1024;
15pub const MAX_STREAMS: usize = 256;
17pub const MAX_FRAME_DATA: usize = 64 * 1024;
19
20const T_DATA: u8 = 0;
21const T_WINDOW: u8 = 1;
22const T_PING: u8 = 2;
23const T_GO_AWAY: u8 = 3;
24const F_SYN: u16 = 1;
25const F_ACK: u16 = 2;
26const F_FIN: u16 = 4;
27const F_RST: u16 = 8;
28
29fn header(kind: u8, flags: u16, id: u32, len: u32) -> [u8; 12] {
30 let mut h = [0u8; 12];
31 h[1] = kind;
32 h[2..4].copy_from_slice(&flags.to_be_bytes());
33 h[4..8].copy_from_slice(&id.to_be_bytes());
34 h[8..12].copy_from_slice(&len.to_be_bytes());
35 h
36}
37
38enum Cmd {
39 Open(u32),
40 Ack(u32),
41 Data(u32, Vec<u8>),
42 Fin(u32),
43 Rst(u32),
44 Grant(u32, u32),
45 PeerWindow(u32, u32),
46 Pong(u32),
47 GoAway(u32),
48}
49
50struct Inbound {
51 tx: mpsc::UnboundedSender<Vec<u8>>,
52 window: Arc<AtomicI64>,
54}
55
56type Table = Arc<Mutex<HashMap<u32, Inbound>>>;
57
58pub struct Stream {
61 pub id: u32,
62 rx: mpsc::UnboundedReceiver<Vec<u8>>,
63 buf: Vec<u8>,
64 pos: usize,
65 consumed: u32,
66 window: Arc<AtomicI64>,
67 writer: StreamWriter,
68}
69
70#[derive(Clone)]
72pub struct StreamWriter {
73 pub id: u32,
74 cmd: mpsc::UnboundedSender<Cmd>,
75}
76
77impl StreamWriter {
78 pub fn send(&self, data: Vec<u8>) -> Result<()> {
79 for chunk in data.chunks(MAX_FRAME_DATA) {
80 self.cmd
81 .send(Cmd::Data(self.id, chunk.to_vec()))
82 .map_err(|_| anyhow::anyhow!("connection closed"))?;
83 }
84 Ok(())
85 }
86 pub fn close(&self) {
88 let _ = self.cmd.send(Cmd::Fin(self.id));
89 }
90 pub fn reset(&self) {
91 let _ = self.cmd.send(Cmd::Rst(self.id));
92 }
93}
94
95impl Stream {
96 pub fn writer(&self) -> StreamWriter {
97 self.writer.clone()
98 }
99 pub async fn next_chunk(&mut self) -> Option<Vec<u8>> {
101 if self.pos < self.buf.len() {
102 let rest = self.buf[self.pos..].to_vec();
103 self.pos = self.buf.len();
104 return Some(rest);
105 }
106 let chunk = self.rx.recv().await?;
107 self.consumed(chunk.len());
108 Some(chunk)
109 }
110 fn consumed(&mut self, n: usize) {
111 self.consumed += n as u32;
112 if self.consumed >= INITIAL_WINDOW / 2 {
113 let delta = self.consumed;
114 self.consumed = 0;
115 self.window.fetch_add(i64::from(delta), Ordering::SeqCst);
116 let _ = self.writer.cmd.send(Cmd::Grant(self.id, delta));
117 }
118 }
119}
120
121impl Io for Stream {
122 async fn read_exact(&mut self, out: &mut [u8]) -> Result<()> {
123 let mut done = 0;
124 while done < out.len() {
125 if self.pos == self.buf.len() {
126 match self.rx.recv().await {
127 Some(chunk) => {
128 self.consumed(chunk.len());
129 self.buf = chunk;
130 self.pos = 0;
131 }
132 None => bail!("stream closed by the peer"),
133 }
134 continue;
135 }
136 let n = (out.len() - done).min(self.buf.len() - self.pos);
137 out[done..done + n].copy_from_slice(&self.buf[self.pos..self.pos + n]);
138 self.pos += n;
139 done += n;
140 }
141 Ok(())
142 }
143 fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> impl Future<Output = Result<()>> + Send + 'a {
144 let r = self.writer.send(buf.to_vec());
145 async move { r }
146 }
147}
148
149#[derive(Clone)]
151pub struct Opener {
152 cmd: mpsc::UnboundedSender<Cmd>,
153 next_id: Arc<AtomicU32>,
154 table: Table,
155}
156
157impl Opener {
158 pub fn open(&self) -> Result<Stream> {
159 let id = self.next_id.fetch_add(2, Ordering::SeqCst);
160 let (tx, rx) = mpsc::unbounded_channel();
161 let window = Arc::new(AtomicI64::new(i64::from(INITIAL_WINDOW)));
162 {
163 let mut t = self.table.lock().unwrap_or_else(|e| e.into_inner());
164 ensure!(t.len() < MAX_STREAMS, "too many open streams");
165 t.insert(
166 id,
167 Inbound {
168 tx,
169 window: window.clone(),
170 },
171 );
172 }
173 self.cmd
174 .send(Cmd::Open(id))
175 .map_err(|_| anyhow::anyhow!("connection closed"))?;
176 Ok(Stream {
177 id,
178 rx,
179 buf: Vec::new(),
180 pos: 0,
181 consumed: 0,
182 window,
183 writer: StreamWriter {
184 id,
185 cmd: self.cmd.clone(),
186 },
187 })
188 }
189 pub fn go_away(&self) {
191 let _ = self.cmd.send(Cmd::GoAway(0));
192 }
193}
194
195pub struct Session {
198 pub opener: Opener,
199 pub incoming: mpsc::Receiver<Stream>,
200 pub reader: std::pin::Pin<Box<dyn Future<Output = Result<()>> + Send>>,
201 pub writer: std::pin::Pin<Box<dyn Future<Output = Result<()>> + Send>>,
202}
203
204pub fn session(conn: NoiseConn, client: bool) -> Session {
206 let (cmd, cmd_rx) = mpsc::unbounded_channel();
207 let table: Table = Arc::new(Mutex::new(HashMap::new()));
208 let (incoming_tx, incoming) = mpsc::channel(MAX_STREAMS);
209 let opener = Opener {
210 cmd: cmd.clone(),
211 next_id: Arc::new(AtomicU32::new(if client { 1 } else { 2 })),
212 table: table.clone(),
213 };
214 Session {
215 opener,
216 incoming,
217 reader: Box::pin(read_loop(conn.reader, cmd, table, incoming_tx, client)),
218 writer: Box::pin(write_loop(conn.writer, cmd_rx)),
219 }
220}
221
222async fn read_loop(
223 mut r: NoiseReader,
224 cmd: mpsc::UnboundedSender<Cmd>,
225 table: Table,
226 incoming: mpsc::Sender<Stream>,
227 client: bool,
228) -> Result<()> {
229 let result = async {
230 loop {
231 let mut h = [0u8; 12];
232 r.read_exact(&mut h).await?;
233 ensure!(h[0] == 0, "unsupported yamux version {}", h[0]);
234 let kind = h[1];
235 let flags = u16::from_be_bytes([h[2], h[3]]);
236 let id = u32::from_be_bytes([h[4], h[5], h[6], h[7]]);
237 let len = u32::from_be_bytes([h[8], h[9], h[10], h[11]]);
238 match kind {
239 T_PING => {
240 if flags & F_SYN != 0 {
241 let _ = cmd.send(Cmd::Pong(len));
242 }
243 continue;
244 }
245 T_GO_AWAY => return Ok(()),
246 T_DATA | T_WINDOW => {}
247 k => bail!("unknown yamux frame type {k}"),
248 }
249 if flags & F_SYN != 0 {
250 let theirs = (id % 2 == 1) != client;
252 let mut t = table.lock().unwrap_or_else(|e| e.into_inner());
253 if !theirs || id == 0 || t.contains_key(&id) {
254 drop(t);
255 bail!("invalid stream id {id} in SYN");
256 }
257 if t.len() >= MAX_STREAMS {
258 drop(t);
259 let _ = cmd.send(Cmd::Rst(id));
260 } else {
261 let (tx, rx) = mpsc::unbounded_channel();
262 let window = Arc::new(AtomicI64::new(i64::from(INITIAL_WINDOW)));
263 t.insert(
264 id,
265 Inbound {
266 tx,
267 window: window.clone(),
268 },
269 );
270 drop(t);
271 let _ = cmd.send(Cmd::Ack(id));
272 let stream = Stream {
273 id,
274 rx,
275 buf: Vec::new(),
276 pos: 0,
277 consumed: 0,
278 window,
279 writer: StreamWriter {
280 id,
281 cmd: cmd.clone(),
282 },
283 };
284 if incoming.try_send(stream).is_err() {
285 table.lock().unwrap_or_else(|e| e.into_inner()).remove(&id);
286 }
287 }
288 }
289 if kind == T_WINDOW {
290 if len > 0 {
291 let _ = cmd.send(Cmd::PeerWindow(id, len));
292 }
293 } else if len > 0 {
294 let mut data = vec![0u8; len as usize];
295 let window = table
297 .lock()
298 .unwrap_or_else(|e| e.into_inner())
299 .get(&id)
300 .map(|s| s.window.clone());
301 if let Some(w) = &window {
302 ensure!(
303 w.fetch_sub(i64::from(len), Ordering::SeqCst) >= i64::from(len),
304 "peer sent {len} bytes past stream {id}'s receive window"
305 );
306 } else {
307 ensure!(
308 len as usize <= INITIAL_WINDOW as usize,
309 "data frame larger than any window"
310 );
311 }
312 r.read_exact(&mut data).await?;
313 if let Some(s) = table.lock().unwrap_or_else(|e| e.into_inner()).get(&id) {
314 let _ = s.tx.send(data);
315 }
316 }
317 if flags & (F_FIN | F_RST) != 0 {
318 table.lock().unwrap_or_else(|e| e.into_inner()).remove(&id);
320 }
321 }
322 }
323 .await;
324 table.lock().unwrap_or_else(|e| e.into_inner()).clear();
325 let _ = cmd.send(Cmd::GoAway(0));
326 result
327}
328
329#[derive(Default)]
330struct Outbound {
331 window: u32,
332 pending: VecDeque<Vec<u8>>,
333 fin: bool,
334}
335
336async fn write_loop(mut w: NoiseWriter, mut cmds: mpsc::UnboundedReceiver<Cmd>) -> Result<()> {
337 let mut streams: HashMap<u32, Outbound> = HashMap::new();
338 let fresh = || Outbound {
339 window: INITIAL_WINDOW,
340 ..Default::default()
341 };
342 while let Some(c) = cmds.recv().await {
343 let flush_id = match c {
344 Cmd::Open(id) => {
345 streams.insert(id, fresh());
346 w.write_all(&header(T_WINDOW, F_SYN, id, 0)).await?;
347 None
348 }
349 Cmd::Ack(id) => {
350 streams.insert(id, fresh());
351 w.write_all(&header(T_WINDOW, F_ACK, id, 0)).await?;
352 None
353 }
354 Cmd::Data(id, data) => {
355 if let Some(s) = streams.get_mut(&id) {
356 if !s.fin {
357 s.pending.push_back(data);
358 }
359 }
360 Some(id)
361 }
362 Cmd::Fin(id) => {
363 if let Some(s) = streams.get_mut(&id) {
364 s.fin = true;
365 }
366 Some(id)
367 }
368 Cmd::Rst(id) => {
369 streams.remove(&id);
370 w.write_all(&header(T_WINDOW, F_RST, id, 0)).await?;
371 None
372 }
373 Cmd::Grant(id, delta) => {
374 w.write_all(&header(T_WINDOW, 0, id, delta)).await?;
375 None
376 }
377 Cmd::PeerWindow(id, delta) => {
378 if let Some(s) = streams.get_mut(&id) {
379 s.window = s.window.saturating_add(delta);
380 }
381 Some(id)
382 }
383 Cmd::Pong(v) => {
384 w.write_all(&header(T_PING, F_ACK, 0, v)).await?;
385 None
386 }
387 Cmd::GoAway(code) => {
388 let _ = w.write_all(&header(T_GO_AWAY, 0, 0, code)).await;
389 w.shutdown().await;
390 return Ok(());
391 }
392 };
393 let Some(id) = flush_id else { continue };
394 let Some(s) = streams.get_mut(&id) else {
395 continue;
396 };
397 let mut out = Vec::new();
398 while s.window > 0 {
399 let Some(front) = s.pending.front_mut() else {
400 break;
401 };
402 let n = front.len().min(s.window as usize);
403 out.extend_from_slice(&header(T_DATA, 0, id, n as u32));
404 out.extend(front.drain(..n));
405 s.window -= n as u32;
406 if front.is_empty() {
407 s.pending.pop_front();
408 }
409 }
410 if s.fin && s.pending.is_empty() {
411 out.extend_from_slice(&header(T_WINDOW, F_FIN, id, 0));
412 streams.remove(&id);
413 }
414 if !out.is_empty() {
415 w.write_all(&out).await?;
416 }
417 }
418 w.shutdown().await;
419 Ok(())
420}