1use std::collections::{HashMap, HashSet};
2use std::sync::Arc;
3
4use async_channel::Sender;
5use async_trait::async_trait;
6use log::{debug, error};
7
8use karyon_core::async_util::{AsyncQueue, TaskGroup, TaskResult};
9use karyon_net::{FramedReader, FramedWriter};
10
11#[cfg(feature = "quic")]
12use karyon_core::async_runtime::io::{AsyncReadExt, AsyncWriteExt};
13#[cfg(feature = "quic")]
14use karyon_net::{framed, quic::QuicConn, StreamMux};
15
16#[cfg(feature = "quic")]
17use crate::{message::StreamInit, peer::ConnDirection, util::encode};
18
19use crate::{
20 codec::PeerNetMsgCodec,
21 conn_queue::QueuedConn,
22 message::{PeerNetCmd, PeerNetMsg, ProtocolMsg, ShutdownMsg},
23 protocol::{ProtocolEvent, ProtocolID},
24 util::decode,
25 Error, Result,
26};
27
28const SEND_QUEUE_SIZE: usize = 128;
29const RECV_QUEUE_SIZE: usize = 128;
30
31#[async_trait]
34pub(crate) trait Wire: Send + Sync {
35 async fn send(&self, proto_id: &ProtocolID, payload: Vec<u8>) -> Result<()>;
37 async fn recv(&self, proto_id: &ProtocolID) -> Result<ProtocolEvent>;
40 async fn shutdown(&self) -> Result<()>;
43}
44
45pub(crate) async fn from_queued(
48 queued: QueuedConn,
49 negotiated: &HashSet<ProtocolID>,
50 proto_ids: impl IntoIterator<Item = ProtocolID> + Clone,
51 task_group: &TaskGroup,
52 stop_chan: Sender<Result<()>>,
53) -> Result<Arc<dyn Wire>> {
54 #[cfg(feature = "quic")]
55 if queued.quic_conn.is_some() {
56 let conn = MuxConnection::from_queued(queued, negotiated, proto_ids, task_group).await?;
57 return Ok(Arc::new(conn) as Arc<dyn Wire>);
58 }
59
60 let _ = negotiated;
61 let conn = SingleConnection::from_queued(queued, proto_ids, task_group, stop_chan);
62 Ok(Arc::new(conn))
63}
64
65pub(crate) struct SingleConnection {
68 send_queue: Arc<AsyncQueue<PeerNetMsg>>,
69 recv_queues: HashMap<ProtocolID, Arc<AsyncQueue<ProtocolEvent>>>,
70}
71
72impl SingleConnection {
73 pub(crate) fn from_queued(
75 queued: QueuedConn,
76 proto_ids: impl IntoIterator<Item = ProtocolID>,
77 task_group: &TaskGroup,
78 stop_chan: Sender<Result<()>>,
79 ) -> Self {
80 let send_queue = AsyncQueue::new(SEND_QUEUE_SIZE);
81 let recv_queues = build_recv_queues(proto_ids);
82
83 spawn_writer_task(task_group, queued.writer, send_queue.clone());
84 spawn_demux_reader(task_group, queued.reader, recv_queues.clone(), stop_chan);
85
86 Self {
87 send_queue,
88 recv_queues,
89 }
90 }
91}
92
93#[async_trait]
94impl Wire for SingleConnection {
95 async fn send(&self, proto_id: &ProtocolID, payload: Vec<u8>) -> Result<()> {
96 let proto_msg = ProtocolMsg {
97 protocol_id: proto_id.clone(),
98 payload,
99 };
100 let net_msg = PeerNetMsg::new(PeerNetCmd::Protocol, &proto_msg)?;
101 self.send_queue.push(net_msg).await;
102 Ok(())
103 }
104
105 async fn recv(&self, proto_id: &ProtocolID) -> Result<ProtocolEvent> {
106 match self.recv_queues.get(proto_id) {
107 Some(q) => Ok(q.recv().await),
108 None => Err(Error::UnsupportedProtocol(proto_id.clone())),
109 }
110 }
111
112 async fn shutdown(&self) -> Result<()> {
113 let m = PeerNetMsg::new(PeerNetCmd::Shutdown, ShutdownMsg(0))?;
114 self.send_queue.push(m).await;
115 broadcast_shutdown(&self.recv_queues).await;
116 Ok(())
117 }
118}
119
120#[cfg(feature = "quic")]
123pub(crate) struct MuxConnection {
124 send_queues: HashMap<ProtocolID, Arc<AsyncQueue<PeerNetMsg>>>,
125 recv_queues: HashMap<ProtocolID, Arc<AsyncQueue<ProtocolEvent>>>,
126 quic_conn: QuicConn,
127}
128
129#[cfg(feature = "quic")]
130impl MuxConnection {
131 pub(crate) async fn from_queued(
134 queued: QueuedConn,
135 negotiated: &HashSet<ProtocolID>,
136 proto_ids: impl IntoIterator<Item = ProtocolID>,
137 task_group: &TaskGroup,
138 ) -> Result<Self> {
139 let quic_conn = queued
140 .quic_conn
141 .ok_or_else(|| Error::InvalidMsg("MuxConnection requires a QUIC conn".into()))?;
142
143 let recv_queues = build_recv_queues(proto_ids);
144 let (send_queues, streams) =
145 setup_quic_streams(&quic_conn, &queued.direction, negotiated).await?;
146
147 for stream in streams {
148 let q_send = send_queues
149 .get(&stream.proto_id)
150 .expect("send queue installed in setup_quic_streams")
151 .clone();
152 let q_recv = recv_queues
153 .get(&stream.proto_id)
154 .expect("recv queue installed in build_recv_queues")
155 .clone();
156 spawn_writer_task(task_group, stream.writer, q_send);
157 spawn_quic_reader(task_group, stream.proto_id, stream.reader, q_recv);
158 }
159
160 Ok(Self {
161 send_queues,
162 recv_queues,
163 quic_conn,
164 })
165 }
166}
167
168#[cfg(feature = "quic")]
169#[async_trait]
170impl Wire for MuxConnection {
171 async fn send(&self, proto_id: &ProtocolID, payload: Vec<u8>) -> Result<()> {
172 let proto_msg = ProtocolMsg {
173 protocol_id: proto_id.clone(),
174 payload,
175 };
176 let net_msg = PeerNetMsg::new(PeerNetCmd::Protocol, &proto_msg)?;
177 match self.send_queues.get(proto_id) {
178 Some(q) => {
179 q.push(net_msg).await;
180 Ok(())
181 }
182 None => Err(Error::UnsupportedProtocol(proto_id.clone())),
183 }
184 }
185
186 async fn recv(&self, proto_id: &ProtocolID) -> Result<ProtocolEvent> {
187 match self.recv_queues.get(proto_id) {
188 Some(q) => Ok(q.recv().await),
189 None => Err(Error::UnsupportedProtocol(proto_id.clone())),
190 }
191 }
192
193 async fn shutdown(&self) -> Result<()> {
194 self.quic_conn.close(0, b"shutdown");
195 broadcast_shutdown(&self.recv_queues).await;
196 Ok(())
197 }
198}
199
200#[cfg(feature = "quic")]
202struct MuxStream {
203 proto_id: ProtocolID,
204 reader: FramedReader<PeerNetMsgCodec>,
205 writer: FramedWriter<PeerNetMsgCodec>,
206}
207
208fn spawn_writer_task(
211 task_group: &TaskGroup,
212 mut writer: FramedWriter<PeerNetMsgCodec>,
213 queue: Arc<AsyncQueue<PeerNetMsg>>,
214) {
215 task_group.spawn_then(
216 async move {
217 loop {
218 let msg = queue.recv().await;
219 if writer.send_msg(msg).await.is_err() {
220 break;
221 }
222 }
223 Ok::<(), Error>(())
224 },
225 |res: TaskResult<Result<()>>| async move {
226 debug!("Peer writer task ended: {res}");
227 },
228 );
229}
230
231fn spawn_demux_reader(
235 task_group: &TaskGroup,
236 mut reader: FramedReader<PeerNetMsgCodec>,
237 recv_queues: HashMap<ProtocolID, Arc<AsyncQueue<ProtocolEvent>>>,
238 stop_chan: Sender<Result<()>>,
239) {
240 task_group.spawn_then(
241 async move {
242 loop {
243 let msg = match reader.recv_msg().await {
244 Ok(m) => m,
245 Err(e) => {
246 let _ = stop_chan.try_send(Err(e.into()));
247 break;
248 }
249 };
250 match msg.header.command {
251 PeerNetCmd::Protocol => {
252 let proto_msg: ProtocolMsg = match decode(&msg.payload) {
253 Ok((m, _)) => m,
254 Err(e) => {
255 let _ = stop_chan.try_send(Err(e));
256 break;
257 }
258 };
259 match recv_queues.get(&proto_msg.protocol_id) {
260 Some(q) => {
261 q.push(ProtocolEvent::Message(proto_msg.payload)).await;
262 }
263 None => {
264 error!("No recv queue for protocol {}", proto_msg.protocol_id);
265 }
266 }
267 }
268 PeerNetCmd::Shutdown => {
269 let _ = stop_chan.try_send(Err(Error::PeerShutdown));
270 break;
271 }
272 command => {
273 let _ = stop_chan.try_send(Err(Error::InvalidMsg(format!(
274 "Unexpected msg {command:?}"
275 ))));
276 break;
277 }
278 }
279 }
280 Ok::<(), Error>(())
281 },
282 |res: TaskResult<Result<()>>| async move {
283 debug!("Peer reader task ended: {res}");
284 },
285 );
286}
287
288#[cfg(feature = "quic")]
291fn spawn_quic_reader(
292 task_group: &TaskGroup,
293 proto_id: ProtocolID,
294 mut reader: FramedReader<PeerNetMsgCodec>,
295 recv_queue: Arc<AsyncQueue<ProtocolEvent>>,
296) {
297 task_group.spawn_then(
298 async move {
299 loop {
300 let msg = match reader.recv_msg().await {
301 Ok(m) => m,
302 Err(_) => break,
303 };
304 match msg.header.command {
305 PeerNetCmd::Protocol => {
306 let proto_msg: ProtocolMsg = decode(&msg.payload)?.0;
307 recv_queue
308 .push(ProtocolEvent::Message(proto_msg.payload))
309 .await;
310 }
311 PeerNetCmd::Shutdown => break,
312 cmd => {
313 error!("Unexpected msg on QUIC stream {proto_id}: {cmd:?}");
314 }
315 }
316 }
317 Ok::<(), Error>(())
318 },
319 |res: TaskResult<Result<()>>| async move {
320 debug!("QUIC stream reader task ended: {res}");
321 },
322 );
323}
324
325#[cfg(feature = "quic")]
328async fn setup_quic_streams(
329 quic_conn: &QuicConn,
330 direction: &ConnDirection,
331 negotiated: &HashSet<ProtocolID>,
332) -> Result<(
333 HashMap<ProtocolID, Arc<AsyncQueue<PeerNetMsg>>>,
334 Vec<MuxStream>,
335)> {
336 let mut queues = HashMap::new();
337 let mut streams = Vec::new();
338
339 match direction {
340 ConnDirection::Outbound => {
341 for proto_id in negotiated.iter() {
342 let mut stream = quic_conn.open_stream().await?;
343
344 let init = StreamInit {
345 protocol_id: proto_id.clone(),
346 };
347 let encoded = encode(&init)?;
348 stream.write_all(&encoded).await?;
349 stream.flush().await?;
350
351 let conn = framed(stream, PeerNetMsgCodec::new());
352 let (reader, writer) = conn.split();
353
354 let q = AsyncQueue::new(SEND_QUEUE_SIZE);
355 queues.insert(proto_id.clone(), q);
356 streams.push(MuxStream {
357 proto_id: proto_id.clone(),
358 reader,
359 writer,
360 });
361 }
362 }
363 ConnDirection::Inbound => {
364 let expected = negotiated.len();
365 let mut received = 0;
366
367 while received < expected {
368 let mut stream = quic_conn.accept_stream().await?;
369
370 let mut header_buf = vec![0u8; 256];
371 let n = stream.read(&mut header_buf).await?;
372 if n == 0 {
373 continue;
374 }
375
376 let (init, _): (StreamInit, _) = decode(&header_buf[..n])?;
377
378 if !negotiated.contains(&init.protocol_id) {
379 error!("Unsupported protocol: {}", init.protocol_id);
380 continue;
381 }
382
383 let conn = framed(stream, PeerNetMsgCodec::new());
384 let (reader, writer) = conn.split();
385
386 let q = AsyncQueue::new(SEND_QUEUE_SIZE);
387 queues.insert(init.protocol_id.clone(), q);
388 streams.push(MuxStream {
389 proto_id: init.protocol_id,
390 reader,
391 writer,
392 });
393
394 received += 1;
395 }
396 }
397 }
398
399 Ok((queues, streams))
400}
401
402fn build_recv_queues(
404 proto_ids: impl IntoIterator<Item = ProtocolID>,
405) -> HashMap<ProtocolID, Arc<AsyncQueue<ProtocolEvent>>> {
406 proto_ids
407 .into_iter()
408 .map(|id| (id, AsyncQueue::new(RECV_QUEUE_SIZE)))
409 .collect()
410}
411
412async fn broadcast_shutdown(queues: &HashMap<ProtocolID, Arc<AsyncQueue<ProtocolEvent>>>) {
415 for q in queues.values() {
416 q.push(ProtocolEvent::Shutdown).await;
417 }
418}