Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/SwiftNetwork/Endpoint/AddressEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ internal import os

@_spi(Essentials)
@available(Network 0.1.0, *)
public struct AddressEndpoint: EndpointProtocol, EndpointCommonProtocol {
public struct AddressEndpoint: Sendable, EndpointProtocol, EndpointCommonProtocol {
public var common: EndpointCommon
public enum AddressEndpointType: Equatable {
public enum AddressEndpointType: Sendable, Equatable {
case v4(IPv4Address, UInt16)
case v6(IPv6Address, UInt16)
case unix(String)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftNetwork/Endpoint/EndpointCommon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ protocol EndpointProtocol: CustomStringConvertible {

@_spi(Essentials)
@available(Network 0.1.0, *)
public struct EndpointCommon: Equatable, Hashable {
public struct EndpointCommon: Sendable, Equatable, Hashable {
let interface: Interface?
#if NETWORK_PRIVATE
let commonPrivate: EndpointCommon_Private?
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftNetwork/Endpoint/EthernetAddress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//===----------------------------------------------------------------------===//

@available(Network 0.1.0, *)
public struct EthernetAddress: Hashable, CustomDebugStringConvertible {
public struct EthernetAddress: Sendable, Hashable, CustomDebugStringConvertible {

public static var broadcast: EthernetAddress {
EthernetAddress(EthernetAddressStorage(repeating: 0xff))
Expand Down
10 changes: 10 additions & 0 deletions Sources/SwiftNetwork/Protocols/HarnessProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,8 @@ public class NewFlowHarness<LinkageType: InboundFlowLinkage, HarnessType: UpperH
public var disconnected: (() -> Void)?
var newFlow = Deque<(() -> Void)>()
public var error: ((NetworkError) -> Void)? // invoked when error detected
public var pathChanged: ((QUICPathInfo) -> Void)?
public var pathValidated: ((QUICPathInfo) -> Void)?
public init() {}
}
public var completions: Completions = .init()
Expand Down Expand Up @@ -818,6 +820,14 @@ public class NewFlowHarness<LinkageType: InboundFlowLinkage, HarnessType: UpperH
switch quicEvent {
case .newInboundConnectionID: newInboundCIDEventCount += 1
case .newOutboundConnectionID: newOutboundCIDEventCount += 1
case .pathChanged(let info):
if let completion = completions.pathChanged {
completion(info)
}
case .pathValidated(let info):
if let completion = completions.pathValidated {
completion(info)
}
default: break
}
}
Expand Down
10 changes: 10 additions & 0 deletions Sources/SwiftNetwork/Protocols/ManyToManyProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,16 @@ public protocol MultiplexingPath: UpperProtocolHandler {
public protocol MultiplexingDatapathPath: MultiplexingPath
where LowerProtocol: OutboundDataLinkage, ParentProtocol: ManyToManyDatapathProtocol {}

@_spi(ProtocolProvider)
@available(Network 0.1.0, *)
public struct DatagramPathEndpoints: Sendable, Equatable {
let local: AddressEndpoint
let remote: AddressEndpoint
public static func == (lhs: DatagramPathEndpoints, rhs: DatagramPathEndpoints) -> Bool {
lhs.local == rhs.local && lhs.remote == rhs.remote
}
}

// MARK: Implementations

@available(Network 0.1.0, *)
Expand Down
16 changes: 16 additions & 0 deletions Sources/SwiftNetwork/Protocols/NetworkEvents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ public struct NetworkEventDomain: Sendable, Hashable, CustomStringConvertible {
public var description: String { domain }
}

@_spi(ProtocolProvider)
@available(Network 0.1.0, *)
public struct QUICPathInfo: Sendable, Equatable {
public let isValidated: Bool
public let remote: AddressEndpoint
public let local: AddressEndpoint
}

@_spi(ProtocolProvider)
@available(Network 0.1.0, *)
/// An extensible event that a lower protocol reports to upper protocols.
Expand Down Expand Up @@ -226,6 +234,8 @@ public enum QUICEvent: DomainSpecificNetworkProtocolEvent {
case maxStreamsLimitUnidirectionalUpdated(maximumStreams: Int)
case earlyDataRejected
case receivedRemoteTransportParameters(transportParameters: [UInt8])
case pathChanged(_ info: QUICPathInfo)
case pathValidated(_ info: QUICPathInfo)

public var description: String {
switch self {
Expand All @@ -251,6 +261,12 @@ public enum QUICEvent: DomainSpecificNetworkProtocolEvent {
return "QUIC: Early data rejected"
case .receivedRemoteTransportParameters:
return "QUIC: Received remote transport parameters"
case .pathChanged(let pathInfo):
return
"QUIC: Path changed local: \(pathInfo.local.description) remote: \(pathInfo.remote.description) validated: \(pathInfo.isValidated)"
case .pathValidated(let pathInfo):
return
"QUIC: Path validated local: \(pathInfo.local.description) remote: \(pathInfo.remote.description)"
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions Sources/SwiftNetwork/Protocols/ProtocolControlHandlers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,18 @@ extension ProtocolInstanceReference {
}
}

public func getPathEndpoints(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this supposed to work when we have multiple underlying paths? Which set is returned?

Also, it would be nice to return a struct type instead of a tuple

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did switch a struct in the latest changes c42008f. This change does not take into account multiple underlying paths, only the the particular path that going through the events for handlePathChanged and handlePathChallengeResponse callbacks.

_ from: ProtocolInstanceReference
) -> DatagramPathEndpoints? {
guard !isNone else { return nil }
return self.handleCallFromUpperProtocol {
switch self.reference {
case .udp(let index): return context.udpInstances[index].pathEndpoints()
default: return nil
}
}
}

public func getMetrics(
_ from: ProtocolInstanceReference,
requestedNetworkMetric: RequestedNetworkMetrics
Expand Down
6 changes: 6 additions & 0 deletions Sources/SwiftNetwork/Protocols/ProtocolLinkage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ public struct OutboundDatagramLinkage: OutboundDataLinkage {
) throws(NetworkError) {
try reference.sendDatagrams(from, datagrams: datagrams)
}

public func invokeGetPathEndpoints(
_ from: ProtocolInstanceReference
) -> DatagramPathEndpoints? {
reference.getPathEndpoints(from)
}
}

@_spi(ProtocolProvider)
Expand Down
14 changes: 14 additions & 0 deletions Sources/SwiftNetwork/Protocols/UDPProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,20 @@ public struct UDPProtocol: NetworkProtocol {
snapshot.receivedTransportByteCount = UInt64(receiveByteCount)
snapshot.sentTransportByteCount = UInt64(transmitByteCount)
}

func pathEndpoints() -> DatagramPathEndpoints? {
if isIPv4 {
return DatagramPathEndpoints(
local: AddressEndpoint(address: ipv4Local, port: localPort),
remote: AddressEndpoint(address: ipv4Remote, port: remotePort)
)
} else {
return DatagramPathEndpoints(
local: AddressEndpoint(address: ipv6Local, port: localPort),
remote: AddressEndpoint(address: ipv6Remote, port: remotePort)
)
}
}
}

public init() {}
Expand Down
9 changes: 9 additions & 0 deletions Sources/SwiftNetwork/QUIC/Migration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ extension QUICConnection {
"Path \(path.identifier) \(path.state) over \(path.interface?.description ?? "nil")"
)
}
// Notify the stack about a path change event
if let datagramPathEndpoints = path.lower.invokeGetPathEndpoints(path.reference) {
let pathInfo = QUICPathInfo(
isValidated: path.isValidated,
remote: datagramPathEndpoints.remote,
local: datagramPathEndpoints.local
)
deliverNetworkProtocolEvent(flow: .allFlows, event: .init(quicEvent: .pathChanged(pathInfo)))
}

// This is a new primary path. Migrate to it if we are the client.
if !isServer, path != currentPath, isPrimary, path.isRouteEstablished {
Expand Down
12 changes: 12 additions & 0 deletions Sources/SwiftNetwork/QUIC/QUICPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,18 @@ public final class QUICPath: MultiplexingDatagramPath<QUICConnection>, Equatable
// Initialize RTT based on the PATH_RESPONSE duration so that we have a proper RTT estimate when we reset the timers.
rtt.processNewSample(ackDuration: responseDuration, packetAckedTime: now, ackDelay: .zero)
parentProtocol.migration.resetTimer(connection: parentProtocol)
// Notify the stack about the path becoming validated
if let datagramPathEndpoints = lower.invokeGetPathEndpoints(reference) {
let pathInfo = QUICPathInfo(
isValidated: self.isValidated,
remote: datagramPathEndpoints.remote,
local: datagramPathEndpoints.local
)
parentProtocol.deliverNetworkProtocolEvent(
flow: .allFlows,
event: .init(quicEvent: .pathValidated(pathInfo))
)
}
if migrationPending {
migrationPending = false
parentProtocol.migration.migrate(to: self, connection: parentProtocol)
Expand Down
Loading
Loading