conmonrs/streaming_server/remotecommand/
websocket.rs1use super::{
5 CLOSE_BYTE, ClientEvent, ClientInput, Output, RESIZE_BYTE, STDIN_BYTE, Stdin, Transport,
6 Version,
7};
8use crate::streaming_server::{
9 channel,
10 idle::Activity,
11 stream_protocol::{ClientBytes, ClientStr, first_supported},
12 web::{self, ClientMessage, Message, WebSocket},
13};
14use bytes::Bytes;
15use futures::stream::SplitStream;
16use tracing::{debug, trace};
17
18#[derive(Clone, Copy, Debug, PartialEq)]
20pub(crate) struct Framing {
21 pub(crate) version: Version,
22
23 pub(crate) base64: bool,
26}
27
28pub(super) const WEBSOCKET_PROTOCOLS: [(&str, Framing); 5] = [
30 ("v5.channel.k8s.io", Framing::V5),
31 (
32 "v4.channel.k8s.io",
33 Framing {
34 version: Version::V4,
35 base64: false,
36 },
37 ),
38 (
39 "v4.base64.channel.k8s.io",
40 Framing {
41 version: Version::V4,
42 base64: true,
43 },
44 ),
45 (
46 "channel.k8s.io",
47 Framing {
48 version: Version::V1,
49 base64: false,
50 },
51 ),
52 (
53 "base64.channel.k8s.io",
54 Framing {
55 version: Version::V1,
56 base64: true,
57 },
58 ),
59];
60
61impl Framing {
62 pub(crate) const V5: Self = Self {
64 version: Version::V5,
65 base64: false,
66 };
67
68 pub(crate) fn negotiate<'a>(
71 requested: impl IntoIterator<Item = &'a str>,
72 ) -> Option<(Self, Option<&'static str>)> {
73 let mut requested = requested.into_iter().peekable();
74 if requested.peek().is_none() {
75 return Some((
76 Self {
77 version: Version::V1,
78 base64: false,
79 },
80 None,
81 ));
82 }
83 first_supported(requested, &WEBSOCKET_PROTOCOLS).map(|(framing, p)| (framing, Some(p)))
84 }
85
86 pub(crate) fn message(self, channel: u8, data: &[u8]) -> Message {
88 channel::message(self.base64, channel, data)
89 }
90
91 pub(crate) fn event(self, message: Message) -> Option<ClientEvent> {
95 let data = match message {
96 Message::Binary(data) => data,
97 Message::Text(text) => Bytes::from(text),
98 Message::Close(c) => {
99 if let Some(cf) = c {
100 debug!(
101 "Got WebSocket close with code {} and reason {:?}",
102 cf.code,
103 ClientStr(&cf.reason)
104 );
105 } else {
106 debug!("Got WebSocket close without close frame");
107 }
108 return Some(ClientEvent::Closed);
109 }
110 Message::Pong(_) | Message::Ping(_) | Message::Frame(_) => return None,
112 };
113
114 if self.version == Version::V5 {
115 let Some(&channel) = data.first() else {
116 trace!("Got empty message");
117 return None;
118 };
119 trace!("Got {} bytes", data.len());
120 return match channel {
121 STDIN_BYTE => Stdin::data(data.slice(1..)).map(ClientEvent::Stdin),
122 CLOSE_BYTE if data[..] == [CLOSE_BYTE, STDIN_BYTE] => {
125 debug!("Got close message");
126 Some(ClientEvent::Stdin(Stdin::Close))
127 }
128 CLOSE_BYTE => {
129 debug!("Ignoring close message {:?}", ClientBytes(&data[1..]));
130 None
131 }
132 RESIZE_BYTE => Some(ClientEvent::Resize(data.slice(1..))),
133 x => {
134 debug!("Ignoring message for unknown channel {x}");
135 None
136 }
137 };
138 }
139
140 match channel::decode(self.base64, data)? {
143 (STDIN_BYTE, payload) => Stdin::data(payload).map(ClientEvent::Stdin),
144 (RESIZE_BYTE, payload) => Some(ClientEvent::Resize(payload)),
145 (channel, _) => {
146 trace!("Ignoring message for channel {channel}");
147 None
148 }
149 }
150 }
151}
152
153pub(crate) struct WebSocketInput {
155 pub(crate) messages: SplitStream<WebSocket>,
156 pub(crate) framing: Framing,
157
158 pub(crate) activity: Activity,
162
163 pub(crate) written: Activity,
166
167 pub(crate) discard: web::Discard,
170}
171
172impl ClientInput for WebSocketInput {
173 async fn next(&mut self) -> Option<ClientEvent> {
174 loop {
175 let message = match web::next_message(&mut self.messages, &self.discard).await {
176 ClientMessage::Data(message) => message,
177 ClientMessage::Closed(frame) => Message::Close(frame),
179 ClientMessage::TooLarge => return Some(ClientEvent::TooLarge),
180 ClientMessage::Gone => return None,
181 };
182 self.activity.touch();
183 if let Some(event) = self.framing.event(message) {
184 return Some(event);
185 }
186 }
187 }
188
189 async fn answer_close(&mut self) {
190 web::answer_close(&mut self.messages, &self.written).await;
191 }
192
193 async fn drain(self, output: Output) {
194 if let Transport::WebSocket { sender, .. } = output.transport {
195 web::drain(sender, self.messages).await;
196 }
197 }
198}