summaryrefslogtreecommitdiffstats
path: root/vendor/gix-protocol/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /vendor/gix-protocol/src
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-protocol/src')
-rw-r--r--vendor/gix-protocol/src/fetch/arguments/async_io.rs10
-rw-r--r--vendor/gix-protocol/src/fetch/arguments/blocking_io.rs10
-rw-r--r--vendor/gix-protocol/src/fetch/arguments/mod.rs6
-rw-r--r--vendor/gix-protocol/src/fetch/response/async_io.rs15
-rw-r--r--vendor/gix-protocol/src/fetch/response/blocking_io.rs19
-rw-r--r--vendor/gix-protocol/src/fetch/tests.rs11
-rw-r--r--vendor/gix-protocol/src/fetch_fn.rs26
-rw-r--r--vendor/gix-protocol/src/handshake/function.rs1
-rw-r--r--vendor/gix-protocol/src/lib.rs6
-rw-r--r--vendor/gix-protocol/src/ls_refs.rs6
-rw-r--r--vendor/gix-protocol/src/remote_progress.rs13
-rw-r--r--vendor/gix-protocol/src/util.rs3
12 files changed, 88 insertions, 38 deletions
diff --git a/vendor/gix-protocol/src/fetch/arguments/async_io.rs b/vendor/gix-protocol/src/fetch/arguments/async_io.rs
index fc876d02c..e92d8faab 100644
--- a/vendor/gix-protocol/src/fetch/arguments/async_io.rs
+++ b/vendor/gix-protocol/src/fetch/arguments/async_io.rs
@@ -9,7 +9,7 @@ impl Arguments {
&mut self,
transport: &'a mut T,
add_done_argument: bool,
- ) -> Result<Box<dyn client::ExtendedBufRead + Unpin + 'a>, client::Error> {
+ ) -> Result<Box<dyn client::ExtendedBufRead<'a> + Unpin + 'a>, client::Error> {
if self.haves.is_empty() {
assert!(add_done_argument, "If there are no haves, is_done must be true.");
}
@@ -19,8 +19,11 @@ impl Arguments {
transport.connection_persists_across_multiple_requests(),
add_done_argument,
)?;
- let mut line_writer =
- transport.request(client::WriteMode::OneLfTerminatedLinePerWriteCall, on_into_read)?;
+ let mut line_writer = transport.request(
+ client::WriteMode::OneLfTerminatedLinePerWriteCall,
+ on_into_read,
+ self.trace,
+ )?;
let had_args = !self.args.is_empty();
for arg in self.args.drain(..) {
line_writer.write_all(&arg).await?;
@@ -47,6 +50,7 @@ impl Arguments {
Command::Fetch.as_str(),
self.features.iter().filter(|(_, v)| v.is_some()).cloned(),
Some(std::mem::replace(&mut self.args, retained_state).into_iter()),
+ self.trace,
)
.await
}
diff --git a/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs b/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs
index c946d46e1..0852f5a88 100644
--- a/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs
+++ b/vendor/gix-protocol/src/fetch/arguments/blocking_io.rs
@@ -10,7 +10,7 @@ impl Arguments {
&mut self,
transport: &'a mut T,
add_done_argument: bool,
- ) -> Result<Box<dyn client::ExtendedBufRead + Unpin + 'a>, client::Error> {
+ ) -> Result<Box<dyn client::ExtendedBufRead<'a> + Unpin + 'a>, client::Error> {
if self.haves.is_empty() {
assert!(add_done_argument, "If there are no haves, is_done must be true.");
}
@@ -20,8 +20,11 @@ impl Arguments {
transport.connection_persists_across_multiple_requests(),
add_done_argument,
)?;
- let mut line_writer =
- transport.request(client::WriteMode::OneLfTerminatedLinePerWriteCall, on_into_read)?;
+ let mut line_writer = transport.request(
+ client::WriteMode::OneLfTerminatedLinePerWriteCall,
+ on_into_read,
+ self.trace,
+ )?;
let had_args = !self.args.is_empty();
for arg in self.args.drain(..) {
line_writer.write_all(&arg)?;
@@ -47,6 +50,7 @@ impl Arguments {
Command::Fetch.as_str(),
self.features.iter().filter(|(_, v)| v.is_some()).cloned(),
Some(std::mem::replace(&mut self.args, retained_state).into_iter()),
+ self.trace,
)
}
}
diff --git a/vendor/gix-protocol/src/fetch/arguments/mod.rs b/vendor/gix-protocol/src/fetch/arguments/mod.rs
index 50145bb15..8ad391d5f 100644
--- a/vendor/gix-protocol/src/fetch/arguments/mod.rs
+++ b/vendor/gix-protocol/src/fetch/arguments/mod.rs
@@ -23,6 +23,8 @@ pub struct Arguments {
features_for_first_want: Option<Vec<String>>,
#[cfg(any(feature = "async-client", feature = "blocking-client"))]
version: gix_transport::Protocol,
+
+ trace: bool,
}
impl Arguments {
@@ -194,8 +196,9 @@ impl Arguments {
}
/// Create a new instance to help setting up arguments to send to the server as part of a `fetch` operation
/// for which `features` are the available and configured features to use.
+ /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
#[cfg(any(feature = "async-client", feature = "blocking-client"))]
- pub fn new(version: gix_transport::Protocol, features: Vec<crate::command::Feature>) -> Self {
+ pub fn new(version: gix_transport::Protocol, features: Vec<crate::command::Feature>, trace: bool) -> Self {
use crate::Command;
let has = |name: &str| features.iter().any(|f| f.0 == name);
let filter = has("filter");
@@ -242,6 +245,7 @@ impl Arguments {
ref_in_want,
deepen_since,
features_for_first_want,
+ trace,
}
}
}
diff --git a/vendor/gix-protocol/src/fetch/response/async_io.rs b/vendor/gix-protocol/src/fetch/response/async_io.rs
index c510a1ad4..547881331 100644
--- a/vendor/gix-protocol/src/fetch/response/async_io.rs
+++ b/vendor/gix-protocol/src/fetch/response/async_io.rs
@@ -10,7 +10,7 @@ use crate::fetch::{
async fn parse_v2_section<T>(
line: &mut String,
- reader: &mut (impl client::ExtendedBufRead + Unpin),
+ reader: &mut (impl client::ExtendedBufRead<'_> + Unpin),
res: &mut Vec<T>,
parse: impl Fn(&str) -> Result<T, response::Error>,
) -> Result<bool, response::Error> {
@@ -37,10 +37,16 @@ impl Response {
/// and if `true` we will keep parsing until we get a pack as the client already signalled to the server that it's done.
/// This way of doing things allows us to exploit knowledge about more recent versions of the protocol, which keeps code easier
/// and more localized without having to support all the cruft that there is.
+ ///
+ /// `wants_to_negotiate` should be `false` for clones which is when we don't have sent any haves. The reason for this flag to exist
+ /// is to predict how to parse V1 output only, and neither `client_expects_pack` nor `wants_to_negotiate` are relevant for V2.
+ /// This ugliness is in place to avoid having to resort to an [an even more complex ugliness](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L583-L594)
+ /// that `git` has to use to predict how many acks are supposed to be read. We also genuinely hope that this covers it all….
pub async fn from_line_reader(
version: Protocol,
- reader: &mut (impl client::ExtendedBufRead + Unpin),
+ reader: &mut (impl client::ExtendedBufRead<'_> + Unpin),
client_expects_pack: bool,
+ wants_to_negotiate: bool,
) -> Result<Response, response::Error> {
match version {
Protocol::V0 | Protocol::V1 => {
@@ -89,7 +95,10 @@ impl Response {
);
// When the server sends ready, we know there is going to be a pack so no need to stop early.
saw_ready |= matches!(acks.last(), Some(Acknowledgement::Ready));
- if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack && !saw_ready) {
+ if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack || !saw_ready) {
+ if !wants_to_negotiate {
+ continue;
+ }
break 'lines false;
}
};
diff --git a/vendor/gix-protocol/src/fetch/response/blocking_io.rs b/vendor/gix-protocol/src/fetch/response/blocking_io.rs
index 309f5a7c5..08460ae39 100644
--- a/vendor/gix-protocol/src/fetch/response/blocking_io.rs
+++ b/vendor/gix-protocol/src/fetch/response/blocking_io.rs
@@ -8,9 +8,9 @@ use crate::fetch::{
Response,
};
-fn parse_v2_section<T>(
+fn parse_v2_section<'a, T>(
line: &mut String,
- reader: &mut impl client::ExtendedBufRead,
+ reader: &mut impl client::ExtendedBufRead<'a>,
res: &mut Vec<T>,
parse: impl Fn(&str) -> Result<T, response::Error>,
) -> Result<bool, response::Error> {
@@ -37,10 +37,16 @@ impl Response {
/// and if `true` we will keep parsing until we get a pack as the client already signalled to the server that it's done.
/// This way of doing things allows us to exploit knowledge about more recent versions of the protocol, which keeps code easier
/// and more localized without having to support all the cruft that there is.
- pub fn from_line_reader(
+ ///
+ /// `wants_to_negotiate` should be `false` for clones which is when we don't have sent any haves. The reason for this flag to exist
+ /// is to predict how to parse V1 output only, and neither `client_expects_pack` nor `wants_to_negotiate` are relevant for V2.
+ /// This ugliness is in place to avoid having to resort to an [an even more complex ugliness](https://github.com/git/git/blob/9e49351c3060e1fa6e0d2de64505b7becf157f28/fetch-pack.c#L583-L594)
+ /// that `git` has to use to predict how many acks are supposed to be read. We also genuinely hope that this covers it all….
+ pub fn from_line_reader<'a>(
version: Protocol,
- reader: &mut impl client::ExtendedBufRead,
+ reader: &mut impl client::ExtendedBufRead<'a>,
client_expects_pack: bool,
+ wants_to_negotiate: bool,
) -> Result<Response, response::Error> {
match version {
Protocol::V0 | Protocol::V1 => {
@@ -85,7 +91,10 @@ impl Response {
assert_ne!(reader.readline_str(&mut line)?, 0, "consuming a peeked line works");
// When the server sends ready, we know there is going to be a pack so no need to stop early.
saw_ready |= matches!(acks.last(), Some(Acknowledgement::Ready));
- if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack && !saw_ready) {
+ if let Some(Acknowledgement::Nak) = acks.last().filter(|_| !client_expects_pack || !saw_ready) {
+ if !wants_to_negotiate {
+ continue;
+ }
break 'lines false;
}
};
diff --git a/vendor/gix-protocol/src/fetch/tests.rs b/vendor/gix-protocol/src/fetch/tests.rs
index 93cf6e8db..e3c46f57e 100644
--- a/vendor/gix-protocol/src/fetch/tests.rs
+++ b/vendor/gix-protocol/src/fetch/tests.rs
@@ -6,11 +6,11 @@ mod arguments {
use crate::fetch;
fn arguments_v1(features: impl IntoIterator<Item = &'static str>) -> fetch::Arguments {
- fetch::Arguments::new(Protocol::V1, features.into_iter().map(|n| (n, None)).collect())
+ fetch::Arguments::new(Protocol::V1, features.into_iter().map(|n| (n, None)).collect(), false)
}
fn arguments_v2(features: impl IntoIterator<Item = &'static str>) -> fetch::Arguments {
- fetch::Arguments::new(Protocol::V2, features.into_iter().map(|n| (n, None)).collect())
+ fetch::Arguments::new(Protocol::V2, features.into_iter().map(|n| (n, None)).collect(), false)
}
struct Transport<T> {
@@ -40,8 +40,9 @@ mod arguments {
&mut self,
write_mode: WriteMode,
on_into_read: MessageKind,
+ trace: bool,
) -> Result<RequestWriter<'_>, Error> {
- self.inner.request(write_mode, on_into_read)
+ self.inner.request(write_mode, on_into_read, trace)
}
fn to_url(&self) -> Cow<'_, BStr> {
@@ -97,8 +98,9 @@ mod arguments {
&mut self,
write_mode: WriteMode,
on_into_read: MessageKind,
+ trace: bool,
) -> Result<RequestWriter<'_>, Error> {
- self.inner.request(write_mode, on_into_read)
+ self.inner.request(write_mode, on_into_read, trace)
}
fn to_url(&self) -> Cow<'_, BStr> {
@@ -145,6 +147,7 @@ mod arguments {
b"does/not/matter".as_bstr().to_owned(),
None::<(&str, _)>,
gix_transport::client::git::ConnectMode::Process, // avoid header to be sent
+ false,
),
stateful,
}
diff --git a/vendor/gix-protocol/src/fetch_fn.rs b/vendor/gix-protocol/src/fetch_fn.rs
index b350976ef..73c9a753a 100644
--- a/vendor/gix-protocol/src/fetch_fn.rs
+++ b/vendor/gix-protocol/src/fetch_fn.rs
@@ -41,8 +41,13 @@ pub enum FetchConnection {
/// if the server indicates 'permission denied'. Note that not all transport support authentication or authorization.
/// * `progress` is used to emit progress messages.
/// * `name` is the name of the git client to present as `agent`, like `"my-app (v2.0)"`".
+/// * If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
///
/// _Note_ that depending on the `delegate`, the actual action performed can be `ls-refs`, `clone` or `fetch`.
+///
+/// # WARNING - Do not use!
+///
+/// As it will hang when having multiple negotiation rounds.
#[allow(clippy::result_large_err)]
#[maybe_async]
// TODO: remove this without losing test coverage - we have the same but better in `gix` and it's
@@ -54,6 +59,7 @@ pub async fn fetch<F, D, T, P>(
mut progress: P,
fetch_mode: FetchConnection,
agent: impl Into<String>,
+ trace: bool,
) -> Result<(), Error>
where
F: FnMut(credentials::helper::Action) -> credentials::protocol::Result,
@@ -87,6 +93,7 @@ where
res
},
&mut progress,
+ trace,
)
.await?
}
@@ -99,7 +106,7 @@ where
return if matches!(protocol_version, gix_transport::Protocol::V1)
|| matches!(fetch_mode, FetchConnection::TerminateOnSuccessfulCompletion)
{
- indicate_end_of_interaction(transport).await.map_err(Into::into)
+ indicate_end_of_interaction(transport, trace).await.map_err(Into::into)
} else {
Ok(())
};
@@ -108,7 +115,7 @@ where
fetch.validate_argument_prefixes_or_panic(protocol_version, &capabilities, &[], &fetch_features);
}
Err(err) => {
- indicate_end_of_interaction(transport).await?;
+ indicate_end_of_interaction(transport, trace).await?;
return Err(err.into());
}
}
@@ -116,7 +123,7 @@ where
Response::check_required_features(protocol_version, &fetch_features)?;
let sideband_all = fetch_features.iter().any(|(n, _)| *n == "sideband-all");
fetch_features.push(("agent", Some(Cow::Owned(agent))));
- let mut arguments = Arguments::new(protocol_version, fetch_features);
+ let mut arguments = Arguments::new(protocol_version, fetch_features, trace);
let mut previous_response = None::<Response>;
let mut round = 1;
'negotiation: loop {
@@ -131,7 +138,8 @@ where
let response = Response::from_line_reader(
protocol_version,
&mut reader,
- true, /* hack, telling us we don't want this delegate approach anymore */
+ true, /* hack, telling us we don't want this delegate approach anymore */
+ false, /* just as much of a hack which causes us to expect a pack immediately */
)
.await?;
previous_response = if response.has_pack() {
@@ -152,13 +160,15 @@ where
if matches!(protocol_version, gix_transport::Protocol::V2)
&& matches!(fetch_mode, FetchConnection::TerminateOnSuccessfulCompletion)
{
- indicate_end_of_interaction(transport).await?;
+ indicate_end_of_interaction(transport, trace).await?;
}
Ok(())
}
-fn setup_remote_progress<P>(progress: &mut P, reader: &mut Box<dyn gix_transport::client::ExtendedBufRead + Unpin + '_>)
-where
+fn setup_remote_progress<P>(
+ progress: &mut P,
+ reader: &mut Box<dyn gix_transport::client::ExtendedBufRead<'_> + Unpin + '_>,
+) where
P: NestedProgress,
P::SubProgress: 'static,
{
@@ -168,5 +178,5 @@ where
crate::RemoteProgress::translate_to_progress(is_err, data, &mut remote_progress);
gix_transport::packetline::read::ProgressAction::Continue
}
- }) as gix_transport::client::HandleProgress));
+ }) as gix_transport::client::HandleProgress<'_>));
}
diff --git a/vendor/gix-protocol/src/handshake/function.rs b/vendor/gix-protocol/src/handshake/function.rs
index 9e75c18d0..daf405e10 100644
--- a/vendor/gix-protocol/src/handshake/function.rs
+++ b/vendor/gix-protocol/src/handshake/function.rs
@@ -22,6 +22,7 @@ where
AuthFn: FnMut(credentials::helper::Action) -> credentials::protocol::Result,
T: client::Transport,
{
+ let _span = gix_features::trace::detail!("gix_protocol::handshake()", service = ?service, extra_parameters = ?extra_parameters);
let (server_protocol_version, refs, capabilities) = {
progress.init(None, progress::steps());
progress.set_name("handshake".into());
diff --git a/vendor/gix-protocol/src/lib.rs b/vendor/gix-protocol/src/lib.rs
index 7f7355711..6b4ee69d2 100644
--- a/vendor/gix-protocol/src/lib.rs
+++ b/vendor/gix-protocol/src/lib.rs
@@ -4,10 +4,10 @@
//! the actual client implementation.
//! ## Feature Flags
#![cfg_attr(
- feature = "document-features",
- cfg_attr(doc, doc = ::document_features::document_features!())
+ all(doc, feature = "document-features"),
+ doc = ::document_features::document_features!()
)]
-#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
+#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]
#![deny(missing_docs, rust_2018_idioms, unsafe_code)]
/// A selector for V2 commands to invoke on the server for purpose of pre-invocation validation.
diff --git a/vendor/gix-protocol/src/ls_refs.rs b/vendor/gix-protocol/src/ls_refs.rs
index c5b71da93..052576402 100644
--- a/vendor/gix-protocol/src/ls_refs.rs
+++ b/vendor/gix-protocol/src/ls_refs.rs
@@ -54,6 +54,7 @@ pub(crate) mod function {
/// Invoke an ls-refs V2 command on `transport`, which requires a prior handshake that yielded
/// server `capabilities`. `prepare_ls_refs(capabilities, arguments, features)` can be used to alter the _ls-refs_. `progress` is used to provide feedback.
/// Note that `prepare_ls_refs()` is expected to add the `(agent, Some(name))` to the list of `features`.
+ /// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
#[maybe_async]
pub async fn ls_refs(
mut transport: impl Transport,
@@ -64,7 +65,9 @@ pub(crate) mod function {
&mut Vec<(&str, Option<Cow<'static, str>>)>,
) -> std::io::Result<Action>,
progress: &mut impl Progress,
+ trace: bool,
) -> Result<Vec<Ref>, Error> {
+ let _span = gix_features::trace::detail!("gix_protocol::ls_refs()", capabilities = ?capabilities);
let ls_refs = Command::LsRefs;
let mut ls_features = ls_refs.default_features(gix_transport::Protocol::V2, capabilities);
let mut ls_args = ls_refs.initial_arguments(&ls_features);
@@ -96,12 +99,13 @@ pub(crate) mod function {
} else {
Some(ls_args.into_iter())
},
+ trace,
)
.await?;
from_v2_refs(&mut remote_refs).await?
}
Err(err) => {
- indicate_end_of_interaction(transport).await?;
+ indicate_end_of_interaction(transport, trace).await?;
return Err(err.into());
}
};
diff --git a/vendor/gix-protocol/src/remote_progress.rs b/vendor/gix-protocol/src/remote_progress.rs
index b516a06bf..af12cd35e 100644
--- a/vendor/gix-protocol/src/remote_progress.rs
+++ b/vendor/gix-protocol/src/remote_progress.rs
@@ -2,10 +2,9 @@ use std::convert::TryFrom;
use bstr::ByteSlice;
use winnow::{
- combinator::opt,
- combinator::{preceded, terminated},
+ combinator::{opt, preceded, terminated},
prelude::*,
- token::{tag, take_till0, take_till1},
+ token::{tag, take_till},
};
/// The information usually found in remote progress messages as sent by a git server during
@@ -75,7 +74,7 @@ impl<'a> RemoteProgress<'a> {
}
fn parse_number(i: &mut &[u8]) -> PResult<usize, ()> {
- take_till0(|c: u8| !c.is_ascii_digit())
+ take_till(0.., |c: u8| !c.is_ascii_digit())
.try_map(btoi::btoi)
.parse_next(i)
}
@@ -83,7 +82,7 @@ fn parse_number(i: &mut &[u8]) -> PResult<usize, ()> {
fn next_optional_percentage(i: &mut &[u8]) -> PResult<Option<u32>, ()> {
opt(terminated(
preceded(
- take_till0(|c: u8| c.is_ascii_digit()),
+ take_till(0.., |c: u8| c.is_ascii_digit()),
parse_number.try_map(u32::try_from),
),
tag(b"%"),
@@ -92,11 +91,11 @@ fn next_optional_percentage(i: &mut &[u8]) -> PResult<Option<u32>, ()> {
}
fn next_optional_number(i: &mut &[u8]) -> PResult<Option<usize>, ()> {
- opt(preceded(take_till0(|c: u8| c.is_ascii_digit()), parse_number)).parse_next(i)
+ opt(preceded(take_till(0.., |c: u8| c.is_ascii_digit()), parse_number)).parse_next(i)
}
fn parse_progress<'i>(line: &mut &'i [u8]) -> PResult<RemoteProgress<'i>, ()> {
- let action = take_till1(|c| c == b':').parse_next(line)?;
+ let action = take_till(1.., |c| c == b':').parse_next(line)?;
let percent = next_optional_percentage.parse_next(line)?;
let step = next_optional_number.parse_next(line)?;
let max = next_optional_number.parse_next(line)?;
diff --git a/vendor/gix-protocol/src/util.rs b/vendor/gix-protocol/src/util.rs
index a790aebd5..09636d752 100644
--- a/vendor/gix-protocol/src/util.rs
+++ b/vendor/gix-protocol/src/util.rs
@@ -8,10 +8,12 @@ pub fn agent(name: impl Into<String>) -> String {
}
/// Send a message to indicate the remote side that there is nothing more to expect from us, indicating a graceful shutdown.
+/// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
#[maybe_async::maybe_async]
pub async fn indicate_end_of_interaction(
mut transport: impl gix_transport::client::Transport,
+ trace: bool,
) -> Result<(), gix_transport::client::Error> {
// An empty request marks the (early) end of the interaction. Only relevant in stateful transports though.
if transport.connection_persists_across_multiple_requests() {
@@ -19,6 +21,7 @@ pub async fn indicate_end_of_interaction(
.request(
gix_transport::client::WriteMode::Binary,
gix_transport::client::MessageKind::Flush,
+ trace,
)?
.into_read()
.await?;