Skip to content

Commit ac4aabc

Browse files
authored
chore(NODE-2998): move connection-string parsing into separate package (#2848)
1 parent ca9e2dc commit ac4aabc

File tree

5 files changed

+43
-72
lines changed

5 files changed

+43
-72
lines changed

package-lock.json

Lines changed: 33 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
},
2828
"dependencies": {
2929
"bson": "^4.4.0",
30-
"denque": "^1.5.0"
30+
"denque": "^1.5.0",
31+
"mongodb-connection-string-url": "^1.0.0"
3132
},
3233
"devDependencies": {
3334
"@istanbuljs/nyc-config-typescript": "^1.0.1",

src/connection_string.ts

Lines changed: 4 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as dns from 'dns';
22
import * as fs from 'fs';
3-
import { URL, URLSearchParams } from 'url';
3+
import ConnectionString from 'mongodb-connection-string-url';
4+
import { URLSearchParams } from 'url';
45
import { AuthMechanism } from './cmap/auth/defaultAuthProviders';
56
import { ReadPreference, ReadPreferenceMode } from './read_preference';
67
import { ReadConcern, ReadConcernLevel } from './read_concern';
@@ -146,68 +147,6 @@ export function checkTLSOptions(options: AnyOptions): void {
146147
check('tlsDisableCertificateRevocationCheck', 'tlsDisableOCSPEndpointCheck');
147148
}
148149

149-
const HOSTS_REGEX = new RegExp(
150-
String.raw`(?<protocol>mongodb(?:\+srv|)):\/\/(?:(?<username>[^:]*)(?::(?<password>[^@]*))?@)?(?<hosts>(?!:)[^\/?@]+)(?<rest>.*)`
151-
);
152-
153-
/** @internal */
154-
export function parseURI(uri: string): { isSRV: boolean; url: URL; hosts: string[] } {
155-
const match = uri.match(HOSTS_REGEX);
156-
if (!match) {
157-
throw new MongoParseError(`Invalid connection string ${uri}`);
158-
}
159-
160-
const protocol = match.groups?.protocol;
161-
const username = match.groups?.username;
162-
const password = match.groups?.password;
163-
const hosts = match.groups?.hosts;
164-
const rest = match.groups?.rest;
165-
166-
if (!protocol || !hosts) {
167-
throw new MongoParseError('Invalid connection string, protocol and host(s) required');
168-
}
169-
170-
decodeURIComponent(username ?? '');
171-
decodeURIComponent(password ?? '');
172-
173-
// characters not permitted in username nor password Set([':', '/', '?', '#', '[', ']', '@'])
174-
const illegalCharacters = new RegExp(String.raw`[:/?#\[\]@]`, 'gi');
175-
if (username?.match(illegalCharacters)) {
176-
throw new MongoParseError(`Username contains unescaped characters ${username}`);
177-
}
178-
if (!username || !password) {
179-
const uriWithoutProtocol = uri.replace(`${protocol}://`, '');
180-
if (uriWithoutProtocol.startsWith('@') || uriWithoutProtocol.startsWith(':')) {
181-
throw new MongoParseError('URI contained empty userinfo section');
182-
}
183-
}
184-
185-
if (password?.match(illegalCharacters)) {
186-
throw new MongoParseError('Password contains unescaped characters');
187-
}
188-
189-
let authString = '';
190-
if (typeof username === 'string') authString += username;
191-
if (typeof password === 'string') authString += `:${password}`;
192-
193-
const isSRV = protocol.includes('srv');
194-
const hostList = hosts.split(',');
195-
const url = new URL(`${protocol.toLowerCase()}://${authString}@dummyHostname${rest}`);
196-
197-
if (isSRV && hostList.length !== 1) {
198-
throw new MongoParseError('mongodb+srv URI cannot have multiple service names');
199-
}
200-
if (isSRV && hostList[0].includes(':')) {
201-
throw new MongoParseError('mongodb+srv URI cannot have port number');
202-
}
203-
204-
return {
205-
isSRV,
206-
url,
207-
hosts: hosts.split(',')
208-
};
209-
}
210-
211150
const TRUTHS = new Set(['true', 't', '1', 'y', 'yes']);
212151
const FALSEHOODS = new Set(['false', 'f', '0', 'n', 'no', '-1']);
213152
function getBoolean(name: string, value: unknown): boolean {
@@ -285,7 +224,8 @@ export function parseOptions(
285224
mongoClient = undefined;
286225
}
287226

288-
const { url, hosts, isSRV } = parseURI(uri);
227+
const url = new ConnectionString(uri);
228+
const { hosts, isSRV } = url;
289229

290230
const mongoOptions = Object.create(null);
291231
mongoOptions.hosts = isSRV ? [] : hosts.map(HostAddress.fromString);

test/functional/mongo_client.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('MongoClient', function () {
6666
},
6767
test() {
6868
expect(() => this.configuration.newClient('user:password@localhost:27017/test')).to.throw(
69-
'Invalid connection string user:password@localhost:27017/test'
69+
'Invalid connection string "user:password@localhost:27017/test"'
7070
);
7171
}
7272
});

test/tools/runner/config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use strict';
2+
const ConnectionString = require('mongodb-connection-string-url').default;
23
const url = require('url');
34
const qs = require('querystring');
45
const { expect } = require('chai');
56

67
const { MongoClient } = require('../../../src/mongo_client');
78
const { Topology } = require('../../../src/sdam/topology');
89
const { TopologyType } = require('../../../src/sdam/common');
9-
const { parseURI } = require('../../../src/connection_string');
1010
const { HostAddress } = require('../../../src/utils');
1111

1212
/**
@@ -35,7 +35,8 @@ function convertToConnStringMap(obj) {
3535

3636
class TestConfiguration {
3737
constructor(uri, context) {
38-
const { url, hosts } = parseURI(uri);
38+
const url = new ConnectionString(uri);
39+
const { hosts } = url;
3940
const hostAddresses = hosts.map(HostAddress.fromString);
4041
this.topologyType = context.topologyType;
4142
this.version = context.version;

0 commit comments

Comments
 (0)