Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions driver-core/src/main/com/mongodb/ConnectionString.java
Original file line number Diff line number Diff line change
Expand Up @@ -368,24 +368,27 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient

// Split out the user and host information
String userAndHostInformation;
int idx = unprocessedConnectionString.indexOf("/");
if (idx == -1) {
if (unprocessedConnectionString.contains("?")) {
throw new IllegalArgumentException("The connection string contains options without trailing slash");
int firstForwardSlashIdx = unprocessedConnectionString.indexOf("/");
int firstQuestionMarkIdx = unprocessedConnectionString.indexOf("?");
if (firstForwardSlashIdx == -1 || (firstQuestionMarkIdx != -1 && firstQuestionMarkIdx < firstForwardSlashIdx)) {
if (firstQuestionMarkIdx == -1) {
userAndHostInformation = unprocessedConnectionString;
unprocessedConnectionString = "";
} else {
userAndHostInformation = unprocessedConnectionString.substring(0, firstQuestionMarkIdx);
unprocessedConnectionString = unprocessedConnectionString.substring(firstQuestionMarkIdx);
}
userAndHostInformation = unprocessedConnectionString;
unprocessedConnectionString = "";
} else {
userAndHostInformation = unprocessedConnectionString.substring(0, idx);
unprocessedConnectionString = unprocessedConnectionString.substring(idx + 1);
userAndHostInformation = unprocessedConnectionString.substring(0, firstForwardSlashIdx);
unprocessedConnectionString = unprocessedConnectionString.substring(firstForwardSlashIdx + 1);
}

// Split the user and host information
String userInfo;
String hostIdentifier;
String userName = null;
char[] password = null;
idx = userAndHostInformation.lastIndexOf("@");
int idx = userAndHostInformation.lastIndexOf("@");
if (idx > 0) {
userInfo = userAndHostInformation.substring(0, idx).replace("+", "%2B");
hostIdentifier = userAndHostInformation.substring(idx + 1);
Expand Down
27 changes: 18 additions & 9 deletions driver-core/src/test/resources/connection-string/invalid-uris.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,6 @@
"auth": null,
"options": null
},
{
"description": "Missing delimiting slash between hosts and options",
"uri": "mongodb://example.com?w=1",
"valid": false,
"warning": null,
"hosts": null,
"auth": null,
"options": null
},
{
"description": "Incomplete key value pair for option",
"uri": "mongodb://example.com/?w",
Expand Down Expand Up @@ -269,6 +260,24 @@
"hosts": null,
"auth": null,
"options": null
},
{
"description": "Username with password containing an unescaped percent sign and an escaped one",
"uri": "mongodb://user%20%:password@localhost",
"valid": false,
"warning": null,
"hosts": null,
"auth": null,
"options": null
},
{
"description": "Username with password containing an unescaped percent sign (non hex digit)",
"uri": "mongodb://user%w:password@localhost",
"valid": false,
"warning": null,
"hosts": null,
"auth": null,
"options": null
}
]
}
11 changes: 6 additions & 5 deletions driver-core/src/test/resources/connection-string/valid-auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@
},
{
"description": "Subdelimiters in user/pass don't need escaping (MONGODB-CR)",
"uri": "mongodb://!$&'()*,;=:!$&'()*,;=@127.0.0.1/admin?authMechanism=MONGODB-CR",
"uri": "mongodb://!$&'()*+,;=:!$&'()*+,;=@127.0.0.1/admin?authMechanism=MONGODB-CR",
"valid": true,
"warning": false,
"hosts": [
Expand All @@ -253,8 +253,8 @@
}
],
"auth": {
"username": "!$&'()*,;=",
"password": "!$&'()*,;=",
"username": "!$&'()*+,;=",
"password": "!$&'()*+,;=",
"db": "admin"
},
"options": {
Expand Down Expand Up @@ -284,7 +284,7 @@
},
{
"description": "Escaped username (GSSAPI)",
"uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI",
"uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com&authMechanism=GSSAPI",
"valid": true,
"warning": false,
"hosts": [
Expand All @@ -303,7 +303,8 @@
"authmechanism": "GSSAPI",
"authmechanismproperties": {
"SERVICE_NAME": "other",
"CANONICALIZE_HOST_NAME": true
"SERVICE_HOST": "example.com",
"CANONICALIZE_HOST_NAME": "forward"
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,26 @@
"authmechanism": "MONGODB-CR"
}
},
{
"description": "Missing delimiting slash between hosts and options",
"uri": "mongodb://example.com?tls=true",
"valid": true,
"warning": false,
"hosts": [
{
"type": "hostname",
"host": "example.com",
"port": null
}
],
"auth": null,
"options": {
"tls": true
}
},
{
"description": "Colon in a key value pair",
"uri": "mongodb://example.com/?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster",
"uri": "mongodb://example.com?authMechanism=MONGODB-OIDC&authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster",
"valid": true,
"warning": false,
"hosts": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@
"auth": null,
"options": null
},
{
"description": "Unix domain socket (mixed case)",
"uri": "mongodb://%2Ftmp%2FMongoDB-27017.sock",
"valid": true,
"warning": false,
"hosts": [
{
"type": "unix",
"host": "/tmp/MongoDB-27017.sock",
"port": null
}
],
"auth": null,
"options": null
},
{
"description": "Unix domain socket (absolute path with spaces in path)",
"uri": "mongodb://%2Ftmp%2F %2Fmongodb-27017.sock",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@
"auth": null,
"options": null
},
{
"description": "Unix domain socket (mixed case)",
"uri": "mongodb://rel%2FMongoDB-27017.sock",
"valid": true,
"warning": false,
"hosts": [
{
"type": "unix",
"host": "rel/MongoDB-27017.sock",
"port": null
}
],
"auth": null,
"options": null
},
{
"description": "Unix domain socket (relative path with spaces)",
"uri": "mongodb://rel%2F %2Fmongodb-27017.sock",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,51 @@
"options": {
"wtimeoutms": 10
}
},
{
"description": "Empty integer option values are ignored",
"uri": "mongodb://localhost/?maxIdleTimeMS=",
"valid": true,
"warning": true,
"hosts": [
{
"type": "hostname",
"host": "localhost",
"port": null
}
],
"auth": null,
"options": null
},
{
"description": "Empty boolean option value are ignored",
"uri": "mongodb://localhost/?journal=",
"valid": true,
"warning": true,
"hosts": [
{
"type": "hostname",
"host": "localhost",
"port": null
}
],
"auth": null,
"options": null
},
{
"description": "Comma in a key value pair causes a warning",
"uri": "mongodb://example.com?authMechanismProperties=TOKEN_RESOURCE:mongodb://host1%2Chost2",
"valid": true,
"warning": true,
"hosts": [
{
"type": "hostname",
"host": "example.com",
"port": null
}
],
"auth": null,
"options": null
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,29 @@ class ConnectionStringSpecification extends Specification {
.withWTimeout(5, MILLISECONDS).withJournal(true)
}

@Unroll
def 'should treat trailing slash before query parameters as optional'() {
expect:
uri.getApplicationName() == appName
uri.getDatabase() == db

where:
uri | appName | db
new ConnectionString('mongodb://mongodb.com') | null | null
new ConnectionString('mongodb://mongodb.com?') | null | null
new ConnectionString('mongodb://mongodb.com/') | null | null
new ConnectionString('mongodb://mongodb.com/?') | null | null
new ConnectionString('mongodb://mongodb.com/test') | null | "test"
new ConnectionString('mongodb://mongodb.com/test?') | null | "test"
new ConnectionString('mongodb://mongodb.com/?appName=a1') | "a1" | null
new ConnectionString('mongodb://mongodb.com?appName=a1') | "a1" | null
new ConnectionString('mongodb://mongodb.com/?appName=a1/a2') | "a1/a2" | null
new ConnectionString('mongodb://mongodb.com?appName=a1/a2') | "a1/a2" | null
new ConnectionString('mongodb://mongodb.com/test?appName=a1/a2') | "a1/a2" | "test"
new ConnectionString('mongodb://mongodb.com/test?appName=a1') | "a1" | "test"
new ConnectionString('mongodb://mongodb.com/test?appName=a1/a2') | "a1/a2" | "test"
}

def 'should correctly parse different UUID representations'() {
expect:
uri.getUuidRepresentation() == uuidRepresentation
Expand Down Expand Up @@ -473,7 +496,6 @@ class ConnectionStringSpecification extends Specification {
'has an empty host' | 'mongodb://localhost:27017,,localhost:27019'
'has an malformed IPv6 host' | 'mongodb://[::1'
'has unescaped colons' | 'mongodb://locahost::1'
'is missing a slash' | 'mongodb://localhost?wTimeout=5'
'contains an invalid port string' | 'mongodb://localhost:twenty'
'contains an invalid port negative' | 'mongodb://localhost:-1'
'contains an invalid port out of range' | 'mongodb://localhost:1000000'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.util.Collection;
import java.util.List;

import static org.junit.Assume.assumeFalse;

// See https://github.com/mongodb/specifications/tree/master/source/connection-string/tests
public class ConnectionStringTest extends AbstractConnectionStringTest {
public ConnectionStringTest(final String filename, final String description, final String input, final BsonDocument definition) {
Expand All @@ -37,6 +39,7 @@ public ConnectionStringTest(final String filename, final String description, fin

@Test
public void shouldPassAllOutcomes() {
assumeFalse(getDescription().equals("Empty integer option values are ignored"));
if (getFilename().equals("invalid-uris.json")) {
testInvalidUris();
} else if (getFilename().equals("valid-auth.json")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS

class MongoClientURISpecification extends Specification {

def 'should throw Exception if URI does not have a trailing slash'() {
when:
new MongoClientURI('mongodb://localhost?wtimeoutMS=5')

then:
thrown(IllegalArgumentException)
}

def 'should not throw an Exception if URI contains an unknown option'() {
when:
new MongoClientURI('mongodb://localhost/?unknownOption=5')
Expand Down