1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork
32 
33 #include "mongo/platform/basic.h"
34 
35 #include "mongo/client/connection_string.h"
36 
37 #include "mongo/base/status_with.h"
38 #include "mongo/util/mongoutils/str.h"
39 
40 namespace mongo {
41 
ConnectionString(const HostAndPort & server)42 ConnectionString::ConnectionString(const HostAndPort& server) : _type(MASTER) {
43     _servers.push_back(server);
44     _finishInit();
45 }
46 
ConnectionString(StringData setName,std::vector<HostAndPort> servers)47 ConnectionString::ConnectionString(StringData setName, std::vector<HostAndPort> servers)
48     : _type(SET), _servers(std::move(servers)), _setName(setName.toString()) {
49     _finishInit();
50 }
51 
52 // TODO: unify c-tors
ConnectionString(ConnectionType type,const std::string & s,const std::string & setName)53 ConnectionString::ConnectionString(ConnectionType type,
54                                    const std::string& s,
55                                    const std::string& setName) {
56     _type = type;
57     _setName = setName;
58     _fillServers(s);
59     _finishInit();
60 }
61 
ConnectionString(ConnectionType type,std::vector<HostAndPort> servers,const std::string & setName)62 ConnectionString::ConnectionString(ConnectionType type,
63                                    std::vector<HostAndPort> servers,
64                                    const std::string& setName)
65     : _type(type), _servers(std::move(servers)), _setName(setName) {
66     _finishInit();
67 }
68 
ConnectionString(const std::string & s,ConnectionType connType)69 ConnectionString::ConnectionString(const std::string& s, ConnectionType connType)
70     : _type(connType) {
71     _fillServers(s);
72     _finishInit();
73 }
74 
ConnectionString(ConnectionType connType)75 ConnectionString::ConnectionString(ConnectionType connType) : _type(connType), _string("<local>") {
76     invariant(_type == LOCAL);
77 }
78 
forReplicaSet(StringData setName,std::vector<HostAndPort> servers)79 ConnectionString ConnectionString::forReplicaSet(StringData setName,
80                                                  std::vector<HostAndPort> servers) {
81     return ConnectionString(setName, std::move(servers));
82 }
83 
forLocal()84 ConnectionString ConnectionString::forLocal() {
85     return ConnectionString(LOCAL);
86 }
87 
88 // TODO: rewrite parsing  make it more reliable
_fillServers(std::string s)89 void ConnectionString::_fillServers(std::string s) {
90     //
91     // Custom-handled servers/replica sets start with '$'
92     // According to RFC-1123/952, this will not overlap with valid hostnames
93     // (also disallows $replicaSetName hosts)
94     //
95 
96     if (s.find('$') == 0) {
97         _type = CUSTOM;
98     }
99 
100     std::string::size_type idx = s.find('/');
101     if (idx != std::string::npos) {
102         _setName = s.substr(0, idx);
103         s = s.substr(idx + 1);
104         if (_type != CUSTOM)
105             _type = SET;
106     }
107 
108     while ((idx = s.find(',')) != std::string::npos) {
109         _servers.push_back(HostAndPort(s.substr(0, idx)));
110         s = s.substr(idx + 1);
111     }
112 
113     _servers.push_back(HostAndPort(s));
114 
115     if (_servers.size() == 1 && _type == INVALID) {
116         _type = MASTER;
117     }
118 }
119 
_finishInit()120 void ConnectionString::_finishInit() {
121     switch (_type) {
122         case MASTER:
123             uassert(ErrorCodes::FailedToParse,
124                     "Cannot specify a replica set name for a ConnectionString of type MASTER",
125                     _setName.empty());
126             break;
127         case SET:
128             uassert(ErrorCodes::FailedToParse,
129                     "Must specify set name for replica set ConnectionStrings",
130                     !_setName.empty());
131             uassert(ErrorCodes::FailedToParse,
132                     "Replica set ConnectionStrings must have at least one server specified",
133                     _servers.size() >= 1);
134             break;
135         default:
136             uassert(ErrorCodes::FailedToParse,
137                     "ConnectionStrings must specify at least one server",
138                     _servers.size() > 0);
139     }
140 
141     // Needed here as well b/c the parsing logic isn't used in all constructors
142     // TODO: Refactor so that the parsing logic *is* used in all constructors
143     if (_type == MASTER && _servers.size() > 0) {
144         if (_servers[0].host().find('$') == 0) {
145             _type = CUSTOM;
146         }
147     }
148 
149     std::stringstream ss;
150 
151     if (_type == SET) {
152         ss << _setName << "/";
153     }
154 
155     for (unsigned i = 0; i < _servers.size(); i++) {
156         if (i > 0) {
157             ss << ",";
158         }
159 
160         ss << _servers[i].toString();
161     }
162 
163     _string = ss.str();
164 }
165 
operator ==(const ConnectionString & other) const166 bool ConnectionString::operator==(const ConnectionString& other) const {
167     if (_type != other._type) {
168         return false;
169     }
170 
171     switch (_type) {
172         case INVALID:
173             return true;
174         case MASTER:
175             return _servers[0] == other._servers[0];
176         case SET:
177             return _setName == other._setName && _servers == other._servers;
178         case CUSTOM:
179             return _string == other._string;
180         case LOCAL:
181             return true;
182     }
183 
184     MONGO_UNREACHABLE;
185 }
186 
operator !=(const ConnectionString & other) const187 bool ConnectionString::operator!=(const ConnectionString& other) const {
188     return !(*this == other);
189 }
190 
parse(const std::string & url)191 StatusWith<ConnectionString> ConnectionString::parse(const std::string& url) {
192     const std::string::size_type i = url.find('/');
193 
194     // Replica set
195     if (i != std::string::npos && i != 0) {
196         return ConnectionString(SET, url.substr(i + 1), url.substr(0, i));
197     }
198 
199     const int numCommas = str::count(url, ',');
200 
201     // Single host
202     if (numCommas == 0) {
203         HostAndPort singleHost;
204         Status status = singleHost.initialize(url);
205         if (!status.isOK()) {
206             return status;
207         }
208 
209         return ConnectionString(singleHost);
210     }
211 
212     if (numCommas == 2) {
213         return Status(ErrorCodes::FailedToParse,
214                       str::stream() << "mirrored config server connections are not supported; for "
215                                        "config server replica sets be sure to use the replica set "
216                                        "connection string");
217     }
218 
219     return Status(ErrorCodes::FailedToParse, str::stream() << "invalid url [" << url << "]");
220 }
221 
typeToString(ConnectionType type)222 std::string ConnectionString::typeToString(ConnectionType type) {
223     switch (type) {
224         case INVALID:
225             return "invalid";
226         case MASTER:
227             return "master";
228         case SET:
229             return "set";
230         case CUSTOM:
231             return "custom";
232         case LOCAL:
233             return "local";
234     }
235 
236     MONGO_UNREACHABLE;
237 }
238 
239 }  // namespace mongo
240