diff --git a/src/core/remote.js b/src/core/remote.js index 2961fac3..c3d6ed78 100644 --- a/src/core/remote.js +++ b/src/core/remote.js @@ -74,7 +74,7 @@ function Remote(options = {}) { this.local_fee = true; } - this._servers = [ ]; + this._servers = []; this._primary_server = undefined; // Cache information for accounts. @@ -149,6 +149,9 @@ function Remote(options = {}) { if (!Array.isArray(this.servers)) { throw new TypeError('servers must be an array'); } + if (!(_.isUndefined(this.basic_auth) || _.isString(this.basic_auth))) { + throw new TypeError('basic_auth must be a string'); + } this.setMaxListeners(this.max_listeners); @@ -202,7 +205,7 @@ Remote.DEFAULTS = { pathfind_timeout: 1000 * 10, automatic_resubmission: true, last_ledger_offset: 3, - servers: [ ], + servers: [], max_listeners: 0 // remove Node EventEmitter warnings }; diff --git a/src/core/server.js b/src/core/server.js index f8235b57..f0a4fdc9 100644 --- a/src/core/server.js +++ b/src/core/server.js @@ -5,6 +5,7 @@ const assert = require('assert'); const util = require('util'); const url = require('url'); const LRU = require('lru-cache'); +const HttpsProxyAgent = require('https-proxy-agent'); const EventEmitter = require('events').EventEmitter; const RippleError = require('./rippleerror').RippleError; const Amount = require('./amount').Amount; @@ -418,7 +419,6 @@ Server.prototype.reconnect = function() { Server.prototype.connect = function() { const self = this; - const WebSocket = Server.websocketConstructor(); if (!WebSocket) { @@ -442,23 +442,24 @@ Server.prototype.connect = function() { log.info(this.getServerID(), 'connect'); } - if (this._remote.proxy !== undefined) { - const parsed = url.parse(this._opts.url); - const opts = url.parse(this._remote.proxy); - opts.secureEndpoint = parsed.protocol === 'wss:'; - let HttpsProxyAgent; - try { - HttpsProxyAgent = require('https-proxy-agent'); - } catch (error) { - throw new Error('"proxy" option is not supported in the browser'); - } - const agent = new HttpsProxyAgent(opts); + const wsOptions = {}; - this._ws = new WebSocket(this._opts.url, {agent: agent}); - } else { - this._ws = new WebSocket(this._opts.url); + if (!_.isUndefined(this._remote.proxy)) { + const proxyOptions = _.merge(url.parse(this._remote.proxy), { + secureEndpoint: /^wss/.test(this._opts.url) + }); + + wsOptions.agent = new HttpsProxyAgent(proxyOptions); } + if (!_.isUndefined(this._remote.basic_auth)) { + const authOptions = this._remote.basic_auth; + const auth = new Buffer(authOptions).toString('base64'); + wsOptions.headers = {Authorization: `Basic ${auth}`}; + } + + this._ws = new WebSocket(this._opts.url, wsOptions); + const ws = this._ws; this._shouldConnect = true; @@ -505,11 +506,13 @@ Server.prototype.connect = function() { self.emit('socket_error'); if (self._remote.trace) { - log.info(self.getServerID(), 'onerror:', e.data || e); + log.info(`${self.getServerID()} onerror: ${e.toString()}`); } - if (Server.TLS_ERRORS.indexOf(e.message) !== -1) { - // Unrecoverable + if (Server.TLS_ERRORS.includes(e.message)) { + throw e; + } + if (e.message.includes('unexpected')) { throw e; } diff --git a/src/index.js b/src/index.js index fe75bdc1..9c7120e0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,11 @@ 'use strict'; + +/* eslint-disable max-len */ +// Enable core-js polyfills. This allows use of ES6/7 extensions listed here: +// https://github.com/zloirock/core-js/blob/fb0890f32dabe8d4d88a4350d1b268446127132e/shim.js#L1-L103 +/* eslint-enable max-len */ +require('babel-core/polyfill'); + const core = require('./core'); const RippleAPI = require('./api'); diff --git a/test/remote-test.js b/test/remote-test.js index 42c949c5..f702fd11 100644 --- a/test/remote-test.js +++ b/test/remote-test.js @@ -307,7 +307,7 @@ describe('Remote', function() { }); it('Server initialization -- set servers', function() { - assert.deepEqual(new Remote({servers: []}).servers, [ ]); + assert.deepEqual(new Remote({servers: []}).servers, []); }); it('Server initialization -- set servers -- invalid', function() { assert.throws(function() { @@ -792,11 +792,7 @@ describe('Remote', function() { it('Get server', function() { remote.addServer('wss://sasdf.ripple.com:443'); - remote.connect(); - remote._connected = true; - remote._servers.forEach(function(s) { - s._connected = true; - }); + remote._servers.concat(remote).forEach(s => s._connected = true); const message = { type: 'ledgerClosed', @@ -828,19 +824,6 @@ describe('Remote', function() { assert.strictEqual(remote._servers.length, 2); assert.strictEqual(remote.getServer(), null); }); - it('Get server -- primary server', function() { - const server = remote.addServer({ - host: 'sasdf.ripple.com', - port: 443, - secure: true, - primary: true - }); - - remote.connect(); - server._connected = true; - - assert.strictEqual(remote.getServer().getServerID(), server.getServerID()); - }); it('Parse binary transaction', function() { const binaryTransaction = require('./fixtures/binary-transaction.json'); diff --git a/test/server-test.js b/test/server-test.js index 95b1f491..2754318b 100644 --- a/test/server-test.js +++ b/test/server-test.js @@ -1,6 +1,6 @@ 'use strict'; -/* eslint-disable no-new */ +/* eslint-disable no-new, max-len */ const _ = require('lodash'); const assert = require('assert'); @@ -417,6 +417,67 @@ describe('Server', function() { server.connect(); }); + it('Connect -- with basic auth', function(done) { + const port = 5748; + function verifyClient(info, callback) { + assert.strictEqual(info.req.headers.authorization, 'Basic dXNlcm5hbWU6cGFzc3dvcmQ='); + callback(true); + } + const wss = new ws.Server({port, verifyClient}); + + function handleSubscribeRequest(_ws, message) { + const m = JSON.parse(message); + + assert.deepEqual(m, { + command: 'subscribe', + id: 0, + streams: ['ledger', 'server'] + }); + + _ws.send(JSON.stringify({ + id: 0, + status: 'success', + type: 'response', + result: { + fee_base: 10, + fee_ref: 10, + ledger_hash: + '1838539EE12463C36F2C53B079D807C697E3D93A1936B717E565A4A912E11776', + ledger_index: 7053695, + ledger_time: 455414390, + load_base: 256, + load_factor: 256, + random: + 'E56C9154D9BE94D49C581179356C2E084E16D18D74E8B09093F2D61207625E6A', + reserve_base: 20000000, + reserve_inc: 5000000, + server_status: 'full', + validated_ledgers: '32570-7053695' + } + })); + } + + + wss.once('connection', function(_ws) { + _ws.once('message', _.partial(handleSubscribeRequest, _ws)); + }); + + const remote = new Remote({ + basic_auth: 'username:password' + }); + const server = new Server(remote, `ws://localhost:${port}`); + + server.once('connect', function() { + server.once('disconnect', function() { + wss.close(); + done(); + }); + server.disconnect(); + }); + + server.connect(); + }); + it('Connect - already connected', function(done) { const server = new Server(new Remote(), 'ws://localhost:5748'); server._connected = true;