// Transport abstractions shared by the resolver and the native socket adapters.
///|
pub enum TransportError {
Timeout(Int)
Truncated
ConnectionFailed(String)
SendFailed(String)
RecvFailed(String)
InvalidServer(String)
InvalidMessage(String)
} derive(Debug, Eq)
///|
pub fn transport_error_message(err : TransportError) -> String {
match err {
Timeout(ms) => "Timeout after " + ms.to_string() + "ms"
Truncated => "UDP response truncated and TCP fallback failed"
ConnectionFailed(reason) => "Connection failed: " + reason
SendFailed(reason) => "Send failed: " + reason
RecvFailed(reason) => "Receive failed: " + reason
InvalidServer(reason) => "Invalid DNS server: " + reason
InvalidMessage(reason) => "Invalid DNS message: " + reason
}
}
///|
pub fn is_retryable(err : TransportError) -> Bool {
match err {
Timeout(_) | ConnectionFailed(_) | RecvFailed(_) => true
_ => false
}
}
///|
pub fn is_timeout(err : TransportError) -> Bool {
match err {
Timeout(_) => true
_ => false
}
}
///|
/// An injectable asynchronous DNS transport.
///
/// The callback boundary keeps the resolver independent from socket backends and
/// makes deterministic resolver tests possible without a network connection.
pub(all) struct DnsTransport {
request : async (Array[Byte]) -> Result[Array[Byte], TransportError]
udp_payload_size : UInt16
}
///|
pub fn DnsTransport::from_request(
request : async (Array[Byte]) -> Result[Array[Byte], TransportError],
udp_payload_size? : UInt16 = 1232,
) -> DnsTransport {
{ request, udp_payload_size }
}
///|
pub async fn DnsTransport::send(
self : DnsTransport,
payload : Array[Byte],
) -> Result[Array[Byte], TransportError] {
(self.request)(payload)
}
///|
pub fn DnsTransport::max_payload(self : DnsTransport) -> UInt16 {
self.udp_payload_size
}
///|
fn array_to_bytes(payload : Array[Byte]) -> Bytes {
FixedArray::from_array(payload).unsafe_reinterpret_as_bytes()
}
///|
fn bytes_to_array(payload : Bytes) -> Array[Byte] {
Array::from_fixed_array(payload.to_fixedarray())
}
///|
pub struct TransportStats {
bytes_sent : Ref[Int]
bytes_received : Ref[Int]
packets_sent : Ref[Int]
packets_received : Ref[Int]
errors : Ref[Int]
timeouts : Ref[Int]
truncations : Ref[Int]
}
///|
pub fn TransportStats::new() -> TransportStats {
{
bytes_sent: Ref(0),
bytes_received: Ref(0),
packets_sent: Ref(0),
packets_received: Ref(0),
errors: Ref(0),
timeouts: Ref(0),
truncations: Ref(0),
}
}
///|
pub fn TransportStats::record_send(self : TransportStats, bytes : Int) -> Unit {
self.bytes_sent.val += bytes
self.packets_sent.val += 1
}
///|
pub fn TransportStats::record_recv(self : TransportStats, bytes : Int) -> Unit {
self.bytes_received.val += bytes
self.packets_received.val += 1
}
///|
pub fn TransportStats::record_error(
self : TransportStats,
err : TransportError,
) -> Unit {
self.errors.val += 1
if is_timeout(err) {
self.timeouts.val += 1
}
if err is Truncated {
self.truncations.val += 1
}
}
///|
pub fn TransportStats::average_send_size(self : TransportStats) -> Int {
if self.packets_sent.val == 0 {
0
} else {
self.bytes_sent.val / self.packets_sent.val
}
}