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::*;