Skip to content

Commit 4b17218

Browse files
committed
Forward slash in connection string is optional
JAVA-5166
1 parent 3c8b44e commit 4b17218

File tree

6 files changed

+72
-29
lines changed

6 files changed

+72
-29
lines changed

driver-core/src/main/com/mongodb/ConnectionString.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -368,24 +368,27 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient
368368

369369
// Split out the user and host information
370370
String userAndHostInformation;
371-
int idx = unprocessedConnectionString.indexOf("/");
372-
if (idx == -1) {
373-
if (unprocessedConnectionString.contains("?")) {
374-
throw new IllegalArgumentException("The connection string contains options without trailing slash");
371+
int firstForwardSlashIdx = unprocessedConnectionString.indexOf("/");
372+
int firstQuestionMarkIdx = unprocessedConnectionString.indexOf("?");
373+
if (firstForwardSlashIdx == -1 || (firstQuestionMarkIdx != -1 && firstQuestionMarkIdx < firstForwardSlashIdx)) {
374+
if (firstQuestionMarkIdx == -1) {
375+
userAndHostInformation = unprocessedConnectionString;
376+
unprocessedConnectionString = "";
377+
} else {
378+
userAndHostInformation = unprocessedConnectionString.substring(0, firstQuestionMarkIdx);
379+
unprocessedConnectionString = unprocessedConnectionString.substring(firstQuestionMarkIdx);
375380
}
376-
userAndHostInformation = unprocessedConnectionString;
377-
unprocessedConnectionString = "";
378381
} else {
379-
userAndHostInformation = unprocessedConnectionString.substring(0, idx);
380-
unprocessedConnectionString = unprocessedConnectionString.substring(idx + 1);
382+
userAndHostInformation = unprocessedConnectionString.substring(0, firstForwardSlashIdx);
383+
unprocessedConnectionString = unprocessedConnectionString.substring(firstForwardSlashIdx + 1);
381384
}
382385

383386
// Split the user and host information
384387
String userInfo;
385388
String hostIdentifier;
386389
String userName = null;
387390
char[] password = null;
388-
idx = userAndHostInformation.lastIndexOf("@");
391+
int idx = userAndHostInformation.lastIndexOf("@");
389392
if (idx > 0) {
390393
userInfo = userAndHostInformation.substring(0, idx).replace("+", "%2B");
391394
hostIdentifier = userAndHostInformation.substring(idx + 1);

driver-core/src/test/resources/connection-string/invalid-uris.json

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -162,15 +162,6 @@
162162
"auth": null,
163163
"options": null
164164
},
165-
{
166-
"description": "Missing delimiting slash between hosts and options",
167-
"uri": "mongodb://example.com?w=1",
168-
"valid": false,
169-
"warning": null,
170-
"hosts": null,
171-
"auth": null,
172-
"options": null
173-
},
174165
{
175166
"description": "Incomplete key value pair for option",
176167
"uri": "mongodb://example.com/?w",
@@ -269,6 +260,24 @@
269260
"hosts": null,
270261
"auth": null,
271262
"options": null
263+
},
264+
{
265+
"description": "Username with password containing an unescaped percent sign and an escaped one",
266+
"uri": "mongodb://user%20%:password@localhost",
267+
"valid": false,
268+
"warning": null,
269+
"hosts": null,
270+
"auth": null,
271+
"options": null
272+
},
273+
{
274+
"description": "Username with password containing an unescaped percent sign (non hex digit)",
275+
"uri": "mongodb://user%w:password@localhost",
276+
"valid": false,
277+
"warning": null,
278+
"hosts": null,
279+
"auth": null,
280+
"options": null
272281
}
273282
]
274283
}

driver-core/src/test/resources/connection-string/valid-options.json

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,26 @@
2121
"authmechanism": "MONGODB-CR"
2222
}
2323
},
24+
{
25+
"description": "Missing delimiting slash between hosts and options",
26+
"uri": "mongodb://example.com?tls=true",
27+
"valid": true,
28+
"warning": false,
29+
"hosts": [
30+
{
31+
"type": "hostname",
32+
"host": "example.com",
33+
"port": null
34+
}
35+
],
36+
"auth": null,
37+
"options": {
38+
"tls": true
39+
}
40+
},
2441
{
2542
"description": "Colon in a key value pair",
26-
"uri": "mongodb://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster",
43+
"uri": "mongodb://example.com?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster",
2744
"valid": true,
2845
"warning": false,
2946
"hosts": [

driver-core/src/test/unit/com/mongodb/AbstractConnectionStringTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ protected void testValidOptions() {
100100
ConnectionString connectionString = null;
101101

102102
try {
103-
connectionString = new ConnectionString(input);
103+
connectionString = new ConnectionString(input);
104104
} catch (Throwable t) {
105105
fail(String.format("Connection string '%s' should not have thrown an exception: %s", input, t));
106106
}

driver-core/src/test/unit/com/mongodb/ConnectionStringSpecification.groovy

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,29 @@ class ConnectionStringSpecification extends Specification {
141141
.withWTimeout(5, MILLISECONDS).withJournal(true)
142142
}
143143

144+
@Unroll
145+
def 'should treat trailing slash before query parameters as optional'() {
146+
expect:
147+
uri.getApplicationName() == appName
148+
uri.getDatabase() == db
149+
150+
where:
151+
uri | appName | db
152+
new ConnectionString('mongodb://mongodb.com') | null | null
153+
new ConnectionString('mongodb://mongodb.com?') | null | null
154+
new ConnectionString('mongodb://mongodb.com/') | null | null
155+
new ConnectionString('mongodb://mongodb.com/?') | null | null
156+
new ConnectionString('mongodb://mongodb.com/test') | null | "test"
157+
new ConnectionString('mongodb://mongodb.com/test?') | null | "test"
158+
new ConnectionString('mongodb://mongodb.com/?appName=a1') | "a1" | null
159+
new ConnectionString('mongodb://mongodb.com?appName=a1') | "a1" | null
160+
new ConnectionString('mongodb://mongodb.com/?appName=a1/a2') | "a1/a2" | null
161+
new ConnectionString('mongodb://mongodb.com?appName=a1/a2') | "a1/a2" | null
162+
new ConnectionString('mongodb://mongodb.com/test?appName=a1/a2') | "a1/a2" | "test"
163+
new ConnectionString('mongodb://mongodb.com/test?appName=a1') | "a1" | "test"
164+
new ConnectionString('mongodb://mongodb.com/test?appName=a1/a2') | "a1/a2" | "test"
165+
}
166+
144167
def 'should correctly parse different UUID representations'() {
145168
expect:
146169
uri.getUuidRepresentation() == uuidRepresentation
@@ -473,7 +496,6 @@ class ConnectionStringSpecification extends Specification {
473496
'has an empty host' | 'mongodb://localhost:27017,,localhost:27019'
474497
'has an malformed IPv6 host' | 'mongodb://[::1'
475498
'has unescaped colons' | 'mongodb://locahost::1'
476-
'is missing a slash' | 'mongodb://localhost?wTimeout=5'
477499
'contains an invalid port string' | 'mongodb://localhost:twenty'
478500
'contains an invalid port negative' | 'mongodb://localhost:-1'
479501
'contains an invalid port out of range' | 'mongodb://localhost:1000000'

driver-legacy/src/test/unit/com/mongodb/MongoClientURISpecification.groovy

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,6 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS
3434

3535
class MongoClientURISpecification extends Specification {
3636

37-
def 'should throw Exception if URI does not have a trailing slash'() {
38-
when:
39-
new MongoClientURI('mongodb://localhost?wtimeoutMS=5')
40-
41-
then:
42-
thrown(IllegalArgumentException)
43-
}
44-
4537
def 'should not throw an Exception if URI contains an unknown option'() {
4638
when:
4739
new MongoClientURI('mongodb://localhost/?unknownOption=5')

0 commit comments

Comments
 (0)