From 3c3b1687f467ac2856208a765601f1e5af3945f3 Mon Sep 17 00:00:00 2001 From: Hongcai Deng Date: Sun, 12 Oct 2025 05:55:14 +0000 Subject: [PATCH 1/2] refactor: Use new URL for connection string parsing This commit refactors the connection URL parsing logic in `src/index.js` to use the `new URL()` constructor. This change provides a more robust and standardized way to parse connection strings, addressing several limitations of the previous implementation: - **IPv6 Support:** The new parsing logic correctly handles IPv6 addresses in connection strings. - **Encoded Password Handling:** Usernames and passwords with special characters that have been URL-encoded are now correctly decoded. To achieve this, the `parseUrl` function was rewritten to replace the `postgres://` or `postgresql://` protocol with `http://` before passing the string to the `URL` constructor. This allows the use of the standard URL parsing mechanism for a custom protocol. Additionally, two new helper functions, `parseHost` and `parsePort`, have been introduced to correctly extract host and port information from various formats, including single-host, multi-host, and IPv6 addresses. --- src/index.js | 52 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/index.js b/src/index.js index 944d50cf..c5491244 100644 --- a/src/index.js +++ b/src/index.js @@ -462,9 +462,9 @@ function parseOptions(a, b) { } return { - host : Array.isArray(host) ? host : host.split(',').map(x => x.split(':')[0]), - port : Array.isArray(port) ? port : host.split(',').map(x => parseInt(x.split(':')[1] || port)), - path : o.path || host.indexOf('/') > -1 && host + '/.s.PGSQL.' + port, + host : Array.isArray(host) ? host : host.split(',').map(x => parseHost(x)), + port : Array.isArray(port) ? port : host.split(',').map(x => parsePort(x, port)), + path : o.path || (host.indexOf('/') > -1 && host + '/.s.PGSQL.' + port), database : o.database || o.db || (url.pathname || '').slice(1) || env.PGDATABASE || user, user : user, pass : o.pass || o.password || url.password || env.PGPASSWORD || '', @@ -533,27 +533,25 @@ function parseTransform(x) { } } -function parseUrl(url) { - if (!url || typeof url !== 'string') +function parseUrl(x) { + if (!x || typeof x !== 'string') return { url: { searchParams: new Map() } } - let host = url - host = host.slice(host.indexOf('://') + 3).split(/[?/]/)[0] - host = decodeURIComponent(host.slice(host.indexOf('@') + 1)) + const url = new URL(x.replace(/^postgres(ql)?:/, 'http:')) - const urlObj = new URL(url.replace(host, host.split(',')[0])) + const multihost = url.hostname.includes(',') && decodeURIComponent(url.hostname) return { url: { - username: decodeURIComponent(urlObj.username), - password: decodeURIComponent(urlObj.password), - host: urlObj.host, - hostname: urlObj.hostname, - port: urlObj.port, - pathname: urlObj.pathname, - searchParams: urlObj.searchParams + username: decodeURIComponent(url.username), + password: decodeURIComponent(url.password), + host: url.host, + hostname: multihost ? multihost.split(',')[0] : url.hostname, + port: url.port, + pathname: url.pathname, + searchParams: url.searchParams }, - multihost: host.indexOf(',') > -1 && host + multihost: multihost } } @@ -564,3 +562,23 @@ function osUsername() { return process.env.USERNAME || process.env.USER || process.env.LOGNAME // eslint-disable-line } } + +function parseHost(host) { + if (host.charAt(0) === '[') + return host.slice(1, -1) + + if (host.includes(':') && host.split(':').length > 2) + return '' + + return host.split(':')[0] +} + +function parsePort(host, port) { + if (host.charAt(0) === '[' && host.includes(']:')) + return parseInt(host.split(']:')[1]) + + if (host.includes(':') && host.split(':').length === 2) + return parseInt(host.split(':')[1]) + + return parseInt(port) +} From 5b6e7c7afd2b2b136842ae46d85d54304aa3e7af Mon Sep 17 00:00:00 2001 From: Hongcai Deng Date: Tue, 14 Oct 2025 14:58:46 +0000 Subject: [PATCH 2/2] fix: Correctly parse multi-host connection URLs The previous implementation passed the entire connection string to the URL constructor, which caused a `TypeError: ERR_INVALID_URL` when the string contained multiple hosts. This change updates the `parseUrl` function to first check for multiple hosts in the connection string. If multiple hosts are found, it extracts the full multi-host string for later use and replaces it with only the first host before passing it to the URL constructor. This ensures that the URL is always valid and prevents the parsing error, while still allowing the application to handle multi-host configurations. --- src/index.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index c5491244..efaf6b61 100644 --- a/src/index.js +++ b/src/index.js @@ -537,9 +537,20 @@ function parseUrl(x) { if (!x || typeof x !== 'string') return { url: { searchParams: new Map() } } - const url = new URL(x.replace(/^postgres(ql)?:/, 'http:')) + let str = x.replace(/^postgres(ql)?:/, 'http:') + if (str.startsWith('http:') && !str.startsWith('http://')) { + str = 'http://' + str.substring(5); + } + let multihost = false + + const hostPartMatch = str.match(/\/\/([^@/?#]*@)?([^/?#]+)/) + if (hostPartMatch && hostPartMatch[2].includes(',')) { + const hosts = hostPartMatch[2] + multihost = decodeURIComponent(hosts) + str = str.replace(hosts, hosts.split(',')[0]) + } - const multihost = url.hostname.includes(',') && decodeURIComponent(url.hostname) + const url = new URL(str) return { url: {