Skip to main content

BannerTransport

Struct BannerTransport 

Source
pub struct BannerTransport { /* private fields */ }
Expand description

A GitTransport that writes one line describing the request it was given, then reads the client out. A test double.

It exists to prove a specific thing that a mock recording a call cannot: that the byte stream reached the seam, in both directions, after a real SSH handshake. The line it writes names the service and the repository, so a test asserting on it is asserting on applied output rather than on a flag someone set (measured, not asserted).

Implementations§

Source§

impl BannerTransport

Source

pub fn new() -> Self

A transport that has served nothing yet.

Source

pub fn settling_after_it_releases_the_stream() -> Self

The same transport, but it drops the stream and then waits before it returns.

This is the red half of the exit-status ordering guard, and it is worth spelling out why it is not a contrivance.

russh’s Channel::into_stream() wraps the channel in ChannelCloseOnDrop, whose Drop spawns a task that sends ChannelMsg::Close. A server that hands the transport such a stream and sends exit-status afterwards is in a race with that task: the ordinary path wins it on a current-thread runtime (the send takes an available permit without yielding) and loses it often enough on a multi-threaded one to make a successful push report failure (measured 2026-08-03, ssh exit 255 with a complete 244-byte advertisement).

A test on the ordinary path therefore proves nothing: it is green with the bug present. This mode gives the spawned task the scheduling it needs to run, which turns “usually” into “always” in the failing direction only. A server that closes nothing on drop is unaffected by any amount of waiting here, so a green under this transport is not a timing accident; it is the absence of the close.

Source

pub fn holding() -> (Self, TransportHold)

A transport whose transfers stay in flight until the returned TransportHold is released.

This is what makes the admission gate testable at all. A limit on concurrent transfers cannot be reached by a transport that finishes instantly: a hundred sequential clones never put two in flight together, and a test written against the ordinary banner would be asserting on scheduling luck. Here the first N transfers are genuinely simultaneous and stay that way until the test says otherwise.

The banner is written before the wait, so a client knows its transfer was admitted rather than merely accepted.

Source

pub fn refusing(message: impl Into<String>) -> Self

A transport that refuses without writing a byte, the way a transfer whose repository will not open does.

Distinct from BannerTransport::that_then_fails on the one axis that matters: that one writes its banner first, so the client has already had output and the framing is no longer the session’s to add to. This one has written nothing, which is the only condition under which the session may still hand the client a reason.

Source

pub fn that_then_fails(self) -> Self

…and then reports that the transfer failed.

The other direction of the same guard: a non-zero status has to survive the stream being released too. Without this, the exit-status assertions could all be satisfied by a server that hard-coded zero.

Source

pub fn served(&self) -> Vec<String>

Everything it has served, as "<verb> <ns>/<repo> <principal-id>".

Source

pub fn git_protocols(&self) -> Vec<Option<String>>

The GIT_PROTOCOL value that arrived with each served request, in the same order as BannerTransport::served.

Recorded separately from the banner so that a test can assert what the wire would negotiate with without changing the byte the existing end-to-end assertions read back over SSH.

Source

pub fn banner(request: &ServiceRequest) -> String

The line this transport writes for request.

Trait Implementations§

Source§

impl Debug for BannerTransport

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for BannerTransport

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl GitTransport for BannerTransport

Source§

fn serve<'a>( &'a self, request: ServiceRequest, stream: Box<dyn DuplexStream>, ) -> Pin<Box<dyn Future<Output = Result<(), TransportError>> + Send + 'a>>

Serve one request to completion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more