/**
* Copyright (C) 2009-2015 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork
#include "mongo/platform/basic.h"
#include "mongo/client/connection_string.h"
#include "mongo/base/status_with.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
ConnectionString::ConnectionString(const HostAndPort& server) : _type(MASTER) {
_servers.push_back(server);
_finishInit();
}
ConnectionString::ConnectionString(StringData setName, std::vector servers)
: _type(SET), _servers(std::move(servers)), _setName(setName.toString()) {
_finishInit();
}
// TODO: unify c-tors
ConnectionString::ConnectionString(ConnectionType type,
const std::string& s,
const std::string& setName) {
_type = type;
_setName = setName;
_fillServers(s);
_finishInit();
}
ConnectionString::ConnectionString(ConnectionType type,
std::vector servers,
const std::string& setName)
: _type(type), _servers(std::move(servers)), _setName(setName) {
_finishInit();
}
ConnectionString::ConnectionString(const std::string& s, ConnectionType connType)
: _type(connType) {
_fillServers(s);
_finishInit();
}
ConnectionString::ConnectionString(ConnectionType connType) : _type(connType), _string("") {
invariant(_type == LOCAL);
}
ConnectionString ConnectionString::forReplicaSet(StringData setName,
std::vector servers) {
return ConnectionString(setName, std::move(servers));
}
ConnectionString ConnectionString::forLocal() {
return ConnectionString(LOCAL);
}
// TODO: rewrite parsing make it more reliable
void ConnectionString::_fillServers(std::string s) {
//
// Custom-handled servers/replica sets start with '$'
// According to RFC-1123/952, this will not overlap with valid hostnames
// (also disallows $replicaSetName hosts)
//
if (s.find('$') == 0) {
_type = CUSTOM;
}
std::string::size_type idx = s.find('/');
if (idx != std::string::npos) {
_setName = s.substr(0, idx);
s = s.substr(idx + 1);
if (_type != CUSTOM)
_type = SET;
}
while ((idx = s.find(',')) != std::string::npos) {
_servers.push_back(HostAndPort(s.substr(0, idx)));
s = s.substr(idx + 1);
}
_servers.push_back(HostAndPort(s));
if (_servers.size() == 1 && _type == INVALID) {
_type = MASTER;
}
}
void ConnectionString::_finishInit() {
switch (_type) {
case MASTER:
uassert(ErrorCodes::FailedToParse,
"Cannot specify a replica set name for a ConnectionString of type MASTER",
_setName.empty());
uassert(ErrorCodes::FailedToParse,
"ConnectionStrings of type MASTER must contain exactly one server",
_servers.size() == 1);
break;
case SET:
uassert(ErrorCodes::FailedToParse,
"Must specify set name for replica set ConnectionStrings",
!_setName.empty());
uassert(ErrorCodes::FailedToParse,
"Replica set ConnectionStrings must have at least one server specified",
_servers.size() >= 1);
break;
default:
uassert(ErrorCodes::FailedToParse,
"ConnectionStrings must specify at least one server",
_servers.size() > 0);
}
// Needed here as well b/c the parsing logic isn't used in all constructors
// TODO: Refactor so that the parsing logic *is* used in all constructors
if (_type == MASTER && _servers.size() > 0) {
if (_servers[0].host().find('$') == 0) {
_type = CUSTOM;
}
}
std::stringstream ss;
if (_type == SET) {
ss << _setName << "/";
}
for (unsigned i = 0; i < _servers.size(); i++) {
if (i > 0) {
ss << ",";
}
ss << _servers[i].toString();
}
_string = ss.str();
}
bool ConnectionString::operator==(const ConnectionString& other) const {
if (_type != other._type) {
return false;
}
switch (_type) {
case INVALID:
return true;
case MASTER:
return _servers[0] == other._servers[0];
case SET:
return _setName == other._setName && _servers == other._servers;
case CUSTOM:
return _string == other._string;
case LOCAL:
return true;
}
MONGO_UNREACHABLE;
}
bool ConnectionString::operator!=(const ConnectionString& other) const {
return !(*this == other);
}
StatusWith ConnectionString::parse(const std::string& url) {
const std::string::size_type i = url.find('/');
// Replica set
if (i != std::string::npos && i != 0) {
return ConnectionString(SET, url.substr(i + 1), url.substr(0, i));
}
const int numCommas = str::count(url, ',');
// Single host
if (numCommas == 0) {
HostAndPort singleHost;
Status status = singleHost.initialize(url);
if (!status.isOK()) {
return status;
}
return ConnectionString(singleHost);
}
if (numCommas == 2) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "mirrored config server connections are not supported; for "
"config server replica sets be sure to use the replica set "
"connection string");
}
return Status(ErrorCodes::FailedToParse, str::stream() << "invalid url [" << url << "]");
}
std::string ConnectionString::typeToString(ConnectionType type) {
switch (type) {
case INVALID:
return "invalid";
case MASTER:
return "master";
case SET:
return "set";
case CUSTOM:
return "custom";
case LOCAL:
return "local";
}
MONGO_UNREACHABLE;
}
} // namespace mongo