Upgrade async-tungstenite
to v17 and update usage accordingly (#15219)
This PR upgrades `async-tungstenite` to v17.0.3. We previously attempted upgrading `async-tungstenite` in #15039, but broke authentication with collab in the process. Upon further investigation, I determined that the root cause is due to this change in `tungstenite` v0.17.0: > Overhaul of the client's request generation process. Now the users are able to pass the constructed `http::Request` "as is" to `tungstenite-rs`, letting the library to check the correctness of the request and specifying their own headers (including its own key if necessary). No changes for those ones who used the client in a normal way by connecting using a URL/URI (most common use-case). We _were_ relying on passing an `http::Request` directly to `tungstenite`, meaning we did not benefit from the changes to the common path (of passing a URL/URI). This meant that—due to changes in `tungstenite`—we were now missing the `Sec-WebSocket-Key` header that `tungstenite` would otherwise set for us. Since we were only passing a custom `http::Request` to set headers, our approach has been adjusted to construct the initial WebSocket request using `tungstenite`'s `IntoClientRequest::into_client_request` and then modifying the request to set our additional desired headers. Release Notes: - N/A
This commit is contained in:
parent
f3ad754396
commit
9d736fe80c
4 changed files with 81 additions and 38 deletions
|
@ -7,8 +7,9 @@ pub mod user;
|
|||
use anyhow::{anyhow, Context as _, Result};
|
||||
use async_recursion::async_recursion;
|
||||
use async_tungstenite::tungstenite::{
|
||||
client::IntoClientRequest,
|
||||
error::Error as WebsocketError,
|
||||
http::{Request, StatusCode},
|
||||
http::{HeaderValue, Request, StatusCode},
|
||||
};
|
||||
use clock::SystemClock;
|
||||
use collections::HashMap;
|
||||
|
@ -235,6 +236,8 @@ pub enum EstablishConnectionError {
|
|||
#[error("{0}")]
|
||||
Http(#[from] http_client::Error),
|
||||
#[error("{0}")]
|
||||
InvalidHeaderValue(#[from] async_tungstenite::tungstenite::http::header::InvalidHeaderValue),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
Websocket(#[from] async_tungstenite::tungstenite::http::Error),
|
||||
|
@ -1159,19 +1162,24 @@ impl Client {
|
|||
.ok()
|
||||
.unwrap_or_default();
|
||||
|
||||
let request = Request::builder()
|
||||
.header("Authorization", credentials.authorization_header())
|
||||
.header("x-zed-protocol-version", rpc::PROTOCOL_VERSION)
|
||||
.header("x-zed-app-version", app_version)
|
||||
.header(
|
||||
"x-zed-release-channel",
|
||||
release_channel.map(|r| r.dev_name()).unwrap_or("unknown"),
|
||||
);
|
||||
|
||||
let http = self.http.clone();
|
||||
let credentials = credentials.clone();
|
||||
let rpc_url = self.rpc_url(http, release_channel);
|
||||
cx.background_executor().spawn(async move {
|
||||
use HttpOrHttps::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum HttpOrHttps {
|
||||
Http,
|
||||
Https,
|
||||
}
|
||||
|
||||
let mut rpc_url = rpc_url.await?;
|
||||
let url_scheme = match rpc_url.scheme() {
|
||||
"https" => Https,
|
||||
"http" => Http,
|
||||
_ => Err(anyhow!("invalid rpc url: {}", rpc_url))?,
|
||||
};
|
||||
let rpc_host = rpc_url
|
||||
.host_str()
|
||||
.zip(rpc_url.port_or_known_default())
|
||||
|
@ -1180,10 +1188,37 @@ impl Client {
|
|||
|
||||
log::info!("connected to rpc endpoint {}", rpc_url);
|
||||
|
||||
match rpc_url.scheme() {
|
||||
"https" => {
|
||||
rpc_url.set_scheme("wss").unwrap();
|
||||
let request = request.uri(rpc_url.as_str()).body(())?;
|
||||
rpc_url
|
||||
.set_scheme(match url_scheme {
|
||||
Https => "wss",
|
||||
Http => "ws",
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// We call `into_client_request` to let `tungstenite` construct the WebSocket request
|
||||
// for us from the RPC URL.
|
||||
//
|
||||
// Among other things, it will generate and set a `Sec-WebSocket-Key` header for us.
|
||||
let mut request = rpc_url.into_client_request()?;
|
||||
|
||||
// We then modify the request to add our desired headers.
|
||||
let request_headers = request.headers_mut();
|
||||
request_headers.insert(
|
||||
"Authorization",
|
||||
HeaderValue::from_str(&credentials.authorization_header())?,
|
||||
);
|
||||
request_headers.insert(
|
||||
"x-zed-protocol-version",
|
||||
HeaderValue::from_str(&rpc::PROTOCOL_VERSION.to_string())?,
|
||||
);
|
||||
request_headers.insert("x-zed-app-version", HeaderValue::from_str(&app_version)?);
|
||||
request_headers.insert(
|
||||
"x-zed-release-channel",
|
||||
HeaderValue::from_str(&release_channel.map(|r| r.dev_name()).unwrap_or("unknown"))?,
|
||||
);
|
||||
|
||||
match url_scheme {
|
||||
Https => {
|
||||
let (stream, _) =
|
||||
async_tungstenite::async_std::client_async_tls(request, stream).await?;
|
||||
Ok(Connection::new(
|
||||
|
@ -1192,9 +1227,7 @@ impl Client {
|
|||
.sink_map_err(|error| anyhow!(error)),
|
||||
))
|
||||
}
|
||||
"http" => {
|
||||
rpc_url.set_scheme("ws").unwrap();
|
||||
let request = request.uri(rpc_url.as_str()).body(())?;
|
||||
Http => {
|
||||
let (stream, _) = async_tungstenite::client_async(request, stream).await?;
|
||||
Ok(Connection::new(
|
||||
stream
|
||||
|
@ -1202,7 +1235,6 @@ impl Client {
|
|||
.sink_map_err(|error| anyhow!(error)),
|
||||
))
|
||||
}
|
||||
_ => Err(anyhow!("invalid rpc url: {}", rpc_url))?,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue