From 1a8ad99c7419ff98b4ebefdcc71a3050537eaa80 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 12 Mar 2019 02:44:55 +0300 Subject: [PATCH] Make it possible for apps to provide a custom connection name Closes #155, references #154. --- RMQClient/RMQConnection.h | 40 +++- RMQClient/RMQConnection.m | 188 +++++++++++++++++- RMQClient/RMQConnectionConfig.h | 17 ++ RMQClient/RMQConnectionConfig.m | 39 ++++ RMQClient/RMQHandshaker.m | 19 +- .../ConnectionLifecycleIntegrationTest.swift | 13 ++ .../ConnectionRecoveryIntegrationTest.swift | 9 +- 7 files changed, 307 insertions(+), 18 deletions(-) diff --git a/RMQClient/RMQConnection.h b/RMQClient/RMQConnection.h index bea63729..e2c362e6 100644 --- a/RMQClient/RMQConnection.h +++ b/RMQClient/RMQConnection.h @@ -80,9 +80,9 @@ waiterFactory:(nonnull id)waiterFactory heartbeatSender:(nonnull id)heartbeatSender; -/// @brief Connection tuning, customisable TLS, all recovery options. +/// @brief Connection tuning, customisable config, all recovery options. - (nonnull instancetype)initWithUri:(nonnull NSString *)uri - tlsOptions:(nonnull RMQTLSOptions *)tlsOptions + userProvidedConnectionName:(nonnull NSString *)connectionName channelMax:(nonnull NSNumber *)channelMax frameMax:(nonnull NSNumber *)frameMax heartbeat:(nonnull NSNumber *)heartbeat @@ -96,6 +96,42 @@ recoveryAttempts:(nonnull NSNumber *)recoveryAttempts recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose; +/// @brief Connection tuning, customisable TLS, key recovery options. +- (nonnull instancetype)initWithUri:(nonnull NSString *)uri + tlsOptions:(nonnull RMQTLSOptions *)tlsOptions + delegate:(nullable id)delegate + recoverAfter:(nonnull NSNumber *)recoveryInterval + recoveryAttempts:(nonnull NSNumber *)recoveryAttempts + recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose; + +/// @brief Connection tuning, customisable TLS and connection name, key recovery options. +- (nonnull instancetype)initWithUri:(nonnull NSString *)uri + tlsOptions:(nonnull RMQTLSOptions *)tlsOptions + userProvidedConnectionName:(nullable NSString *)connectionName + delegate:(nullable id)delegate + recoverAfter:(nonnull NSNumber *)recoveryInterval + recoveryAttempts:(nonnull NSNumber *)recoveryAttempts + recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose; + +/// @brief Connection tuning, customisable TLS, all recovery options. +- (nonnull instancetype)initWithUri:(nonnull NSString *)uri + userProvidedConnectionName:(nullable NSString *)connectionName + delegate:(nullable id)delegate + recoverAfter:(nonnull NSNumber *)recoveryInterval + recoveryAttempts:(nonnull NSNumber *)recoveryAttempts + recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose; + +/// @brief Connection configuration. +- (nonnull instancetype)initWithUri:(nonnull NSString *)uri + userProvidedConnectionName:(nonnull NSString *)connectionName + delegate:(nullable id)delegate; + +/// @brief TLS, connection configuration and delegate. +- (nonnull instancetype)initWithUri:(nonnull NSString *)uri + tlsOptions:(nonnull RMQTLSOptions *)tlsOptions + userProvidedConnectionName:(nonnull NSString *)connectionName + delegate:(nullable id)delegate; + /// @brief Allows setting of timeouts - (nonnull instancetype)initWithUri:(nonnull NSString *)uri connectTimeout:(nonnull NSNumber*)connectTimeout diff --git a/RMQClient/RMQConnection.m b/RMQClient/RMQConnection.m index 56cfb9b8..a73b7f85 100644 --- a/RMQClient/RMQConnection.m +++ b/RMQClient/RMQConnection.m @@ -135,6 +135,7 @@ - (instancetype)initWithTransport:(id)transport - (nonnull instancetype)initWithUri:(NSString *)uri tlsOptions:(RMQTLSOptions *)tlsOptions + userProvidedConnectionName:(NSString *)connectionName channelMax:(nonnull NSNumber *)channelMax frameMax:(nonnull NSNumber *)frameMax heartbeat:(nonnull NSNumber *)heartbeat @@ -165,13 +166,74 @@ - (nonnull instancetype)initWithUri:(NSString *)uri RMQSemaphoreWaiterFactory *waiterFactory = [RMQSemaphoreWaiterFactory new]; RMQGCDHeartbeatSender *heartbeatSender = [[RMQGCDHeartbeatSender alloc] initWithTransport:transport clock:[RMQTickingClock new]]; + RMQProcessInfoNameGenerator *nameGenerator = [RMQProcessInfoNameGenerator new]; + RMQGCDSerialQueue *commandQueue = [[RMQGCDSerialQueue alloc] + initWithName:[nameGenerator generateWithPrefix:@"connection-commands"]]; + RMQConnectionRecover *recovery = [[RMQConnectionRecover alloc] initWithInterval:recoveryInterval + attemptLimit:recoveryAttempts + onlyErrors:!shouldRecoverFromConnectionClose + heartbeatSender:heartbeatSender + commandQueue:commandQueue + delegate:delegateProxy]; + RMQCredentials *credentials = [[RMQCredentials alloc] initWithUsername:rmqURI.username password:rmqURI.password]; + RMQConnectionConfig *config = [[RMQConnectionConfig alloc] initWithCredentials:credentials + channelMax:channelMax + frameMax:frameMax + heartbeat:heartbeat + vhost:rmqURI.vhost + authMechanism:tlsOptions.authMechanism + userProvidedConnectionName:connectionName + recovery:recovery]; + return [self initWithTransport:transport + config:config + handshakeTimeout:syncTimeout + channelAllocator:allocator + frameHandler:allocator + delegate:delegateProxy + commandQueue:commandQueue + waiterFactory:waiterFactory + heartbeatSender:heartbeatSender]; +} + +- (nonnull instancetype)initWithUri:(NSString *)uri + userProvidedConnectionName:(nonnull NSString *)connectionName + channelMax:(nonnull NSNumber *)channelMax + frameMax:(nonnull NSNumber *)frameMax + heartbeat:(nonnull NSNumber *)heartbeat + connectTimeout:(nonnull NSNumber*)connectTimeout + readTimeout:(nonnull NSNumber*)readTimeout + writeTimeout:(nonnull NSNumber*)writeTimeout + syncTimeout:(nonnull NSNumber *)syncTimeout + delegate:(id)delegate + delegateQueue:(dispatch_queue_t)delegateQueue + recoverAfter:(nonnull NSNumber *)recoveryInterval + recoveryAttempts:(nonnull NSNumber *)recoveryAttempts + recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose { + NSError *error = NULL; + RMQURI *rmqURI = [RMQURI parse:uri error:&error]; + + RMQTCPSocketTransport *transport = [[RMQTCPSocketTransport alloc] initWithHost:rmqURI.host + port:rmqURI.portNumber + tlsOptions:[RMQTLSOptions fromURI:uri] + connectTimeout:connectTimeout + readTimeout:readTimeout + writeTimeout:writeTimeout]; + RMQMultipleChannelAllocator *allocator = [[RMQMultipleChannelAllocator alloc] + initWithMaxCapacity:[channelMax unsignedIntegerValue] + channelSyncTimeout:syncTimeout]; + RMQQueuingConnectionDelegateProxy *delegateProxy = [[RMQQueuingConnectionDelegateProxy alloc] + initWithDelegate:delegate + queue:delegateQueue]; + RMQSemaphoreWaiterFactory *waiterFactory = [RMQSemaphoreWaiterFactory new]; + RMQGCDHeartbeatSender *heartbeatSender = [[RMQGCDHeartbeatSender alloc] initWithTransport:transport + clock:[RMQTickingClock new]]; RMQProcessInfoNameGenerator *nameGenerator = [RMQProcessInfoNameGenerator new]; RMQGCDSerialQueue *commandQueue = [[RMQGCDSerialQueue alloc] - initWithName:[nameGenerator generateWithPrefix:@"connection-commands"]]; + initWithName:[nameGenerator generateWithPrefix:@"connection-commands"]]; RMQConnectionRecover *recovery = [[RMQConnectionRecover alloc] initWithInterval:recoveryInterval attemptLimit:recoveryAttempts onlyErrors:!shouldRecoverFromConnectionClose @@ -179,12 +241,15 @@ - (nonnull instancetype)initWithUri:(NSString *)uri commandQueue:commandQueue delegate:delegateProxy]; + RMQCredentials *credentials = [[RMQCredentials alloc] initWithUsername:rmqURI.username + password:rmqURI.password]; RMQConnectionConfig *config = [[RMQConnectionConfig alloc] initWithCredentials:credentials channelMax:channelMax frameMax:frameMax heartbeat:heartbeat vhost:rmqURI.vhost - authMechanism:tlsOptions.authMechanism + authMechanism:@"PLAIN" + userProvidedConnectionName:connectionName recovery:recovery]; return [self initWithTransport:transport config:config @@ -197,11 +262,127 @@ - (nonnull instancetype)initWithUri:(NSString *)uri heartbeatSender:heartbeatSender]; } +- (nonnull instancetype)initWithUri:(NSString *)uri + userProvidedConnectionName:(NSString *)connectionName + delegate:(id)delegate + recoverAfter:(nonnull NSNumber *)recoveryInterval + recoveryAttempts:(nonnull NSNumber *)recoveryAttempts + recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose { + return [self initWithUri:uri + tlsOptions:[RMQTLSOptions fromURI:uri] + userProvidedConnectionName:connectionName + delegate:delegate + recoverAfter:recoveryInterval + recoveryAttempts:recoveryAttempts + recoverFromConnectionClose:shouldRecoverFromConnectionClose]; +} + +- (nonnull instancetype)initWithUri:(NSString *)uri + userProvidedConnectionName:(NSString *)connectionName + delegate:(id)delegate { + return [self initWithUri:uri + tlsOptions:[RMQTLSOptions fromURI:uri] + userProvidedConnectionName:connectionName + delegate:delegate + recoverAfter:RMQDefaultRecoveryInterval + recoveryAttempts:@(NSUIntegerMax) + recoverFromConnectionClose:YES]; +} + +- (nonnull instancetype)initWithUri:(NSString *)uri + tlsOptions:(RMQTLSOptions *)tlsOptions + delegate:(id)delegate + recoverAfter:(nonnull NSNumber *)recoveryInterval + recoveryAttempts:(nonnull NSNumber *)recoveryAttempts + recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose { + return [self initWithUri:uri + tlsOptions:tlsOptions + userProvidedConnectionName:NULL + delegate:delegate + recoverAfter:recoveryInterval + recoveryAttempts:recoveryAttempts + recoverFromConnectionClose:shouldRecoverFromConnectionClose]; +} + +- (nonnull instancetype)initWithUri:(NSString *)uri + tlsOptions:(RMQTLSOptions *)tlsOptions + userProvidedConnectionName:(NSString *)connectionName + delegate:(id)delegate + recoverAfter:(nonnull NSNumber *)recoveryInterval + recoveryAttempts:(nonnull NSNumber *)recoveryAttempts + recoverFromConnectionClose:(BOOL)shouldRecoverFromConnectionClose { + NSError *error = NULL; + RMQURI *rmqURI = [RMQURI parse:uri error:&error]; + + RMQTCPSocketTransport *transport = [[RMQTCPSocketTransport alloc] initWithHost:rmqURI.host + port:rmqURI.portNumber + tlsOptions:[RMQTLSOptions fromURI:uri] + connectTimeout:RMQDefaultConnectTimeout + readTimeout:RMQDefaultReadTimeout + writeTimeout:RMQDefaultWriteTimeout]; + RMQMultipleChannelAllocator *allocator = [[RMQMultipleChannelAllocator alloc] + initWithMaxCapacity:[@(RMQChannelMaxDefault) unsignedIntegerValue] + channelSyncTimeout:RMQDefaultSyncTimeout]; + RMQQueuingConnectionDelegateProxy *delegateProxy = + [[RMQQueuingConnectionDelegateProxy alloc] + initWithDelegate:delegate + queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; + RMQSemaphoreWaiterFactory *waiterFactory = [RMQSemaphoreWaiterFactory new]; + RMQGCDHeartbeatSender *heartbeatSender = [[RMQGCDHeartbeatSender alloc] initWithTransport:transport + clock:[RMQTickingClock new]]; + + + RMQProcessInfoNameGenerator *nameGenerator = [RMQProcessInfoNameGenerator new]; + RMQGCDSerialQueue *commandQueue = [[RMQGCDSerialQueue alloc] + initWithName:[nameGenerator generateWithPrefix:@"connection-commands"]]; + RMQConnectionRecover *recovery = [[RMQConnectionRecover alloc] initWithInterval:recoveryInterval + attemptLimit:recoveryAttempts + onlyErrors:!shouldRecoverFromConnectionClose + heartbeatSender:heartbeatSender + commandQueue:commandQueue + delegate:delegateProxy]; + + RMQCredentials *credentials = [[RMQCredentials alloc] initWithUsername:rmqURI.username + password:rmqURI.password]; + RMQConnectionConfig *config = [[RMQConnectionConfig alloc] initWithCredentials:credentials + channelMax:@(RMQChannelMaxDefault) + frameMax:@(RMQFrameMax) + heartbeat:RMQDefaultHeartbeatTimeout + vhost:rmqURI.vhost + authMechanism:tlsOptions.authMechanism + userProvidedConnectionName:connectionName + recovery:recovery]; + + return [self initWithTransport:transport + config:config + handshakeTimeout:RMQDefaultHeartbeatTimeout + channelAllocator:allocator + frameHandler:allocator + delegate:delegateProxy + commandQueue:commandQueue + waiterFactory:waiterFactory + heartbeatSender:heartbeatSender]; +} + +- (instancetype)initWithUri:(NSString *)uri + tlsOptions:(RMQTLSOptions *)tlsOptions + userProvidedConnectionName:(nonnull NSString *)connectionName + delegate:(id)delegate { + return [self initWithUri:uri + tlsOptions:tlsOptions + userProvidedConnectionName:connectionName + delegate:delegate + recoverAfter:RMQDefaultRecoveryInterval + recoveryAttempts:@(NSUIntegerMax) + recoverFromConnectionClose:YES]; +} + - (instancetype)initWithUri:(NSString *)uri delegate:(id)delegate recoverAfter:(NSNumber *)recoveryInterval { return [self initWithUri:uri tlsOptions:[RMQTLSOptions fromURI:uri] + userProvidedConnectionName:NULL channelMax:@(RMQChannelMaxDefault) frameMax:@(RMQFrameMax) heartbeat:RMQDefaultHeartbeatTimeout @@ -220,6 +401,7 @@ - (instancetype)initWithUri:(NSString *)uri delegate:(id)delegate { return [self initWithUri:uri tlsOptions:[RMQTLSOptions fromURI:uri] + userProvidedConnectionName:NULL channelMax:@(RMQChannelMaxDefault) frameMax:@(RMQFrameMax) heartbeat:RMQDefaultHeartbeatTimeout @@ -241,6 +423,7 @@ - (instancetype)initWithUri:(NSString *)uri delegate:(id)delegate { return [self initWithUri:uri tlsOptions:[RMQTLSOptions fromURI:uri] + userProvidedConnectionName:NULL channelMax:@(RMQChannelMaxDefault) frameMax:@(RMQFrameMax) heartbeat:RMQDefaultHeartbeatTimeout @@ -268,6 +451,7 @@ - (instancetype)initWithUri:(NSString *)uri delegateQueue:(dispatch_queue_t)delegateQueue { return [self initWithUri:uri tlsOptions:tlsOptions + userProvidedConnectionName:NULL channelMax:channelMax frameMax:frameMax heartbeat:heartbeat diff --git a/RMQClient/RMQConnectionConfig.h b/RMQClient/RMQConnectionConfig.h index 6e88c748..85913990 100644 --- a/RMQClient/RMQConnectionConfig.h +++ b/RMQClient/RMQConnectionConfig.h @@ -63,6 +63,7 @@ extern NSInteger const RMQChannelMaxDefault; @property (nonnull, nonatomic, readonly) NSString *vhost; @property (nonnull, nonatomic, readonly) RMQCredentials *credentials; @property (nonnull, nonatomic, readonly) NSString *authMechanism; +@property (nonatomic, readonly) NSString *userProvidedConnectionName; @property (nonnull, nonatomic, readonly) id recovery; - (nonnull instancetype)initWithCredentials:(nonnull RMQCredentials *)credentials @@ -78,4 +79,20 @@ extern NSInteger const RMQChannelMaxDefault; vhost:(nonnull NSString *)vhost authMechanism:(nonnull NSString *)authMechanism recovery:(nonnull id)recovery; + +- (nonnull instancetype)initWithCredentials:(nonnull RMQCredentials *)credentials + channelMax:(nonnull NSNumber *)channelMax + frameMax:(nonnull NSNumber *)frameMax + heartbeat:(nonnull NSNumber *)heartbeat + vhost:(nonnull NSString *)vhost + authMechanism:(nonnull NSString *)authMechanism + userProvidedConnectionName:(nonnull NSString *)userProvidedConnectionName + recovery:(nonnull id)recovery; + +- (nonnull instancetype)initWithCredentials:(nonnull RMQCredentials *)credentials + heartbeat:(nonnull NSNumber *)heartbeat + vhost:(nonnull NSString *)vhost + authMechanism:(nonnull NSString *)authMechanism + userProvidedConnectionName:(nonnull NSString *)userProvidedConnectionName + recovery:(nonnull id)recovery; @end diff --git a/RMQClient/RMQConnectionConfig.m b/RMQClient/RMQConnectionConfig.m index 1e7d8dbd..4e153ea7 100644 --- a/RMQClient/RMQConnectionConfig.m +++ b/RMQClient/RMQConnectionConfig.m @@ -66,6 +66,7 @@ @interface RMQConnectionConfig () @property (nonnull, nonatomic, readwrite) NSString *vhost; @property (nonnull, nonatomic, readwrite) RMQCredentials *credentials; @property (nonnull, nonatomic, readwrite) NSString *authMechanism; +@property (nonatomic, readwrite) NSString *userProvidedConnectionName; @property (nonnull, nonatomic, readwrite) id recovery; @end @@ -103,4 +104,42 @@ - (instancetype)initWithCredentials:(RMQCredentials *)credentials authMechanism:authMechanism recovery:recovery]; } + +- (instancetype)initWithCredentials:(RMQCredentials *)credentials + channelMax:(NSNumber *)channelMax + frameMax:(NSNumber *)frameMax + heartbeat:(NSNumber *)heartbeat + vhost:(nonnull NSString *)vhost + authMechanism:(nonnull NSString *)authMechanism + userProvidedConnectionName:(nonnull NSString *)userProvidedConnectionName + recovery:(nonnull id)recovery { + self = [super init]; + if (self) { + self.credentials = credentials; + self.channelMax = channelMax; + self.frameMax = frameMax; + self.heartbeat = heartbeat; + self.vhost = vhost; + self.authMechanism = authMechanism; + self.userProvidedConnectionName = userProvidedConnectionName; + self.recovery = recovery; + } + return self; +} + +- (instancetype)initWithCredentials:(RMQCredentials *)credentials + heartbeat:(NSNumber *)heartbeat + vhost:(nonnull NSString *)vhost + authMechanism:(nonnull NSString *)authMechanism + userProvidedConnectionName:(nonnull NSString *)userProvidedConnectionName + recovery:(nonnull id)recovery { + return [self initWithCredentials:credentials + channelMax:[NSNumber numberWithInteger:RMQChannelMaxDefault] + frameMax:[NSNumber numberWithInteger:RMQFrameMax] + heartbeat:heartbeat + vhost:vhost + authMechanism:authMechanism + userProvidedConnectionName:userProvidedConnectionName + recovery:recovery]; +} @end diff --git a/RMQClient/RMQHandshaker.m b/RMQClient/RMQHandshaker.m index 0c92fd14..59e8ef72 100644 --- a/RMQClient/RMQHandshaker.m +++ b/RMQClient/RMQHandshaker.m @@ -110,12 +110,19 @@ - (RMQConnectionStartOk *)startOk { NSBundle *bundle = [NSBundle bundleWithIdentifier:@"io.pivotal.RMQClient"]; NSString *version = bundle.infoDictionary[@"CFBundleShortVersionString"]; - RMQTable *clientProperties = [[RMQTable alloc] init: - @{@"capabilities" : capabilities, - @"product" : [[RMQLongstr alloc] init:@"RMQClient"], - @"platform" : [[RMQLongstr alloc] init:@"iOS"], - @"version" : [[RMQLongstr alloc] init:version], - @"information" : [[RMQLongstr alloc] init:@"https://github.com/rabbitmq/rabbitmq-objc-client"]}]; + NSDictionary *libraryProperties = @{@"capabilities" : capabilities, + @"product" : [[RMQLongstr alloc] init:@"RMQClient"], + @"platform" : [[RMQLongstr alloc] init:@"iOS"], + @"version" : [[RMQLongstr alloc] init:version], + @"information" : [[RMQLongstr alloc] init:@"https://github.com/rabbitmq/rabbitmq-objc-client"]}; + NSMutableDictionary *combinedProperties = [[NSMutableDictionary alloc] initWithDictionary:libraryProperties]; + + NSString *userProvidedConnectionName = [self.config userProvidedConnectionName]; + if (userProvidedConnectionName != nil) { + [combinedProperties setObject:[[RMQLongstr alloc] init:userProvidedConnectionName] + forKey:@"connection_name"]; + } + RMQTable *clientProperties = [[RMQTable alloc] init:combinedProperties]; return [[RMQConnectionStartOk alloc] initWithClientProperties:clientProperties mechanism:[[RMQShortstr alloc] init:self.config.authMechanism] diff --git a/RMQClientIntegrationTests/ConnectionLifecycleIntegrationTest.swift b/RMQClientIntegrationTests/ConnectionLifecycleIntegrationTest.swift index 9e85eb5b..c30ec02e 100644 --- a/RMQClientIntegrationTests/ConnectionLifecycleIntegrationTest.swift +++ b/RMQClientIntegrationTests/ConnectionLifecycleIntegrationTest.swift @@ -120,5 +120,18 @@ class ConnectionLifecycleIntegrationTest: XCTestCase { XCTAssertNotNil(props["product"] ?? nil) XCTAssertNotNil(props["version"] ?? nil) XCTAssertNotNil(props["capabilities"] ?? nil) + + conn.blockingClose() + } + + func testUserProvidedConnectionName() { + let conn = RMQConnection(uri: IntegrationHelper.defaultEndpoint, + userProvidedConnectionName: "testUserProvidedConnectionName.1", + delegate: RMQConnectionDelegateLogger()) + conn.start() + XCTAssertTrue(IntegrationHelper.pollUntilConnected(conn)) + + XCTAssert(conn.isOpen()) + conn.blockingClose() } } diff --git a/RMQClientIntegrationTests/ConnectionRecoveryIntegrationTest.swift b/RMQClientIntegrationTests/ConnectionRecoveryIntegrationTest.swift index dead8d6c..b494395d 100644 --- a/RMQClientIntegrationTests/ConnectionRecoveryIntegrationTest.swift +++ b/RMQClientIntegrationTests/ConnectionRecoveryIntegrationTest.swift @@ -157,15 +157,8 @@ class ConnectionRecoveryIntegrationTest: XCTestCase { let conn = RMQConnection(uri: plainEndpoint, tlsOptions: RMQTLSOptions.fromURI(plainEndpoint), - channelMax: RMQChannelMaxDefault as NSNumber, - frameMax: RMQFrameMax as NSNumber, - heartbeat: 10, - connectTimeout: 15, - readTimeout: 30, - writeTimeout: 30, - syncTimeout: 10, + userProvidedConnectionName: nil, delegate: delegate, - delegateQueue: DispatchQueue.main, recoverAfter: recoveryInterval as NSNumber, recoveryAttempts: 5, recoverFromConnectionClose: true)