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