Skip to content

Commit b806db6

Browse files
authored
Merge pull request #228 from lutovich/1.2-error-codes
Cleanup error codes
2 parents 2ad1f52 + 1ab28f2 commit b806db6

12 files changed

+351
-99
lines changed

src/v1/driver.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class Driver {
6868
*/
6969
_createConnection(url, release) {
7070
let sessionId = this._sessionIdGenerator++;
71-
let conn = connect(url, this._config);
71+
let conn = connect(url, this._config, this._connectionErrorCode());
7272
let streamObserver = new _ConnectionStreamObserver(this, conn);
7373
conn.initialize(this._userAgent, this._token, streamObserver);
7474
conn._id = sessionId;
@@ -126,16 +126,22 @@ class Driver {
126126
return mode;
127127
}
128128

129-
//Extension point
129+
// Extension point
130130
_createConnectionProvider(address, connectionPool, driverOnErrorCallback) {
131131
return new DirectConnectionProvider(address, connectionPool, driverOnErrorCallback);
132132
}
133133

134-
//Extension point
134+
// Extension point
135135
_createSession(mode, connectionProvider, bookmark, config) {
136136
return new Session(mode, connectionProvider, bookmark, config);
137137
}
138138

139+
// Extension point
140+
_connectionErrorCode() {
141+
// connection errors might result in different error codes depending on the driver
142+
return SERVICE_UNAVAILABLE;
143+
}
144+
139145
_driverOnErrorCallback(error) {
140146
const userDefinedOnErrorCallback = this.onError;
141147
if (userDefinedOnErrorCallback && error.code === SERVICE_UNAVAILABLE) {

src/v1/internal/ch-config.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Copyright (c) 2002-2017 "Neo Technology,","
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
import hasFeature from './features';
21+
import {SERVICE_UNAVAILABLE} from '../error';
22+
23+
export default class ChannelConfig {
24+
25+
constructor(host, port, driverConfig, connectionErrorCode) {
26+
this.host = host;
27+
this.port = port;
28+
this.encrypted = ChannelConfig._extractEncrypted(driverConfig);
29+
this.trust = ChannelConfig._extractTrust(driverConfig);
30+
this.trustedCertificates = ChannelConfig._extractTrustedCertificates(driverConfig);
31+
this.knownHostsPath = ChannelConfig._extractKnownHostsPath(driverConfig);
32+
this.connectionErrorCode = connectionErrorCode || SERVICE_UNAVAILABLE;
33+
}
34+
35+
static _extractEncrypted(driverConfig) {
36+
// check if encryption was configured by the user, use explicit null check because we permit boolean value
37+
const encryptionConfigured = driverConfig.encrypted == null;
38+
// default to using encryption if trust-all-certificates is available
39+
return encryptionConfigured ? hasFeature('trust_all_certificates') : driverConfig.encrypted;
40+
}
41+
42+
static _extractTrust(driverConfig) {
43+
if (driverConfig.trust) {
44+
return driverConfig.trust;
45+
}
46+
// default to using TRUST_ALL_CERTIFICATES if it is available
47+
return hasFeature('trust_all_certificates') ? 'TRUST_ALL_CERTIFICATES' : 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES';
48+
}
49+
50+
static _extractTrustedCertificates(driverConfig) {
51+
return driverConfig.trustedCertificates || [];
52+
}
53+
54+
static _extractKnownHostsPath(driverConfig) {
55+
return driverConfig.knownHosts || null;
56+
}
57+
};

src/v1/internal/ch-dummy.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,32 @@ const observer = {
2626
};
2727

2828
class DummyChannel {
29-
constructor(opts) {
29+
30+
/**
31+
* @constructor
32+
* @param {ChannelConfig} config - configuration for the new channel.
33+
*/
34+
constructor(config) {
3035
this.written = [];
3136
}
37+
3238
isEncrypted() {
3339
return false;
3440
}
41+
3542
write( buf ) {
3643
this.written.push(buf);
3744
observer.updateInstance(this);
3845
}
46+
3947
toHex() {
4048
var out = "";
4149
for( var i=0; i<this.written.length; i++ ) {
4250
out += this.written[i].toHex();
4351
}
4452
return out;
4553
}
54+
4655
toBuffer () {
4756
return new CombinedBuffer( this.written );
4857
}

src/v1/internal/ch-node.js

+43-37
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
* See the License for the specific language governing permissions and
1717
* limitations under the License.
1818
*/
19-
import net from "net";
20-
import tls from "tls";
21-
import fs from "fs";
22-
import path from "path";
23-
import {EOL} from "os";
24-
import {NodeBuffer} from "./buf";
25-
import {ENCRYPTION_OFF, isEmptyObjectOrNull} from "./util";
26-
import {newError, SESSION_EXPIRED} from "./../error";
19+
import net from 'net';
20+
import tls from 'tls';
21+
import fs from 'fs';
22+
import path from 'path';
23+
import {EOL} from 'os';
24+
import {NodeBuffer} from './buf';
25+
import {ENCRYPTION_OFF, isEmptyObjectOrNull} from './util';
26+
import {newError} from './../error';
2727

2828
let _CONNECTION_IDGEN = 0;
2929

@@ -107,13 +107,13 @@ const TrustStrategy = {
107107
/**
108108
* @deprecated Since version 1.0. Will be deleted in a future version. {@link #TRUST_CUSTOM_CA_SIGNED_CERTIFICATES}.
109109
*/
110-
TRUST_SIGNED_CERTIFICATES: function( opts, onSuccess, onFailure ) {
110+
TRUST_SIGNED_CERTIFICATES: function( config, onSuccess, onFailure ) {
111111
console.log("`TRUST_SIGNED_CERTIFICATES` has been deprecated as option and will be removed in a future version of " +
112112
"the driver. Please use `TRUST_CUSTOM_CA_SIGNED_CERTIFICATES` instead.");
113-
return TrustStrategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES(opts, onSuccess, onFailure);
113+
return TrustStrategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES(config, onSuccess, onFailure);
114114
},
115-
TRUST_CUSTOM_CA_SIGNED_CERTIFICATES : function( opts, onSuccess, onFailure ) {
116-
if( !opts.trustedCertificates || opts.trustedCertificates.length == 0 ) {
115+
TRUST_CUSTOM_CA_SIGNED_CERTIFICATES : function( config, onSuccess, onFailure ) {
116+
if( !config.trustedCertificates || config.trustedCertificates.length === 0 ) {
117117
onFailure(newError("You are using TRUST_CUSTOM_CA_SIGNED_CERTIFICATES as the method " +
118118
"to verify trust for encrypted connections, but have not configured any " +
119119
"trustedCertificates. You must specify the path to at least one trusted " +
@@ -124,13 +124,13 @@ const TrustStrategy = {
124124
}
125125

126126
let tlsOpts = {
127-
ca: opts.trustedCertificates.map((f) => fs.readFileSync(f)),
127+
ca: config.trustedCertificates.map((f) => fs.readFileSync(f)),
128128
// Because we manually check for this in the connect callback, to give
129129
// a more helpful error to the user
130130
rejectUnauthorized: false
131131
};
132132

133-
let socket = tls.connect(opts.port, opts.host, tlsOpts, function () {
133+
let socket = tls.connect(config.port, config.host, tlsOpts, function () {
134134
if (!socket.authorized) {
135135
onFailure(newError("Server certificate is not trusted. If you trust the database you are connecting to, add" +
136136
" the signing certificate, or the server certificate, to the list of certificates trusted by this driver" +
@@ -145,14 +145,14 @@ const TrustStrategy = {
145145
socket.on('error', onFailure);
146146
return socket;
147147
},
148-
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES : function( opts, onSuccess, onFailure ) {
148+
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES : function( config, onSuccess, onFailure ) {
149149

150150
let tlsOpts = {
151151
// Because we manually check for this in the connect callback, to give
152152
// a more helpful error to the user
153153
rejectUnauthorized: false
154154
};
155-
let socket = tls.connect(opts.port, opts.host, tlsOpts, function () {
155+
let socket = tls.connect(config.port, config.host, tlsOpts, function () {
156156
if (!socket.authorized) {
157157
onFailure(newError("Server certificate is not trusted. If you trust the database you are connecting to, use " +
158158
"TRUST_CUSTOM_CA_SIGNED_CERTIFICATES and add" +
@@ -171,7 +171,7 @@ const TrustStrategy = {
171171
/**
172172
* @deprecated in 1.1 in favour of {@link #TRUST_ALL_CERTIFICATES}. Will be deleted in a future version.
173173
*/
174-
TRUST_ON_FIRST_USE : function( opts, onSuccess, onFailure ) {
174+
TRUST_ON_FIRST_USE : function( config, onSuccess, onFailure ) {
175175
console.log("`TRUST_ON_FIRST_USE` has been deprecated as option and will be removed in a future version of " +
176176
"the driver. Please use `TRUST_ALL_CERTIFICATES` instead.");
177177

@@ -180,7 +180,7 @@ const TrustStrategy = {
180180
rejectUnauthorized: false
181181
};
182182

183-
let socket = tls.connect(opts.port, opts.host, tlsOpts, function () {
183+
let socket = tls.connect(config.port, config.host, tlsOpts, function () {
184184
var serverCert = socket.getPeerCertificate(/*raw=*/true);
185185

186186
if( !serverCert.raw ) {
@@ -195,9 +195,9 @@ const TrustStrategy = {
195195
return;
196196
}
197197

198-
var serverFingerprint = require('crypto').createHash('sha512').update(serverCert.raw).digest("hex");
199-
let knownHostsPath = opts.knownHosts || path.join(userHome(), ".neo4j", "known_hosts");
200-
let serverId = opts.host + ":" + opts.port;
198+
const serverFingerprint = require('crypto').createHash('sha512').update(serverCert.raw).digest("hex");
199+
const knownHostsPath = config.knownHostsPath || path.join(userHome(), ".neo4j", "known_hosts");
200+
const serverId = config.host + ":" + config.port;
201201

202202
loadFingerprint(serverId, knownHostsPath, (knownFingerprint) => {
203203
if( knownFingerprint === serverFingerprint ) {
@@ -228,11 +228,11 @@ const TrustStrategy = {
228228
return socket;
229229
},
230230

231-
TRUST_ALL_CERTIFICATES: function (opts, onSuccess, onFailure) {
231+
TRUST_ALL_CERTIFICATES: function (config, onSuccess, onFailure) {
232232
const tlsOpts = {
233233
rejectUnauthorized: false
234234
};
235-
const socket = tls.connect(opts.port, opts.host, tlsOpts, function () {
235+
const socket = tls.connect(config.port, config.host, tlsOpts, function () {
236236
const certificate = socket.getPeerCertificate();
237237
if (isEmptyObjectOrNull(certificate)) {
238238
onFailure(newError("Secure connection was successful but server did not return any valid " +
@@ -249,16 +249,23 @@ const TrustStrategy = {
249249
}
250250
};
251251

252-
function connect( opts, onSuccess, onFailure=(()=>null) ) {
252+
/**
253+
* Connect using node socket.
254+
* @param {ChannelConfig} config - configuration of this channel.
255+
* @param {function} onSuccess - callback to execute on connection success.
256+
* @param {function} onFailure - callback to execute on connection failure.
257+
* @return {*} socket connection.
258+
*/
259+
function connect( config, onSuccess, onFailure=(()=>null) ) {
253260
//still allow boolean for backwards compatibility
254-
if (opts.encrypted === false || opts.encrypted === ENCRYPTION_OFF) {
255-
var conn = net.connect(opts.port, opts.host, onSuccess);
261+
if (config.encrypted === false || config.encrypted === ENCRYPTION_OFF) {
262+
var conn = net.connect(config.port, config.host, onSuccess);
256263
conn.on('error', onFailure);
257264
return conn;
258-
} else if( TrustStrategy[opts.trust]) {
259-
return TrustStrategy[opts.trust](opts, onSuccess, onFailure);
265+
} else if( TrustStrategy[config.trust]) {
266+
return TrustStrategy[config.trust](config, onSuccess, onFailure);
260267
} else {
261-
onFailure(newError("Unknown trust strategy: " + opts.trust + ". Please use either " +
268+
onFailure(newError("Unknown trust strategy: " + config.trust + ". Please use either " +
262269
"trust:'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES' or trust:'TRUST_ALL_CERTIFICATES' in your driver " +
263270
"configuration. Alternatively, you can disable encryption by setting " +
264271
"`encrypted:\"" + ENCRYPTION_OFF + "\"`. There is no mechanism to use encryption without trust verification, " +
@@ -277,11 +284,9 @@ class NodeChannel {
277284

278285
/**
279286
* Create new instance
280-
* @param {Object} opts - Options object
281-
* @param {string} opts.host - The host, including protocol to connect to.
282-
* @param {Integer} opts.port - The port to use.
287+
* @param {ChannelConfig} config - configuration for this channel.
283288
*/
284-
constructor (opts) {
289+
constructor (config) {
285290
let self = this;
286291

287292
this.id = _CONNECTION_IDGEN++;
@@ -291,9 +296,10 @@ class NodeChannel {
291296
this._error = null;
292297
this._handleConnectionError = this._handleConnectionError.bind(this);
293298
this._handleConnectionTerminated = this._handleConnectionTerminated.bind(this);
299+
this._connectionErrorCode = config.connectionErrorCode;
294300

295-
this._encrypted = opts.encrypted;
296-
this._conn = connect(opts, () => {
301+
this._encrypted = config.encrypted;
302+
this._conn = connect(config, () => {
297303
if(!self._open) {
298304
return;
299305
}
@@ -318,14 +324,14 @@ class NodeChannel {
318324

319325
_handleConnectionError( err ) {
320326
let msg = err.message || 'Failed to connect to server';
321-
this._error = newError(msg, SESSION_EXPIRED);
327+
this._error = newError(msg, this._connectionErrorCode);
322328
if( this.onerror ) {
323329
this.onerror(this._error);
324330
}
325331
}
326332

327333
_handleConnectionTerminated() {
328-
this._error = newError('Connection was closed by server', SESSION_EXPIRED);
334+
this._error = newError('Connection was closed by server', this._connectionErrorCode);
329335
if( this.onerror ) {
330336
this.onerror(this._error);
331337
}

src/v1/internal/ch-websocket.js

+10-11
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import {HeapBuffer} from './buf';
2121
import {newError} from './../error';
22-
import {ENCRYPTION_ON, ENCRYPTION_OFF} from './util';
22+
import {ENCRYPTION_OFF, ENCRYPTION_ON} from './util';
2323

2424
/**
2525
* Create a new WebSocketChannel to be used in web browsers.
@@ -29,33 +29,32 @@ class WebSocketChannel {
2929

3030
/**
3131
* Create new instance
32-
* @param {Object} opts - Options object
33-
* @param {string} opts.host - The host, including protocol to connect to.
34-
* @param {Integer} opts.port - The port to use.
32+
* @param {ChannelConfig} config - configuration for this channel.
3533
*/
36-
constructor (opts) {
34+
constructor(config) {
3735

3836
this._open = true;
3937
this._pending = [];
4038
this._error = null;
4139
this._handleConnectionError = this._handleConnectionError.bind(this);
40+
this._connectionErrorCode = config.connectionErrorCode;
4241

43-
this._encrypted = opts.encrypted;
42+
this._encrypted = config.encrypted;
4443

4544
let scheme = "ws";
4645
//Allow boolean for backwards compatibility
47-
if( opts.encrypted === true || opts.encrypted === ENCRYPTION_ON) {
48-
if((!opts.trust) || opts.trust === "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES" ) {
46+
if (config.encrypted === true || config.encrypted === ENCRYPTION_ON) {
47+
if ((!config.trust) || config.trust === 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES') {
4948
scheme = "wss";
5049
} else {
5150
this._error = newError("The browser version of this driver only supports one trust " +
52-
"strategy, 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES'. "+opts.trust+" is not supported. Please " +
51+
'strategy, \'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES\'. ' + config.trust + ' is not supported. Please ' +
5352
"either use TRUST_CUSTOM_CA_SIGNED_CERTIFICATES or disable encryption by setting " +
5453
"`encrypted:\"" + ENCRYPTION_OFF + "\"` in the driver configuration.");
5554
return;
5655
}
5756
}
58-
this._url = scheme + "://" + opts.host + ":" + opts.port;
57+
this._url = scheme + '://' + config.host + ':' + config.port;
5958
this._ws = new WebSocket(this._url);
6059
this._ws.binaryType = "arraybuffer";
6160

@@ -95,7 +94,7 @@ class WebSocketChannel {
9594
"the root cause of the failure. Common reasons include the database being " +
9695
"unavailable, using the wrong connection URL or temporary network problems. " +
9796
"If you have enabled encryption, ensure your browser is configured to trust the " +
98-
"certificate Neo4j is configured to use. WebSocket `readyState` is: " + this._ws.readyState );
97+
"certificate Neo4j is configured to use. WebSocket `readyState` is: " + this._ws.readyState, this._connectionErrorCode );
9998
if (this.onerror) {
10099
this.onerror(this._error);
101100
}

src/v1/internal/connection-providers.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* limitations under the License.
1818
*/
1919

20-
import {newError, SERVICE_UNAVAILABLE, SESSION_EXPIRED} from '../error';
20+
import {newError, SERVICE_UNAVAILABLE} from '../error';
2121
import {READ, WRITE} from '../driver';
2222
import Session from '../session';
2323
import RoundRobinArray from './round-robin-array';
@@ -96,7 +96,7 @@ export class LoadBalancer extends ConnectionProvider {
9696
_acquireConnectionToServer(serversRoundRobinArray, serverName) {
9797
const address = serversRoundRobinArray.next();
9898
if (!address) {
99-
return Promise.reject(newError('No ' + serverName + ' servers available', SESSION_EXPIRED));
99+
return Promise.reject(newError('No ' + serverName + ' servers available', SERVICE_UNAVAILABLE));
100100
}
101101
return this._connectionPool.acquire(address);
102102
}

0 commit comments

Comments
 (0)