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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
use std::{
any::Any,
ops::{Deref, DerefMut},
};
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
use crate::client::{MessageKind, RequestWriter, WriteMode};
use crate::{client::Error, Protocol};
/// This trait represents all transport related functions that don't require any input/output to be done which helps
/// implementation to share more code across blocking and async programs.
pub trait TransportWithoutIO {
/// If the handshake or subsequent reads failed with [std::io::ErrorKind::PermissionDenied], use this method to
/// inform the transport layer about the identity to use for subsequent calls.
/// If authentication continues to fail even with an identity set, consider communicating this to the provider
/// of the identity in order to mark it as invalid. Otherwise the user might have difficulty updating obsolete
/// credentials.
/// Please note that most transport layers are unauthenticated and thus return [an error][Error::AuthenticationUnsupported] here.
fn set_identity(&mut self, _identity: git_sec::identity::Account) -> Result<(), Error> {
Err(Error::AuthenticationUnsupported)
}
/// Get a writer for sending data and obtaining the response. It can be configured in various ways
/// to support the task at hand.
/// `write_mode` determines how calls to the `write(…)` method are interpreted, and `on_into_read` determines
/// which message to write when the writer is turned into the response reader using [`into_read()`][RequestWriter::into_read()].
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error>;
/// Returns the canonical URL pointing to the destination of this transport.
/// Please note that local paths may not be represented correctly, as they will go through a potentially lossy
/// unicode conversion.
fn to_url(&self) -> String;
/// If the actually advertised server version is contained in the returned slice or empty, continue as normal,
/// assume the server's protocol version is desired or acceptable.
///
/// Otherwise, abort the fetch operation with an error to avoid continuing any interaction with the transport.
///
/// In V1 this means a potentially large list of advertised refs won't be read, instead the connection is ignored
/// leaving the server with a client who potentially unexpectedly terminated the connection.
///
/// Note that `transport.close()` is not called explicitly.
fn supported_protocol_versions(&self) -> &[Protocol] {
&[]
}
/// Returns true if the transport provides persistent connections across multiple requests, or false otherwise.
/// Not being persistent implies that certain information has to be resent on each 'turn'
/// of the fetch negotiation or that the end of interaction (i.e. no further request will be made) has to be indicated
/// to the server for most graceful termination of the connection.
fn connection_persists_across_multiple_requests(&self) -> bool;
/// Pass `config` can be cast and interpreted by the implementation, as documented separately.
///
/// The caller must know how that `config` data looks like for the intended implementation.
fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>;
}
// Would be nice if the box implementation could auto-forward to all implemented traits.
impl<T: TransportWithoutIO + ?Sized> TransportWithoutIO for Box<T> {
fn set_identity(&mut self, identity: git_sec::identity::Account) -> Result<(), Error> {
self.deref_mut().set_identity(identity)
}
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error> {
self.deref_mut().request(write_mode, on_into_read)
}
fn to_url(&self) -> String {
self.deref().to_url()
}
fn supported_protocol_versions(&self) -> &[Protocol] {
self.deref().supported_protocol_versions()
}
fn connection_persists_across_multiple_requests(&self) -> bool {
self.deref().connection_persists_across_multiple_requests()
}
fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
self.deref_mut().configure(config)
}
}
impl<T: TransportWithoutIO + ?Sized> TransportWithoutIO for &mut T {
fn set_identity(&mut self, identity: git_sec::identity::Account) -> Result<(), Error> {
self.deref_mut().set_identity(identity)
}
#[cfg(any(feature = "blocking-client", feature = "async-client"))]
fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error> {
self.deref_mut().request(write_mode, on_into_read)
}
fn to_url(&self) -> String {
self.deref().to_url()
}
fn supported_protocol_versions(&self) -> &[Protocol] {
self.deref().supported_protocol_versions()
}
fn connection_persists_across_multiple_requests(&self) -> bool {
self.deref().connection_persists_across_multiple_requests()
}
fn configure(&mut self, config: &dyn Any) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
self.deref_mut().configure(config)
}
}