Hey all here is an example you can try out:
https://github.com/paxsonsa/quic-swift-demo
I am prototype a QUIC base application system with a client and server.
My server is a simple test to experiment with QUIC and Network Framework but I am see some odd behaviour.
Selecting Stream Direction for new streams
In the example below, we are creating a new multiplexed QUIC connection and establish a new stream once the group connection is ready. In some cases, I want to be able to use a different stream kind (uni/bi).
By specifying the options, I get an error in Xcode console like so:
running....
group state: waiting(POSIXErrorCode(rawValue: 50): Network is down)
group state: ready
Connected using QUIC!
nw_endpoint_flow_setup_cloned_protocols [C3 127.0.0.1:4567 in_progress socket-flow (satisfied (Path is satisfied), viable, interface: lo0)] could not find protocol to join in existing protocol stack
nw_endpoint_flow_failed_with_error [C3 127.0.0.1:4567 in_progress socket-flow (satisfied (Path is satisfied), viable, interface: lo0)] failed to clone from flow, moving directly to failed state
Main Connection State: failed(POSIXErrorCode(rawValue: 50): Network is down)
quic_recovery_pto PTO fired after validation
Here is my swift code:
//
// main.swift
// QuicTool
//
// Created by Andrew Paxson on 2024-01-14.
//
import Foundation
import Network
/// Helper function to create a message frame.
func createMessage(version: UInt8, messageType: UInt8, message: String) -> Data {
let messageData = message.data(using: .utf8) ?? Data()
let length = UInt32(messageData.count)
var data = Data()
data.append(version)
data.append(messageType)
// Convert length to 4 bytes and append (big-endian format)
let bigEndianLength = length.bigEndian
data.append(contentsOf: withUnsafeBytes(of: bigEndianLength) { Array($0) })
// Append 2 bytes of padding for 8-byte alignment
data.append(Data(repeating: 0, count: 2))
// Add Message Data.
data.append(messageData)
return data
}
// Queue for QUIC things.
let queue = DispatchQueue(label: "quic", qos: .userInteractive)
// Create Inital Options for the tunnel.
// This is using an insecure connection as this operation is meant to be local network.
let endpoint = NWEndpoint.hostPort(host: "127.0.0.1", port: .init(integerLiteral: 4567))
let options = NWProtocolQUIC.Options(alpn: ["demo"])
// Set the initial stream to bidirectional.
options.direction = .bidirectional
sec_protocol_options_set_verify_block(options.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in
sec_protocol_verify_complete(true)
}, queue)
let parameters = NWParameters(quic: options)
// 1) Create a new multiplexed connection
let descriptor = NWMultiplexGroup(to: endpoint)
let group = NWConnectionGroup(with: descriptor, using: parameters)
var mainConn: NWConnection? = nil
// Here we are establishing a state handler for when the connection to the
// the server is neogiated and "ready". Once its ready we want to establish a
// stream using the group with the options set.
//
// This is the main location of the issue we are seeing where the stream is
// established and the data is sent but never updated.
group.stateUpdateHandler = { newState in
print("group state: \(newState)")
switch newState {
// Once the tunnel is established, create a new stream with bidirectional parameters.
case .ready:
print("Connected using QUIC!")
// 2) In normal application I may want to open different kinds of streams in providing
// new options. Is there a better way to select the stream kind for subsequent streams?
let options = NWProtocolQUIC.Options(alpn: ["demo"])
options.direction = .bidirectional
// When providing unique options the stream will fail. Removeing the using argument works.
mainConn = group.extract()! // force unwrap
mainConn?.stateUpdateHandler = { state in
print("Main Connection State: \(state)")
switch state {
case .ready:
// Once the connection is ready, lets send some sweet data sauce.
//
// By establishing this new stream and sending data, on the server this causes the inital
// stream with no handle to be open.
let version: UInt8 = 1
let messageType: UInt8 = 1
let message = "hello, I am from the multiplex group ready."
let messageData = createMessage(version: version, messageType: messageType, message: message)
mainConn?.send(content: messageData, isComplete: true, completion: .contentProcessed({ sendError in
if let error = sendError {
print("There was an error sending data: \(error)")
} else {
print("Data was sent successfully from Main Connection.")
}
}))
default:
break
}
}
// Don't forget to start the connection.
mainConn?.start(queue: queue)
default:
break
}
}
// Receive new incoming streams initiated by the remote endpoint
// this is not used for this example.
group.newConnectionHandler = { conn in
print("New Connection: \(conn)")
// Set state update handler on incoming stream
conn.stateUpdateHandler = { newState in
print("newState: \(newState) for \(conn)")
switch newState {
case .ready:
print("got a new stream!")
default:
break
}
}
// Start the incoming stream
conn.start(queue: queue)
}
// Start the group with callback queue
group.start(queue: queue)
print("running....")
// We iterate trying to send data on the new stream we created after the
// connection is established.
while true {
switch mainConn?.state {
case .ready:
// Once the connection is ready, lets send some sweet data sauce.
let version: UInt8 = 1
let messageType: UInt8 = 1
let message = "hello, im from the main loop"
let messageData = createMessage(version: version, messageType: messageType, message: message)
print("Local Stream Send: \(messageData)")
mainConn?.send(content: messageData, completion: .contentProcessed({ sendError in
if let error = sendError {
print("There was an error sending data: \(error)")
}
}))
sleep(1)
default:
continue
}
}
Post
Replies
Boosts
Views
Activity
Strange issue, just updated my macbook to 10.15.4 and got the new Xcode 11.4 release. The actool errors on this hardware compiling texture sets as data with the macos target.Reproduction steps:New macOS projectAdd to asset catalogadd new texture set to catalogadd image to catalog (set type to data)CompileHardwareGraphics: Intel Iris Plus Graphics 645 (1536MB)Proc: 1.4 Ghz Quad Core i5Macbook pro (13in 2019 Two TB ports)RAM: 16GBcd /Users/apaxson/dev/projects/metal_by_tutorial/TestAssetCompiler
/Applications/Xcode-beta.app/Contents/Developer/usr/bin/actool --output-format human-readable-text --notices --warnings --export-dependency-info /Users/apaxson/Library/Developer/Xcode/DerivedData/TestAssetCompiler-ebhvtxoncxawpvdhxnmbieogjjqd/Build/Intermediates.noindex/TestAssetCompiler.build/Debug/TestAssetCompiler.build/assetcatalog_dependencies --output-partial-info-plist /Users/apaxson/Library/Developer/Xcode/DerivedData/TestAssetCompiler-ebhvtxoncxawpvdhxnmbieogjjqd/Build/Intermediates.noindex/TestAssetCompiler.build/Debug/TestAssetCompiler.build/assetcatalog_generated_info.plist --app-icon AppIcon --enable-on-demand-resources NO --sticker-pack-identifier-prefix com.paxsonsa.TestAssetCompiler.sticker-pack. --development-region en --target-device mac --minimum-deployment-target 10.15 --platform macosx --product-type com.apple.product-type.application --compile /Users/apaxson/Library/Developer/Xcode/DerivedData/TestAssetCompiler-ebhvtxoncxawpvdhxnmbieogjjqd/Build/Products/Debug/TestAssetCompiler.app/Contents/Resources /Users/apaxson/dev/projects/metal_by_tutorial/TestAssetCompiler/TestAssetCompiler/Textures.xcassets /Users/apaxson/dev/projects/metal_by_tutorial/TestAssetCompiler/TestAssetCompiler/Preview\ Content/Preview\ Assets.xcassets /Users/apaxson/dev/projects/metal_by_tutorial/TestAssetCompiler/TestAssetCompiler/Assets.xcassets
2020-03-26 14:04:54.297 ibtoold[9514:367395] NSFileCoordinator is doing nothing.
Assertion failed: (maxCountIncludingZeroTerminator > 0 && tokenCount < maxCountIncludingZeroTerminator), function CUIRenditionKeyCopy, file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/CoreUI/CoreUI-609.4/CoreTheme/ThemeStorage/CUIThemeRendition_Support.m, line 849.
Command CompileAssetCatalog failed with a nonzero exit code