Skip to content

Commit

Permalink
Add more options to AMQPConnectionConfiguration convenience inits
Browse files Browse the repository at this point in the history
 - These options are defaulted to nil so that if they are not provided, the defaults are used.
 - Also cleaned up some whitespace.
  • Loading branch information
xtremekforever committed Nov 20, 2024
1 parent cb9c294 commit 25b78f2
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 16 deletions.
44 changes: 33 additions & 11 deletions Sources/AMQPClient/AMQPConnectionConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ public struct AMQPConnectionConfiguration: Sendable {
public var vhost: String
public var timeout: TimeAmount
public var connectionName: String

public init(host: String? = nil,
port: Int? = nil,
user: String? = nil,
password: String? = nil,
vhost: String? = nil,
timeout: TimeAmount? = nil,
connectionName: String? = nil) {

self.host = host ?? Defaults.host
self.port = port ?? Defaults.port
self.user = user ?? Defaults.user
Expand All @@ -62,23 +62,41 @@ public extension AMQPConnectionConfiguration {
enum UrlScheme: String {
case amqp = "amqp"
case amqps = "amqps"

var defaultPort: Int {
switch self {
case .amqp: return Server.Defaults.port
case .amqps: return Server.Defaults.tlsPort
}
}
}

init(url: String, tls: TLSConfiguration? = nil) throws {

/// Convenience init to create connection configuration from a URL string + key options.
///
/// - Parameters:
/// - url: A string that contains the URL to create configuration from.
/// - tls: Optional TLS configuration. If `nil`, default will be used.
/// - sniServerName: Server name to use for TLS connection. If `nil`, default will be used.
/// - timeout: Optional connection timeout. If `nil`, default timeout will be used.
/// - connectionName: Optional connection name. If `nil`, default connection name will be used.
/// - Throws: `AMQPConnectionError.invalidUrl` if URL is invalid, or `AMQPConnectionError.invalidUrlScheme` if URL scheme is not supported.
init(url: String, tls: TLSConfiguration? = nil, sniServerName: String? = nil, timeout: TimeAmount? = nil, connectionName: String? = nil) throws {
guard let url = URL(string: url) else { throw AMQPConnectionError.invalidUrl }
try self.init(url: url, tls: tls)
try self.init(url: url, tls: tls, sniServerName: sniServerName, timeout: timeout, connectionName: connectionName)
}

init(url: URL, tls: TLSConfiguration? = nil) throws {
/// Convenience init to create connection configuration from a URL + key options.
///
/// - Parameters:
/// - url: A URL to create the configuration from.
/// - tls: Optional TLS configuration. If `nil`, default will be used.
/// - sniServerName: Server name to use for TLS connection. If `nil`, default will be used.
/// - timeout: Optional connection timeout. If `nil`, default timeout will be used.
/// - connectionName: Optional connection name. If `nil`, default connection name will be used.
/// - Throws: `AMQPConnectionError.invalidUrlScheme` if URL scheme is not supported.
init(url: URL, tls: TLSConfiguration? = nil, sniServerName: String? = nil, timeout: TimeAmount? = nil, connectionName: String? = nil) throws {
guard let scheme = UrlScheme(rawValue: url.scheme ?? "") else { throw AMQPConnectionError.invalidUrlScheme }

// there is no such thing as a "" host
let host = url.host?.isEmpty == true ? nil : url.host
//special path magic for vhost interpretation (see https://www.rabbitmq.com/uri-spec.html)
Expand All @@ -89,11 +107,15 @@ public extension AMQPConnectionConfiguration {
vhost = "/"
}

let server = Server(host: host, port: url.port ?? scheme.defaultPort, user: url.user, password: url.password?.removingPercentEncoding, vhost: vhost)

let server = Server(
host: host, port: url.port ?? scheme.defaultPort,
user: url.user, password: url.password?.removingPercentEncoding,
vhost: vhost, timeout: timeout, connectionName: connectionName
)

switch scheme {
case .amqp: self = .init(connection: .plain, server: server)
case .amqps: self = .init(connection: .tls(tls, sniServerName: nil), server: server)
case .amqps: self = .init(connection: .tls(tls, sniServerName: sniServerName), server: server)
}
}
}
Expand Down
29 changes: 24 additions & 5 deletions Tests/AMQPClientTests/AMQPConnectionConfigurationTest.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import XCTest
import AMQPClient
import NIOSSL

@testable import AMQPClient

@available(macOS 13.0, *)
Expand All @@ -14,7 +16,7 @@ final class AMQPClientConfigurationTest: XCTestCase {
XCTAssertEqual(config.server.password, "myPass")
XCTAssertEqual(config.server.vhost, "myVHost")
}

func testFromPlainURLWithDefaults() throws {
let config = try AMQPConnectionConfiguration(url: "amqp://myHost")
guard case .plain = config.connection else { return XCTFail("plain expected") }
Expand All @@ -25,7 +27,7 @@ final class AMQPClientConfigurationTest: XCTestCase {
XCTAssertEqual(config.server.password, "guest")
XCTAssertEqual(config.server.vhost, "/")
}

func testFromTlsURLWithOnlyUserAndPassword() throws {
let config = try AMQPConnectionConfiguration(url: "amqps://top:secret@")

Expand All @@ -37,7 +39,7 @@ final class AMQPClientConfigurationTest: XCTestCase {
XCTAssertEqual(config.server.password, "secret")
XCTAssertEqual(config.server.vhost, "/")
}

func testWithEmpties() throws {
let c = try AMQPConnectionConfiguration(url: "amqp://@:/")

Expand All @@ -47,7 +49,7 @@ final class AMQPClientConfigurationTest: XCTestCase {
XCTAssertEqual(c.server.password, "")
XCTAssertEqual(c.server.vhost, "")
}

func testWithUrlEncodedVHost() throws {
let c = try AMQPConnectionConfiguration(url: "amqps://hello@host:1234/%2f")

Expand All @@ -57,7 +59,7 @@ final class AMQPClientConfigurationTest: XCTestCase {
XCTAssertEqual(c.server.password, "")
XCTAssertEqual(c.server.vhost, "/")
}

func testWithUrlEncodedEverything() throws {
let c = try AMQPConnectionConfiguration(url: "amqp://user%61:%61pass@ho%61st:10000/v%2fhost")

Expand All @@ -67,4 +69,21 @@ final class AMQPClientConfigurationTest: XCTestCase {
XCTAssertEqual(c.server.password, "apass")
XCTAssertEqual(c.server.vhost, "v/host")
}

func testWithTlsConfigurationPassed() throws {
let tls = TLSConfiguration.makeClientConfiguration()
let c = try AMQPConnectionConfiguration(url: "amqps://myHost", tls: tls, sniServerName: "wow")

if case let .tls(tls, sniServerName) = c.connection {
XCTAssertNotNil(tls)
XCTAssertEqual(sniServerName, "wow")
}
}

func testWithOtherConfigurationPassed() throws {
let c = try AMQPConnectionConfiguration(url: "amqp://myHost", timeout: .seconds(10), connectionName: "MyConnection")

XCTAssertEqual(c.server.timeout, .seconds(10))
XCTAssertEqual(c.server.connectionName, "MyConnection")
}
}

0 comments on commit 25b78f2

Please sign in to comment.