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