Establish WebSocket connection to Cloud (#35734)

This PR adds a new WebSocket connection to Cloud.

This connection will be used to push down notifications from the server
to the client.

Release Notes:

- N/A

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
Richard Feldman 2025-08-06 21:28:41 -04:00 committed by Marshall Bowers
parent 6ac4a57fce
commit c1056991e3
8 changed files with 242 additions and 19 deletions

50
Cargo.lock generated
View file

@ -1366,7 +1366,7 @@ dependencies = [
"anyhow",
"arrayvec",
"log",
"nom",
"nom 7.1.3",
"num-rational",
"v_frame",
]
@ -2740,7 +2740,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
"nom 7.1.3",
]
[[package]]
@ -3026,10 +3026,13 @@ dependencies = [
"anyhow",
"cloud_api_types",
"futures 0.3.31",
"gpui",
"gpui_tokio",
"http_client",
"parking_lot",
"serde_json",
"workspace-hack",
"yawc",
]
[[package]]
@ -10477,6 +10480,15 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nom"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
dependencies = [
"memchr",
]
[[package]]
name = "noop_proc_macro"
version = "0.3.0"
@ -15200,7 +15212,7 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790"
dependencies = [
"nom",
"nom 7.1.3",
"unicode_categories",
]
@ -19747,7 +19759,9 @@ dependencies = [
"heck 0.4.1",
"hmac",
"hyper 0.14.32",
"hyper 1.6.0",
"hyper-rustls 0.27.5",
"hyper-util",
"idna",
"indexmap",
"inout",
@ -19768,7 +19782,7 @@ dependencies = [
"mio",
"naga",
"nix 0.29.0",
"nom",
"nom 7.1.3",
"num-bigint",
"num-bigint-dig",
"num-integer",
@ -20103,6 +20117,34 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
[[package]]
name = "yawc"
version = "0.2.4"
source = "git+https://github.com/deviant-forks/yawc?rev=1899688f3e69ace4545aceb97b2a13881cf26142#1899688f3e69ace4545aceb97b2a13881cf26142"
dependencies = [
"base64 0.22.1",
"bytes 1.10.1",
"flate2",
"futures 0.3.31",
"http-body-util",
"hyper 1.6.0",
"hyper-util",
"js-sys",
"nom 8.0.0",
"pin-project",
"rand 0.8.5",
"sha1",
"thiserror 1.0.69",
"tokio",
"tokio-rustls 0.26.2",
"tokio-util",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
]
[[package]]
name = "yazi"
version = "0.2.1"

View file

@ -651,6 +651,9 @@ which = "6.0.0"
windows-core = "0.61"
wit-component = "0.221"
workspace-hack = "0.1.0"
# We can switch back to the published version once https://github.com/infinitefield/yawc/pull/16 is merged and a new
# version is released.
yawc = { git = "https://github.com/deviant-forks/yawc", rev = "1899688f3e69ace4545aceb97b2a13881cf26142" }
zstd = "0.11"
[workspace.dependencies.async-stripe]

View file

@ -14,6 +14,7 @@ use async_tungstenite::tungstenite::{
};
use clock::SystemClock;
use cloud_api_client::CloudApiClient;
use cloud_api_client::websocket_protocol::MessageToClient;
use credentials_provider::CredentialsProvider;
use futures::{
AsyncReadExt, FutureExt, SinkExt, Stream, StreamExt, TryFutureExt as _, TryStreamExt,
@ -933,6 +934,32 @@ impl Client {
}
}
/// Establishes a WebSocket connection with Cloud for receiving updates from the server.
async fn connect_to_cloud(self: &Arc<Self>, cx: &AsyncApp) -> Result<()> {
let connect_task = cx.update({
let cloud_client = self.cloud_client.clone();
move |cx| cloud_client.connect(cx)
})??;
let connection = connect_task.await?;
let (mut messages, task) = cx.update(|cx| connection.spawn(cx))?;
task.detach();
cx.spawn({
let this = self.clone();
async move |cx| {
while let Some(message) = messages.next().await {
if let Some(message) = message.log_err() {
this.handle_message_to_client(message, cx);
}
}
}
})
.detach();
Ok(())
}
/// Performs a sign-in and also connects to Collab.
///
/// This is called in places where we *don't* need to connect in the future. We will replace these calls with calls
@ -944,6 +971,8 @@ impl Client {
) -> Result<()> {
let credentials = self.sign_in(try_provider, cx).await?;
self.connect_to_cloud(cx).await.log_err();
let connect_result = match self.connect_with_credentials(credentials, cx).await {
ConnectionResult::Timeout => Err(anyhow!("connection timed out")),
ConnectionResult::ConnectionReset => Err(anyhow!("connection reset")),
@ -1622,6 +1651,12 @@ impl Client {
}
}
fn handle_message_to_client(self: &Arc<Client>, message: MessageToClient, _cx: &AsyncApp) {
match message {
MessageToClient::UserUpdated => {}
}
}
pub fn telemetry(&self) -> &Arc<Telemetry> {
&self.telemetry
}

View file

@ -15,7 +15,10 @@ path = "src/cloud_api_client.rs"
anyhow.workspace = true
cloud_api_types.workspace = true
futures.workspace = true
gpui.workspace = true
gpui_tokio.workspace = true
http_client.workspace = true
parking_lot.workspace = true
serde_json.workspace = true
workspace-hack.workspace = true
yawc.workspace = true

View file

@ -1,11 +1,19 @@
mod websocket;
use std::sync::Arc;
use anyhow::{Context, Result, anyhow};
use cloud_api_types::websocket_protocol::{PROTOCOL_VERSION, PROTOCOL_VERSION_HEADER_NAME};
pub use cloud_api_types::*;
use futures::AsyncReadExt as _;
use gpui::{App, Task};
use gpui_tokio::Tokio;
use http_client::http::request;
use http_client::{AsyncBody, HttpClientWithUrl, Method, Request, StatusCode};
use parking_lot::RwLock;
use yawc::WebSocket;
use crate::websocket::Connection;
struct Credentials {
user_id: u32,
@ -78,6 +86,41 @@ impl CloudApiClient {
Ok(serde_json::from_str(&body)?)
}
pub fn connect(&self, cx: &App) -> Result<Task<Result<Connection>>> {
let mut connect_url = self
.http_client
.build_zed_cloud_url("/client/users/connect", &[])?;
connect_url
.set_scheme(match connect_url.scheme() {
"https" => "wss",
"http" => "ws",
scheme => Err(anyhow!("invalid URL scheme: {scheme}"))?,
})
.map_err(|_| anyhow!("failed to set URL scheme"))?;
let credentials = self.credentials.read();
let credentials = credentials.as_ref().context("no credentials provided")?;
let authorization_header = format!("{} {}", credentials.user_id, credentials.access_token);
Ok(cx.spawn(async move |cx| {
let handle = cx
.update(|cx| Tokio::handle(cx))
.ok()
.context("failed to get Tokio handle")?;
let _guard = handle.enter();
let ws = WebSocket::connect(connect_url)
.with_request(
request::Builder::new()
.header("Authorization", authorization_header)
.header(PROTOCOL_VERSION_HEADER_NAME, PROTOCOL_VERSION.to_string()),
)
.await?;
Ok(Connection::new(ws))
}))
}
pub async fn accept_terms_of_service(&self) -> Result<AcceptTermsOfServiceResponse> {
let request = self.build_request(
Request::builder().method(Method::POST).uri(

View file

@ -0,0 +1,73 @@
use std::pin::Pin;
use std::time::Duration;
use anyhow::Result;
use cloud_api_types::websocket_protocol::MessageToClient;
use futures::channel::mpsc::unbounded;
use futures::stream::{SplitSink, SplitStream};
use futures::{FutureExt as _, SinkExt as _, Stream, StreamExt as _, TryStreamExt as _, pin_mut};
use gpui::{App, BackgroundExecutor, Task};
use yawc::WebSocket;
use yawc::frame::{FrameView, OpCode};
const KEEPALIVE_INTERVAL: Duration = Duration::from_secs(1);
pub type MessageStream = Pin<Box<dyn Stream<Item = Result<MessageToClient>>>>;
pub struct Connection {
tx: SplitSink<WebSocket, FrameView>,
rx: SplitStream<WebSocket>,
}
impl Connection {
pub fn new(ws: WebSocket) -> Self {
let (tx, rx) = ws.split();
Self { tx, rx }
}
pub fn spawn(self, cx: &App) -> (MessageStream, Task<()>) {
let (mut tx, rx) = (self.tx, self.rx);
let (message_tx, message_rx) = unbounded();
let handle_io = |executor: BackgroundExecutor| async move {
// Send messages on this frequency so the connection isn't closed.
let keepalive_timer = executor.timer(KEEPALIVE_INTERVAL).fuse();
futures::pin_mut!(keepalive_timer);
let rx = rx.fuse();
pin_mut!(rx);
loop {
futures::select_biased! {
_ = keepalive_timer => {
let _ = tx.send(FrameView::ping(Vec::new())).await;
keepalive_timer.set(executor.timer(KEEPALIVE_INTERVAL).fuse());
}
frame = rx.next() => {
let Some(frame) = frame else {
break;
};
match frame.opcode {
OpCode::Binary => {
let message_result = MessageToClient::deserialize(&frame.payload);
message_tx.unbounded_send(message_result).ok();
}
OpCode::Close => {
break;
}
_ => {}
}
}
}
}
};
let task = cx.spawn(async move |cx| handle_io(cx.background_executor().clone()).await);
(message_rx.into_stream().boxed(), task)
}
}

View file

@ -8,7 +8,7 @@ pub const PROTOCOL_VERSION: u32 = 0;
pub const PROTOCOL_VERSION_HEADER_NAME: &str = "x-zed-protocol-version";
/// A message from Cloud to the Zed client.
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub enum MessageToClient {
/// The user was updated and should be refreshed.
UserUpdated,

View file

@ -70,7 +70,7 @@ handlebars = { version = "4", features = ["rust-embed"] }
hashbrown-3575ec1268b04181 = { package = "hashbrown", version = "0.15", features = ["serde"] }
hashbrown-582f2526e08bb6a0 = { package = "hashbrown", version = "0.14", features = ["raw"] }
hmac = { version = "0.12", default-features = false, features = ["reset"] }
hyper = { version = "0.14", features = ["client", "http1", "http2", "runtime", "server", "stream"] }
hyper-582f2526e08bb6a0 = { package = "hyper", version = "0.14", features = ["client", "http1", "http2", "runtime", "server", "stream"] }
idna = { version = "1" }
indexmap = { version = "2", features = ["serde"] }
jiff = { version = "0.2" }
@ -199,7 +199,7 @@ hashbrown-3575ec1268b04181 = { package = "hashbrown", version = "0.15", features
hashbrown-582f2526e08bb6a0 = { package = "hashbrown", version = "0.14", features = ["raw"] }
heck = { version = "0.4", features = ["unicode"] }
hmac = { version = "0.12", default-features = false, features = ["reset"] }
hyper = { version = "0.14", features = ["client", "http1", "http2", "runtime", "server", "stream"] }
hyper-582f2526e08bb6a0 = { package = "hyper", version = "0.14", features = ["client", "http1", "http2", "runtime", "server", "stream"] }
idna = { version = "1" }
indexmap = { version = "2", features = ["serde"] }
itertools-594e8ee84c453af0 = { package = "itertools", version = "0.13" }
@ -287,7 +287,9 @@ core-foundation-sys = { version = "0.8" }
foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "25", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal", "user"] }
@ -303,7 +305,7 @@ scopeguard = { version = "1" }
security-framework = { version = "3", features = ["OSX_10_14"] }
security-framework-sys = { version = "2", features = ["OSX_10_14"] }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
tower = { version = "0.5", default-features = false, features = ["timeout", "util"] }
@ -315,7 +317,9 @@ core-foundation-sys = { version = "0.8" }
foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "25", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal", "user"] }
@ -332,7 +336,7 @@ scopeguard = { version = "1" }
security-framework = { version = "3", features = ["OSX_10_14"] }
security-framework-sys = { version = "2", features = ["OSX_10_14"] }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
tower = { version = "0.5", default-features = false, features = ["timeout", "util"] }
@ -344,7 +348,9 @@ core-foundation-sys = { version = "0.8" }
foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "25", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal", "user"] }
@ -360,7 +366,7 @@ scopeguard = { version = "1" }
security-framework = { version = "3", features = ["OSX_10_14"] }
security-framework-sys = { version = "2", features = ["OSX_10_14"] }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
tower = { version = "0.5", default-features = false, features = ["timeout", "util"] }
@ -372,7 +378,9 @@ core-foundation-sys = { version = "0.8" }
foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "25", features = ["msl-out", "wgsl-in"] }
nix = { version = "0.29", features = ["fs", "pthread", "signal", "user"] }
@ -389,7 +397,7 @@ scopeguard = { version = "1" }
security-framework = { version = "3", features = ["OSX_10_14"] }
security-framework-sys = { version = "2", features = ["OSX_10_14"] }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
tower = { version = "0.5", default-features = false, features = ["timeout", "util"] }
@ -407,7 +415,9 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] }
@ -426,7 +436,7 @@ rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs",
scopeguard = { version = "1" }
syn-f595c2ba2a3f28df = { package = "syn", version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
toml_datetime = { version = "0.6", default-features = false, features = ["serde"] }
@ -447,7 +457,9 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] }
@ -464,7 +476,7 @@ rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["ev
rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", "net", "process", "termios", "time"] }
scopeguard = { version = "1" }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
toml_datetime = { version = "0.6", default-features = false, features = ["serde"] }
@ -485,7 +497,9 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] }
@ -504,7 +518,7 @@ rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs",
scopeguard = { version = "1" }
syn-f595c2ba2a3f28df = { package = "syn", version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
toml_datetime = { version = "0.6", default-features = false, features = ["serde"] }
@ -525,7 +539,9 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] }
@ -542,7 +558,7 @@ rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["ev
rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", "net", "process", "termios", "time"] }
scopeguard = { version = "1" }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
toml_datetime = { version = "0.6", default-features = false, features = ["serde"] }
@ -556,14 +572,16 @@ flume = { version = "0.11" }
foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "25", features = ["spv-out", "wgsl-in"] }
ring = { version = "0.17", features = ["std"] }
rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] }
scopeguard = { version = "1" }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
tower = { version = "0.5", default-features = false, features = ["timeout", "util"] }
@ -580,7 +598,9 @@ flume = { version = "0.11" }
foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
naga = { version = "25", features = ["spv-out", "wgsl-in"] }
proc-macro2 = { version = "1", default-features = false, features = ["span-locations"] }
@ -588,7 +608,7 @@ ring = { version = "0.17", features = ["std"] }
rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] }
scopeguard = { version = "1" }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
tower = { version = "0.5", default-features = false, features = ["timeout", "util"] }
@ -612,7 +632,9 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] }
@ -631,7 +653,7 @@ rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs",
scopeguard = { version = "1" }
syn-f595c2ba2a3f28df = { package = "syn", version = "2", features = ["extra-traits", "fold", "full", "visit", "visit-mut"] }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
toml_datetime = { version = "0.6", default-features = false, features = ["serde"] }
@ -652,7 +674,9 @@ foldhash = { version = "0.1", default-features = false, features = ["std"] }
getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-features = false, features = ["std"] }
getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] }
hyper-dff4ba8e3ae991db = { package = "hyper", version = "1", features = ["client", "http1", "http2"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2"] }
inout = { version = "0.1", default-features = false, features = ["block-padding"] }
itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] }
@ -669,7 +693,7 @@ rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["ev
rustix-dff4ba8e3ae991db = { package = "rustix", version = "1", features = ["fs", "net", "process", "termios", "time"] }
scopeguard = { version = "1" }
sync_wrapper = { version = "1", default-features = false, features = ["futures"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["ring"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "ring"] }
tokio-socks = { version = "0.5", features = ["futures-io"] }
tokio-stream = { version = "0.1", features = ["fs"] }
toml_datetime = { version = "0.6", default-features = false, features = ["serde"] }