Skip to content

Cleanup error codes #228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/v1/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Driver {
*/
_createConnection(url, release) {
let sessionId = this._sessionIdGenerator++;
let conn = connect(url, this._config);
let conn = connect(url, this._config, this._connectionErrorCode());
let streamObserver = new _ConnectionStreamObserver(this, conn);
conn.initialize(this._userAgent, this._token, streamObserver);
conn._id = sessionId;
Expand Down Expand Up @@ -126,16 +126,22 @@ class Driver {
return mode;
}

//Extension point
// Extension point
_createConnectionProvider(address, connectionPool, driverOnErrorCallback) {
return new DirectConnectionProvider(address, connectionPool, driverOnErrorCallback);
}

//Extension point
// Extension point
_createSession(mode, connectionProvider, bookmark, config) {
return new Session(mode, connectionProvider, bookmark, config);
}

// Extension point
_connectionErrorCode() {
// connection errors might result in different error codes depending on the driver
return SERVICE_UNAVAILABLE;
}

_driverOnErrorCallback(error) {
const userDefinedOnErrorCallback = this.onError;
if (userDefinedOnErrorCallback && error.code === SERVICE_UNAVAILABLE) {
Expand Down
57 changes: 57 additions & 0 deletions src/v1/internal/ch-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2002-2017 "Neo Technology,","
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import hasFeature from './features';
import {SERVICE_UNAVAILABLE} from '../error';

export default class ChannelConfig {

constructor(host, port, driverConfig, connectionErrorCode) {
this.host = host;
this.port = port;
this.encrypted = ChannelConfig._extractEncrypted(driverConfig);
this.trust = ChannelConfig._extractTrust(driverConfig);
this.trustedCertificates = ChannelConfig._extractTrustedCertificates(driverConfig);
this.knownHostsPath = ChannelConfig._extractKnownHostsPath(driverConfig);
this.connectionErrorCode = connectionErrorCode || SERVICE_UNAVAILABLE;
}

static _extractEncrypted(driverConfig) {
// check if encryption was configured by the user, use explicit null check because we permit boolean value
const encryptionConfigured = driverConfig.encrypted == null;
// default to using encryption if trust-all-certificates is available
return encryptionConfigured ? hasFeature('trust_all_certificates') : driverConfig.encrypted;
}

static _extractTrust(driverConfig) {
if (driverConfig.trust) {
return driverConfig.trust;
}
// default to using TRUST_ALL_CERTIFICATES if it is available
return hasFeature('trust_all_certificates') ? 'TRUST_ALL_CERTIFICATES' : 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES';
}

static _extractTrustedCertificates(driverConfig) {
return driverConfig.trustedCertificates || [];
}

static _extractKnownHostsPath(driverConfig) {
return driverConfig.knownHosts || null;
}
};
11 changes: 10 additions & 1 deletion src/v1/internal/ch-dummy.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,32 @@ const observer = {
};

class DummyChannel {
constructor(opts) {

/**
* @constructor
* @param {ChannelConfig} config - configuration for the new channel.
*/
constructor(config) {
this.written = [];
}

isEncrypted() {
return false;
}

write( buf ) {
this.written.push(buf);
observer.updateInstance(this);
}

toHex() {
var out = "";
for( var i=0; i<this.written.length; i++ ) {
out += this.written[i].toHex();
}
return out;
}

toBuffer () {
return new CombinedBuffer( this.written );
}
Expand Down
80 changes: 43 additions & 37 deletions src/v1/internal/ch-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import net from "net";
import tls from "tls";
import fs from "fs";
import path from "path";
import {EOL} from "os";
import {NodeBuffer} from "./buf";
import {ENCRYPTION_OFF, isEmptyObjectOrNull} from "./util";
import {newError, SESSION_EXPIRED} from "./../error";
import net from 'net';
import tls from 'tls';
import fs from 'fs';
import path from 'path';
import {EOL} from 'os';
import {NodeBuffer} from './buf';
import {ENCRYPTION_OFF, isEmptyObjectOrNull} from './util';
import {newError} from './../error';

let _CONNECTION_IDGEN = 0;

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

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

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

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

Expand All @@ -180,7 +180,7 @@ const TrustStrategy = {
rejectUnauthorized: false
};

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

if( !serverCert.raw ) {
Expand All @@ -195,9 +195,9 @@ const TrustStrategy = {
return;
}

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

loadFingerprint(serverId, knownHostsPath, (knownFingerprint) => {
if( knownFingerprint === serverFingerprint ) {
Expand Down Expand Up @@ -228,11 +228,11 @@ const TrustStrategy = {
return socket;
},

TRUST_ALL_CERTIFICATES: function (opts, onSuccess, onFailure) {
TRUST_ALL_CERTIFICATES: function (config, onSuccess, onFailure) {
const tlsOpts = {
rejectUnauthorized: false
};
const socket = tls.connect(opts.port, opts.host, tlsOpts, function () {
const socket = tls.connect(config.port, config.host, tlsOpts, function () {
const certificate = socket.getPeerCertificate();
if (isEmptyObjectOrNull(certificate)) {
onFailure(newError("Secure connection was successful but server did not return any valid " +
Expand All @@ -249,16 +249,23 @@ const TrustStrategy = {
}
};

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

/**
* Create new instance
* @param {Object} opts - Options object
* @param {string} opts.host - The host, including protocol to connect to.
* @param {Integer} opts.port - The port to use.
* @param {ChannelConfig} config - configuration for this channel.
*/
constructor (opts) {
constructor (config) {
let self = this;

this.id = _CONNECTION_IDGEN++;
Expand All @@ -291,9 +296,10 @@ class NodeChannel {
this._error = null;
this._handleConnectionError = this._handleConnectionError.bind(this);
this._handleConnectionTerminated = this._handleConnectionTerminated.bind(this);
this._connectionErrorCode = config.connectionErrorCode;

this._encrypted = opts.encrypted;
this._conn = connect(opts, () => {
this._encrypted = config.encrypted;
this._conn = connect(config, () => {
if(!self._open) {
return;
}
Expand All @@ -318,14 +324,14 @@ class NodeChannel {

_handleConnectionError( err ) {
let msg = err.message || 'Failed to connect to server';
this._error = newError(msg, SESSION_EXPIRED);
this._error = newError(msg, this._connectionErrorCode);
if( this.onerror ) {
this.onerror(this._error);
}
}

_handleConnectionTerminated() {
this._error = newError('Connection was closed by server', SESSION_EXPIRED);
this._error = newError('Connection was closed by server', this._connectionErrorCode);
if( this.onerror ) {
this.onerror(this._error);
}
Expand Down
21 changes: 10 additions & 11 deletions src/v1/internal/ch-websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import {HeapBuffer} from './buf';
import {newError} from './../error';
import {ENCRYPTION_ON, ENCRYPTION_OFF} from './util';
import {ENCRYPTION_OFF, ENCRYPTION_ON} from './util';

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

/**
* Create new instance
* @param {Object} opts - Options object
* @param {string} opts.host - The host, including protocol to connect to.
* @param {Integer} opts.port - The port to use.
* @param {ChannelConfig} config - configuration for this channel.
*/
constructor (opts) {
constructor(config) {

this._open = true;
this._pending = [];
this._error = null;
this._handleConnectionError = this._handleConnectionError.bind(this);
this._connectionErrorCode = config.connectionErrorCode;

this._encrypted = opts.encrypted;
this._encrypted = config.encrypted;

let scheme = "ws";
//Allow boolean for backwards compatibility
if( opts.encrypted === true || opts.encrypted === ENCRYPTION_ON) {
if((!opts.trust) || opts.trust === "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES" ) {
if (config.encrypted === true || config.encrypted === ENCRYPTION_ON) {
if ((!config.trust) || config.trust === 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES') {
scheme = "wss";
} else {
this._error = newError("The browser version of this driver only supports one trust " +
"strategy, 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES'. "+opts.trust+" is not supported. Please " +
'strategy, \'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES\'. ' + config.trust + ' is not supported. Please ' +
"either use TRUST_CUSTOM_CA_SIGNED_CERTIFICATES or disable encryption by setting " +
"`encrypted:\"" + ENCRYPTION_OFF + "\"` in the driver configuration.");
return;
}
}
this._url = scheme + "://" + opts.host + ":" + opts.port;
this._url = scheme + '://' + config.host + ':' + config.port;
this._ws = new WebSocket(this._url);
this._ws.binaryType = "arraybuffer";

Expand Down Expand Up @@ -95,7 +94,7 @@ class WebSocketChannel {
"the root cause of the failure. Common reasons include the database being " +
"unavailable, using the wrong connection URL or temporary network problems. " +
"If you have enabled encryption, ensure your browser is configured to trust the " +
"certificate Neo4j is configured to use. WebSocket `readyState` is: " + this._ws.readyState );
"certificate Neo4j is configured to use. WebSocket `readyState` is: " + this._ws.readyState, this._connectionErrorCode );
if (this.onerror) {
this.onerror(this._error);
}
Expand Down
4 changes: 2 additions & 2 deletions src/v1/internal/connection-providers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* limitations under the License.
*/

import {newError, SERVICE_UNAVAILABLE, SESSION_EXPIRED} from '../error';
import {newError, SERVICE_UNAVAILABLE} from '../error';
import {READ, WRITE} from '../driver';
import Session from '../session';
import RoundRobinArray from './round-robin-array';
Expand Down Expand Up @@ -96,7 +96,7 @@ export class LoadBalancer extends ConnectionProvider {
_acquireConnectionToServer(serversRoundRobinArray, serverName) {
const address = serversRoundRobinArray.next();
if (!address) {
return Promise.reject(newError('No ' + serverName + ' servers available', SESSION_EXPIRED));
return Promise.reject(newError('No ' + serverName + ' servers available', SERVICE_UNAVAILABLE));
}
return this._connectionPool.acquire(address);
}
Expand Down
Loading