// 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 "RMQURI.h" #import "RMQErrors.h" @interface RMQURI () @property (nonatomic,nonnull,readwrite) NSString *host; @property (nonatomic,nonnull,readwrite) NSString *vhost; @property (nonatomic,nonnull,readwrite) NSNumber *portNumber; @property (nonatomic,nonnull,readwrite) NSString *username; @property (nonatomic,nonnull,readwrite) NSString *password; @property (nonatomic,readwrite) BOOL isTLS; @end @implementation RMQURI + (instancetype)parse:(NSString *)uri error:(NSError *__autoreleasing _Nullable *)error { NSURLComponents *components = [NSURLComponents componentsWithString:uri]; if (![self isValidScheme:components.scheme]) { *error = [NSError errorWithDomain:RMQErrorDomain code:RMQErrorInvalidScheme userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Connection URI must use amqp or amqps schema (example: amqp://bus.megacorp.internal:5766), learn more at http://bit.ly/ks8MXK", nil)}]; return nil; } NSError *parseVhostError = NULL; NSString *vhost = [self parseVhost:components error:&parseVhostError]; if (parseVhostError) { *error = parseVhostError; return nil; } RMQURI *u = [[RMQURI alloc] init]; u.host = components.host; u.vhost = vhost; u.username = components.user; u.password = components.password; u.portNumber = components.port; if ([components.scheme isEqualToString:@"amqp"]) { u.portNumber = u.portNumber ?: @5672; u.isTLS = NO; } else if ([components.scheme isEqualToString:@"amqps"]) { u.portNumber = u.portNumber ?: @5671; u.isTLS = YES; } return u; } + (BOOL)isValidScheme:(NSString *)scheme { return [scheme isEqualToString:@"amqp"] || [scheme isEqualToString:@"amqps"]; } + (NSString *)parseVhost:(NSURLComponents *)components error:(NSError **)error { NSRegularExpression *r = [NSRegularExpression regularExpressionWithPattern:@"/" options:NSRegularExpressionCaseInsensitive error:NULL]; NSUInteger numberOfSlashes = [r numberOfMatchesInString:components.path options:0 range:NSMakeRange(0, components.path.length)]; if (numberOfSlashes > 2) { NSString *msg = [NSString stringWithFormat:@"%@ has multiple-segment path; please percent-encode any slashes in the vhost name (e.g. /production => %%2Fproduction). Learn more at http://bit.ly/amqp-gem-and-connection-uris", components.URL]; *error = [NSError errorWithDomain:RMQErrorDomain code:RMQErrorInvalidPath userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(msg, nil)}]; return nil; } else if (components.path.length == 0) { return @"/"; } else { return [components.path substringFromIndex:1]; } } @end