Skip to content

Commit 2f32370

Browse files
committed
agnosticdev/SpinBitVariance: Observe spin bit from the bridge protocol
1 parent 456e104 commit 2f32370

5 files changed

Lines changed: 45 additions & 19 deletions

File tree

Sources/SwiftNetwork/Protocols/BridgeProtocol.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ public struct BridgeDatagramProtocol: NetworkProtocol {
142142

143143
var linkDelay: NetworkDuration = .zero
144144
var datagramDrops: DatagramDrops? = nil
145+
var observeFirstByte: ((UInt8) -> Void)? = nil
145146

146147
private var timerSet = false
147148
func deliverInboundDataAvailableEvent() {
@@ -275,6 +276,14 @@ public struct BridgeDatagramProtocol: NetworkProtocol {
275276
datagrams = remainingDatagrams
276277
}
277278
log.datapath("forwarding \(datagrams.count) datagrams to port: \(remotePort)")
279+
if let observeFirstByte {
280+
datagrams.iterateMutableFrames { frame in
281+
if let bytes = frame.bytes, bytes.byteCount > 0 {
282+
observeFirstByte(bytes[0])
283+
}
284+
return true
285+
}
286+
}
278287
remoteInstance.incomingFrames.add(frames: datagrams)
279288
remoteInstance.deliverInboundDataAvailableEvent()
280289
}

Sources/SwiftNetwork/QUIC/QUICConnection.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2322,9 +2322,6 @@ public final class QUICConnection: ManyToManyApplicationStreamProtocol,
23222322
// Reflect the spin bit.
23232323
let spinValue = packet.spinValue
23242324
path.spinValue = spinValue
2325-
if spinValue {
2326-
path.spinValueForTestingSeen = true
2327-
}
23282325
} else {
23292326
// Spin it!
23302327
let spinValue = packet.spinValue

Sources/SwiftNetwork/QUIC/QUICPath.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ public final class QUICPath: MultiplexingDatagramPath<QUICConnection>, Equatable
188188
static let isFlowControlled = Flags(rawValue: 1 << 9)
189189
static let l4sEnabled = Flags(rawValue: 1 << 10)
190190
static let reportedIdleEvent = Flags(rawValue: 1 << 11)
191-
static let spinValueForTestingSeen = Flags(rawValue: 1 << 12)
192191
}
193192
private var flags = Flags()
194193

@@ -242,10 +241,6 @@ public final class QUICPath: MultiplexingDatagramPath<QUICConnection>, Equatable
242241
get { flags.contains(.reportedIdleEvent) }
243242
set { if newValue { flags.insert(.reportedIdleEvent) } else { flags.remove(.reportedIdleEvent) } }
244243
}
245-
var spinValueForTestingSeen: Bool {
246-
get { flags.contains(.spinValueForTestingSeen) }
247-
set { if newValue { flags.insert(.spinValueForTestingSeen) } else { flags.remove(.spinValueForTestingSeen) } }
248-
}
249244

250245
var log: LogPrefixer {
251246
parentProtocol.logPrefixer

Tests/SwiftNetworkTests/QUICTestHarness.swift

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ final class QUICTestHarness {
7676

7777
let clientInstance: QUICProtocol.Instance
7878
let serverInstance: QUICProtocol.Instance
79+
80+
let clientBridge: BridgeDatagramProtocol.Instance
81+
let serverBridge: BridgeDatagramProtocol.Instance
7982
}
8083
var state: QUICHarnessState? = nil
8184

@@ -165,7 +168,8 @@ final class QUICTestHarness {
165168
clientOptions.setProtocolInstance(clientReference)
166169
clientParameters.defaultStack.transport = .quic(clientOptions)
167170

168-
let clientBridge = BridgeDatagramProtocol.instance(context: self.context)
171+
let clientBridgeInstance = BridgeDatagramProtocol.Instance(context: self.context)
172+
let clientBridge = clientBridgeInstance.reference
169173
let clientBridgeOptions = BridgeDatagramProtocol.options()
170174
clientBridgeOptions.setProtocolInstance(clientBridge)
171175
clientBridgeOptions.linkDelay = clientLinkDelay
@@ -192,7 +196,8 @@ final class QUICTestHarness {
192196
serverOptions.setProtocolInstance(serverReference)
193197
serverParameters.defaultStack.transport = .quic(serverOptions)
194198

195-
let serverBridge = BridgeDatagramProtocol.instance(context: self.context)
199+
let serverBridgeInstance = BridgeDatagramProtocol.Instance(context: self.context)
200+
let serverBridge = serverBridgeInstance.reference
196201
let serverBridgeOptions = BridgeDatagramProtocol.options()
197202
serverBridgeOptions.setProtocolInstance(serverBridge)
198203
serverBridgeOptions.linkDelay = serverLinkDelay
@@ -292,7 +297,9 @@ final class QUICTestHarness {
292297
clientReference: clientReference,
293298
serverReference: serverReference,
294299
clientInstance: clientInstance,
295-
serverInstance: serverInstance
300+
serverInstance: serverInstance,
301+
clientBridge: clientBridgeInstance,
302+
serverBridge: serverBridgeInstance
296303
)
297304

298305
clientHarness.waitForError { error in
@@ -543,6 +550,18 @@ final class QUICTestHarness {
543550
wait(for: [stopCompleteExpectation], timeout: 5.0)
544551
}
545552

553+
// Installs a callback on the client and server bridge that fires passing the raw first byte to the
554+
// caller for a short header packet. Used to detect a spin bit passing between the client and server.
555+
func observeFirstByteForOutboundShortHeaderPacket(handler: @escaping (_ firstByte: UInt8) -> Void) {
556+
guard let state else { return }
557+
let observe: ((UInt8) -> Void) = { firstByte in
558+
guard (firstByte & 0xC0) == 0x40 else { return }
559+
handler(firstByte)
560+
}
561+
state.clientBridge.observeFirstByte = observe
562+
state.serverBridge.observeFirstByte = observe
563+
}
564+
546565
func echoDataOnStream(
547566
dataGenerator: TestDataGenerator,
548567
streamIndex: Int,

Tests/SwiftNetworkTests/SwiftNetworkQUICSpinBitTests.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ final class SwiftNetworkQUICSpinBitTests: NetTestCase {
6060
// it from the server.
6161

6262
var hasSpinBit = true
63+
var observedSpinBitValues: Set<Bool> = []
6364
QUICTestHarness().runQUICTest(
6465
dataBlock: Array("Hello World!".utf8),
6566
afterHandshake: { harness in
@@ -78,6 +79,11 @@ final class SwiftNetworkQUICSpinBitTests: NetTestCase {
7879
hasSpinBit = false
7980
return
8081
}
82+
// If we have made it this far we should at least be able to detect at least one spin bit value going back and forth
83+
// Detect at least 1 outbound datagram sending a spin bit value
84+
harness.observeFirstByteForOutboundShortHeaderPacket { firstByte in
85+
observedSpinBitValues.insert((firstByte & 0x20) != 0)
86+
}
8187
}
8288
self.wait(for: [expectation], timeout: 5.0)
8389
},
@@ -89,14 +95,14 @@ final class SwiftNetworkQUICSpinBitTests: NetTestCase {
8995
let expectation = XCTestExpectation(description: "Wait to validate spin bit")
9096
harness.context.async {
9197
defer { expectation.fulfill() }
92-
93-
let clientSpinBit = harness.state?.clientInstance.currentPath?.spinValue ?? false
94-
// Note that spinValueForTestingSeen is checked here because it could be that by the time
95-
// this test checks the server spin bit value it has received the ACK from the client and the
96-
// spin bit value has already changed. This reliably make sure that the spin bit on the server was present.
97-
let serverSpinBit = harness.state?.serverInstance.currentPath?.spinValueForTestingSeen ?? false
98-
XCTAssertFalse(clientSpinBit, "Client should have the spin bit set to false")
99-
XCTAssertTrue(serverSpinBit, "Server should have the spin bit set to true")
98+
XCTAssertFalse(
99+
observedSpinBitValues.isEmpty,
100+
"BridgeDatagramProtocol should have observed short-header packets with spin bit values"
101+
)
102+
XCTAssertTrue(
103+
observedSpinBitValues.contains(true),
104+
"BridgeDatagramProtocol should have observed at least one packet with spin bit set"
105+
)
100106
}
101107
}
102108
)

0 commit comments

Comments
 (0)