kube_client/client/
upgrade.rs1use http::{self, Response, StatusCode};
2use thiserror::Error;
3use tokio_tungstenite::tungstenite as ws;
4
5use crate::client::Body;
6
7pub const WS_PROTOCOL: &str = "v4.channel.k8s.io";
9
10#[cfg(feature = "ws")]
12#[cfg_attr(docsrs, doc(cfg(feature = "ws")))]
13#[derive(Debug, Error)]
14pub enum UpgradeConnectionError {
15 #[error("failed to switch protocol: {0}")]
20 ProtocolSwitch(http::status::StatusCode),
21
22 #[error("upgrade header was not set to websocket")]
24 MissingUpgradeWebSocketHeader,
25
26 #[error("connection header was not set to Upgrade")]
28 MissingConnectionUpgradeHeader,
29
30 #[error("Sec-WebSocket-Accept key mismatched")]
32 SecWebSocketAcceptKeyMismatch,
33
34 #[error("Sec-WebSocket-Protocol mismatched")]
36 SecWebSocketProtocolMismatch,
37
38 #[error("failed to get pending HTTP upgrade: {0}")]
40 GetPendingUpgrade(#[source] hyper::Error),
41}
42
43pub fn verify_response(res: &Response<Body>, key: &str) -> Result<(), UpgradeConnectionError> {
46 if res.status() != StatusCode::SWITCHING_PROTOCOLS {
47 return Err(UpgradeConnectionError::ProtocolSwitch(res.status()));
48 }
49
50 let headers = res.headers();
51 if !headers
52 .get(http::header::UPGRADE)
53 .and_then(|h| h.to_str().ok())
54 .map(|h| h.eq_ignore_ascii_case("websocket"))
55 .unwrap_or(false)
56 {
57 return Err(UpgradeConnectionError::MissingUpgradeWebSocketHeader);
58 }
59
60 if !headers
61 .get(http::header::CONNECTION)
62 .and_then(|h| h.to_str().ok())
63 .map(|h| h.eq_ignore_ascii_case("Upgrade"))
64 .unwrap_or(false)
65 {
66 return Err(UpgradeConnectionError::MissingConnectionUpgradeHeader);
67 }
68
69 let accept_key = ws::handshake::derive_accept_key(key.as_ref());
70 if !headers
71 .get(http::header::SEC_WEBSOCKET_ACCEPT)
72 .map(|h| h == &accept_key)
73 .unwrap_or(false)
74 {
75 return Err(UpgradeConnectionError::SecWebSocketAcceptKeyMismatch);
76 }
77
78 if !headers
80 .get(http::header::SEC_WEBSOCKET_PROTOCOL)
81 .map(|h| h == WS_PROTOCOL)
82 .unwrap_or(false)
83 {
84 return Err(UpgradeConnectionError::SecWebSocketProtocolMismatch);
85 }
86
87 Ok(())
88}
89
90pub fn sec_websocket_key() -> String {
93 use base64::Engine;
94 let r: [u8; 16] = rand::random();
95 base64::engine::general_purpose::STANDARD.encode(r)
96}