// This source code is dual-licensed under the Mozilla Public License ("MPL"), // version 1.1 and the Apache License ("ASL"), version 2.0. // // The ASL v2.0: // // --------------------------------------------------------------------------- // Copyright 2016 Pivotal Software, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // --------------------------------------------------------------------------- // // The MPL v1.1: // // --------------------------------------------------------------------------- // The contents of this file are subject to the Mozilla Public License // Version 1.1 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // https://www.mozilla.org/MPL/ // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations // under the License. // // The Original Code is RabbitMQ // // The Initial Developer of the Original Code is Pivotal Software, Inc. // All Rights Reserved. // // Alternatively, the contents of this file may be used under the terms // of the Apache Standard license (the "ASL License"), in which case the // provisions of the ASL License are applicable instead of those // above. If you wish to allow use of your version of this file only // under the terms of the ASL License and not to allow others to use // your version of this file under the MPL, indicate your decision by // deleting the provisions above and replace them with the notice and // other provisions required by the ASL License. If you do not delete // the provisions above, a recipient may use your version of this file // under either the MPL or the ASL License. // --------------------------------------------------------------------------- #import "RMQHandshaker.h" @interface RMQHandshaker () @property (nonatomic, weak) id sender; @property (nonatomic, weak) RMQConnectionConfig *config; @property (nonatomic, copy) void (^completionHandler)(NSNumber *heartbeatTimeout); @property (nonatomic, strong) NSNumber *heartbeatTimeout; @end @implementation RMQHandshaker - (instancetype)initWithSender:(id)sender config:(RMQConnectionConfig *)config completionHandler:(void (^)(NSNumber *heartbeatTimeout))completionHandler { self = [super init]; if (self) { self.sender = sender; self.config = config; self.completionHandler = completionHandler; self.heartbeatTimeout = @0; } return self; } - (void)handleFrameset:(RMQFrameset *)frameset { id method = frameset.method; if ([method isKindOfClass:[RMQConnectionStart class]]) { [self sendMethod:self.startOk channelNumber:frameset.channelNumber]; [self.reader run]; } else if ([method isKindOfClass:[RMQConnectionTune class]]) { RMQConnectionTuneOk *tuneOk = [self tuneOkForTune:method]; self.heartbeatTimeout = @(tuneOk.heartbeat.integerValue); [self sendMethod:tuneOk channelNumber:frameset.channelNumber]; [self sendMethod:self.connectionOpen channelNumber:frameset.channelNumber]; [self.reader run]; } else { self.completionHandler(self.heartbeatTimeout); } } #pragma mark - Private - (RMQConnectionStartOk *)startOk { RMQBoolean *yes = [[RMQBoolean alloc] init:YES]; RMQTable *capabilities = [[RMQTable alloc] init:@{@"publisher_confirms" : yes, @"consumer_cancel_notify" : yes, @"exchange_exchange_bindings" : yes, @"basic.nack" : yes, @"connection.blocked" : yes, @"authentication_failure_close" : yes}]; 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"]}]; return [[RMQConnectionStartOk alloc] initWithClientProperties:clientProperties mechanism:[[RMQShortstr alloc] init:self.config.authMechanism] response:self.config.credentials locale:[[RMQShortstr alloc] init:@"en_GB"]]; } - (RMQConnectionTuneOk *)tuneOkForTune:(RMQConnectionTune *)tune { RMQConnectionConfig *client = self.config; RMQConnectionTune *server = tune; NSNumber *channelMax = [self negotiateBetweenClientValue:client.channelMax serverValue:@(server.channelMax.integerValue)]; NSNumber *frameMax = [self negotiateBetweenClientValue:client.frameMax serverValue:@(server.frameMax.integerValue)]; NSNumber *heartbeat = [self negotiateBetweenClientValue:client.heartbeat serverValue:@(server.heartbeat.integerValue)]; return [[RMQConnectionTuneOk alloc] initWithChannelMax:[[RMQShort alloc] init:channelMax.integerValue] frameMax:[[RMQLong alloc] init:frameMax.integerValue] heartbeat:[[RMQShort alloc] init:heartbeat.integerValue]]; } - (RMQConnectionOpen *)connectionOpen { return [[RMQConnectionOpen alloc] initWithVirtualHost:[[RMQShortstr alloc] init:self.config.vhost] reserved1:[[RMQShortstr alloc] init:@""] options:0]; } - (NSNumber *)negotiateBetweenClientValue:(NSNumber *)client serverValue:(NSNumber *)server { if ([client isEqualToNumber:@0] || [server isEqualToNumber:@0]) { return client.integerValue > server.integerValue ? client : server; } else { return client.integerValue < server.integerValue ? client : server; } } - (void)sendMethod:(id)amqMethod channelNumber:(NSNumber *)channelNumber { RMQFrameset *frameset = [[RMQFrameset alloc] initWithChannelNumber:channelNumber method:amqMethod]; [self.sender sendFrameset:frameset force:YES]; } @end