conmonrs/streaming_server/port_forward/
tunnel.rs1use super::{
5 BASE64_CHANNEL_PROTOCOL, CHANNEL_PROTOCOL, PortForward, SPDY_TUNNEL_PROTOCOL, TUNNEL_BUFFER,
6};
7use crate::streaming_server::{
8 stream_protocol::first_supported,
9 web::{self, ClientMessage, Message, WebSocket},
10};
11use bytes::BytesMut;
12use futures::{SinkExt, StreamExt};
13use std::pin::pin;
14use tokio::{
15 io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, DuplexStream},
16 select, task, time,
17};
18use tokio_tungstenite::WebSocketStream;
19use tokio_util::sync::CancellationToken;
20use tracing::debug;
21
22#[derive(Clone, Copy, Debug, PartialEq)]
24pub(crate) enum WebSocketProtocol {
25 SpdyTunnel,
27
28 Channels { base64: bool },
31}
32
33const CHANNEL_PROTOCOLS: [(&str, WebSocketProtocol); 2] = [
35 (
36 CHANNEL_PROTOCOL,
37 WebSocketProtocol::Channels { base64: false },
38 ),
39 (
40 BASE64_CHANNEL_PROTOCOL,
41 WebSocketProtocol::Channels { base64: true },
42 ),
43];
44
45impl WebSocketProtocol {
46 pub(crate) fn negotiate<'a>(
50 requested: impl IntoIterator<Item = &'a str>,
51 ) -> Option<(Self, Option<&'static str>)> {
52 let requested: Vec<_> = requested.into_iter().collect();
53 if requested.contains(&SPDY_TUNNEL_PROTOCOL) {
54 return Some((Self::SpdyTunnel, Some(SPDY_TUNNEL_PROTOCOL)));
55 }
56 if requested.is_empty() {
57 return Some((Self::Channels { base64: false }, None));
58 }
59 first_supported(requested, &CHANNEL_PROTOCOLS).map(|(protocol, p)| (protocol, Some(p)))
60 }
61}
62
63impl PortForward {
64 pub(crate) async fn serve_websocket(
67 self,
68 socket: WebSocket,
69 protocol: WebSocketProtocol,
70 ports: Vec<u16>,
71 ) {
72 match protocol {
73 WebSocketProtocol::SpdyTunnel => {
74 let (server, tunnel) = tokio::io::duplex(TUNNEL_BUFFER);
75 let spdy = task::spawn(self.serve_spdy(server));
76 Self::tunnel(socket, tunnel).await;
77 let _ = spdy.await;
78 }
79 WebSocketProtocol::Channels { base64 } => {
80 self.serve_channels(socket, ports, base64).await;
81 }
82 }
83 debug!("Port forward WebSocket ended");
84 }
85
86 pub(super) async fn tunnel<T>(
89 socket: WebSocketStream<web::ClientIo<T>>,
90 connection: DuplexStream,
91 ) where
92 T: AsyncRead + AsyncWrite + Unpin + Send + 'static,
93 {
94 let discard = socket.get_ref().discarder();
95 let written = socket.get_ref().written();
96 let (mut sink, mut messages) = socket.split();
97 let (mut from_server, mut to_server) = tokio::io::split(connection);
98 let too_large = CancellationToken::new();
101
102 let mut to_client = task::spawn({
104 let too_large = too_large.clone();
105 async move {
106 let mut buf = BytesMut::new();
107 loop {
108 buf.reserve(TUNNEL_BUFFER);
109 match from_server.read_buf(&mut buf).await {
110 Ok(0) | Err(_) => break,
111 Ok(_) => {
112 if sink
113 .send(Message::Binary(buf.split().freeze()))
114 .await
115 .is_err()
116 {
117 return None;
118 }
119 }
120 }
121 }
122 web::send_close(sink, too_large.is_cancelled()).await
123 }
124 });
125
126 let from_client = async {
127 loop {
128 match web::next_message(&mut messages, &discard).await {
129 ClientMessage::Data(Message::Binary(data)) => {
130 if to_server.write_all(&data).await.is_err() {
134 web::await_close_answer(&mut messages).await;
135 break;
136 }
137 }
138 ClientMessage::Data(_) => {
139 debug!("Ending port forward tunnel, the client sent a text message");
140 break;
141 }
142 ClientMessage::TooLarge => {
143 too_large.cancel();
144 break;
145 }
146 ClientMessage::Closed(_) => {
147 web::answer_close(&mut messages, &written).await;
148 break;
149 }
150 ClientMessage::Gone => break,
151 }
152 }
153 let _ = to_server.shutdown().await;
155 };
156
157 let server_done = {
160 let mut from_client = pin!(from_client);
161 select! {
162 () = &mut from_client => None,
163 sink = &mut to_client => {
164 let sink = sink.ok().flatten();
165 if sink.is_some() {
168 let _ = time::timeout(web::CLOSE_TIMEOUT, from_client).await;
169 }
170 Some(sink)
171 }
172 }
173 };
174 let end = if too_large.is_cancelled() {
175 web::End::TooLarge
176 } else {
177 web::End::Gone
178 };
179 match server_done {
180 None => web::end(to_client, &written, messages, end).await,
181 Some(Some(sink)) if end == web::End::TooLarge => web::drain(sink, messages).await,
183 Some(_) => {}
184 }
185 }
186}