Skip to content

Commit 038b9bc

Browse files
committed
Use dedicated ServerAddress class for holding address information
1 parent 84b42d7 commit 038b9bc

28 files changed

+894
-765
lines changed

src/v1/driver.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@ class Driver {
6262
/**
6363
* You should not be calling this directly, instead use {@link driver}.
6464
* @constructor
65-
* @param {string} hostPort
65+
* @param {ServerAddress} address
6666
* @param {string} userAgent
6767
* @param {object} authToken
6868
* @param {object} config
6969
* @protected
7070
*/
71-
constructor(hostPort, userAgent, authToken = {}, config = {}) {
71+
constructor(address, userAgent, authToken = {}, config = {}) {
7272
sanitizeConfig(config);
7373

7474
this._id = idGenerator++;
75-
this._hostPort = hostPort;
75+
this._address = address;
7676
this._userAgent = userAgent;
7777
this._openConnections = {};
7878
this._authToken = authToken;
@@ -102,7 +102,7 @@ class Driver {
102102
* @protected
103103
*/
104104
_afterConstruction() {
105-
this._log.info(`Direct driver ${this._id} created for server address ${this._hostPort}`);
105+
this._log.info(`Direct driver ${this._id} created for server address ${this._address}`);
106106
}
107107

108108
/**
@@ -133,9 +133,9 @@ class Driver {
133133
* @return {Promise<Connection>} promise resolved with a new connection or rejected when failed to connect.
134134
* @access private
135135
*/
136-
_createConnection(hostPort, release) {
137-
const connection = Connection.create(hostPort, this._config, this._createConnectionErrorHandler(), this._log);
138-
connection._release = () => release(hostPort, connection);
136+
_createConnection(address, release) {
137+
const connection = Connection.create(address, this._config, this._createConnectionErrorHandler(), this._log);
138+
connection._release = () => release(address, connection);
139139
this._openConnections[connection.id] = connection;
140140

141141
return connection.connect(this._userAgent, this._authToken)
@@ -206,8 +206,8 @@ class Driver {
206206
}
207207

208208
// Extension point
209-
_createConnectionProvider(hostPort, connectionPool, driverOnErrorCallback) {
210-
return new DirectConnectionProvider(hostPort, connectionPool, driverOnErrorCallback);
209+
_createConnectionProvider(address, connectionPool, driverOnErrorCallback) {
210+
return new DirectConnectionProvider(address, connectionPool, driverOnErrorCallback);
211211
}
212212

213213
// Extension point
@@ -218,7 +218,7 @@ class Driver {
218218
_getOrCreateConnectionProvider() {
219219
if (!this._connectionProvider) {
220220
const driverOnErrorCallback = this._driverOnErrorCallback.bind(this);
221-
this._connectionProvider = this._createConnectionProvider(this._hostPort, this._pool, driverOnErrorCallback);
221+
this._connectionProvider = this._createConnectionProvider(this._address, this._pool, driverOnErrorCallback);
222222
}
223223
return this._connectionProvider;
224224
}

src/v1/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import urlUtil from './internal/url-util';
3131
import HttpDriver from './internal/http/http-driver';
3232
import {isPoint, Point} from './spatial-types';
3333
import {Date, DateTime, Duration, isDate, isDateTime, isDuration, isLocalDateTime, isLocalTime, isTime, LocalDateTime, LocalTime, Time} from './temporal-types';
34+
import ServerAddress from './internal/server-address';
3435

3536
/**
3637
* @property {function(username: string, password: string, realm: ?string)} basic the function to create a
@@ -227,12 +228,12 @@ function driver(url, authToken, config = {}) {
227228
assertString(url, 'Bolt URL');
228229
const parsedUrl = urlUtil.parseDatabaseUrl(url);
229230
if (parsedUrl.scheme === 'bolt+routing') {
230-
return new RoutingDriver(parsedUrl.hostAndPort, parsedUrl.query, USER_AGENT, authToken, config);
231+
return new RoutingDriver(ServerAddress.fromUrl(parsedUrl.hostAndPort), parsedUrl.query, USER_AGENT, authToken, config);
231232
} else if (parsedUrl.scheme === 'bolt') {
232233
if (!isEmptyObjectOrNull(parsedUrl.query)) {
233234
throw new Error(`Parameters are not supported with scheme 'bolt'. Given URL: '${url}'`);
234235
}
235-
return new Driver(parsedUrl.hostAndPort, USER_AGENT, authToken, config);
236+
return new Driver(ServerAddress.fromUrl(parsedUrl.hostAndPort), USER_AGENT, authToken, config);
236237
} else if (parsedUrl.scheme === 'http' || parsedUrl.scheme === 'https') {
237238
return new HttpDriver(parsedUrl, USER_AGENT, authToken, config);
238239
} else {

src/v1/internal/browser/browser-channel.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default class WebSocketChannel {
4646
return;
4747
}
4848

49-
this._ws = createWebSocket(scheme, config.url);
49+
this._ws = createWebSocket(scheme, config.address);
5050
this._ws.binaryType = "arraybuffer";
5151

5252
let self = this;
@@ -169,13 +169,13 @@ export default class WebSocketChannel {
169169
}
170170
}
171171

172-
function createWebSocket(scheme, parsedUrl) {
173-
const url = scheme + '://' + parsedUrl.hostAndPort;
172+
function createWebSocket(scheme, address) {
173+
const url = scheme + '://' + address.asHostPort();
174174

175175
try {
176176
return new WebSocket(url);
177177
} catch (error) {
178-
if (isIPv6AddressIssueOnWindows(error, parsedUrl)) {
178+
if (isIPv6AddressIssueOnWindows(error, address)) {
179179

180180
// WebSocket in IE and Edge browsers on Windows do not support regular IPv6 address syntax because they contain ':'.
181181
// It's an invalid character for UNC (https://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_UNC_path_names)
@@ -190,34 +190,33 @@ function createWebSocket(scheme, parsedUrl) {
190190
// Creation of WebSocket with unconverted address results in SyntaxError without message or stacktrace.
191191
// That is why here we "catch" SyntaxError and rewrite IPv6 address if needed.
192192

193-
const windowsFriendlyUrl = asWindowsFriendlyIPv6Address(scheme, parsedUrl);
193+
const windowsFriendlyUrl = asWindowsFriendlyIPv6Address(scheme, address);
194194
return new WebSocket(windowsFriendlyUrl);
195195
} else {
196196
throw error;
197197
}
198198
}
199199
}
200200

201-
function isIPv6AddressIssueOnWindows(error, parsedUrl) {
202-
return error.name === 'SyntaxError' && isIPv6Address(parsedUrl);
201+
function isIPv6AddressIssueOnWindows(error, address) {
202+
return error.name === 'SyntaxError' && isIPv6Address(address.asHostPort());
203203
}
204204

205-
function isIPv6Address(parsedUrl) {
206-
const hostAndPort = parsedUrl.hostAndPort;
205+
function isIPv6Address(hostAndPort) {
207206
return hostAndPort.charAt(0) === '[' && hostAndPort.indexOf(']') !== -1;
208207
}
209208

210-
function asWindowsFriendlyIPv6Address(scheme, parsedUrl) {
209+
function asWindowsFriendlyIPv6Address(scheme, address) {
211210
// replace all ':' with '-'
212-
const hostWithoutColons = parsedUrl.host.replace(new RegExp(':', 'g'), '-');
211+
const hostWithoutColons = address.host().replace(new RegExp(':', 'g'), '-');
213212

214213
// replace '%' with 's' for link-local IPv6 address like 'fe80::1%lo0'
215214
const hostWithoutPercent = hostWithoutColons.replace('%', 's');
216215

217216
// append magic '.ipv6-literal.net' suffix
218217
const ipv6Host = hostWithoutPercent + '.ipv6-literal.net';
219218

220-
return `${scheme}://${ipv6Host}:${parsedUrl.port}`;
219+
return `${scheme}://${ipv6Host}:${address.port()}`;
221220
}
222221

223222
/**

src/v1/internal/channel-config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ export default class ChannelConfig {
3131

3232
/**
3333
* @constructor
34-
* @param {Url} url the URL for the channel to connect to.
34+
* @param {ServerAddress} address the address for the channel to connect to.
3535
* @param {object} driverConfig the driver config provided by the user when driver is created.
3636
* @param {string} connectionErrorCode the default error code to use on connection errors.
3737
*/
38-
constructor(url, driverConfig, connectionErrorCode) {
39-
this.url = url;
38+
constructor(address, driverConfig, connectionErrorCode) {
39+
this.address = address;
4040
this.encrypted = extractEncrypted(driverConfig);
4141
this.trust = extractTrust(driverConfig);
4242
this.trustedCertificates = extractTrustedCertificates(driverConfig);

src/v1/internal/connection-error-handler.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ export default class ConnectionErrorHandler {
3838
/**
3939
* Handle and transform the error.
4040
* @param {Neo4jError} error the original error.
41-
* @param {string} hostPort the host and port of the connection where the error happened.
41+
* @param {ServerAddress} address the address of the connection where the error happened.
4242
* @return {Neo4jError} new error that should be propagated to the user.
4343
*/
44-
handleAndTransformError(error, hostPort) {
44+
handleAndTransformError(error, address) {
4545
if (isAvailabilityError(error)) {
46-
return this._handleUnavailability(error, hostPort);
46+
return this._handleUnavailability(error, address);
4747
}
4848
if (isFailureToWrite(error)) {
49-
return this._handleWriteFailure(error, hostPort);
49+
return this._handleWriteFailure(error, address);
5050
}
5151
return error;
5252
}

src/v1/internal/connection-providers.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import Session from '../session';
2323
import RoutingTable from './routing-table';
2424
import Rediscovery from './rediscovery';
2525
import RoutingUtil from './routing-util';
26+
import { HostNameResolver } from './node';
27+
import { flatMap } from 'lodash/collection';
2628

2729
const UNAUTHORIZED_ERROR_CODE = 'Neo.ClientError.Security.Unauthorized';
2830

@@ -45,38 +47,40 @@ class ConnectionProvider {
4547

4648
export class DirectConnectionProvider extends ConnectionProvider {
4749

48-
constructor(hostPort, connectionPool, driverOnErrorCallback) {
50+
constructor(address, connectionPool, driverOnErrorCallback) {
4951
super();
50-
this._hostPort = hostPort;
52+
this._address = address;
5153
this._connectionPool = connectionPool;
5254
this._driverOnErrorCallback = driverOnErrorCallback;
5355
}
5456

5557
acquireConnection(mode) {
56-
const connectionPromise = this._connectionPool.acquire(this._hostPort);
58+
const connectionPromise = this._connectionPool.acquire(this._address);
5759
return this._withAdditionalOnErrorCallback(connectionPromise, this._driverOnErrorCallback);
5860
}
5961
}
6062

6163
export class LoadBalancer extends ConnectionProvider {
6264

63-
constructor(hostPort, routingContext, connectionPool, loadBalancingStrategy, hostNameResolver, driverOnErrorCallback, log) {
65+
constructor(address, routingContext, connectionPool, loadBalancingStrategy, hostNameResolver, driverOnErrorCallback, log) {
6466
super();
65-
this._seedRouter = hostPort;
67+
this._seedRouter = address;
6668
this._routingTable = new RoutingTable([this._seedRouter]);
6769
this._rediscovery = new Rediscovery(new RoutingUtil(routingContext));
6870
this._connectionPool = connectionPool;
6971
this._driverOnErrorCallback = driverOnErrorCallback;
7072
this._loadBalancingStrategy = loadBalancingStrategy;
7173
this._hostNameResolver = hostNameResolver;
74+
this._dnsResolver = new HostNameResolver();
7275
this._log = log;
7376
this._useSeedRouter = false;
7477
}
7578

7679
acquireConnection(accessMode) {
80+
let that = this;
7781
const connectionPromise = this._freshRoutingTable(accessMode).then(routingTable => {
7882
if (accessMode === READ) {
79-
const address = this._loadBalancingStrategy.selectReader(routingTable.readers);
83+
const address = that._loadBalancingStrategy.selectReader(routingTable.readers);
8084
return this._acquireConnectionToServer(address, 'read');
8185
} else if (accessMode === WRITE) {
8286
const address = this._loadBalancingStrategy.selectWriter(routingTable.writers);
@@ -173,14 +177,26 @@ export class LoadBalancer extends ConnectionProvider {
173177
}
174178

175179
_fetchRoutingTableUsingSeedRouter(seenRouters, seedRouter) {
176-
const resolvedAddresses = this._hostNameResolver.resolve(seedRouter);
180+
const resolvedAddresses = this._resolveSeedRouter(seedRouter);
177181
return resolvedAddresses.then(resolvedRouterAddresses => {
178182
// filter out all addresses that we've already tried
179183
const newAddresses = resolvedRouterAddresses.filter(address => seenRouters.indexOf(address) < 0);
180184
return this._fetchRoutingTable(newAddresses, null);
181185
});
182186
}
183187

188+
_resolveSeedRouter(seedRouter) {
189+
const customResolution = this._hostNameResolver.resolve(seedRouter);
190+
const dnsResolutions = customResolution.then(resolvedAddresses => {
191+
return Promise.all(resolvedAddresses.map(address => {
192+
return this._dnsResolver.resolve(address);
193+
}));
194+
});
195+
return dnsResolutions.then(results => {
196+
return [].concat.apply([], results);
197+
});
198+
}
199+
184200
_fetchRoutingTable(routerAddresses, routingTable) {
185201
return routerAddresses.reduce((refreshedTablePromise, currentRouter, currentIndex) => {
186202
return refreshedTablePromise.then(newRoutingTable => {

src/v1/internal/connection.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ export default class Connection {
5050
* @constructor
5151
* @param {Channel} channel - channel with a 'write' function and a 'onmessage' callback property.
5252
* @param {ConnectionErrorHandler} errorHandler the error handler.
53-
* @param {string} hostPort - the hostname and port to connect to.
53+
* @param {ServerAddress} address - the server address to connect to.
5454
* @param {Logger} log - the configured logger.
5555
* @param {boolean} disableLosslessIntegers if this connection should convert all received integers to native JS numbers.
5656
*/
57-
constructor(channel, errorHandler, hostPort, log, disableLosslessIntegers = false) {
57+
constructor(channel, errorHandler, address, log, disableLosslessIntegers = false) {
5858
this.id = idGenerator++;
59-
this.hostPort = hostPort;
60-
this.server = {address: hostPort};
59+
this.address = address;
60+
this.server = { address: address.asHostPort() };
6161
this.creationTimestamp = Date.now();
6262
this._errorHandler = errorHandler;
6363
this._disableLosslessIntegers = disableLosslessIntegers;
@@ -81,22 +81,21 @@ export default class Connection {
8181
this._isBroken = false;
8282

8383
if (this._log.isDebugEnabled()) {
84-
this._log.debug(`${this} created towards ${hostPort}`);
84+
this._log.debug(`${this} created towards ${address}`);
8585
}
8686
}
8787

8888
/**
8989
* Crete new connection to the provided address. Returned connection is not connected.
90-
* @param {string} url - the Bolt endpoint to connect to.
90+
* @param {ServerAddress} address - the Bolt endpoint to connect to.
9191
* @param {object} config - this driver configuration.
9292
* @param {ConnectionErrorHandler} errorHandler - the error handler for connection errors.
9393
* @param {Logger} log - configured logger.
9494
* @return {Connection} - new connection.
9595
*/
96-
static create(url, config, errorHandler, log) {
97-
const parsedAddress = urlUtil.parseDatabaseUrl(url);
98-
const channelConfig = new ChannelConfig(parsedAddress, config, errorHandler.errorCode());
99-
return new Connection(new Channel(channelConfig), errorHandler, parsedAddress.hostAndPort, log, config.disableLosslessIntegers);
96+
static create(address, config, errorHandler, log) {
97+
const channelConfig = new ChannelConfig(address, config, errorHandler.errorCode());
98+
return new Connection(new Channel(channelConfig), errorHandler, address, log, config.disableLosslessIntegers);
10099
}
101100

102101
/**
@@ -217,7 +216,7 @@ export default class Connection {
217216
*/
218217
_handleFatalError(error) {
219218
this._isBroken = true;
220-
this._error = this._errorHandler.handleAndTransformError(error, this.hostPort);
219+
this._error = this._errorHandler.handleAndTransformError(error, this.address);
221220

222221
if (this._log.isErrorEnabled()) {
223222
this._log.error(`${this} experienced a fatal error ${JSON.stringify(this._error)}`);
@@ -267,7 +266,7 @@ export default class Connection {
267266
}
268267
try {
269268
const error = newError(payload.message, payload.code);
270-
this._currentFailure = this._errorHandler.handleAndTransformError(error, this.hostPort);
269+
this._currentFailure = this._errorHandler.handleAndTransformError(error, this.address);
271270
this._currentObserver.onError( this._currentFailure );
272271
} finally {
273272
this._updateCurrentObserver();

src/v1/internal/http/http-driver.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@
2020
import Driver from '../../driver';
2121
import HttpSession from './http-session';
2222
import HttpSessionTracker from './http-session-tracker';
23+
import ServerAddress from '../server-address';
2324

2425
export default class HttpDriver extends Driver {
2526

26-
constructor(hostPort, userAgent, token, config) {
27-
super(hostPort, userAgent, token, config);
27+
constructor(url, userAgent, token, config) {
28+
super(ServerAddress.fromUrl(url.hostAndPort), userAgent, token, config);
29+
this._url = url;
2830
this._sessionTracker = new HttpSessionTracker();
2931
}
3032

3133
session() {
32-
return new HttpSession(this._hostPort, this._authToken, this._config, this._sessionTracker);
34+
return new HttpSession(this._url, this._authToken, this._config, this._sessionTracker);
3335
}
3436

3537
close() {

0 commit comments

Comments
 (0)