gix_protocol/util.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
/// The name of the `git` client in a format suitable for presentation to a `git` server, using `name` as user-defined portion of the value.
pub fn agent(name: impl Into<String>) -> String {
let mut name = name.into();
if !name.starts_with("git/") {
name.insert_str(0, "git/");
}
name
}
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
mod with_transport {
use gix_transport::client::Transport;
/// 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.
#[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() {
transport
.request(
gix_transport::client::WriteMode::Binary,
gix_transport::client::MessageKind::Flush,
trace,
)?
.into_read()
.await?;
}
Ok(())
}
/// A utility to automatically send a flush packet when the instance is dropped, assuring a graceful termination of any
/// interaction with the server.
pub struct SendFlushOnDrop<T>
where
T: Transport,
{
/// The actual transport instance.
pub inner: T,
/// If `true`, the packetline used to indicate the end of interaction will be traced using `gix-trace`.
trace_packetlines: bool,
/// If `true`, we should not send another flush packet.
flush_packet_sent: bool,
}
impl<T> SendFlushOnDrop<T>
where
T: Transport,
{
/// Create a new instance with `transport`, while optionally tracing packetlines with `trace_packetlines`.
pub fn new(transport: T, trace_packetlines: bool) -> Self {
Self {
inner: transport,
trace_packetlines,
flush_packet_sent: false,
}
}
/// Useful to explicitly invalidate the connection by sending a flush-packet.
/// This will happen exactly once, and it is not considered an error to call it multiple times.
///
/// For convenience, this is not consuming, but could be to assure the underlying transport isn't used anymore.
#[maybe_async::maybe_async]
pub async fn indicate_end_of_interaction(&mut self) -> Result<(), gix_transport::client::Error> {
if self.flush_packet_sent {
return Ok(());
}
self.flush_packet_sent = true;
indicate_end_of_interaction(&mut self.inner, self.trace_packetlines).await
}
}
impl<T> Drop for SendFlushOnDrop<T>
where
T: Transport,
{
fn drop(&mut self) {
#[cfg(feature = "async-client")]
{
// TODO: this should be an async drop once the feature is available.
// Right now we block the executor by forcing this communication, but that only
// happens if the user didn't actually try to receive a pack, which consumes the
// connection in an async context.
crate::futures_lite::future::block_on(self.indicate_end_of_interaction()).ok();
}
#[cfg(not(feature = "async-client"))]
{
self.indicate_end_of_interaction().ok();
}
}
}
}
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
pub use with_transport::*;