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
42 changes: 42 additions & 0 deletions Sources/SwiftNetwork/Protocols/BaseProtocolLinkages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,48 @@ public struct BaseOutboundDatagramLinkage: OutboundDatagramLinkage {
}
}

public func getDatagramToSend(
minimumDatagramSize: Int,
for instance: InstanceIdentifier,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
switch protocolType {
case .udp(let index):
return try storage!.udpInstances[index].getDatagramToSend(
minimumDatagramSize: minimumDatagramSize,
for: instance,
in: &eventContext
)
case .ip(let index):
return try storage!.ipInstances[index].getDatagramToSend(
minimumDatagramSize: minimumDatagramSize,
for: instance,
in: &eventContext
)
case .bridgeDatagram(let index):
return try storage!.bridgeDatagramInstances[index].getDatagramToSend(
minimumDatagramSize: minimumDatagramSize,
for: instance,
in: &eventContext
)
#if NETWORK_RTKIT
case .netif(let box):
var protocolInstance = box.instance
return try protocolInstance.getDatagramToSend(
minimumDatagramSize: minimumDatagramSize,
for: instance,
in: &eventContext
)
#endif
default:
return try getDatagramToSendFromBatch(
minimumDatagramSize: minimumDatagramSize,
for: instance,
in: &eventContext
)
}
}

public func sendDatagrams(
_ datagrams: consuming FrameArray,
from instance: InstanceIdentifier,
Expand Down
37 changes: 37 additions & 0 deletions Sources/SwiftNetwork/Protocols/BottomProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ where LinkageType: OutboundDatagramLinkage, LinkageType.PairedUpperLinkage == Up
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> FrameArray?

/// Returns a single datagram frame the upper protocol can use to send.
///
/// Protocols can implement this function to customize behavior; the default implementation
/// asks `getDatagramsToSend` for a batch of one.
mutating func getDatagramToSend(
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame?

/// Sends datagrams created by the upper protocol.
///
/// Protocols can implement this function to customize behavior.
Expand Down Expand Up @@ -413,6 +422,34 @@ extension BottomDatagramProtocol where Self: ~Copyable {
in: &eventContext
)
}
public mutating func getDatagramToSend(
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
guard
var datagrams = try self.getDatagramsToSend(
maximumDatagramCount: 1,
minimumDatagramSize: minimumDatagramSize,
in: &eventContext
)
else {
return nil
}
let datagram = datagrams.popFirst()
// A batch of one must hold at most one frame; anything left here would trap in
// `Frame.deinit` instead of reaching the driver.
precondition(datagrams.isEmpty)
return datagram
}
public mutating func getDatagramToSend(
minimumDatagramSize: Int,
for instance: InstanceIdentifier,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
do { try validate(upper: instance, #function) } catch { throw NetworkError.posix(EINVAL) }
guard isConnected(in: &eventContext) else { throw NetworkError.posix(ENOTCONN) }
return try self.getDatagramToSend(minimumDatagramSize: minimumDatagramSize, in: &eventContext)
}
public mutating func sendDatagrams(
_ datagrams: consuming FrameArray,
from instance: InstanceIdentifier,
Expand Down
38 changes: 29 additions & 9 deletions Sources/SwiftNetwork/Protocols/BridgeProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,8 @@ public struct BridgeDatagramProtocol: NetworkProtocol {
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> FrameArray? {
if datagramDrops?.blockPacketGeneration ?? false {
if datagramDrops?.shouldDropPacket() ?? false {
log.datapath("blocking \(maximumDatagramCount) datagrams to port: \(self.remoteEndpoint!.port)")
self.async(in: &eventContext) { eventContext in
self.log.datapath("unblocking outbound data")
self.upper.deliverOutboundRoomAvailableEvent(from: self.identifier, in: &eventContext)
}
return nil
}
if blocksPacketGeneration(datagramCount: maximumDatagramCount, in: &eventContext) {
return nil
}

let frameSize = min(minimumDatagramSize, self.maximumOutputSize)
Expand All @@ -289,6 +282,33 @@ public struct BridgeDatagramProtocol: NetworkProtocol {
return frameArray
}

public func getDatagramToSend(
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
if blocksPacketGeneration(datagramCount: 1, in: &eventContext) {
return nil
}
return Frame(count: min(minimumDatagramSize, self.maximumOutputSize))
}

/// Whether the configured drops withhold the next `datagramCount` datagrams. When they do, the upper
/// protocol is told it has room again on the next turn, so that it retries.
private func blocksPacketGeneration(
datagramCount: Int,
in eventContext: inout NetworkContext.EventContext
) -> Bool {
guard datagramDrops?.blockPacketGeneration ?? false, datagramDrops?.shouldDropPacket() ?? false else {
return false
}
log.datapath("blocking \(datagramCount) datagrams to port: \(self.remoteEndpoint!.port)")
self.async(in: &eventContext) { eventContext in
self.log.datapath("unblocking outbound data")
self.upper.deliverOutboundRoomAvailableEvent(from: self.identifier, in: &eventContext)
}
return true
}

public func sendDatagrams(
_ datagrams: consuming FrameArray,
in eventContext: inout NetworkContext.EventContext
Expand Down
54 changes: 43 additions & 11 deletions Sources/SwiftNetwork/Protocols/IPProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -986,9 +986,13 @@ public struct IPProtocol: NetworkProtocol {
inboundFrames.add(frames: processedFrames)
}

func prepareOutboundFrame(_ outboundFrame: inout Frame) {
_ = outboundFrame.claim(fromStart: IPv4Instance.headerLength)
}

func prepareOutboundFrames(_ outboundFrames: inout FrameArray) {
outboundFrames.iterateMutableFrames { frame in
_ = frame.claim(fromStart: IPv4Instance.headerLength)
prepareOutboundFrame(&frame)
return true
}
}
Expand Down Expand Up @@ -1832,18 +1836,22 @@ public struct IPProtocol: NetworkProtocol {
inboundFrames.add(frames: processedFrames)
}

func prepareOutboundFrame(_ outboundFrame: inout Frame) {
if flags.useMinimumMTU {
var trailerClaim = 0
let frameLength = outboundFrame.unclaimedLength
if frameLength > IPv6Instance.minimalMTU {
trailerClaim = frameLength - IPv6Instance.minimalMTU
}
_ = outboundFrame.claim(fromStart: IPv6Instance.headerLength, fromEnd: trailerClaim)
} else {
_ = outboundFrame.claim(fromStart: IPv6Instance.headerLength)
}
}

func prepareOutboundFrames(_ outboundFrames: inout FrameArray) {
outboundFrames.iterateMutableFrames { frame in
if flags.useMinimumMTU {
var trailerClaim = 0
let frameLength = frame.unclaimedLength
if frameLength > IPv6Instance.minimalMTU {
trailerClaim = frameLength - IPv6Instance.minimalMTU
}
_ = frame.claim(fromStart: IPv6Instance.headerLength, fromEnd: trailerClaim)
} else {
_ = frame.claim(fromStart: IPv6Instance.headerLength)
}
prepareOutboundFrame(&frame)
return true
}
}
Expand Down Expand Up @@ -2232,6 +2240,30 @@ public struct IPProtocol: NetworkProtocol {
}
}

func getDatagramToSend(
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
switch self.instanceType {
case .ipv4(let instance):
let outboundFrame = try invokeGetDatagramToSend(
minimumDatagramSize: instance.incrementByHeaderLength(minimumDatagramSize),
in: &eventContext
)
guard var outboundFrame else { return nil }
instance.prepareOutboundFrame(&outboundFrame)
return outboundFrame
case .ipv6(let instance):
let outboundFrame = try invokeGetDatagramToSend(
minimumDatagramSize: instance.incrementByHeaderLength(minimumDatagramSize),
in: &eventContext
)
guard var outboundFrame else { return nil }
instance.prepareOutboundFrame(&outboundFrame)
return outboundFrame
}
}

mutating func sendDatagrams(
_ datagrams: consuming FrameArray,
in eventContext: inout NetworkContext.EventContext
Expand Down
14 changes: 14 additions & 0 deletions Sources/SwiftNetwork/Protocols/ManyToManyProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2189,6 +2189,20 @@ extension ManyToManyOutboundDatagramProtocol where Path: AutomaticLowerDatagramP
)
}

/// Fetches a single datagram to send using an event context the caller already holds.
public func getDatagramToSend(
path pathID: MultiplexingPathIdentifier,
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
guard let path = self.path(for: pathID) else { throw NetworkError.posix(EINVAL) }
return try path.lower.invokeGetDatagramToSend(
minimumDatagramSize: minimumDatagramSize,
for: path.identifier,
in: &eventContext
)
}

public func enqueueOutboundDatagrams(
path pathID: MultiplexingPathIdentifier,
datagrams: consuming FrameArray
Expand Down
49 changes: 49 additions & 0 deletions Sources/SwiftNetwork/Protocols/OneToOneProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,15 @@ where UpperProtocol: InboundDatagramLinkage, LowerProtocol: OutboundDatagramLink
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> FrameArray?

/// Returns a single datagram frame the upper protocol can use to send.
///
/// Protocols can implement this function to customize behavior; the default implementation
/// asks `getDatagramsToSend` for a batch of one.
mutating func getDatagramToSend(
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame?

/// Sends datagrams created by the upper protocol.
///
/// Protocols can implement this function to customize behavior.
Expand Down Expand Up @@ -694,6 +703,18 @@ extension OneToOneProtocolHandler where Self: ~Copyable, LowerProtocol: Outbound
)
}

@inline(always)
public func invokeGetDatagramToSend(
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
try lower.invokeGetDatagramToSend(
minimumDatagramSize: minimumDatagramSize,
for: effectiveSelfInstance,
in: &eventContext
)
}

public func invokeSendDatagrams(
_ datagrams: consuming FrameArray,
in eventContext: inout NetworkContext.EventContext
Expand Down Expand Up @@ -727,6 +748,34 @@ extension OneToOneDatagramProtocol where Self: ~Copyable {
in: &eventContext
)
}
public mutating func getDatagramToSend(
minimumDatagramSize: Int,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
guard
var datagrams = try self.getDatagramsToSend(
maximumDatagramCount: 1,
minimumDatagramSize: minimumDatagramSize,
in: &eventContext
)
else {
return nil
}
let datagram = datagrams.popFirst()
// A batch of one must hold at most one frame; anything left here would trap in
// `Frame.deinit` instead of reaching the driver.
precondition(datagrams.isEmpty)
return datagram
}
public mutating func getDatagramToSend(
minimumDatagramSize: Int,
for instance: InstanceIdentifier,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
do { try validate(upper: instance, #function) } catch { throw NetworkError.posix(EINVAL) }
guard passthroughEvents || isConnected(in: &eventContext) else { throw NetworkError.posix(ENOTCONN) }
return try self.getDatagramToSend(minimumDatagramSize: minimumDatagramSize, in: &eventContext)
}
public mutating func sendDatagrams(
_ datagrams: consuming FrameArray,
from instance: InstanceIdentifier,
Expand Down
52 changes: 52 additions & 0 deletions Sources/SwiftNetwork/Protocols/ProtocolLinkage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,16 @@ public protocol OutboundDatagramLinkage: OutboundDataLinkage where PairedUpperLi
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> FrameArray?

/// Returns a single datagram frame the upper protocol can use to send.
///
/// Linkages whose protocols can hand back one frame without building a container should implement this
/// function; the default implementation asks `getDatagramsToSend` for a batch of one.
func getDatagramToSend(
minimumDatagramSize: Int,
for instance: InstanceIdentifier,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame?

func sendDatagrams(
_ datagrams: consuming FrameArray,
from instance: InstanceIdentifier,
Expand Down Expand Up @@ -445,6 +455,48 @@ extension OutboundDatagramLinkage {
}
}

public func getDatagramToSend(
minimumDatagramSize: Int,
for instance: InstanceIdentifier,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
try getDatagramToSendFromBatch(minimumDatagramSize: minimumDatagramSize, for: instance, in: &eventContext)
}

/// Takes a single frame from `getDatagramsToSend`, for protocols with no way to hand one back directly.
func getDatagramToSendFromBatch(
minimumDatagramSize: Int,
for instance: InstanceIdentifier,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
guard
var datagrams = try getDatagramsToSend(
maximumDatagramCount: 1,
minimumDatagramSize: minimumDatagramSize,
for: instance,
in: &eventContext
)
else {
return nil
}
let datagram = datagrams.popFirst()
// A batch of one must hold at most one frame; anything left here would trap in
// `Frame.deinit` instead of reaching the driver.
precondition(datagrams.isEmpty)
return datagram
}

public func invokeGetDatagramToSend(
minimumDatagramSize: Int,
for instance: InstanceIdentifier,
in eventContext: inout NetworkContext.EventContext
) throws(NetworkError) -> Frame? {
guard !identifier.isNone else { return nil }
return try identifier.handleCallFromUpperProtocol(in: &eventContext) { eventContext throws(NetworkError) in
try self.getDatagramToSend(minimumDatagramSize: minimumDatagramSize, for: instance, in: &eventContext)
}
}

public func invokeSendDatagrams(
_ datagrams: consuming FrameArray,
from instance: InstanceIdentifier,
Expand Down
Loading
Loading