From 88aafd73fa02c35b7469bb6e38f6bef59d03825e Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Sat, 5 Jul 2014 16:22:20 -0700 Subject: [PATCH 01/30] Initial commit --- .gitignore | 25 +++++++++++++++++++++++++ LICENSE | 21 +++++++++++++++++++++ README.md | 4 ++++ 3 files changed, 50 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..da23d0d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# Deployed apps should consider commenting this line out: +# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git +node_modules diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..b068a6cb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Iced Development + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..46afa27d --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +pg-connection-string +==================== + +Functions for dealing with a PostgresSQL connection string From 92c1fede8e7e3582b7040557cf2cb30705f59ad6 Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Sat, 5 Jul 2014 16:43:58 -0700 Subject: [PATCH 02/30] initial commit --- .travis.yml | 3 ++ README.md | 14 ++++++++ index.js | 54 +++++++++++++++++++++++++++++++ package.json | 29 +++++++++++++++++ test/parse.js | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 .travis.yml create mode 100644 index.js create mode 100644 package.json create mode 100644 test/parse.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..244b7e88 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: node_js +node_js: + - '0.10' diff --git a/README.md b/README.md index 46afa27d..49862702 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,18 @@ pg-connection-string ==================== +[![Build Status](https://travis-ci.org/iceddev/pg-connection-string.svg?branch=master)](https://travis-ci.org/iceddev/pg-connection-string) + Functions for dealing with a PostgresSQL connection string + +`parse` method taken from [node-postgres](https://github.com/brianc/node-postgres.git) +Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com) +MIT License + +## Usage + +```js +var parse = require('pg-connection-string').parse; + +var config = parse('postgres://someuser:somepassword@somehost:381/sometable') +``` diff --git a/index.js b/index.js new file mode 100644 index 00000000..6c8fb68d --- /dev/null +++ b/index.js @@ -0,0 +1,54 @@ +'use strict'; + +var url = require('url'); + +//Parse method copied from https://github.com/brianc/node-postgres +//Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com) +//MIT License + +//parses a connection string +function parse(str) { + var config; + //unix socket + if(str.charAt(0) === '/') { + config = str.split(' '); + return { host: config[0], database: config[1] }; + } + // url parse expects spaces encoded as %20 + if(/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str)) { + str = encodeURI(str).replace(/\%25(\d\d)/g, "%$1"); + } + var result = url.parse(str, true); + config = {}; + + if (result.query.application_name) { + config.application_name = result.query.application_name; + } + if (result.query.fallback_application_name) { + config.fallback_application_name = result.query.fallback_application_name; + } + + if(result.protocol == 'socket:') { + config.host = decodeURI(result.pathname); + config.database = result.query.db; + config.client_encoding = result.query.encoding; + return config; + } + config.host = result.hostname; + config.database = result.pathname ? decodeURI(result.pathname.slice(1)) : null; + var auth = (result.auth || ':').split(':'); + config.user = auth[0]; + config.password = auth[1]; + config.port = result.port; + + var ssl = result.query.ssl; + if (ssl === 'true' || ssl === '1') { + config.ssl = true; + } + + return config; +} + +module.exports = { + parse: parse +}; diff --git a/package.json b/package.json new file mode 100644 index 00000000..56f535dd --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "pg-connection-string", + "version": "0.1.0", + "description": "Functions for dealing with a PostgresSQL connection string", + "main": "index.js", + "scripts": { + "test": "tap ./test" + }, + "repository": { + "type": "git", + "url": "https://github.com/iceddev/pg-connection-string" + }, + "keywords": [ + "pg", + "connection", + "string", + "parse" + ], + "author": "Blaine Bublitz (http://iceddev.com/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/iceddev/pg-connection-string/issues" + }, + "homepage": "https://github.com/iceddev/pg-connection-string", + "dependencies": {}, + "devDependencies": { + "tap": "^0.4.11" + } +} diff --git a/test/parse.js b/test/parse.js new file mode 100644 index 00000000..1b9b203c --- /dev/null +++ b/test/parse.js @@ -0,0 +1,89 @@ +'use strict'; + +var test = require('tap').test; + +var parse = require('../').parse; + +test('using connection string in client constructor', function(t){ + var subject = parse('postgres://brian:pw@boom:381/lala'); + t.equal(subject.user,'brian'); + t.equal(subject.password, 'pw'); + t.equal(subject.host, 'boom'); + t.equal(subject.port, '381'); + t.equal(subject.database, 'lala'); + t.end(); +}); + +test('escape spaces if present', function(t){ + var subject = parse('postgres://localhost/post gres'); + t.equal(subject.database, 'post gres'); + t.end(); +}); + +test('do not double escape spaces', function(t){ + var subject = parse('postgres://localhost/post%20gres'); + t.equal(subject.database, 'post gres'); + t.end(); +}); + +test('initializing with unix domain socket', function(t){ + var subject = parse('/var/run/'); + t.equal(subject.host, '/var/run/'); + t.end(); +}); + +test('initializing with unix domain socket and a specific database, the simple way', function(t){ + var subject = parse('/var/run/ mydb'); + t.equal(subject.host, '/var/run/'); + t.equal(subject.database, 'mydb'); + t.end(); +}); + +test('initializing with unix domain socket, the health way', function(t){ + var subject = parse('socket:/some path/?db=my[db]&encoding=utf8'); + t.equal(subject.host, '/some path/'); + t.equal(subject.database, 'my[db]', 'must to be escaped and unescaped trough "my%5Bdb%5D"'); + t.equal(subject.client_encoding, 'utf8'); + t.end(); +}); + +test('initializing with unix domain socket, the escaped health way', function(t){ + var subject = parse('socket:/some%20path/?db=my%2Bdb&encoding=utf8'); + t.equal(subject.host, '/some path/'); + t.equal(subject.database, 'my+db'); + t.equal(subject.client_encoding, 'utf8'); + t.end(); +}); + +test('password contains < and/or > characters', function(t){ + var sourceConfig = { + user:'brian', + password: 'helloe', + port: 5432, + host: 'localhost', + database: 'postgres' + }; + var connectionString = 'postgres://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; + var subject = parse(connectionString); + t.equal(subject.password, sourceConfig.password); + t.end(); +}); + +test('username or password contains weird characters', function(t){ + var strang = 'pg://my f%irst name:is&%awesome!@localhost:9000'; + var subject = parse(strang); + t.equal(subject.user, 'my f%irst name'); + t.equal(subject.password, 'is&%awesome!'); + t.equal(subject.host, 'localhost'); + t.end(); +}); + +test('url is properly encoded', function(t){ + var encoded = 'pg://bi%25na%25%25ry%20:s%40f%23@localhost/%20u%2520rl'; + var subject = parse(encoded); + t.equal(subject.user, 'bi%na%%ry '); + t.equal(subject.password, 's@f#'); + t.equal(subject.host, 'localhost'); + t.equal(subject.database, ' u%20rl'); + t.end(); +}); From df2a24c55550d48afe9d6b57dff7b7c027ba124e Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Sun, 6 Jul 2014 16:33:53 -0700 Subject: [PATCH 03/30] attach port always - ref brianc/node-postgres#604 --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 6c8fb68d..afb36996 100644 --- a/index.js +++ b/index.js @@ -28,6 +28,7 @@ function parse(str) { config.fallback_application_name = result.query.fallback_application_name; } + config.port = result.port; if(result.protocol == 'socket:') { config.host = decodeURI(result.pathname); config.database = result.query.db; @@ -39,7 +40,6 @@ function parse(str) { var auth = (result.auth || ':').split(':'); config.user = auth[0]; config.password = auth[1]; - config.port = result.port; var ssl = result.query.ssl; if (ssl === 'true' || ssl === '1') { From cb9bee1bc9d65366d516fd2808726f285c9ad72f Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Sun, 6 Jul 2014 16:34:14 -0700 Subject: [PATCH 04/30] 0.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 56f535dd..2cb8d962 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-connection-string", - "version": "0.1.0", + "version": "0.1.1", "description": "Functions for dealing with a PostgresSQL connection string", "main": "index.js", "scripts": { From ba511f7803dd159447ca7801177dd9008b9958b3 Mon Sep 17 00:00:00 2001 From: "matthew.blasius" Date: Fri, 12 Sep 2014 11:21:33 -0400 Subject: [PATCH 05/30] Support usage of relative urls to set database on the default host --- index.js | 10 +++++++++- test/parse.js | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index afb36996..b041a208 100644 --- a/index.js +++ b/index.js @@ -36,7 +36,15 @@ function parse(str) { return config; } config.host = result.hostname; - config.database = result.pathname ? decodeURI(result.pathname.slice(1)) : null; + + // result.pathname is not always guaranteed to have a '/' prefix (e.g. relative urls) + // only strip the slash if it is present. + var pathname = result.pathname; + if (pathname && pathname.charAt(0) === '/') { + pathname = result.pathname.slice(1) || null; + } + config.database = pathname && decodeURI(pathname); + var auth = (result.auth || ':').split(':'); config.user = auth[0]; config.password = auth[1]; diff --git a/test/parse.js b/test/parse.js index 1b9b203c..89269429 100644 --- a/test/parse.js +++ b/test/parse.js @@ -87,3 +87,26 @@ test('url is properly encoded', function(t){ t.equal(subject.database, ' u%20rl'); t.end(); }); + +test('relative url sets database', function(t){ + var relative = 'different_db_on_default_host'; + var subject = parse(relative); + t.equal(subject.database, 'different_db_on_default_host'); + t.end(); +}); + +test('no pathname returns null database', function (t) { + var subject = parse('pg://myhost'); + t.equal(subject.host, 'myhost'); + t.type(subject.database, 'null'); + + t.end(); +}); + +test('pathname of "/" returns null database', function (t) { + var subject = parse('pg://myhost/'); + t.equal(subject.host, 'myhost'); + t.type(subject.database, 'null'); + + t.end(); +}); From 245abd6daf838d6d2e0424debcfffc2e8b3e3508 Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Sat, 13 Sep 2014 09:20:28 -0700 Subject: [PATCH 06/30] 0.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2cb8d962..ebac6ca4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-connection-string", - "version": "0.1.1", + "version": "0.1.2", "description": "Functions for dealing with a PostgresSQL connection string", "main": "index.js", "scripts": { From fbdd033d6c90cf5f9c2f6f258bc77a2184345f20 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Fri, 26 Sep 2014 15:59:57 +0300 Subject: [PATCH 07/30] Add supporting password with colon --- index.js | 2 +- test/parse.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index b041a208..ac2fd64c 100644 --- a/index.js +++ b/index.js @@ -47,7 +47,7 @@ function parse(str) { var auth = (result.auth || ':').split(':'); config.user = auth[0]; - config.password = auth[1]; + config.password = auth.splice(1).join(':'); var ssl = result.query.ssl; if (ssl === 'true' || ssl === '1') { diff --git a/test/parse.js b/test/parse.js index 89269429..c1494c31 100644 --- a/test/parse.js +++ b/test/parse.js @@ -69,6 +69,20 @@ test('password contains < and/or > characters', function(t){ t.end(); }); +test('password contains colons', function(t){ + var sourceConfig = { + user:'brian', + password: 'hello:pass:world', + port: 5432, + host: 'localhost', + database: 'postgres' + }; + var connectionString = 'postgres://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; + var subject = parse(connectionString); + t.equal(subject.password, sourceConfig.password); + t.end(); +}); + test('username or password contains weird characters', function(t){ var strang = 'pg://my f%irst name:is&%awesome!@localhost:9000'; var subject = parse(strang); From 4c151b9403420c1c9a9682facef1fcdbb9b79b3b Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Fri, 26 Sep 2014 14:22:46 -0700 Subject: [PATCH 08/30] 0.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ebac6ca4..c6d4512d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-connection-string", - "version": "0.1.2", + "version": "0.1.3", "description": "Functions for dealing with a PostgresSQL connection string", "main": "index.js", "scripts": { From c612dfabd58071e9896b1e9ba0b5e5bcbb0c2f6b Mon Sep 17 00:00:00 2001 From: Mike He Date: Tue, 13 Oct 2015 09:19:26 +1100 Subject: [PATCH 09/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49862702..ddf9bfcb 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,5 @@ MIT License ```js var parse = require('pg-connection-string').parse; -var config = parse('postgres://someuser:somepassword@somehost:381/sometable') +var config = parse('postgres://someuser:somepassword@somehost:381/somedatabase') ``` From cdf06edd14d7b4b1df4c7e3cf438e8d9eeeaf271 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Wed, 30 Dec 2015 15:16:38 +0200 Subject: [PATCH 10/30] Copy all but special-cased params from URL query string to config --- index.js | 19 +++++++++------ test/parse.js | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index ac2fd64c..b2863dde 100644 --- a/index.js +++ b/index.js @@ -21,13 +21,6 @@ function parse(str) { var result = url.parse(str, true); config = {}; - if (result.query.application_name) { - config.application_name = result.query.application_name; - } - if (result.query.fallback_application_name) { - config.fallback_application_name = result.query.fallback_application_name; - } - config.port = result.port; if(result.protocol == 'socket:') { config.host = decodeURI(result.pathname); @@ -54,6 +47,18 @@ function parse(str) { config.ssl = true; } + ['db', 'database', 'encoding', 'client_encoding', 'host', 'port', 'user', 'password', 'ssl'] + .forEach(function(key) { + delete result.query[key]; + }); + + Object.getOwnPropertyNames(result.query).forEach(function(key) { + var value = result.query[key]; + if (Array.isArray(value)) + value = value[value.length-1]; + config[key] = value; + }); + return config; } diff --git a/test/parse.js b/test/parse.js index c1494c31..bd1171ba 100644 --- a/test/parse.js +++ b/test/parse.js @@ -124,3 +124,67 @@ test('pathname of "/" returns null database', function (t) { t.end(); }); + +test('configuration parameter application_name', function(t){ + var connectionString = 'pg:///?application_name=TheApp'; + var subject = parse(connectionString); + t.equal(subject.application_name, 'TheApp'); + t.end(); +}); + +test('configuration parameter fallback_application_name', function(t){ + var connectionString = 'pg:///?fallback_application_name=TheAppFallback'; + var subject = parse(connectionString); + t.equal(subject.fallback_application_name, 'TheAppFallback'); + t.end(); +}); + +test('configuration parameter fallback_application_name', function(t){ + var connectionString = 'pg:///?fallback_application_name=TheAppFallback'; + var subject = parse(connectionString); + t.equal(subject.fallback_application_name, 'TheAppFallback'); + t.end(); +}); + +test('configuration parameter ssl=true', function(t){ + var connectionString = 'pg:///?ssl=true'; + var subject = parse(connectionString); + t.equal(subject.ssl, true); + t.end(); +}); + +test('configuration parameter ssl=1', function(t){ + var connectionString = 'pg:///?ssl=1'; + var subject = parse(connectionString); + t.equal(subject.ssl, true); + t.end(); +}); + +test('configuration parameter keepalives', function(t){ + var connectionString = 'pg:///?keepalives=1'; + var subject = parse(connectionString); + t.equal(subject.keepalives, '1'); + t.end(); +}); + +test('unknown configuration parameter is passed into client', function(t){ + var connectionString = 'pg:///?ThereIsNoSuchPostgresParameter=1234'; + var subject = parse(connectionString); + t.equal(subject.ThereIsNoSuchPostgresParameter, '1234'); + t.end(); +}); + +test('do not override a config field with value from query string', function(t){ + var subject = parse('socket:/some path/?db=my[db]&encoding=utf8&client_encoding=bogus'); + t.equal(subject.host, '/some path/'); + t.equal(subject.database, 'my[db]', 'must to be escaped and unescaped trough "my%5Bdb%5D"'); + t.equal(subject.client_encoding, 'utf8'); + t.end(); +}); + +test('return last value of repeated parameter', function(t){ + var connectionString = 'pg:///?keepalives=1&keepalives=0'; + var subject = parse(connectionString); + t.equal(subject.keepalives, '0'); + t.end(); +}); \ No newline at end of file From 54c204441629b65754dade6cb58a443566fefb22 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Thu, 27 Apr 2017 12:41:30 +0200 Subject: [PATCH 11/30] feat: add basic typings To make this app consumable by Typescript apps a typings file must be present. --- index.d.ts | 14 ++++++++++++++ package.json | 3 ++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 index.d.ts diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 00000000..55637511 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,14 @@ +export function parse(connectionString: string): ConnectionOptions; + +export interface ConnectionOptions { + host: string | null; + password: string | null; + user: string | null; + port: number | null; + database: string | null; + client_encoding: string | null; + ssl: boolean | null; + + application_name: string | null; + fallback_application_name: string | null; +} diff --git a/package.json b/package.json index c6d4512d..f3b14c90 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "pg-connection-string", "version": "0.1.3", "description": "Functions for dealing with a PostgresSQL connection string", - "main": "index.js", + "main": "./index.js", + "types": "./index.d.ts", "scripts": { "test": "tap ./test" }, From 9ab62ff9f3050bc8d1096cd6f1258cd288be75fd Mon Sep 17 00:00:00 2001 From: caub Date: Sat, 1 Jul 2017 11:32:37 +0200 Subject: [PATCH 12/30] allow min/max params for pg-pool --- .gitignore | 1 + .travis.yml | 2 ++ index.js | 36 +++++++++++++----------------------- package.json | 2 +- test/parse.js | 18 +++++++++++++++++- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index da23d0d4..f28f01f7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ build/Release # Deployed apps should consider commenting this line out: # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git node_modules +package-lock.json \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 244b7e88..202c3078 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ language: node_js node_js: - '0.10' + - '6.9' + - '8' diff --git a/index.js b/index.js index b2863dde..0042faec 100644 --- a/index.js +++ b/index.js @@ -8,18 +8,20 @@ var url = require('url'); //parses a connection string function parse(str) { - var config; //unix socket if(str.charAt(0) === '/') { - config = str.split(' '); + var config = str.split(' '); return { host: config[0], database: config[1] }; } + // url parse expects spaces encoded as %20 - if(/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str)) { - str = encodeURI(str).replace(/\%25(\d\d)/g, "%$1"); + var result = url.parse(/ |%[^a-f0-9]|%[a-f0-9][^a-f0-9]/i.test(str) ? encodeURI(str).replace(/\%25(\d\d)/g, "%$1") : str, true); + var config = result.query; + for (var k in config) { + if (Array.isArray(config[k])) { + config[k] = config[k][config[k].length-1]; + } } - var result = url.parse(str, true); - config = {}; config.port = result.port; if(result.protocol == 'socket:') { @@ -42,26 +44,14 @@ function parse(str) { config.user = auth[0]; config.password = auth.splice(1).join(':'); - var ssl = result.query.ssl; - if (ssl === 'true' || ssl === '1') { + if (config.ssl === 'true' || config.ssl === '1') { config.ssl = true; } - ['db', 'database', 'encoding', 'client_encoding', 'host', 'port', 'user', 'password', 'ssl'] - .forEach(function(key) { - delete result.query[key]; - }); - - Object.getOwnPropertyNames(result.query).forEach(function(key) { - var value = result.query[key]; - if (Array.isArray(value)) - value = value[value.length-1]; - config[key] = value; - }); - return config; } -module.exports = { - parse: parse -}; + +module.exports = parse; + +parse.parse = parse; diff --git a/package.json b/package.json index f3b14c90..9b0e62ec 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,6 @@ "homepage": "https://github.com/iceddev/pg-connection-string", "dependencies": {}, "devDependencies": { - "tap": "^0.4.11" + "tap": "^10.3.3" } } diff --git a/test/parse.js b/test/parse.js index bd1171ba..8f4bcb43 100644 --- a/test/parse.js +++ b/test/parse.js @@ -160,6 +160,20 @@ test('configuration parameter ssl=1', function(t){ t.end(); }); +test('set ssl', function (t) { + var subject = parse('pg://myhost/db?ssl=1'); + t.equal(subject.ssl, true); + t.end(); + }); + + test('allow other params like max, ...', function (t) { + var subject = parse('pg://myhost/db?max=18&min=4'); + t.equal(subject.max, '18'); + t.equal(subject.min, '4'); + t.end(); + }); + + test('configuration parameter keepalives', function(t){ var connectionString = 'pg:///?keepalives=1'; var subject = parse(connectionString); @@ -182,9 +196,11 @@ test('do not override a config field with value from query string', function(t){ t.end(); }); + test('return last value of repeated parameter', function(t){ var connectionString = 'pg:///?keepalives=1&keepalives=0'; var subject = parse(connectionString); t.equal(subject.keepalives, '0'); t.end(); -}); \ No newline at end of file +}); + From 13687353c9ecc4b2ea736ee0784a6a158a40a951 Mon Sep 17 00:00:00 2001 From: Luis Montes Date: Wed, 30 Aug 2017 13:58:52 -0700 Subject: [PATCH 13/30] Use mocha, istanbul, and coveralls (#16) * some tests * coveralls and mocha * coveralls post test hook * remove done calls --- .coveralls.yml | 2 + .travis.yml | 1 + README.md | 3 + package.json | 8 +- test/parse.js | 376 +++++++++++++++++++++++-------------------------- 5 files changed, 188 insertions(+), 202 deletions(-) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 00000000..0709f6e0 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,2 @@ +service_name: travis-pro +repo_token: 5F6dODinz9L9uFR6HatKmtsYDoV1A5S2N diff --git a/.travis.yml b/.travis.yml index 202c3078..daf50ba6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,4 @@ node_js: - '0.10' - '6.9' - '8' +after_success: 'npm run coveralls' diff --git a/README.md b/README.md index ddf9bfcb..2228b80e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ pg-connection-string ==================== +[![NPM](https://nodei.co/npm/pg-connection-string.png?compact=true)](https://nodei.co/npm/pg-connection-string/) + [![Build Status](https://travis-ci.org/iceddev/pg-connection-string.svg?branch=master)](https://travis-ci.org/iceddev/pg-connection-string) +[![Coverage Status](https://coveralls.io/repos/iceddev/pg-connection-string/badge.svg?branch=master)](https://coveralls.io/r/iceddev/pg-connection-string?branch=master) Functions for dealing with a PostgresSQL connection string diff --git a/package.json b/package.json index 9b0e62ec..f6ff6e51 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "main": "./index.js", "types": "./index.d.ts", "scripts": { - "test": "tap ./test" + "test": "istanbul cover _mocha && npm run check-coverage", + "check-coverage": "istanbul check-coverage --statements 100 --branches 100 --lines 100 --functions 100", + "coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls" }, "repository": { "type": "git", @@ -25,6 +27,8 @@ "homepage": "https://github.com/iceddev/pg-connection-string", "dependencies": {}, "devDependencies": { - "tap": "^10.3.3" + "chai": "^4.1.1", + "istanbul": "^0.4.5", + "mocha": "^3.5.0" } } diff --git a/test/parse.js b/test/parse.js index 8f4bcb43..8ff3ee81 100644 --- a/test/parse.js +++ b/test/parse.js @@ -1,206 +1,182 @@ 'use strict'; -var test = require('tap').test; +var chai = require('chai'); +var expect = chai.expect; +chai.should(); var parse = require('../').parse; -test('using connection string in client constructor', function(t){ - var subject = parse('postgres://brian:pw@boom:381/lala'); - t.equal(subject.user,'brian'); - t.equal(subject.password, 'pw'); - t.equal(subject.host, 'boom'); - t.equal(subject.port, '381'); - t.equal(subject.database, 'lala'); - t.end(); +describe('parse', function(){ + + it('using connection string in client constructor', function(){ + var subject = parse('postgres://brian:pw@boom:381/lala'); + subject.user.should.equal('brian'); + subject.password.should.equal( 'pw'); + subject.host.should.equal( 'boom'); + subject.port.should.equal( '381'); + subject.database.should.equal( 'lala'); + }); + + it('escape spaces if present', function(){ + var subject = parse('postgres://localhost/post gres'); + subject.database.should.equal('post gres'); + }); + + it('do not double escape spaces', function(){ + var subject = parse('postgres://localhost/post%20gres'); + subject.database.should.equal('post gres'); + }); + + it('initializing with unix domain socket', function(){ + var subject = parse('/var/run/'); + subject.host.should.equal('/var/run/'); + }); + + it('initializing with unix domain socket and a specific database, the simple way', function(){ + var subject = parse('/var/run/ mydb'); + subject.host.should.equal('/var/run/'); + subject.database.should.equal('mydb'); + }); + + it('initializing with unix domain socket, the health way', function(){ + var subject = parse('socket:/some path/?db=my[db]&encoding=utf8'); + subject.host.should.equal('/some path/'); + subject.database.should.equal('my[db]', 'must to be escaped and unescaped trough "my%5Bdb%5D"'); + subject.client_encoding.should.equal('utf8'); + }); + + it('initializing with unix domain socket, the escaped health way', function(){ + var subject = parse('socket:/some%20path/?db=my%2Bdb&encoding=utf8'); + subject.host.should.equal('/some path/'); + subject.database.should.equal('my+db'); + subject.client_encoding.should.equal('utf8'); + }); + + it('password contains < and/or > characters', function(){ + var sourceConfig = { + user:'brian', + password: 'helloe', + port: 5432, + host: 'localhost', + database: 'postgres' + }; + var connectionString = 'postgres://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; + var subject = parse(connectionString); + subject.password.should.equal(sourceConfig.password); + }); + + it('password contains colons', function(){ + var sourceConfig = { + user:'brian', + password: 'hello:pass:world', + port: 5432, + host: 'localhost', + database: 'postgres' + }; + var connectionString = 'postgres://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; + var subject = parse(connectionString); + subject.password.should.equal(sourceConfig.password); + }); + + it('username or password contains weird characters', function(){ + var strang = 'pg://my f%irst name:is&%awesome!@localhost:9000'; + var subject = parse(strang); + subject.user.should.equal('my f%irst name'); + subject.password.should.equal('is&%awesome!'); + subject.host.should.equal('localhost'); + }); + + it('url is properly encoded', function(){ + var encoded = 'pg://bi%25na%25%25ry%20:s%40f%23@localhost/%20u%2520rl'; + var subject = parse(encoded); + subject.user.should.equal('bi%na%%ry '); + subject.password.should.equal('s@f#'); + subject.host.should.equal('localhost'); + subject.database.should.equal(' u%20rl'); + }); + + it('relative url sets database', function(){ + var relative = 'different_db_on_default_host'; + var subject = parse(relative); + subject.database.should.equal('different_db_on_default_host'); + }); + + it('no pathname returns null database', function () { + var subject = parse('pg://myhost'); + (subject.database === null).should.equal(true); + }); + + it('pathname of "/" returns null database', function () { + var subject = parse('pg://myhost/'); + subject.host.should.equal('myhost'); + (subject.database === null).should.equal(true); + }); + + it('configuration parameter application_name', function(){ + var connectionString = 'pg:///?application_name=TheApp'; + var subject = parse(connectionString); + subject.application_name.should.equal('TheApp'); + }); + + it('configuration parameter fallback_application_name', function(){ + var connectionString = 'pg:///?fallback_application_name=TheAppFallback'; + var subject = parse(connectionString); + subject.fallback_application_name.should.equal('TheAppFallback'); + }); + + it('configuration parameter fallback_application_name', function(){ + var connectionString = 'pg:///?fallback_application_name=TheAppFallback'; + var subject = parse(connectionString); + subject.fallback_application_name.should.equal('TheAppFallback'); + }); + + it('configuration parameter ssl=true', function(){ + var connectionString = 'pg:///?ssl=true'; + var subject = parse(connectionString); + subject.ssl.should.equal(true); + }); + + it('configuration parameter ssl=1', function(){ + var connectionString = 'pg:///?ssl=1'; + var subject = parse(connectionString); + subject.ssl.should.equal(true); + }); + + it('set ssl', function () { + var subject = parse('pg://myhost/db?ssl=1'); + subject.ssl.should.equal(true); + }); + + it('allow other params like max, ...', function () { + var subject = parse('pg://myhost/db?max=18&min=4'); + subject.max.should.equal('18'); + subject.min.should.equal('4'); + }); + + + it('configuration parameter keepalives', function(){ + var connectionString = 'pg:///?keepalives=1'; + var subject = parse(connectionString); + subject.keepalives.should.equal('1'); + }); + + it('unknown configuration parameter is passed into client', function(){ + var connectionString = 'pg:///?ThereIsNoSuchPostgresParameter=1234'; + var subject = parse(connectionString); + subject.ThereIsNoSuchPostgresParameter.should.equal('1234'); + }); + + it('do not override a config field with value from query string', function(){ + var subject = parse('socket:/some path/?db=my[db]&encoding=utf8&client_encoding=bogus'); + subject.host.should.equal('/some path/'); + subject.database.should.equal('my[db]', 'must to be escaped and unescaped through "my%5Bdb%5D"'); + subject.client_encoding.should.equal('utf8'); + }); + + + it('return last value of repeated parameter', function(){ + var connectionString = 'pg:///?keepalives=1&keepalives=0'; + var subject = parse(connectionString); + subject.keepalives.should.equal('0'); + }); }); - -test('escape spaces if present', function(t){ - var subject = parse('postgres://localhost/post gres'); - t.equal(subject.database, 'post gres'); - t.end(); -}); - -test('do not double escape spaces', function(t){ - var subject = parse('postgres://localhost/post%20gres'); - t.equal(subject.database, 'post gres'); - t.end(); -}); - -test('initializing with unix domain socket', function(t){ - var subject = parse('/var/run/'); - t.equal(subject.host, '/var/run/'); - t.end(); -}); - -test('initializing with unix domain socket and a specific database, the simple way', function(t){ - var subject = parse('/var/run/ mydb'); - t.equal(subject.host, '/var/run/'); - t.equal(subject.database, 'mydb'); - t.end(); -}); - -test('initializing with unix domain socket, the health way', function(t){ - var subject = parse('socket:/some path/?db=my[db]&encoding=utf8'); - t.equal(subject.host, '/some path/'); - t.equal(subject.database, 'my[db]', 'must to be escaped and unescaped trough "my%5Bdb%5D"'); - t.equal(subject.client_encoding, 'utf8'); - t.end(); -}); - -test('initializing with unix domain socket, the escaped health way', function(t){ - var subject = parse('socket:/some%20path/?db=my%2Bdb&encoding=utf8'); - t.equal(subject.host, '/some path/'); - t.equal(subject.database, 'my+db'); - t.equal(subject.client_encoding, 'utf8'); - t.end(); -}); - -test('password contains < and/or > characters', function(t){ - var sourceConfig = { - user:'brian', - password: 'helloe', - port: 5432, - host: 'localhost', - database: 'postgres' - }; - var connectionString = 'postgres://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; - var subject = parse(connectionString); - t.equal(subject.password, sourceConfig.password); - t.end(); -}); - -test('password contains colons', function(t){ - var sourceConfig = { - user:'brian', - password: 'hello:pass:world', - port: 5432, - host: 'localhost', - database: 'postgres' - }; - var connectionString = 'postgres://' + sourceConfig.user + ':' + sourceConfig.password + '@' + sourceConfig.host + ':' + sourceConfig.port + '/' + sourceConfig.database; - var subject = parse(connectionString); - t.equal(subject.password, sourceConfig.password); - t.end(); -}); - -test('username or password contains weird characters', function(t){ - var strang = 'pg://my f%irst name:is&%awesome!@localhost:9000'; - var subject = parse(strang); - t.equal(subject.user, 'my f%irst name'); - t.equal(subject.password, 'is&%awesome!'); - t.equal(subject.host, 'localhost'); - t.end(); -}); - -test('url is properly encoded', function(t){ - var encoded = 'pg://bi%25na%25%25ry%20:s%40f%23@localhost/%20u%2520rl'; - var subject = parse(encoded); - t.equal(subject.user, 'bi%na%%ry '); - t.equal(subject.password, 's@f#'); - t.equal(subject.host, 'localhost'); - t.equal(subject.database, ' u%20rl'); - t.end(); -}); - -test('relative url sets database', function(t){ - var relative = 'different_db_on_default_host'; - var subject = parse(relative); - t.equal(subject.database, 'different_db_on_default_host'); - t.end(); -}); - -test('no pathname returns null database', function (t) { - var subject = parse('pg://myhost'); - t.equal(subject.host, 'myhost'); - t.type(subject.database, 'null'); - - t.end(); -}); - -test('pathname of "/" returns null database', function (t) { - var subject = parse('pg://myhost/'); - t.equal(subject.host, 'myhost'); - t.type(subject.database, 'null'); - - t.end(); -}); - -test('configuration parameter application_name', function(t){ - var connectionString = 'pg:///?application_name=TheApp'; - var subject = parse(connectionString); - t.equal(subject.application_name, 'TheApp'); - t.end(); -}); - -test('configuration parameter fallback_application_name', function(t){ - var connectionString = 'pg:///?fallback_application_name=TheAppFallback'; - var subject = parse(connectionString); - t.equal(subject.fallback_application_name, 'TheAppFallback'); - t.end(); -}); - -test('configuration parameter fallback_application_name', function(t){ - var connectionString = 'pg:///?fallback_application_name=TheAppFallback'; - var subject = parse(connectionString); - t.equal(subject.fallback_application_name, 'TheAppFallback'); - t.end(); -}); - -test('configuration parameter ssl=true', function(t){ - var connectionString = 'pg:///?ssl=true'; - var subject = parse(connectionString); - t.equal(subject.ssl, true); - t.end(); -}); - -test('configuration parameter ssl=1', function(t){ - var connectionString = 'pg:///?ssl=1'; - var subject = parse(connectionString); - t.equal(subject.ssl, true); - t.end(); -}); - -test('set ssl', function (t) { - var subject = parse('pg://myhost/db?ssl=1'); - t.equal(subject.ssl, true); - t.end(); - }); - - test('allow other params like max, ...', function (t) { - var subject = parse('pg://myhost/db?max=18&min=4'); - t.equal(subject.max, '18'); - t.equal(subject.min, '4'); - t.end(); - }); - - -test('configuration parameter keepalives', function(t){ - var connectionString = 'pg:///?keepalives=1'; - var subject = parse(connectionString); - t.equal(subject.keepalives, '1'); - t.end(); -}); - -test('unknown configuration parameter is passed into client', function(t){ - var connectionString = 'pg:///?ThereIsNoSuchPostgresParameter=1234'; - var subject = parse(connectionString); - t.equal(subject.ThereIsNoSuchPostgresParameter, '1234'); - t.end(); -}); - -test('do not override a config field with value from query string', function(t){ - var subject = parse('socket:/some path/?db=my[db]&encoding=utf8&client_encoding=bogus'); - t.equal(subject.host, '/some path/'); - t.equal(subject.database, 'my[db]', 'must to be escaped and unescaped trough "my%5Bdb%5D"'); - t.equal(subject.client_encoding, 'utf8'); - t.end(); -}); - - -test('return last value of repeated parameter', function(t){ - var connectionString = 'pg:///?keepalives=1&keepalives=0'; - var subject = parse(connectionString); - t.equal(subject.keepalives, '0'); - t.end(); -}); - From eafb7acd951b4ee606d41c0d1ba66b34e72119a3 Mon Sep 17 00:00:00 2001 From: Luis Montes Date: Wed, 30 Aug 2017 14:25:59 -0700 Subject: [PATCH 14/30] 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6ff6e51..5b75ea37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-connection-string", - "version": "0.1.3", + "version": "2.0.0", "description": "Functions for dealing with a PostgresSQL connection string", "main": "./index.js", "types": "./index.d.ts", From 279fdeae2fdd331197d74d6f31a201bba1bdf7cf Mon Sep 17 00:00:00 2001 From: Youngwook Kim Date: Wed, 22 Nov 2017 10:17:22 +0900 Subject: [PATCH 15/30] Add supporting username and password for socket connections This fix adds the ability to use username and password even when using a socket. --- index.js | 8 ++++---- test/parse.js | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 0042faec..3e04f547 100644 --- a/index.js +++ b/index.js @@ -23,6 +23,10 @@ function parse(str) { } } + var auth = (result.auth || ':').split(':'); + config.user = auth[0]; + config.password = auth.splice(1).join(':'); + config.port = result.port; if(result.protocol == 'socket:') { config.host = decodeURI(result.pathname); @@ -40,10 +44,6 @@ function parse(str) { } config.database = pathname && decodeURI(pathname); - var auth = (result.auth || ':').split(':'); - config.user = auth[0]; - config.password = auth.splice(1).join(':'); - if (config.ssl === 'true' || config.ssl === '1') { config.ssl = true; } diff --git a/test/parse.js b/test/parse.js index 8ff3ee81..42936733 100644 --- a/test/parse.js +++ b/test/parse.js @@ -52,6 +52,14 @@ describe('parse', function(){ subject.client_encoding.should.equal('utf8'); }); + it('initializing with unix domain socket, username and password', function(){ + var subject = parse('socket://brian:pw@/var/run/?db=mydb'); + subject.user.should.equal('brian'); + subject.password.should.equal('pw'); + subject.host.should.equal('/var/run/'); + subject.database.should.equal('mydb'); + }); + it('password contains < and/or > characters', function(){ var sourceConfig = { user:'brian', From 929fcb73c3f5128e2d1d50eb5d62a6481d098754 Mon Sep 17 00:00:00 2001 From: benny-medflyt Date: Wed, 13 Dec 2017 11:20:29 +0200 Subject: [PATCH 16/30] Fix typings --- index.d.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/index.d.ts b/index.d.ts index 55637511..d0fc7214 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,14 +1,14 @@ export function parse(connectionString: string): ConnectionOptions; export interface ConnectionOptions { - host: string | null; - password: string | null; - user: string | null; - port: number | null; - database: string | null; - client_encoding: string | null; - ssl: boolean | null; + host: string; + password?: string; + user?: string; + port: string | null; + database: string | null | undefined; + client_encoding?: string | undefined; + ssl?: boolean; - application_name: string | null; - fallback_application_name: string | null; + application_name?: string; + fallback_application_name?: string; } From ece764518774690ea8ee58360ae0b6ca7b248d4d Mon Sep 17 00:00:00 2001 From: benny-medflyt Date: Wed, 13 Dec 2017 11:23:28 +0200 Subject: [PATCH 17/30] typings: turns out "host" can actually be `null` --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index d0fc7214..2a657460 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,7 +1,7 @@ export function parse(connectionString: string): ConnectionOptions; export interface ConnectionOptions { - host: string; + host: string | null; password?: string; user?: string; port: string | null; From c11dbb1c2b3409964372721666d3a2898dd2097e Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 18 Apr 2019 14:46:36 +0100 Subject: [PATCH 18/30] Only publish the required files Details: https://docs.npmjs.com/files/package.json#files --- package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b75ea37..b625a16d 100644 --- a/package.json +++ b/package.json @@ -30,5 +30,9 @@ "chai": "^4.1.1", "istanbul": "^0.4.5", "mocha": "^3.5.0" - } + }, + "files": [ + "index.js", + "index.d.ts" + ] } From e9270e89af6c6e5b84b8c668bf206c8f9ab97a45 Mon Sep 17 00:00:00 2001 From: "Herman J. Radtke III" Date: Fri, 17 May 2019 07:38:25 -0700 Subject: [PATCH 19/30] Add support for TLS parameters in URI The connection string now supports the following parameters: - sslcert - sslkey - sslrootcert Fixes #25. --- index.js | 17 +++++++++++++++++ test/example.ca | 1 + test/example.cert | 1 + test/example.key | 1 + test/parse.js | 24 ++++++++++++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 test/example.ca create mode 100644 test/example.cert create mode 100644 test/example.key diff --git a/index.js b/index.js index 0042faec..981bdcda 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ 'use strict'; var url = require('url'); +var fs = require('fs'); //Parse method copied from https://github.com/brianc/node-postgres //Copyright (c) 2010-2014 Brian Carlson (brian.m.carlson@gmail.com) @@ -48,6 +49,22 @@ function parse(str) { config.ssl = true; } + if (config.sslcert || config.sslkey || config.sslrootcert) { + config.ssl = {}; + } + + if (config.sslcert) { + config.ssl.cert = fs.readFileSync(config.sslcert).toString(); + } + + if (config.sslkey) { + config.ssl.key = fs.readFileSync(config.sslkey).toString(); + } + + if (config.sslrootcert) { + config.ssl.ca = fs.readFileSync(config.sslrootcert).toString(); + } + return config; } diff --git a/test/example.ca b/test/example.ca new file mode 100644 index 00000000..0a6dcf40 --- /dev/null +++ b/test/example.ca @@ -0,0 +1 @@ +example ca diff --git a/test/example.cert b/test/example.cert new file mode 100644 index 00000000..7693b3fe --- /dev/null +++ b/test/example.cert @@ -0,0 +1 @@ +example cert diff --git a/test/example.key b/test/example.key new file mode 100644 index 00000000..1aef9935 --- /dev/null +++ b/test/example.key @@ -0,0 +1 @@ +example key diff --git a/test/parse.js b/test/parse.js index 8ff3ee81..6632cc71 100644 --- a/test/parse.js +++ b/test/parse.js @@ -147,6 +147,30 @@ describe('parse', function(){ subject.ssl.should.equal(true); }); + it('configuration parameter sslcert=/path/to/cert', function(){ + var connectionString = 'pg:///?sslcert=' + __dirname + '/example.cert'; + var subject = parse(connectionString); + subject.ssl.should.eql({ + cert: 'example cert\n' + }); + }); + + it('configuration parameter sslkey=/path/to/key', function(){ + var connectionString = 'pg:///?sslkey=' + __dirname + '/example.key'; + var subject = parse(connectionString); + subject.ssl.should.eql({ + key: 'example key\n' + }); + }); + + it('configuration parameter sslrootcert=/path/to/ca', function(){ + var connectionString = 'pg:///?sslrootcert=' + __dirname + '/example.ca'; + var subject = parse(connectionString); + subject.ssl.should.eql({ + ca: 'example ca\n' + }); + }); + it('allow other params like max, ...', function () { var subject = parse('pg://myhost/db?max=18&min=4'); subject.max.should.equal('18'); From 7b62226d573bdde749e4c94ed21d67c74d1f3bd2 Mon Sep 17 00:00:00 2001 From: "Herman J. Radtke III" Date: Thu, 23 May 2019 14:23:55 -0700 Subject: [PATCH 20/30] ssl=0 is now parses to false Fixes #20 --- index.js | 4 ++++ test/parse.js | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/index.js b/index.js index 66798401..3cabb372 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,10 @@ function parse(str) { config.ssl = true; } + if (config.ssl === '0') { + config.ssl = false; + } + if (config.sslcert || config.sslkey || config.sslrootcert) { config.ssl = {}; } diff --git a/test/parse.js b/test/parse.js index abf2c4f9..4de28719 100644 --- a/test/parse.js +++ b/test/parse.js @@ -150,6 +150,12 @@ describe('parse', function(){ subject.ssl.should.equal(true); }); + it('configuration parameter ssl=0', function(){ + var connectionString = 'pg:///?ssl=0'; + var subject = parse(connectionString); + subject.ssl.should.equal(false); + }); + it('set ssl', function () { var subject = parse('pg://myhost/db?ssl=1'); subject.ssl.should.equal(true); From 726f6202fa7eb89096bf6cb8d32526ebacf4be49 Mon Sep 17 00:00:00 2001 From: benny-medflyt Date: Mon, 27 May 2019 08:30:21 -0400 Subject: [PATCH 21/30] Update index.d.ts --- index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 2a657460..1d2f1606 100644 --- a/index.d.ts +++ b/index.d.ts @@ -4,10 +4,10 @@ export interface ConnectionOptions { host: string | null; password?: string; user?: string; - port: string | null; + port?: string | null; database: string | null | undefined; - client_encoding?: string | undefined; - ssl?: boolean; + client_encoding?: string; + ssl?: boolean | string; application_name?: string; fallback_application_name?: string; From 06c46ac12b2a9540483450305f841938a759ba4d Mon Sep 17 00:00:00 2001 From: Andrew Bowerman Date: Tue, 18 Jun 2019 20:12:03 -0700 Subject: [PATCH 22/30] 2.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b625a16d..c39da5f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-connection-string", - "version": "2.0.0", + "version": "2.1.0", "description": "Functions for dealing with a PostgresSQL connection string", "main": "./index.js", "types": "./index.d.ts", From c9ee9cd19970c93dda8ce66b2dd7fd1efa6b056f Mon Sep 17 00:00:00 2001 From: Andrew Bowerman Date: Tue, 18 Jun 2019 20:25:32 -0700 Subject: [PATCH 23/30] Update coveralls badge Closes #15 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2228b80e..cb4b1acc 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ pg-connection-string [![NPM](https://nodei.co/npm/pg-connection-string.png?compact=true)](https://nodei.co/npm/pg-connection-string/) [![Build Status](https://travis-ci.org/iceddev/pg-connection-string.svg?branch=master)](https://travis-ci.org/iceddev/pg-connection-string) -[![Coverage Status](https://coveralls.io/repos/iceddev/pg-connection-string/badge.svg?branch=master)](https://coveralls.io/r/iceddev/pg-connection-string?branch=master) - +[![Coverage Status](https://coveralls.io/repos/github/iceddev/pg-connection-string/badge.svg?branch=master)](https://coveralls.io/github/iceddev/pg-connection-string?branch=master) Functions for dealing with a PostgresSQL connection string `parse` method taken from [node-postgres](https://github.com/brianc/node-postgres.git) From c75c3929654401fbf8654f6df3613ef2748c1428 Mon Sep 17 00:00:00 2001 From: Andrew Bowerman Date: Tue, 18 Jun 2019 20:41:10 -0700 Subject: [PATCH 24/30] fix readme newline typo --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cb4b1acc..4943d885 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ pg-connection-string [![Build Status](https://travis-ci.org/iceddev/pg-connection-string.svg?branch=master)](https://travis-ci.org/iceddev/pg-connection-string) [![Coverage Status](https://coveralls.io/repos/github/iceddev/pg-connection-string/badge.svg?branch=master)](https://coveralls.io/github/iceddev/pg-connection-string?branch=master) + Functions for dealing with a PostgresSQL connection string `parse` method taken from [node-postgres](https://github.com/brianc/node-postgres.git) From e4c1002e2e9413636c32929236e96f893010a317 Mon Sep 17 00:00:00 2001 From: Andrew Bowerman Date: Tue, 18 Jun 2019 20:49:39 -0700 Subject: [PATCH 25/30] actually include coveralls --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index c39da5f2..415638f0 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "dependencies": {}, "devDependencies": { "chai": "^4.1.1", + "coveralls": "^3.0.4", "istanbul": "^0.4.5", "mocha": "^3.5.0" }, From 0ff40e733b58096dd3e14a7a1c787a22058f54a1 Mon Sep 17 00:00:00 2001 From: Daniel Rozenberg Date: Tue, 28 Jan 2020 19:28:03 -0500 Subject: [PATCH 26/30] host= query param takes precedence --- index.js | 5 ++++- test/parse.js | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 3cabb372..29d3653c 100644 --- a/index.js +++ b/index.js @@ -35,7 +35,10 @@ function parse(str) { config.client_encoding = result.query.encoding; return config; } - config.host = result.hostname; + if (!config.host) { + // Only set the host if there is no equivalent query param. + config.host = result.hostname; + } // result.pathname is not always guaranteed to have a '/' prefix (e.g. relative urls) // only strip the slash if it is present. diff --git a/test/parse.js b/test/parse.js index 4de28719..c973fb5c 100644 --- a/test/parse.js +++ b/test/parse.js @@ -120,6 +120,19 @@ describe('parse', function(){ (subject.database === null).should.equal(true); }); + it('configuration parameter host', function() { + var subject = parse('pg://user:pass@/dbname?host=/unix/socket'); + subject.user.should.equal('user'); + subject.password.should.equal('pass'); + subject.host.should.equal('/unix/socket'); + subject.database.should.equal('dbname'); + }); + + it('configuration parameter host overrides url host', function() { + var subject = parse('pg://user:pass@localhost/dbname?host=/unix/socket'); + subject.host.should.equal('/unix/socket'); + }); + it('configuration parameter application_name', function(){ var connectionString = 'pg:///?application_name=TheApp'; var subject = parse(connectionString); From b309db074ff545ed93c6396a98029b5cce7b943d Mon Sep 17 00:00:00 2001 From: Daniel Rozenberg Date: Tue, 28 Jan 2020 19:29:38 -0500 Subject: [PATCH 27/30] Support URL-encoded socket names --- index.js | 10 ++++++++-- test/parse.js | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 29d3653c..7cfc8293 100644 --- a/index.js +++ b/index.js @@ -40,11 +40,17 @@ function parse(str) { config.host = result.hostname; } + // If the host is missing it might be a URL-encoded path to a socket. + var pathname = result.pathname; + if (!config.host && pathname && pathname.toLowerCase().startsWith('%2f')) { + var pathnameSplit = pathname.split('/'); + config.host = decodeURIComponent(pathnameSplit[0]); + pathname = pathnameSplit.splice(1).join('/'); + } // result.pathname is not always guaranteed to have a '/' prefix (e.g. relative urls) // only strip the slash if it is present. - var pathname = result.pathname; if (pathname && pathname.charAt(0) === '/') { - pathname = result.pathname.slice(1) || null; + pathname = pathname.slice(1) || null; } config.database = pathname && decodeURI(pathname); diff --git a/test/parse.js b/test/parse.js index c973fb5c..07f886e1 100644 --- a/test/parse.js +++ b/test/parse.js @@ -133,6 +133,30 @@ describe('parse', function(){ subject.host.should.equal('/unix/socket'); }); + it('url with encoded socket', function() { + var subject = parse('pg://user:pass@%2Funix%2Fsocket/dbname'); + subject.user.should.equal('user'); + subject.password.should.equal('pass'); + subject.host.should.equal('/unix/socket'); + subject.database.should.equal('dbname'); + }); + + it('url with real host and an encoded db name', function() { + var subject = parse('pg://user:pass@localhost/%2Fdbname'); + subject.user.should.equal('user'); + subject.password.should.equal('pass'); + subject.host.should.equal('localhost'); + subject.database.should.equal('%2Fdbname'); + }); + + it('configuration parameter host treats encoded socket as part of the db name', function() { + var subject = parse('pg://user:pass@%2Funix%2Fsocket/dbname?host=localhost'); + subject.user.should.equal('user'); + subject.password.should.equal('pass'); + subject.host.should.equal('localhost'); + subject.database.should.equal('%2Funix%2Fsocket/dbname'); + }); + it('configuration parameter application_name', function(){ var connectionString = 'pg:///?application_name=TheApp'; var subject = parse(connectionString); From 7ec9b70180b5aaadb75c4fde3f35ff86031ce279 Mon Sep 17 00:00:00 2001 From: Daniel Rozenberg Date: Tue, 28 Jan 2020 19:40:51 -0500 Subject: [PATCH 28/30] Use regex instead of startsWith which is unsupported in node 0.10 --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 7cfc8293..7e914ba1 100644 --- a/index.js +++ b/index.js @@ -42,7 +42,7 @@ function parse(str) { // If the host is missing it might be a URL-encoded path to a socket. var pathname = result.pathname; - if (!config.host && pathname && pathname.toLowerCase().startsWith('%2f')) { + if (!config.host && pathname && /^%2f/i.test(pathname)) { var pathnameSplit = pathname.split('/'); config.host = decodeURIComponent(pathnameSplit[0]); pathname = pathnameSplit.splice(1).join('/'); From b4e0ba329ade3d976e0804dd0d79438274e7233f Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sun, 16 Feb 2020 12:30:46 -0500 Subject: [PATCH 29/30] Include documentation on the URL format in the README This summarizes the common forms, but omits some of the more particular, and unnecessary forms, such as specifying UNIX domain sockets with `pg://` URLs. --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/README.md b/README.md index 4943d885..d5b45ab9 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,54 @@ var parse = require('pg-connection-string').parse; var config = parse('postgres://someuser:somepassword@somehost:381/somedatabase') ``` + +The resulting config contains a subset of the following properties: + +* `host` - Postgres server hostname or, for UNIX doamain sockets, the socket filename +* `port` - port on which to connect +* `user` - User with which to authenticate to the server +* `password` - Corresponding password +* `database` - Database name within the server +* `client_encoding` - string encoding the client will use +* `ssl`, either a boolean or an object with properties + * `cert` + * `key` + * `ca` +* any other query parameters (for example, `application_name`) are preserved intact. + +## Connection Strings + +The short summary of acceptable URLs is: + + * `socket:?` - UNIX domain socket + * `postgres://:@:/?` - TCP connection + +But see below for more details. + +### UNIX Domain Sockets + +When user and password are not given, the socket path follows `socket:`, as in `socket:/var/run/pgsql`. +This form can be shortened to just a path: `/var/run/pgsql`. + +When user and password are given, they are included in the typical URL positions, with an empty `host`, as in `socket://user:pass@/var/run/pgsql`. + +Query parameters follow a `?` character, including the following special query parameters: + + * `db=` - sets the database name (urlencoded) + * `encoding=` - sets the `client_encoding` property + +### TCP Connections + +TCP connections to the Postgres server are indicated with `pg:` or `postgres:` schemes (in fact, any scheme but `socket:` is accepted). +If username and password are included, they should be urlencoded. +The database name, however, should *not* be urlencoded. + +Query parameters follow a `?` character, including the following special query parameters: + * `host=` - sets `host` property, overriding the URL's host + * `encoding=` - sets the `client_encoding` property + * `ssl=1`, `ssl=true`, `ssl=0`, `ssl=false` - sets `ssl` to true or false, accordingly + * `sslcert=` - reads data from the given file and includes the result as `ssl.cert` + * `sslkey=` - reads data from the given file and includes the result as `ssl.key` + * `sslrootcert=` - reads data from the given file and includes the result as `ssl.ca` + +A bare relative URL, such as `salesdata`, will indicate a database name while leaving other properties empty. From 5233b3e77e396a368130709e762fca836290a528 Mon Sep 17 00:00:00 2001 From: "Herman J. Radtke III" Date: Thu, 19 Mar 2020 21:56:45 -0700 Subject: [PATCH 30/30] Release v2.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 415638f0..49345369 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg-connection-string", - "version": "2.1.0", + "version": "2.2.0", "description": "Functions for dealing with a PostgresSQL connection string", "main": "./index.js", "types": "./index.d.ts",