Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
56fc64e
Unchecked sendable everywhere but builds with a change in C and a lot…
waahm7 Dec 27, 2024
2e659ac
some docs
waahm7 Dec 28, 2024
16c705b
warning fix plus tests in progress
waahm7 Dec 30, 2024
149abc4
non working
waahm7 Dec 30, 2024
853fa9e
More fixes
waahm7 Dec 30, 2024
b12eebd
more fixes
waahm7 Jan 6, 2025
5b3f624
more fixes
waahm7 Jan 6, 2025
2f3aac0
update comment
waahm7 Jan 6, 2025
703b650
fix test
waahm7 Jan 6, 2025
2478627
more fixes
waahm7 Jan 6, 2025
2aa220f
reset package.swift
waahm7 Jan 6, 2025
75aea3f
fix stuff
waahm7 Jan 6, 2025
e35ed11
unchecked
waahm7 Jan 6, 2025
015d98c
Some ifdef
waahm7 Jan 7, 2025
54d17c3
try swift 6
waahm7 Jan 7, 2025
82f02db
try ci
waahm7 Jan 7, 2025
28d5c83
sed mac
waahm7 Jan 7, 2025
2c7b1e3
remove it
waahm7 Jan 7, 2025
45cf8d8
Merge branch 'main' into swift6-support
waahm7 Apr 3, 2025
b179c0a
MQTT changes for Swift 6
waahm7 Apr 3, 2025
3322408
unchecked sendable for FileBasedConfiguration
waahm7 Apr 3, 2025
054f55b
manager sendable
waahm7 Apr 3, 2025
ddb7601
gen ai stop adding unnecassary code
waahm7 Apr 3, 2025
8ed9511
Use argument parser for ElasticCurl
waahm7 Apr 4, 2025
dbeeb11
red code is best code
waahm7 Apr 4, 2025
35827ab
huh?
waahm7 Apr 4, 2025
851e492
lint
waahm7 Apr 4, 2025
b047034
try swift 6 CI
waahm7 Apr 4, 2025
a62b69e
checkout
waahm7 Apr 4, 2025
2a50b11
15?15?15?15?15?15?15?15?15?15?15?15?15?15?15?
waahm7 Apr 4, 2025
d708dd7
submodules
waahm7 Apr 4, 2025
6928a56
remove mqtt test for now, too many changes required
waahm7 Apr 4, 2025
62cbbab
disable warning
waahm7 Apr 7, 2025
451d671
request option sendable
waahm7 Apr 8, 2025
f9d294e
place hold for request responds client
xiazhvera Apr 8, 2025
dbcda29
rename struct
xiazhvera Apr 8, 2025
ee67970
Expose Endpoint Property directly?
waahm7 Apr 8, 2025
9b16f05
Update signing config
waahm7 Apr 14, 2025
e736583
update testsemaphore
xiazhvera Apr 14, 2025
7058122
use expectation instead of semaphore
xiazhvera Apr 14, 2025
3459187
clean up test
xiazhvera Apr 14, 2025
3435f04
fix test waiting options
xiazhvera Apr 14, 2025
3fe043e
fix retain test
xiazhvera Apr 14, 2025
e9c738f
add help function to return expecation waiter result
xiazhvera Apr 14, 2025
287edee
format fix
xiazhvera Apr 14, 2025
d5adf13
rename handler&initial tests
xiazhvera Apr 15, 2025
24f2f4b
clean up mqtt5 tests
xiazhvera Apr 15, 2025
580affa
Merge branch 'swift6-support-mqtt' of https://github.com/awslabs/aws-…
xiazhvera Apr 15, 2025
cd365bc
add help functions for connection
xiazhvera Apr 15, 2025
b606679
enable dispatch queue on apple platforms
xiazhvera Apr 15, 2025
4697c66
implement rr client creation/destruction
xiazhvera Apr 15, 2025
0b97e87
clean up documentation
xiazhvera Apr 15, 2025
f98f86a
fix class name
xiazhvera Apr 15, 2025
6410712
validate conversion to native
xiazhvera Apr 15, 2025
f7ea06a
Merge remote-tracking branch 'origin/default_eventloop' into swift6-s…
waahm7 Apr 16, 2025
2738602
Merge branch 'swift6-support' of https://github.com/awslabs/aws-crt-s…
xiazhvera Apr 16, 2025
c514649
more comments and Senable enums
xiazhvera Apr 16, 2025
503c260
Merge branch 'swift6-support-mqtt' of https://github.com/awslabs/aws-…
xiazhvera Apr 16, 2025
cd7aabe
more justification on sendable classes
xiazhvera Apr 16, 2025
7a2cfff
fix merge
xiazhvera Apr 16, 2025
44848dd
Merge branch 'swift6-support-mqtt' of https://github.com/awslabs/aws-…
xiazhvera Apr 16, 2025
f637923
set default value for client options
xiazhvera Apr 16, 2025
590263f
update code review & sendable objects
xiazhvera Apr 17, 2025
14c5bd7
lint
xiazhvera Apr 17, 2025
1734af0
update cr
xiazhvera Apr 21, 2025
c2d5f08
Merge branch 'main' of https://github.com/awslabs/aws-crt-swift into …
xiazhvera Apr 28, 2025
28688ec
Merge branch 'main' into rr_workspace
xiazhvera Apr 30, 2025
050382c
fix merge changes
xiazhvera May 8, 2025
c3c53c5
Merge branch 'main' into rr_workspace
xiazhvera May 14, 2025
99e9beb
Merge branch 'main' of https://github.com/awslabs/aws-crt-swift into …
xiazhvera May 19, 2025
32a0bb3
fix format on files
xiazhvera May 19, 2025
f9ed271
rename MqttRequestResponse
xiazhvera May 19, 2025
97e9b47
remove default value for request response client options
xiazhvera Jun 4, 2025
2679225
Merge branch 'main' into rr_workspace
xiazhvera Jun 4, 2025
d3df57c
update code review changes
xiazhvera Jun 5, 2025
60d557c
remove newFromMqtt5Client
xiazhvera Jun 5, 2025
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
3 changes: 2 additions & 1 deletion Source/AwsCommonRuntimeKit/mqtt/Mqtt5Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ public final class Mqtt5Client: Sendable {
// The rawValue is mutable cross threads and protected by the rwlock.
/// Mqtt5 Client Core, internal class to handle Mqtt5 Client operations
internal class Mqtt5ClientCore: @unchecked Sendable {
fileprivate var rawValue: UnsafeMutablePointer<aws_mqtt5_client>?
// the rawValue is marked as internal to allow rr client to access it
internal var rawValue: UnsafeMutablePointer<aws_mqtt5_client>?
fileprivate let rwlock = ReadWriteLock()

///////////////////////////////////////
Expand Down
352 changes: 352 additions & 0 deletions Source/AwsCommonRuntimeKit/mqtt/MqttRequestResponseClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
/// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
/// SPDX-License-Identifier: Apache-2.0.
import AwsCMqtt
import LibNative
import Foundation

/**
* The type of change to the state of a streaming operation subscription
*/
public enum SubscriptionStatusEventType: Sendable {
/**
* The streaming operation is successfully subscribed to its topic (filter)
*/
case established

/**
* The streaming operation has temporarily lost its subscription to its topic (filter)
*/
case lost

/**
* The streaming operation has entered a terminal state where it has given up trying to subscribe
* to its topic (filter). This is always due to user error (bad topic filter or IoT Core permission
* policy).
*/
case halted
}

extension SubscriptionStatusEventType {
/// Returns the native representation of the Swift enum
var rawValue: aws_rr_streaming_subscription_event_type {
switch self {
case .established: return ARRSSET_SUBSCRIPTION_ESTABLISHED
case .lost: return ARRSSET_SUBSCRIPTION_LOST
case .halted: return ARRSSET_SUBSCRIPTION_HALTED
}
}

/// Initializes Swift enum from native representation
init(_ cEnum: aws_rr_streaming_subscription_event_type) {
switch cEnum {
case ARRSSET_SUBSCRIPTION_ESTABLISHED:
self = .established
case ARRSSET_SUBSCRIPTION_LOST:
self = .lost
case ARRSSET_SUBSCRIPTION_HALTED:
self = .halted
default:
fatalError("Unknown Susbscription Event Type")
}
}
}

/// An event that describes a change in subscription status for a streaming operation.
public struct SubscriptionStatusEvent: Sendable {
/// The type of the event
public let event: SubscriptionStatusEventType

/// An optional error code associated with the event. Only set for SubscriptionLost and SubscriptionHalted.
public let error: CRTError?
}

// TODO: Igor has updated the events for IoT Command. Need update later
/// An event that describes an incoming publish message received on a streaming operation.
public struct IncomingPublishEvent: Sendable {

/// The payload of the publish message in a byte buffer format
let payload: Data

/// The topic associated with this PUBLISH packet.
let topic: String

// TODO: More options for IoT Command changes
}

/// Function signature of a SubscriptionStatusEvent event handler
public typealias SubscriptionStatusEventHandler = @Sendable (SubscriptionStatusEvent) -> Void

/// Function signature of an IncomingPublishEvent event handler
public typealias IncomingPublishEventHandler = @Sendable (IncomingPublishEvent) -> Void

/// Encapsulates a response to an AWS IoT Core MQTT-based service request
public struct MqttRequestResponse: Sendable {
let topic: String
let payload: Data

public init(topic: String, payload: Data) {
self.topic = topic
self.payload = payload
}
}

// We can't mutate this class after initialization. Swift can not verify the sendability due to direct use of c pointer,
// so mark it unchecked Sendable
/// A response path is a pair of values - MQTT topic and a JSON path - that describe where a response to
/// an MQTT-based request may arrive. For a given request type, there may be multiple response paths and each
/// one is associated with a separate JSON schema for the response body.
public class ResponsePath: CStruct, @unchecked Sendable {
let topic: String
let correlationTokenJsonPath: String

init(topic: String, correlationTokenJsonPath: String) {
self.topic = topic
self.correlationTokenJsonPath = correlationTokenJsonPath

withByteCursorFromStrings(self.topic, self.correlationTokenJsonPath) {
cTopicCursor, cCorrelationTokenCursor in
aws_byte_buf_init_copy_from_cursor(&self.topic_buffer, allocator, cTopicCursor)
aws_byte_buf_init_copy_from_cursor(
&self.correlation_token_buffer, allocator, cCorrelationTokenCursor)
}
}

typealias RawType = aws_mqtt_request_operation_response_path
func withCStruct<Result>(_ body: (aws_mqtt_request_operation_response_path) -> Result) -> Result {
var raw_option = aws_mqtt_request_operation_response_path()
raw_option.topic = aws_byte_cursor_from_buf(&self.topic_buffer)
raw_option.correlation_token_json_path = aws_byte_cursor_from_buf(
&self.correlation_token_buffer)
return body(raw_option)
}

// We keep a memory of the buffer storage in the class, and release it on
// destruction
private var topic_buffer: aws_byte_buf = aws_byte_buf()
private var correlation_token_buffer: aws_byte_buf = aws_byte_buf()

deinit {
aws_byte_buf_clean_up(&topic_buffer)
aws_byte_buf_clean_up(&correlation_token_buffer)
}
}

/// Configuration options for request response operation
public struct RequestResponseOperationOptions: CStructWithUserData, Sendable {
let subscriptionTopicFilters: [String]
let responsePaths: [ResponsePath]
let topic: String
let payload: Data
let correlationToken: String?

public init(
subscriptionTopicFilters: [String], responsePaths: [ResponsePath], topic: String, payload: Data,
correlationToken: String?
) {
self.subscriptionTopicFilters = subscriptionTopicFilters
self.responsePaths = responsePaths
self.topic = topic
self.payload = payload
self.correlationToken = correlationToken
}

func validateConversionToNative() throws {
}

typealias RawType = aws_mqtt_request_operation_options
func withCStruct<Result>(userData: UnsafeMutableRawPointer?, _ body: (RawType) -> Result)
-> Result
{
// TODO: convert into aws_mqtt_request_operation_options
let options = aws_mqtt_request_operation_options()
return body(options)
}

}

/// Configuration options for streaming operations
public struct StreamingOperationOptions: CStruct, Sendable {
let subscriptionStatusEventHandler: SubscriptionStatusEventHandler
let incomingPublishEventHandler: IncomingPublishEventHandler
let topicFilter: String

public init() {
// TODO: INIT THE MEMBERS
self.subscriptionStatusEventHandler = { _ in return; }
self.incomingPublishEventHandler = { _ in return; }
self.topicFilter = ""
}

typealias RawType = aws_mqtt_streaming_operation_options
func withCStruct<Result>(_ body: (RawType) -> Result) -> Result {
// TODO: convert into aws_mqtt_request_operation_options
let options = aws_mqtt_streaming_operation_options()
return body(options)
}

}

/// A streaming operation is automatically closed (and an MQTT unsubscribe triggered) when its
// destructor is invoked.
public class StreamingOperation {
fileprivate var rawValue: OpaquePointer? // <aws_mqtt_rr_client_operation>?

public init() {
// TODO: INIT THE MEMBERS
self.rawValue = nil
}

/// Opens a streaming operation by making the appropriate MQTT subscription with the broker.
public func open() {
// TODO: open the stream
}

deinit {
// TODO: close the oepration
}
}

// We can't mutate this class after initialization. Swift can not verify the sendability due to the class is non-final,
// so mark it unchecked Sendable
/// Request-response client configuration options
public class MqttRequestResponseClientOptions: CStructWithUserData, @unchecked Sendable {

/// Maximum number of subscriptions that the client will concurrently use for request-response operations.
public let maxRequestResponseSubscription: Int

/// Maximum number of subscriptions that the client will concurrently use for streaming operations.
public let maxStreamingSubscription: Int

/// Duration, in seconds, that a request-response operation will wait for completion before giving up.
public let operationTimeout: TimeInterval

public init(
maxRequestResponseSubscription: Int, maxStreamingSubscription: Int,
operationTimeout: TimeInterval
) {
self.maxStreamingSubscription = maxStreamingSubscription
self.maxRequestResponseSubscription = maxRequestResponseSubscription
self.operationTimeout = operationTimeout
}

func validateConversionToNative() throws {
do {
_ = try self.operationTimeout.secondUInt32()
} catch {
throw CommonRunTimeError.crtError(
CRTError(
code: AWS_ERROR_INVALID_ARGUMENT.rawValue,
context: "Invalid operationTimeout value"))
}
}

typealias RawType = aws_mqtt_request_response_client_options
func withCStruct<Result>(
userData: UnsafeMutableRawPointer?, _ body: (aws_mqtt_request_response_client_options) -> Result
) -> Result {
var options = aws_mqtt_request_response_client_options()
options.max_request_response_subscriptions = self.maxRequestResponseSubscription
options.max_streaming_subscriptions = self.maxStreamingSubscription
if let _operationTimeout: UInt32 = try? self.operationTimeout.secondUInt32() {
options.operation_timeout_seconds = _operationTimeout
}
options.terminated_callback = MqttRRClientTerminationCallback
options.user_data = userData
return body(options)
}
}

internal func MqttRRClientTerminationCallback(_ userData: UnsafeMutableRawPointer?) {
// Termination callback. This is triggered when the native client is terminated.
// It is safe to release the request response client at this point.
// `takeRetainedValue()` will release the client reference. IT SHOULD ONLY BE CALLED AFTER YOU RELEASE THE CLIENT.
_ = Unmanaged<MqttRequestResponseClientCore>.fromOpaque(userData!).takeRetainedValue()
}

// IMPORTANT: You are responsible for concurrency correctness of MqttRequestResponseClientCore.
// rawValue is only modified by the close() function, which is called exclusively in the
// MqttRequestResponseClient destructor. At that point, no operations should be in progress.
// Therefore, MqttRequestResponseClientCore can be considered thread-safe.
internal class MqttRequestResponseClientCore: @unchecked Sendable {
fileprivate var rawValue: OpaquePointer? // aws_mqtt_request_response_client

internal init(mqttClient: Mqtt5Client, options: MqttRequestResponseClientOptions) throws {
guard
let rawValue =
(options.withCPointer(
userData: Unmanaged<MqttRequestResponseClientCore>.passRetained(self).toOpaque()
) { optionsPointer in
return aws_mqtt_request_response_client_new_from_mqtt5_client(
allocator, mqttClient.clientCore.rawValue, optionsPointer)
})
else {
// Failed to create client, release the callback core
Unmanaged<MqttRequestResponseClientCore>.passUnretained(self).release()
throw CommonRunTimeError.crtError(.makeFromLastError())
}
self.rawValue = rawValue
}

/// submit a request responds operation, throws CRTError if the operation failed
public func submitRequest(operationOptions: RequestResponseOperationOptions) async throws
-> MqttRequestResponse
{
// TODO: sumibt request
return MqttRequestResponse(topic: "", payload: Data())
}

/// create a stream operation, throws CRTError if the creation failed. open() must be called on the operation to start the stream.
public func createStream(streamOptions: StreamingOperationOptions) throws -> StreamingOperation {
// TODO: create streamming operation
return StreamingOperation()
}

/// release the request response client. You must not use the client after calling `close()`.
public func close() {
aws_mqtt_request_response_client_release(self.rawValue)
self.rawValue = nil
}

}

public class MqttRequestResponseClient {
fileprivate var clientCore: MqttRequestResponseClientCore

/// Creates a new request-response client using an MQTT5 client for protocol transport
///
/// - Parameters:
/// - mqtt5Client: MQTT5 client that the request-response client should use as transport
/// - options: request-response client configuration options
///
/// - Throws: CommonRuntimeError.crtError if creation failed
public init(mqtt5Client: Mqtt5Client, options: MqttRequestResponseClientOptions) throws {
clientCore = try MqttRequestResponseClientCore(mqttClient: mqtt5Client, options: options)
}

/// Submit a request responds operation, throws CRTError if the operation failed
///
/// - Parameters:
/// - operationOptions: configuration options for request response operation
/// - Returns: MqttRequestResponse
/// - Throws: CommonRuntimeError.crtError if submit failed
public func submitRequest(operationOptions: RequestResponseOperationOptions) async throws
-> MqttRequestResponse
{
return try await clientCore.submitRequest(operationOptions: operationOptions)
}

/// Create a stream operation, throws CRTError if the creation failed. You would need call open() on the operation to start the stream
/// - Parameters:
/// - streamOptions: Configuration options for streaming operations
/// - Returns:
/// - StreamingOperation
/// - Throws:CommonRuntimeError.crtError if creation failed
public func createStream(streamOptions: StreamingOperationOptions) throws -> StreamingOperation {
return try clientCore.createStream(streamOptions: streamOptions)
}

deinit {
self.clientCore.close()
}

}
Loading