@@ -8,116 +8,163 @@ const promisedMethodCall = require('./promisedMethodCall')
88
99/**
1010 * @typedef {Object } NodeExistConnectionOptions
11+ * @prop {{user:string, pass:string} } [basic_auth] database user credentials, default: {"user":"guest","pass":"guest"}
12+ * @prop {"http:"|"https:" } [protocol] "http:" or "https:", default: "https:"
1113 * @prop {string } [host] database host, default: "localhost"
1214 * @prop {string } [port] database port, default: "8443"
13- * @prop {boolean } [secure] use HTTPS? default: true
14- * @prop {boolean } [rejectUnauthorized] enforce valid SSL connection, default: true
1515 * @prop {string } [path] path to XMLRPC, default: "/exist/xmlrpc"
16- * @prop {{user:string, pass:string} } [basic_auth] database user credentials, default: {"user":"guest","pass":"guest"}
16+ * @prop {boolean } [rejectUnauthorized] enforce valid SSL certs, default: true for remote hosts
17+ */
18+
19+ /**
20+ * @typedef {Object } MergedOptions
21+ * @prop {{user:string, pass:string} } basic_auth database user credentials
22+ * @prop {"http:"|"https:" } protocol "http:" or "https:"
23+ * @prop {string } host database host
24+ * @prop {string } port database port
25+ * @prop {string } path path to XMLRPC, default: "/exist/xmlrpc"
26+ * @prop {boolean } [rejectUnauthorized] enforce valid SSL certs, if https: is used
27+ */
28+
29+ /**
30+ * Default REST endpoint
31+ * @type {string }
1732 */
33+ const defaultRestEndpoint = '/exist/rest'
34+
35+ /**
36+ * Default XML-RPC endpoint
37+ * @type {string }
38+ */
39+ const defaultXmlrpcEndpoint = '/exist/xmlrpc'
1840
1941/**
2042 * Default connection options
2143 * @type {NodeExistConnectionOptions }
2244 */
23- const defaultRPCoptions = {
24- host : 'localhost' ,
25- port : '8443' ,
26- path : '/exist/xmlrpc' ,
45+ const defaultConnectionOptions = {
2746 basic_auth : {
2847 user : 'guest' ,
2948 pass : 'guest'
30- }
31- }
32-
33- const defaultRestOptions = {
34- host : 'localhost' ,
49+ } ,
3550 protocol : 'https:' ,
36- port : '8443' ,
37- path : '/exist/rest' ,
38- basic_auth : {
39- user : 'guest' ,
40- pass : 'guest'
41- }
42- }
43-
44- function isLocalDB ( host ) {
45- return (
46- host === 'localhost' ||
47- host === '127.0.0.1' ||
48- host === '[::1]'
49- )
51+ host : 'localhost' ,
52+ port : '8443'
5053}
5154
52- function useSecureConnection ( options ) {
53- if ( options && 'secure' in options ) {
54- return Boolean ( options . secure )
55+ /**
56+ * get REST client
57+ * @param {NodeExistConnectionOptions } [options] connection options
58+ * @returns {got } Extended HTTP client instance
59+ */
60+ async function restConnection ( options ) {
61+ const { got } = await import ( 'got' )
62+ /* eslint camelcase: "off" */
63+ const { basic_auth, protocol, host, port, path, rejectUnauthorized } = mergeOptions ( defaultRestEndpoint , options )
64+
65+ const prefixUrl = protocol + '//' + host + ( port ? ':' + port : '' ) + path
66+
67+ const httpClientOptions = {
68+ prefixUrl,
69+ headers : {
70+ 'user-agent' : 'node-exist' ,
71+ authorization : basicAuth ( basic_auth )
72+ } ,
73+ https : { rejectUnauthorized }
5574 }
56- return true
75+
76+ return got . extend ( httpClientOptions )
5777}
5878
59- function basicAuth ( name , pass ) {
60- const payload = pass ? `${ name } :${ pass } ` : name
79+ /**
80+ * Basic authorization header value
81+ * @prop {{user:string, pass:string} } auth database user credentials
82+ * @returns {string } header value
83+ */
84+ function basicAuth ( auth ) {
85+ const payload = auth . pass ? `${ auth . user } :${ auth . pass } ` : auth . user
6186 return 'Basic ' + Buffer . from ( payload ) . toString ( 'base64' )
6287}
6388
6489/**
6590 * Connect to database via XML-RPC
66- * @param {NodeExistConnectionOptions } options
91+ * @param {NodeExistConnectionOptions } [options] connection options
6792 * @returns {XMLRPCClient } XMLRPC-client
6893 */
6994function connect ( options ) {
70- const _options = assign ( { } , defaultRPCoptions , options )
71- delete _options . secure // prevent pollution of XML-RPC options
72-
73- let client
74- if ( useSecureConnection ( options ) ) {
75- // allow invalid and self-signed certificates on localhost, if not explicitly
76- // enforced by setting options.rejectUnauthorized to true
77- _options . rejectUnauthorized = ( 'rejectUnauthorized' in _options )
78- ? _options . rejectUnauthorized
79- : ! isLocalDB ( _options . host )
80-
81- client = xmlrpc . createSecureClient ( _options )
82- } else {
83- if ( ! isLocalDB ( _options . host ) ) {
84- console . warn ( 'Connecting to DB using an unencrypted channel.' )
85- }
86- client = xmlrpc . createClient ( _options )
87- }
95+ const mergedOptions = mergeOptions ( defaultXmlrpcEndpoint , options )
96+ const client = getXMLRPCClient ( mergedOptions )
8897 client . promisedMethodCall = promisedMethodCall ( client )
8998 return client
9099}
91100
92- async function restConnection ( options ) {
93- const { got } = await import ( 'got' )
94- const _options = assign ( { } , defaultRestOptions , options )
95- const authorization = basicAuth ( _options . basic_auth . user , _options . basic_auth . pass )
101+ /**
102+ *
103+ * @param {MergedOptions } options
104+ * @returns {XMLRPCClient } XMLRPC-client
105+ */
106+ function getXMLRPCClient ( options ) {
107+ if ( useSecureConnection ( options . protocol ) ) {
108+ return xmlrpc . createSecureClient ( options )
109+ }
110+ return xmlrpc . createClient ( options )
111+ }
112+
113+ /**
114+ * Merge options with defaults
115+ *
116+ * Allow invalid and self-signed certificates on localhost,
117+ * if not explicitly set to be enforced.
118+ * @param {string } path default endpoint
119+ * @param {NodeExistConnectionOptions } [options] given options
120+ * @returns {MergedOptions } merged options
121+ */
122+ function mergeOptions ( path , options ) {
123+ const mergedOptions = assign ( { path } , defaultConnectionOptions , options )
96124
97- const rejectUnauthorized = ( 'rejectUnauthorized' in _options )
98- ? _options . rejectUnauthorized
99- : ! isLocalDB ( _options . host )
125+ // compatibility for older setups
126+ if ( 'secure' in mergedOptions ) {
127+ mergedOptions . protocol = mergedOptions . secure ? 'https:' : 'http:'
128+ delete mergedOptions . secure // remove legacy option
129+ }
100130
101- if ( ! isLocalDB ( _options . host ) && _options . protocol === 'http' ) {
102- console . warn ( 'Connecting to remote DB using an unencrypted channel.' )
131+ const isLocalDb = checkIfLocalHost ( mergedOptions . host )
132+ const isSecureClient = useSecureConnection ( mergedOptions . protocol )
133+ if ( isLocalDb && isSecureClient && ! ( 'rejectUnauthorized' in mergedOptions ) ) {
134+ mergedOptions . rejectUnauthorized = false
103135 }
104136
105- const port = _options . port ? ':' + _options . port : ''
106- const path = _options . path . startsWith ( '/' ) ? _options . path : '/' + _options . path
107- const prefixUrl = `${ _options . protocol } //${ _options . host } ${ port } ${ path } `
108-
109- const client = got . extend (
110- {
111- prefixUrl,
112- headers : {
113- 'user-agent' : 'node-exist' ,
114- authorization
115- } ,
116- https : { rejectUnauthorized }
137+ if ( ! isLocalDb ) {
138+ if ( ! isSecureClient ) {
139+ console . warn ( 'Connecting to remote DB using an unencrypted channel.' )
140+ }
141+ if ( ! mergedOptions . rejectUnauthorized ) {
142+ console . warn ( 'Connecting to remote DB allowing invalid certificate.' )
117143 }
144+ }
145+ return mergedOptions
146+ }
147+
148+ /**
149+ * Is the host considered a local host
150+ * @param {string } host hostname
151+ * @returns {boolean } true, if host is local
152+ */
153+ function checkIfLocalHost ( host ) {
154+ return (
155+ host === 'localhost' ||
156+ host === '127.0.0.1' || // TODO: 127.0.1.1 is also local
157+ host === '[::1]' // TODO: match all ipv6 addresses considered local
118158 )
159+ }
119160
120- return client
161+ /**
162+ * SSL or not?
163+ * @param {string } protocol must end in colon
164+ * @returns {boolean } true, if encrypted connection
165+ */
166+ function useSecureConnection ( protocol ) {
167+ return protocol === 'https:'
121168}
122169
123170/**
@@ -144,10 +191,9 @@ function readOptionsFromEnv () {
144191 throw new Error ( 'Unknown protocol: "' + protocol + '"!' )
145192 }
146193
147- environmentOptions . secure = protocol === 'https:'
194+ environmentOptions . protocol = protocol
148195 environmentOptions . host = hostname
149196 environmentOptions . port = port
150- environmentOptions . protocol = protocol
151197 }
152198
153199 return environmentOptions
@@ -157,6 +203,7 @@ module.exports = {
157203 connect,
158204 readOptionsFromEnv,
159205 restConnection,
160- defaultRPCoptions,
161- defaultRestOptions
206+ defaultConnectionOptions,
207+ defaultXmlrpcEndpoint,
208+ defaultRestEndpoint
162209}
0 commit comments