Skip to content

Commit 291146d

Browse files
refactor(proxy): proxy blockchain accounts so that they are available in the Dapp
1 parent eb44a7d commit 291146d

File tree

9 files changed

+279
-46
lines changed

9 files changed

+279
-46
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@
131131
"multihashes": "0.4.14",
132132
"neo-blessed": "0.2.0",
133133
"netcat": "1.3.5",
134+
"node-http-proxy-json": "0.1.6",
134135
"node-ipc": "9.1.1",
135136
"node-sass": "4.9.3",
136137
"npmlog": "4.1.2",

src/lib/modules/blockchain_process/blockchain.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,17 @@ Blockchain.prototype.initProxy = function () {
147147
};
148148

149149
Blockchain.prototype.setupProxy = async function () {
150+
const AccountParser = require('../../utils/accountParser');
150151
if (!this.proxyIpc) this.proxyIpc = new Ipc({ipcRole: 'client'});
151152

153+
const addresses = AccountParser.parseAccountsConfig(this.userConfig.accounts, false, this.logger);
154+
152155
let wsProxy;
153156
if (this.config.wsRPC) {
154-
wsProxy = proxy.serve(this.proxyIpc, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins, this.certOptions);
157+
wsProxy = proxy.serve(this.proxyIpc, this.config.wsHost, this.config.wsPort, true, this.config.wsOrigins, addresses, this.certOptions);
155158
}
156159

157-
[this.rpcProxy, this.wsProxy] = await Promise.all([proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false, undefined, this.certOptions), wsProxy]);
160+
[this.rpcProxy, this.wsProxy] = await Promise.all([proxy.serve(this.proxyIpc, this.config.rpcHost, this.config.rpcPort, false, null, addresses, this.certOptions), wsProxy]);
158161
};
159162

160163
Blockchain.prototype.shutdownProxy = function () {
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/* global require */
2+
3+
const http = require('http');
4+
const https = require('https');
5+
const httpProxyWsIncoming = require('http-proxy/lib/http-proxy/passes/ws-incoming');
6+
const common = require('http-proxy/lib/http-proxy/common');
7+
8+
const CRLF = '\r\n';
9+
10+
httpProxyWsIncoming.stream = (req, socket, options, head, server, cb) => {
11+
const createHttpHeader = function(line, headers) {
12+
return Object.keys(headers).reduce(function (head, key) {
13+
const value = headers[key];
14+
if (!Array.isArray(value)) {
15+
head.push(`${key}: ${value}`);
16+
return head;
17+
}
18+
for (let i = 0; i < value.length; i++) {
19+
head.push(`${key}: ${value[i]}`);
20+
}
21+
return head;
22+
}, [line])
23+
.join(CRLF) + `${CRLF}${CRLF}`;
24+
};
25+
26+
common.setupSocket(socket);
27+
28+
if (head && head.length) socket.unshift(head);
29+
30+
const protocol = common.isSSL.test(options.target.protocol) ? https : http;
31+
32+
const proxyReq = protocol.request(
33+
common.setupOutgoing(options.ssl || {}, options, req)
34+
);
35+
36+
// Enable developers to modify the proxyReq before headers are sent
37+
if (server) {
38+
server.emit('proxyReqWs', proxyReq, req, socket, options, head);
39+
}
40+
41+
// Error Handler
42+
proxyReq.on('error', onOutgoingError);
43+
proxyReq.on('response', function (res) {
44+
// if upgrade event isn't going to happen, close the socket
45+
if (!res.upgrade) {
46+
const {httpVersion, statusCode, statusMessage, headers} = res;
47+
socket.write(createHttpHeader(
48+
`HTTP/${httpVersion} ${statusCode} ${statusMessage}`,
49+
headers
50+
));
51+
res.pipe(socket);
52+
}
53+
});
54+
55+
proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) {
56+
proxySocket.on('error', onOutgoingError);
57+
58+
// Allow us to listen when the websocket has completed
59+
proxySocket.on('end', function () {
60+
server.emit('close', proxyRes, proxySocket, proxyHead);
61+
});
62+
63+
// The pipe below will end proxySocket if socket closes cleanly, but not
64+
// if it errors (eg, vanishes from the net and starts returning
65+
// EHOSTUNREACH). We need to do that explicitly.
66+
socket.on('error', function () {
67+
proxySocket.end();
68+
});
69+
70+
common.setupSocket(proxySocket);
71+
72+
if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead);
73+
74+
// Remark: Handle writing the headers to the socket when switching protocols
75+
// Also handles when a header is an array
76+
socket.write(createHttpHeader(
77+
'HTTP/1.1 101 Switching Protocols',
78+
proxyRes.headers
79+
));
80+
81+
let proxyStream = proxySocket;
82+
83+
if (options.createWsServerTransformStream) {
84+
const wsServerTransformStream = options.createWsServerTransformStream(
85+
req,
86+
proxyReq,
87+
proxyRes,
88+
);
89+
90+
wsServerTransformStream.on('error', onOutgoingError);
91+
proxyStream = proxyStream.pipe(wsServerTransformStream);
92+
}
93+
94+
proxyStream = proxyStream.pipe(socket);
95+
96+
if (options.createWsClientTransformStream) {
97+
const wsClientTransformStream = options.createWsClientTransformStream(
98+
req,
99+
proxyReq,
100+
proxyRes,
101+
);
102+
103+
wsClientTransformStream.on('error', onOutgoingError);
104+
proxyStream = proxyStream.pipe(wsClientTransformStream);
105+
}
106+
107+
proxyStream.pipe(proxySocket);
108+
109+
server.emit('open', proxySocket);
110+
server.emit('proxySocket', proxySocket); //DEPRECATED.
111+
});
112+
113+
return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT
114+
115+
function onOutgoingError(err) {
116+
if (cb) {
117+
cb(err, req, socket);
118+
} else {
119+
server.emit('error', err, req, socket);
120+
}
121+
socket.end();
122+
}
123+
};

src/lib/modules/blockchain_process/proxy.js

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
11
/* global Buffer __ exports require */
22

3+
require('./httpProxyOverride');
34
const Asm = require('stream-json/Assembler');
45
const {canonicalHost, defaultHost} = require('../../utils/host');
56
const constants = require('../../constants.json');
7+
const {Duplex} = require('stream');
68
const http = require('http');
79
const httpProxy = require('http-proxy');
810
const {parser: jsonParser} = require('stream-json');
911
const pump = require('pump');
1012
const utils = require('../../utils/utils');
1113
const WsParser = require('simples/lib/parsers/ws');
14+
const WsWrapper = require('simples/lib/ws/wrapper');
15+
const modifyResponse = require('node-http-proxy-json');
16+
17+
const METHODS_TO_MODIFY = {accounts: 'eth_accounts'};
18+
19+
const modifyPayload = (toModifyPayloads, body, accounts) => {
20+
switch (toModifyPayloads[body.id]) {
21+
case METHODS_TO_MODIFY.accounts:
22+
body.result = body.result.concat(accounts);
23+
break;
24+
default:
25+
}
26+
return body;
27+
};
1228

1329
const hex = (n) => {
1430
let _n = n.toString(16);
@@ -32,14 +48,18 @@ const parseJsonMaybe = (string) => {
3248
return object;
3349
};
3450

35-
exports.serve = async (ipc, host, port, ws, origin, certOptions={}) => {
51+
exports.serve = async (ipc, host, port, ws, origin, accounts, certOptions={}) => {
3652
const commList = {};
3753
const receipts = {};
3854
const transactions = {};
55+
const toModifyPayloads = {};
3956

4057
const trackRequest = (req) => {
4158
if (!req) return;
4259
try {
60+
if (Object.values(METHODS_TO_MODIFY).includes(req.method)) {
61+
toModifyPayloads[req.id] = req.method;
62+
}
4363
if (req.method === 'eth_sendTransaction') {
4464
commList[req.id] = {
4565
type: 'contract-log',
@@ -121,7 +141,32 @@ exports.serve = async (ipc, host, port, ws, origin, certOptions={}) => {
121141
host: canonicalHost(host),
122142
port: port
123143
},
124-
ws: ws
144+
ws: ws,
145+
createWsServerTransformStream: (_req, _proxyReq, _proxyRes) => {
146+
const parser = new WsParser(0, true);
147+
parser.on('frame', ({data: buffer}) => {
148+
let object = parseJsonMaybe(buffer.toString());
149+
if (object) {
150+
object = modifyPayload(toModifyPayloads, object, accounts);
151+
// track the modified response
152+
trackResponse(object);
153+
// send the modified response
154+
WsWrapper.wrap(
155+
{connection: dupl, masked: 0},
156+
Buffer.from(JSON.stringify(object)),
157+
() => {}
158+
);
159+
}
160+
});
161+
const dupl = new Duplex({
162+
read(_size) {},
163+
write(chunk, encoding, callback) {
164+
parser.write(chunk);
165+
callback();
166+
}
167+
});
168+
return dupl;
169+
}
125170
});
126171

127172
proxy.on('error', (err) => {
@@ -131,15 +176,14 @@ exports.serve = async (ipc, host, port, ws, origin, certOptions={}) => {
131176
);
132177
});
133178

134-
proxy.on('proxyRes', (proxyRes, req, _res) => {
135-
if (req.method === 'POST') {
136-
// messages FROM the target
137-
Asm.connectTo(
138-
pump(proxyRes, jsonParser())
139-
).on('done', ({current: object}) => {
140-
trackResponse(object);
141-
});
142-
}
179+
proxy.on('proxyRes', (proxyRes, req, res) => {
180+
modifyResponse(res, proxyRes, (body) => {
181+
if (body) {
182+
body = modifyPayload(toModifyPayloads, body, accounts);
183+
trackResponse(body);
184+
}
185+
return body;
186+
});
143187
});
144188

145189
const server = http.createServer((req, res) => {
@@ -158,17 +202,11 @@ exports.serve = async (ipc, host, port, ws, origin, certOptions={}) => {
158202
});
159203

160204
if (ws) {
161-
server.on('upgrade', function (msg, socket, head) {
205+
server.on('upgrade', (msg, socket, head) => {
162206
proxy.ws(msg, socket, head);
163207
});
164208

165-
proxy.on('open', (proxySocket) => {
166-
// messages FROM the target
167-
pump(proxySocket, new WsParser(0, true)).on('frame', ({data: buffer}) => {
168-
const object = parseJsonMaybe(buffer.toString());
169-
trackResponse(object);
170-
});
171-
});
209+
proxy.on('open', (_proxySocket) => { /* messages FROM the target */ });
172210

173211
proxy.on('proxyReqWs', (_proxyReq, _req, socket) => {
174212
// messages TO the target

src/lib/modules/blockchain_process/simulator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Simulator {
6767
}
6868

6969
runCommand(cmds, useProxy, host, port) {
70-
const ganache_main = require.resolve('ganache-cli', {paths: fs.embarkPath('node_modules')});
70+
const ganache_main = require.resolve('ganache-cli', {paths: [fs.embarkPath('node_modules')]});
7171
const ganache_json = pkgUp.sync(path.dirname(ganache_main));
7272
const ganache_root = path.dirname(ganache_json);
7373
const ganache_bin = require(ganache_json).bin;

src/lib/modules/code_generator/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ class CodeGenerator {
290290
function getWeb3Location(next) {
291291
self.events.request("version:get:web3", function(web3Version) {
292292
if (web3Version === "1.0.0-beta") {
293-
return next(null, require.resolve("web3", {paths: fs.embarkPath("node_modules")}));
293+
return next(null, require.resolve("web3", {paths: [fs.embarkPath("node_modules")]}));
294294
}
295295
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
296296
return next(null, fs.dappPath(location));
@@ -356,7 +356,7 @@ class CodeGenerator {
356356
function getWeb3Location(next) {
357357
self.events.request("version:get:web3", function(web3Version) {
358358
if (web3Version === "1.0.0-beta") {
359-
return next(null, require.resolve("web3", {paths: fs.embarkPath("node_modules")}));
359+
return next(null, require.resolve("web3", {paths: [fs.embarkPath("node_modules")]}));
360360
}
361361
self.events.request("version:getPackageLocation", "web3", web3Version, function(err, location) {
362362
return next(null, fs.dappPath(location));

0 commit comments

Comments
 (0)