conmonrs/streaming_server/remotecommand/
spdy.rs1use super::{ClientEvent, ClientInput, Stdin, Version};
5use crate::streaming_server::{
6 spdy::{Connection, ConnectionHandle, Headers, Stream, StreamReceiver, StreamSender},
7 stream_protocol::{ClientStr, STREAM_CREATION_TIMEOUT, STREAM_TYPE_HEADER},
8};
9use anyhow::{Context, Result, bail};
10use bytes::{Bytes, BytesMut};
11use serde::de::IgnoredAny;
12use std::{
13 future::{self, Future},
14 sync::{
15 Arc,
16 atomic::{AtomicBool, Ordering},
17 },
18};
19use tokio::{select, task, time};
20use tokio_util::sync::CancellationToken;
21use tracing::debug;
22
23pub(super) const MAX_RESIZE_BUFFER: usize = 4 * 1024;
25
26pub(super) const STREAM_TYPE_RESIZE: &str = "resize";
28
29pub(crate) const SPDY_PROTOCOLS: [(&str, Version); 4] = [
31 ("v4.channel.k8s.io", Version::V4),
32 ("v3.channel.k8s.io", Version::V3),
33 ("v2.channel.k8s.io", Version::V2),
34 ("channel.k8s.io", Version::V1),
35];
36
37pub(crate) struct SpdyInput {
44 stdin: Option<StreamReceiver>,
45
46 peer_closed: Arc<AtomicBool>,
52 resize: Option<StreamReceiver>,
53 resize_events: ResizeEvents,
54
55 connection: Option<Connection>,
58
59 pub(super) done: CancellationToken,
61}
62
63enum SpdyEvent {
65 Stdin(Option<Bytes>),
66 Resize(Option<Bytes>),
67 Stream(Option<Stream>),
68 Done,
69}
70
71async fn or_pending<T, F>(value: Option<T>, f: impl FnOnce(T) -> F) -> F::Output
74where
75 F: Future,
76{
77 match value {
78 Some(value) => f(value).await,
79 None => future::pending().await,
80 }
81}
82
83impl SpdyInput {
84 fn late_stream(&mut self, stream: Stream) {
90 match StreamType::parse(stream.headers()) {
91 Ok(StreamType::Resize) if self.resize.is_none() => {
92 let (receiver, sender) = stream.split();
93 task::spawn(close_sending_side(sender));
94 self.resize = Some(receiver);
95 }
96 Ok(StreamType::Stdout | StreamType::Stderr) => {
97 task::spawn(close_sending_side(stream.split().1));
98 }
99 _ => debug!("Rejecting a stream opened after the start of the session"),
100 }
101 }
102}
103
104#[derive(Debug, Default)]
108pub(super) struct ResizeEvents {
109 pub(super) buf: BytesMut,
110 latest: Option<Bytes>,
111}
112
113impl ResizeEvents {
114 pub(super) fn push(&mut self, data: &[u8]) {
117 self.buf.extend_from_slice(data);
118 let mut stream = serde_json::Deserializer::from_slice(&self.buf).into_iter::<IgnoredAny>();
119 let mut start = 0;
120 let mut latest = None;
121 loop {
122 match stream.next() {
123 Some(Ok(_)) => {
124 let end = stream.byte_offset();
125 latest = Some(start..end);
126 start = end;
127 }
128 Some(Err(e)) if e.is_eof() => break,
129 Some(Err(e)) => {
130 debug!("Dropping invalid resize events: {e}");
131 start = self.buf.len();
132 break;
133 }
134 None => break,
135 }
136 }
137 if let Some(range) = latest {
138 self.latest = Some(Bytes::copy_from_slice(self.buf[range].trim_ascii()));
139 }
140 let _ = self.buf.split_to(start);
141 let whitespace = self
142 .buf
143 .iter()
144 .take_while(|b| b.is_ascii_whitespace())
145 .count();
146 let _ = self.buf.split_to(whitespace);
147 if self.buf.len() > MAX_RESIZE_BUFFER {
148 debug!("Dropping incomplete resize event");
149 self.buf.clear();
150 }
151 }
152
153 pub(super) fn pop(&mut self) -> Option<Bytes> {
156 self.latest.take()
157 }
158}
159
160impl ClientInput for SpdyInput {
161 async fn next(&mut self) -> Option<ClientEvent> {
162 loop {
163 if let Some(event) = self.resize_events.pop() {
164 return Some(ClientEvent::Resize(event));
165 }
166 let event = select! {
171 biased;
172 data = or_pending(self.resize.as_mut(), StreamReceiver::recv) => SpdyEvent::Resize(data),
173 data = or_pending(self.stdin.as_mut(), StreamReceiver::recv) => SpdyEvent::Stdin(data),
174 stream = or_pending(self.connection.as_mut(), Connection::accept) => SpdyEvent::Stream(stream),
175 () = self.done.cancelled() => SpdyEvent::Done,
176 };
177 match event {
178 SpdyEvent::Stdin(Some(data)) => {
179 if let Some(stdin) = Stdin::data(data) {
180 return Some(ClientEvent::Stdin(stdin));
181 }
182 }
183 SpdyEvent::Stdin(None) => {
186 let reset = self
187 .stdin
188 .take()
189 .is_some_and(|s| s.is_reset() && !s.is_closed_by_client());
190 if !reset {
191 self.peer_closed.store(true, Ordering::Release);
192 return Some(ClientEvent::Stdin(Stdin::Close));
193 }
194 }
195 SpdyEvent::Resize(Some(data)) => self.resize_events.push(&data),
196 SpdyEvent::Resize(None) => self.resize = None,
197 SpdyEvent::Stream(Some(stream)) => self.late_stream(stream),
198 SpdyEvent::Stream(None) => self.connection = None,
199 SpdyEvent::Done => {
202 return self
203 .peer_closed
204 .load(Ordering::Acquire)
205 .then_some(ClientEvent::Closed);
206 }
207 }
208 }
209 }
210}
211
212async fn receiver(stream: Option<Stream>) -> Option<StreamReceiver> {
215 let (receiver, sender) = stream?.split();
216 close_sending_side(sender).await;
217 Some(receiver)
218}
219
220async fn close_sending_side(mut sender: StreamSender) {
223 if let Err(e) = sender.close().await {
224 debug!("Unable to close the sending side of a stream: {e:#}");
225 }
226}
227
228#[derive(Clone, Copy, Debug, PartialEq)]
230enum StreamType {
231 Error,
232 Stdin,
233 Stdout,
234 Stderr,
235 Resize,
236}
237
238impl StreamType {
239 fn parse(headers: &Headers) -> Result<Self> {
240 match headers.get(STREAM_TYPE_HEADER) {
241 Some("error") => Ok(Self::Error),
242 Some("stdin") => Ok(Self::Stdin),
243 Some("stdout") => Ok(Self::Stdout),
244 Some("stderr") => Ok(Self::Stderr),
245 Some(STREAM_TYPE_RESIZE) => Ok(Self::Resize),
246 Some(typ) => bail!("unexpected stream type {:?}", ClientStr(typ)),
247 None => bail!("{STREAM_TYPE_HEADER:?} header is required"),
248 }
249 }
250}
251
252#[derive(Clone, Copy, Debug)]
255pub(crate) struct ExpectedStreams {
256 pub(crate) stdin: bool,
257 pub(crate) stdout: bool,
258 pub(crate) stderr: bool,
259}
260
261pub(crate) struct SpdyStreams {
264 error: Stream,
265 stdin: Option<Stream>,
266 stdout: Option<Stream>,
267 stderr: Option<Stream>,
268 resize: Option<Stream>,
269}
270
271impl SpdyStreams {
272 pub(crate) fn accept(headers: &Headers, version: Version) -> Result<()> {
274 match StreamType::parse(headers)? {
275 StreamType::Resize if !version.spdy_resize() => {
276 bail!("unexpected stream type {STREAM_TYPE_RESIZE:?}")
277 }
278 _ => Ok(()),
279 }
280 }
281
282 pub(crate) async fn wait(
287 connection: &mut Connection,
288 expected: ExpectedStreams,
289 ) -> Result<Self> {
290 let mut error = None;
291 let (mut stdin, mut stdout, mut stderr, mut resize) = (None, None, None, None);
292 let timeout = time::sleep(STREAM_CREATION_TIMEOUT);
293 tokio::pin!(timeout);
294 loop {
295 if let Some(error) = error.take_if(|_| {
296 (!expected.stdin || stdin.is_some())
297 && (!expected.stdout || stdout.is_some())
298 && (!expected.stderr || stderr.is_some())
299 }) {
300 return Ok(Self {
301 error,
302 stdin,
303 stdout,
304 stderr,
305 resize,
306 });
307 }
308 let stream = select! {
309 stream = connection.accept() => {
310 stream.context("connection ended before the client created all streams")?
311 }
312 () = &mut timeout => bail!("timed out waiting for client to create streams"),
313 };
314 let slot = match StreamType::parse(stream.headers()) {
317 Ok(StreamType::Error) => &mut error,
318 Ok(StreamType::Stdin) => &mut stdin,
319 Ok(StreamType::Stdout) => &mut stdout,
320 Ok(StreamType::Stderr) => &mut stderr,
321 Ok(StreamType::Resize) => &mut resize,
322 Err(_) => continue,
323 };
324 if slot.is_some() {
325 debug!("Rejecting a second stream of the same type");
327 continue;
328 }
329 *slot = Some(stream);
330 }
331 }
332
333 pub(crate) async fn into_parts(
338 self,
339 connection: Connection,
340 version: Version,
341 ) -> (SpdyInput, SpdyOutput) {
342 let (stdin, stdin_sender) = match self.stdin.map(Stream::split) {
343 Some((receiver, sender)) if version == Version::V1 => {
344 close_sending_side(sender).await;
345 (Some(receiver), None)
346 }
347 Some((receiver, sender)) => (Some(receiver), Some(sender)),
348 None => (None, None),
349 };
350 let sender = |stream: Option<Stream>| stream.map(|s| s.split().1);
351 let peer_closed = Arc::<AtomicBool>::default();
352 let output = SpdyOutput {
353 peer_closed: peer_closed.clone(),
354 version,
355 error: self.error.split().1,
356 stdin: stdin_sender,
357 stdout: sender(self.stdout),
358 stderr: sender(self.stderr),
359 connection: connection.handle(),
360 };
361 let input = SpdyInput {
362 peer_closed,
363 stdin,
364 resize: receiver(self.resize).await,
365 resize_events: ResizeEvents::default(),
366 done: connection.handle().done(),
367 connection: Some(connection),
368 };
369 (input, output)
370 }
371}
372
373pub(crate) struct SpdyOutput {
375 pub(super) peer_closed: Arc<AtomicBool>,
377 pub(super) version: Version,
378 pub(super) error: StreamSender,
379 pub(super) stdin: Option<StreamSender>,
380 pub(super) stdout: Option<StreamSender>,
381 pub(super) stderr: Option<StreamSender>,
382 pub(super) connection: ConnectionHandle,
383}