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:
parent
6ac4a57fce
commit
c1056991e3
8 changed files with 242 additions and 19 deletions
50
Cargo.lock
generated
50
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
73
crates/cloud_api_client/src/websocket.rs
Normal file
73
crates/cloud_api_client/src/websocket.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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"] }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue