gix_protocol/
util.rs

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