-
-
Notifications
You must be signed in to change notification settings - Fork 399
Exec can return stdout data even after stdin is closed. #1693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,82 @@ | ||
| use http::{self, Response, StatusCode}; | ||
| use http::{self, HeaderValue, Response, StatusCode}; | ||
| use thiserror::Error; | ||
| use tokio_tungstenite::tungstenite as ws; | ||
|
|
||
| use crate::client::Body; | ||
| use crate::{client::Body, Error, Result}; | ||
|
|
||
| // Binary subprotocol v4. See `Client::connect`. | ||
| pub const WS_PROTOCOL: &str = "v4.channel.k8s.io"; | ||
| #[derive(Debug)] | ||
| pub enum StreamProtocol { | ||
| /// Binary subprotocol v4. See `Client::connect`. | ||
| V4, | ||
|
|
||
| /// Binary subprotocol v5. See `Client::connect`. | ||
| /// v5 supports CLOSE signals. | ||
| /// https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/util/remotecommand/constants.go#L52C26-L52C43 | ||
| V5, | ||
| } | ||
|
|
||
| impl StreamProtocol { | ||
| pub fn as_str(&self) -> &'static str { | ||
| match self { | ||
| StreamProtocol::V4 => "v4.channel.k8s.io", | ||
| StreamProtocol::V5 => "v5.channel.k8s.io" | ||
| } | ||
| } | ||
|
|
||
| fn as_bytes(&self) -> &'static [u8] { | ||
| self.as_str().as_bytes() | ||
| } | ||
|
|
||
| pub fn supports_stream_close(&self) -> bool { | ||
| match self { | ||
| StreamProtocol::V4 => false, | ||
| StreamProtocol::V5 => true | ||
|
clux marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
|
|
||
| /// Add HTTP header SEC_WEBSOCKET_PROTOCOL with a list of supported protocol. | ||
| pub fn add_to_headers(headers: &mut http::HeaderMap) -> Result<()> { | ||
| // Protocols we support in our preferred order. | ||
| let supported_protocols = vec. | ||
| // There's a comment about v4 and `Status` object in | ||
| // [`kublet/cri/streaming/remotecommand/httpstream.go`](https://git.io/JLQEh). | ||
| StreamProtocol::V4.as_str(), | ||
| ]; | ||
|
|
||
| let header_value_string = supported_protocols.join(", "); | ||
|
clux marked this conversation as resolved.
|
||
|
|
||
| // Note: Multiple headers does not work. Only a single CSV works. | ||
| headers.insert( | ||
| http::header::SEC_WEBSOCKET_PROTOCOL, | ||
| HeaderValue::from_str(&header_value_string).map_err(|e| Error::HttpError(e.into()))?, | ||
| ); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Return the subprotocol of an HTTP response. | ||
| fn get_from_response<B>(res: &Response<B>) -> Option<Self> { | ||
| let headers = res.headers(); | ||
|
|
||
| match headers | ||
| .get(http::header::SEC_WEBSOCKET_PROTOCOL) | ||
| .map(|h| h.as_bytes()) { | ||
| Some(protocol) => if protocol == StreamProtocol::V4.as_bytes() { | ||
| Some(StreamProtocol::V4) | ||
| } else if protocol == StreamProtocol::V5.as_bytes() { | ||
| Some(StreamProtocol::V5) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just for checking my understanding; the idea here is that we send a comma separated list of protocols we support, and the far end gives us the most recent one they can handle, which we can inspect via
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that is correct (or at least correct as far as my understanding goes). I found this doc which explains how the protocol header is intended to operate. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-WebSocket-Protocol |
||
| } else { | ||
| None | ||
| }, | ||
| _ => None, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Possible errors from upgrading to a WebSocket connection | ||
| #[cfg(feature = "ws")] | ||
|
|
@@ -42,7 +113,7 @@ pub enum UpgradeConnectionError { | |
|
|
||
| // Verify upgrade response according to RFC6455. | ||
| // Based on `tungstenite` and added subprotocol verification. | ||
| pub fn verify_response(res: &Response<Body>, key: &str) -> Result<(), UpgradeConnectionError> { | ||
| pub fn verify_response(res: &Response<Body>, key: &str) -> Result<StreamProtocol, UpgradeConnectionError> { | ||
| if res.status() != StatusCode::SWITCHING_PROTOCOLS { | ||
| return Err(UpgradeConnectionError::ProtocolSwitch(res.status())); | ||
| } | ||
|
|
@@ -75,14 +146,11 @@ pub fn verify_response(res: &Response<Body>, key: &str) -> Result<(), UpgradeCon | |
| return Err(UpgradeConnectionError::SecWebSocketAcceptKeyMismatch); | ||
| } | ||
|
|
||
| // Make sure that the server returned the correct subprotocol. | ||
| if !headers | ||
| .get(http::header::SEC_WEBSOCKET_PROTOCOL) | ||
| .map(|h| h == WS_PROTOCOL) | ||
| .unwrap_or(false) | ||
| { | ||
| return Err(UpgradeConnectionError::SecWebSocketProtocolMismatch); | ||
| } | ||
| // Make sure that the server returned an expected subprotocol. | ||
| let protocol = match StreamProtocol::get_from_response(res) { | ||
| Some(p) => p, | ||
| None => return Err(UpgradeConnectionError::SecWebSocketProtocolMismatch) | ||
| }; | ||
|
|
||
| Ok(()) | ||
| Ok(protocol) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.