Skip to content

Commit

Permalink
Add support for client certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Clark committed Dec 1, 2015
1 parent cfc21fd commit 5f5e48e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 19 deletions.
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,10 @@ The RippleAPI constructor optionally takes one argument, an object with the foll
Name | Type | Description
---- | ---- | -----------
authorization | string | *Optional* Username and password for HTTP basic authentication to the rippled server in the format **username:password**.
certificate | string | *Optional* A string containing the certificate key of the client in PEM format. (Can be an array of certificates).
feeCushion | number | *Optional* Factor to multiply estimated fee by to provide a cushion in case the required fee rises during submission of a transaction. Defaults to `1.2`.
key | string | *Optional* A string containing the private key of the client in PEM format. (Can be an array of keys).
passphrase | string | *Optional* The passphrase for the private key of the client.
proxy | uri string | *Optional* URI for HTTP/HTTPS proxy to use to connect to the rippled server.
proxyAuthorization | string | *Optional* Username and password for HTTP basic authentication to the proxy in the format **username:password**.
server | uri string | *Optional* URI for rippled websocket port to connect to. Must start with `wss://` or `ws://`.
Expand Down
48 changes: 29 additions & 19 deletions src/common/connection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
const _ = require('lodash');
const {EventEmitter} = require('events');
const WebSocket = require('ws');
const parseURL = require('url').parse;
Expand Down Expand Up @@ -26,6 +27,9 @@ class Connection extends EventEmitter {
this._proxyAuthorization = options.proxyAuthorization;
this._authorization = options.authorization;
this._trustedCertificates = options.trustedCertificates;
this._key = options.key;
this._passphrase = options.passphrase;
this._certificate = options.certificate;
this._timeout = options.timeout || (20 * 1000);
this._isReady = false;
this._ws = null;
Expand Down Expand Up @@ -104,20 +108,21 @@ class Connection extends EventEmitter {
});
}

_createWebSocket(url, proxyURL, proxyAuthorization, authorization,
trustedCertificates) {
_createWebSocket() {
const options = {};
if (proxyURL !== undefined) {
const parsedURL = parseURL(url);
const proxyOptions = parseURL(proxyURL);
proxyOptions.secureEndpoint = (parsedURL.protocol === 'wss:');
proxyOptions.secureProxy = (proxyOptions.protocol === 'https:');
if (proxyAuthorization !== undefined) {
proxyOptions.auth = proxyAuthorization;
}
if (trustedCertificates !== undefined) {
proxyOptions.ca = trustedCertificates;
}
if (this._proxyURL !== undefined) {
const parsedURL = parseURL(this._url);
const parsedProxyURL = parseURL(this._proxyURL);
const proxyOverrides = _.omit({
secureEndpoint: (parsedURL.protocol === 'wss:'),
secureProxy: (parsedProxyURL.protocol === 'https:'),
auth: this._proxyAuthorization,
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined);
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides);
let HttpsProxyAgent;
try {
HttpsProxyAgent = require('https-proxy-agent');
Expand All @@ -126,11 +131,18 @@ class Connection extends EventEmitter {
}
options.agent = new HttpsProxyAgent(proxyOptions);
}
if (authorization !== undefined) {
const base64 = new Buffer(authorization).toString('base64');
if (this._authorization !== undefined) {
const base64 = new Buffer(this._authorization).toString('base64');
options.headers = {Authorization: `Basic ${base64}`};
}
const websocket = new WebSocket(url, options);
const optionsOverrides = _.omit({
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined);
const websocketOptions = _.assign({}, options, optionsOverrides);
const websocket = new WebSocket(this._url, websocketOptions);
// we will have a listener for each outstanding request,
// so we have to raise the limit (the default is 10)
websocket.setMaxListeners(Infinity);
Expand All @@ -148,9 +160,7 @@ class Connection extends EventEmitter {
} else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', resolve);
} else {
this._ws = this._createWebSocket(this._url, this._proxyURL,
this._proxyAuthorization, this._authorization,
this._trustedCertificates);
this._ws = this._createWebSocket();
this._ws.on('message', this._onMessage.bind(this));
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this);
this._ws.once('close', this._onUnexpectedCloseBound);
Expand Down
12 changes: 12 additions & 0 deletions src/common/schemas/input/api-options.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@
"type": "string",
"description": "A PEM-formatted SSL certificate to trust when connecting to a proxy."
}
},
"key": {
"type": "string",
"description": "A string containing the private key of the client in PEM format. (Can be an array of keys)."
},
"passphrase": {
"type": "string",
"description": "The passphrase for the private key of the client."
},
"certificate": {
"type": "string",
"description": "A string containing the certificate key of the client in PEM format. (Can be an array of certificates)."
}
},
"additionalProperties": false
Expand Down

0 comments on commit 5f5e48e

Please sign in to comment.