///|
/// The exchange phase in which a failure occurred.
#derive(Eq, Debug)
#non_exhaustive
pub(all) enum Phase {
Pool
Dial
Write
Head
Body
Unknown
}
///|
/// Transport failure category, distinct from an HTTP status response.
#derive(Eq, Debug)
#non_exhaustive
pub(all) enum Kind {
Invalid
Connect
Timeout(ms~ : Int32)
Network
Protocol
Proxy(status~ : Int32)
TooLarge(limit~ : Int32)
TooManyRedirects(max~ : Int32)
RedirectRefused
Unreplayable
Closed
}
///|
/// A failure with its category, phase and diagnostic description.
#derive(Debug)
pub(all) suberror TransportError {
TransportError(kind~ : Kind, phase~ : Phase, detail~ : String)
}
///|
/// Whether the failure proves no request bytes reached the origin: it
/// happened in phase Pool or Dial (CONTRACT.md, Backend rule 2), or it is a
/// connect failure or a proxy's refusal, which precede the request. Whether
/// resending is worthwhile is a separate question: an `Invalid` request is
/// unsent and fails the same way again.
pub fn TransportError::is_unsent(self : TransportError) -> Bool {
let TransportError(kind~, phase~, ..) = self
kind is (Connect | Proxy(..)) || phase is (Pool | Dial)
}
///|
/// A human-readable diagnostic including category and phase.
pub fn TransportError::message(self : TransportError) -> String {
let TransportError(kind~, phase~, detail~) = self
"\{@debug.to_string(kind)} during \{@debug.to_string(phase)}: \{detail}"
}
///|
/// A stream with nonempty chunks, sticky EOF and idempotent close.
pub(open) trait BodyStream {
/// Returns the next nonempty chunk or permanent EOF.
async fn read_some(Self) -> Bytes? raise TransportError
/// Releases resources; subsequent reads return EOF.
fn close(Self) -> Unit
}