use std::net::SocketAddr;
use derive_builder::Builder;
use derive_more::From;
use qbase::{
error::{AppError, Error, ErrorKind, QuicError},
frame::{AppCloseFrame, ConnectionCloseFrame, QuicCloseFrame},
net::addr::BoundAddr,
};
use super::{
ApplicationCode, ConnectionID, CryptoError, IPAddress, IpVersion, Owner, PathEndpointInfo,
TransportError,
};
use crate::{Deserialize, PathID, Serialize};
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct ServerListening {
#[builder(default)]
ip_v4: Option<IPAddress>,
#[builder(default)]
ip_v6: Option<IPAddress>,
#[builder(default)]
port_v4: Option<u16>,
#[builder(default)]
port_v6: Option<u16>,
#[builder(default)]
retry_required: Option<bool>,
}
impl ServerListeningBuilder {
pub fn address(&mut self, socket_addr: BoundAddr) -> &mut Self {
match socket_addr {
BoundAddr::Internet(SocketAddr::V4(addr)) => {
self.ip_v4(addr.ip().to_string()).port_v4(addr.port())
}
BoundAddr::Internet(SocketAddr::V6(addr)) => {
self.ip_v6(addr.ip().to_string()).port_v6(addr.port())
}
_ => self,
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct ConnectionStarted {
ip_version: IpVersion,
src_ip: IPAddress,
dst_ip: IPAddress,
#[builder(default = "ConnectionStarted::default_protocol()")]
#[serde(default = "ConnectionStarted::default_protocol")]
protocol: String,
#[builder(default)]
src_port: Option<u16>,
#[builder(default)]
dst_port: Option<u16>,
#[builder(default)]
src_cid: Option<ConnectionID>,
#[builder(default)]
dst_cid: Option<ConnectionID>,
}
impl ConnectionStartedBuilder {
pub fn socket(&mut self, (src, dst): (BoundAddr, BoundAddr)) -> &mut Self {
match (src, dst) {
(BoundAddr::Internet(src), BoundAddr::Internet(dst)) => {
debug_assert_eq!(src.is_ipv4(), dst.is_ipv4());
self.ip_version(if src.is_ipv4() {
IpVersion::V4
} else {
IpVersion::V6
})
.src_ip(src.ip().to_string())
.dst_ip(dst.ip().to_string())
.src_port(src.port())
.dst_port(dst.port())
}
_ => self,
}
}
}
impl ConnectionStarted {
pub fn default_protocol() -> String {
String::from("QUIC")
}
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(
default,
setter(into, strip_option),
build_fn(private, name = "fallible_build")
)]
pub struct ConnectionClosed {
owner: Option<Owner>,
connection_code: Option<ConnectionCode>,
application_code: Option<ApplicationCode>,
internal_code: Option<u32>,
reason: Option<String>,
trigger: Option<ConnectionCloseTrigger>,
}
impl ConnectionClosedBuilder {
pub fn ccf(&mut self, ccf: &ConnectionCloseFrame) -> &mut Self {
match &ccf {
ConnectionCloseFrame::Quic(frame) => self.quic_close_frame(frame),
ConnectionCloseFrame::App(frame) => self.app_close_frame(frame),
}
}
fn quic_close_frame(&mut self, frame: &QuicCloseFrame) -> &mut ConnectionClosedBuilder {
self.connection_code(frame.error_kind())
.reason(frame.reason().to_owned())
}
fn app_close_frame(&mut self, frame: &AppCloseFrame) -> &mut ConnectionClosedBuilder {
self.application_code(frame.error_code() as u32)
.reason(frame.reason().to_owned())
}
pub fn quic_error(&mut self, error: &QuicError) -> &mut Self {
self.connection_code(error.kind())
.reason(error.reason().to_owned())
}
pub fn app_error(&mut self, error: &AppError) -> &mut Self {
self.application_code(error.error_code() as u32)
.reason(error.reason().to_owned())
}
pub fn error(&mut self, error: &Error) {
match error {
Error::Quic(quic_error) => self.quic_error(quic_error),
Error::App(app_error) => self.app_error(app_error),
};
}
}
#[derive(Debug, Clone, Copy, From, Serialize, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ConnectionCode {
TransportError(TransportError),
CryptoError(CryptoError),
Value(u32),
}
impl From<ConnectionCode> for super::ConnectionCloseErrorCode {
fn from(value: ConnectionCode) -> Self {
match value {
ConnectionCode::TransportError(err) => err.into(),
ConnectionCode::CryptoError(err) => err.into(),
ConnectionCode::Value(code) => (code as u64).into(),
}
}
}
impl From<ErrorKind> for ConnectionCode {
fn from(kind: ErrorKind) -> Self {
match kind {
ErrorKind::None => TransportError::NoError.into(),
ErrorKind::Internal => TransportError::InternalError.into(),
ErrorKind::ConnectionRefused => TransportError::ConnectionRefused.into(),
ErrorKind::FlowControl => TransportError::FlowControlError.into(),
ErrorKind::StreamLimit => TransportError::StreamLimitError.into(),
ErrorKind::StreamState => TransportError::StreamStateError.into(),
ErrorKind::FinalSize => TransportError::FinalSizeError.into(),
ErrorKind::FrameEncoding => TransportError::FrameEncodingError.into(),
ErrorKind::TransportParameter => TransportError::TransportParameterError.into(),
ErrorKind::ConnectionIdLimit => TransportError::ConnectionIdLimitError.into(),
ErrorKind::ProtocolViolation => TransportError::ProtocolViolation.into(),
ErrorKind::InvalidToken => TransportError::InvalidToken.into(),
ErrorKind::Application => TransportError::ApplicationError.into(),
ErrorKind::CryptoBufferExceeded => TransportError::CryptoBufferExceeded.into(),
ErrorKind::KeyUpdate => TransportError::KeyUpdateError.into(),
ErrorKind::AeadLimitReached => TransportError::AeadLimitReached.into(),
ErrorKind::NoViablePath => TransportError::NoViablePath.into(),
ErrorKind::Crypto(code) => CryptoError(code).into(),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ConnectionCloseTrigger {
IdleTimeout,
Application,
Error,
VersionMismatch,
StatelessReset,
Unspecified,
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct ConnectionIdUpdated {
owner: Owner,
#[builder(default)]
old: Option<ConnectionID>,
#[builder(default)]
new: Option<ConnectionID>,
}
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[builder(setter(into), build_fn(private, name = "fallible_build"))]
pub struct SpinBitUpdated {
state: bool,
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct ConnectionStateUpdated {
#[builder(default)]
old: Option<ConnectionState>,
new: ConnectionState,
}
#[derive(Debug, Clone, Copy, From, Serialize, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ConnectionState {
Base(BaseConnectionStates),
Granular(GranularConnectionStates),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum BaseConnectionStates {
Attempted,
HandshakeStarted,
HandshakeComplete,
Closed,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum GranularConnectionStates {
PeerValidated,
EarlyWrite,
HandshakeConfirmed,
Closing,
Draining,
Closed,
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct PathAssigned {
path_id: PathID,
#[builder(default)]
path_remote: Option<PathEndpointInfo>,
#[builder(default)]
path_local: Option<PathEndpointInfo>,
}
#[serde_with::skip_serializing_none]
#[derive(Builder, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[builder(setter(into, strip_option), build_fn(private, name = "fallible_build"))]
pub struct MtuUpdated {
#[builder(default)]
old: Option<u32>,
new: u32,
#[builder(default)]
#[serde(default)]
done: bool,
}
crate::gen_builder_method! {
ServerListeningBuilder => ServerListening;
ConnectionStartedBuilder => ConnectionStarted;
ConnectionClosedBuilder => ConnectionClosed;
ConnectionIdUpdatedBuilder => ConnectionIdUpdated;
SpinBitUpdatedBuilder => SpinBitUpdated;
ConnectionStateUpdatedBuilder => ConnectionStateUpdated;
PathAssignedBuilder => PathAssigned;
MtuUpdatedBuilder => MtuUpdated;
}
mod rollback {
use super::*;
use crate::{build, legacy::quic as legacy};
impl From<ServerListening> for legacy::ConnectivityServerListening {
#[inline]
fn from(value: ServerListening) -> Self {
build!(legacy::ConnectivityServerListening {
?ip_v4: value.ip_v4,
?ip_v6: value.ip_v6,
?port_v4: value.port_v4,
?port_v6: value.port_v6,
?retry_required: value.retry_required,
})
}
}
impl From<ConnectionStarted> for legacy::ConnectivityConnectionStarted {
#[inline]
fn from(value: ConnectionStarted) -> Self {
build!(legacy::ConnectivityConnectionStarted {
ip_version: value.ip_version,
src_ip: value.src_ip,
dst_ip: value.dst_ip,
protocol: value.protocol,
?src_port: value.src_port,
?dst_port: value.dst_port,
?src_cid: value.src_cid,
?dst_cid: value.dst_cid,
})
}
}
impl From<CryptoError> for legacy::CryptoError {
#[inline]
fn from(value: CryptoError) -> Self {
legacy::CryptoError::from(value.0)
}
}
impl From<ConnectionCode> for legacy::ConnectionCode {
#[inline]
fn from(value: ConnectionCode) -> Self {
match value {
ConnectionCode::TransportError(err) => legacy::TransportError::from(err).into(),
ConnectionCode::CryptoError(err) => legacy::CryptoError::from(err).into(),
ConnectionCode::Value(code) => code.into(),
}
}
}
impl TryFrom<ConnectionCloseTrigger> for legacy::ConnectivityConnectionClosedTrigger {
type Error = ();
#[inline]
fn try_from(value: ConnectionCloseTrigger) -> Result<Self, ()> {
match value {
ConnectionCloseTrigger::IdleTimeout => {
Ok(legacy::ConnectivityConnectionClosedTrigger::IdleTimeout)
}
ConnectionCloseTrigger::Application => {
Ok(legacy::ConnectivityConnectionClosedTrigger::Application)
}
ConnectionCloseTrigger::Error => {
Ok(legacy::ConnectivityConnectionClosedTrigger::Error)
}
ConnectionCloseTrigger::VersionMismatch => {
Ok(legacy::ConnectivityConnectionClosedTrigger::VersionMismatch)
}
ConnectionCloseTrigger::StatelessReset => {
Ok(legacy::ConnectivityConnectionClosedTrigger::StatelessReset)
}
ConnectionCloseTrigger::Unspecified => Err(()),
}
}
}
impl From<ConnectionClosed> for legacy::ConnectivityConnectionClosed {
#[inline]
fn from(value: ConnectionClosed) -> Self {
build!(legacy::ConnectivityConnectionClosed {
?owner: value.owner,
?connection_code: value.connection_code,
?application_code: value.application_code,
?internal_code: value.internal_code,
?reason: value.reason,
?trigger: value.trigger.and_then(|v| legacy::ConnectivityConnectionClosedTrigger::try_from(v).ok()),
})
}
}
impl From<ConnectionIdUpdated> for legacy::ConnectivityConnectionIdUpdated {
#[inline]
fn from(value: ConnectionIdUpdated) -> Self {
build!(legacy::ConnectivityConnectionIdUpdated {
owner: value.owner,
?old: value.old,
?new: value.new,
})
}
}
impl From<SpinBitUpdated> for legacy::ConnectivitySpinBitUpdated {
#[inline]
fn from(value: SpinBitUpdated) -> Self {
build!(legacy::ConnectivitySpinBitUpdated { state: value.state })
}
}
impl From<ConnectionState> for legacy::ConnectionState {
#[inline]
fn from(value: ConnectionState) -> Self {
match value {
ConnectionState::Base(BaseConnectionStates::Attempted) => {
legacy::ConnectionState::Attempted
}
ConnectionState::Base(BaseConnectionStates::HandshakeStarted) => {
legacy::ConnectionState::HandshakeStarted
}
ConnectionState::Base(BaseConnectionStates::HandshakeComplete) => {
legacy::ConnectionState::HandshakeComplete
}
ConnectionState::Base(BaseConnectionStates::Closed) => {
legacy::ConnectionState::Closed
}
ConnectionState::Granular(GranularConnectionStates::PeerValidated) => {
legacy::ConnectionState::PeerValidated
}
ConnectionState::Granular(GranularConnectionStates::EarlyWrite) => {
legacy::ConnectionState::EarlyWrite
}
ConnectionState::Granular(GranularConnectionStates::HandshakeConfirmed) => {
legacy::ConnectionState::HandshakeConfirmed
}
ConnectionState::Granular(GranularConnectionStates::Closing) => {
legacy::ConnectionState::Closing
}
ConnectionState::Granular(GranularConnectionStates::Draining) => {
legacy::ConnectionState::Draining
}
ConnectionState::Granular(GranularConnectionStates::Closed) => {
legacy::ConnectionState::Closed
}
}
}
}
impl From<ConnectionStateUpdated> for legacy::ConnectivityConnectionStateUpdated {
#[inline]
fn from(value: ConnectionStateUpdated) -> Self {
build!(legacy::ConnectivityConnectionStateUpdated {
?old: value.old,
new: value.new,
})
}
}
}