Skip to content

Conversation

@michaelsbradleyjr
Copy link
Contributor

The problems described in embark PR #1166 can be resolved by implementing the blockchain proxy with http-proxy directly instead of using express together with http-proxy-middleware. The ultimate cause of the buggy behavior (the "stuck sockets" problems described in #1166) is unknown.

The need to swallow some errors as described in embark PR #1181 is also eliminated by dropping http-proxy-middleware and express.

@michaelsbradleyjr michaelsbradleyjr requested a review from a team December 14, 2018 18:48
@michaelsbradleyjr michaelsbradleyjr force-pushed the refactor/blockchain-proxy-round-3 branch from bdfb14f to 299c3fe Compare December 14, 2018 18:58
michaelsbradleyjr added a commit that referenced this pull request Dec 14, 2018
This PR replaces #1166. The "stuck sockets" bug is addressed in #1195 so there
is no longer a need to use timeouts. However a few aspects of the original PR
are still useful, and lessons learned from #1166, #1181, and #1195 can be put
to good use.

Use a websocket client from the `ws` package when pinging websocket endpoints
instead of manually building a request header. The `'upgrade'` event being
listened for was never actually firing; and though a response was received for
those pings, the response messages indicated problems with those requests. It
seems cleaner to use a proper websocket client and callback success upon the
`ws` client's `'open'` event.

Abstract error and success handling across websocket and http pings.

Report network errors other than `ECONNREFUSED`. Only `ECONNREFUSED` is
expected, as that genuinely indicates an endpoint isn't accepting connections
at the specified host and port. If other kinds of network errors are occurring,
it will be helpful to have a visual indicator to prompt investigation.

After success or the first error, cleanup the ping's request/connection
immediately since we're not awaiting `'data'` events on an http request and we
don't want to leave a websocket connection open. Don't callback any additional
`'error'` events that might fire after the first `'error'` event, but do report
them.
michaelsbradleyjr added a commit that referenced this pull request Dec 14, 2018
This PR replaces #1166. The "stuck sockets" bug is addressed in #1195 so there
is no longer a need to use timeouts. However a few aspects of the original PR
are still useful, and lessons learned from #1166, #1181, and #1195 can be put
to good use.

Use a websocket client from the `ws` package when pinging websocket endpoints
instead of manually building a request header. The `'upgrade'` event being
listened for was never actually firing; and though a response was received for
those pings, the response messages indicated problems with those requests. It
seems cleaner to use a proper websocket client and callback success upon the
`ws` client's `'open'` event.

Abstract error and success handling across websocket and http pings.

Report network errors other than `ECONNREFUSED`. Only `ECONNREFUSED` is
expected, as that genuinely indicates an endpoint isn't accepting connections
at the specified host and port. If other kinds of network errors are occurring,
it will be helpful to have a visual indicator to prompt investigation.

After success or the first error, cleanup the ping's request/connection
immediately since we're not awaiting `'data'` events on an http request and we
don't want to leave a websocket connection open. Don't callback any `'error'`
events that might fire after the first `'error'` event or a success event, but
do report them.
};

Blockchain.prototype.setupProxy = async function () {
const AccountParser = require('../../utils/accountParser');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this require statement out of the function? Ideally we refactor this straight to import * as AccountParser ..

Actually it'd be nicer if we could do import { AccountParser } from '... but we'll have to refactor the exports of AccountParser for that.

case METHODS_TO_MODIFY.accounts:
body.result = body.result.concat(accounts);
break;
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think default should return something here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it should not modify or change the behavior in case of default. I used a switch because it's easier that way to add more cases than an ìf`

head.push(`${key}: ${value}`);
return head;
}
for (let i = 0; i < value.length; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we go with value.forEach here?

server.emit('proxyReqWs', proxyReq, req, socket, options, head);
}

// Error Handler
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is unnecessary

const http = require('http');
const https = require('https');
const httpProxyWsIncoming = require('http-proxy/lib/http-proxy/passes/ws-incoming');
const common = require('http-proxy/lib/http-proxy/common');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a newly introduced file, it'd be great if we could go with import statements right away

const CRLF = '\r\n';

httpProxyWsIncoming.stream = (req, socket, options, head, server, cb) => {
const createHttpHeader = function(line, headers) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this function can be defined outside of stream no?

Also, if we go with function initialization instead of declaration, can we use arrow functions instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

httpProxyOverride.js was copied from an unmerged PR for the http-proxy package:

http-party/node-http-proxy#1301

We're using it to patch http-proxy with support for transform streams.

Some light cleanup was done to the formatting of the copied code, but it wasn't otherwise modified. We certainly can introduce additional changes, but more time will need to be spent studying the source to make sure we don't break it.

// The pipe below will end proxySocket if socket closes cleanly, but not
// if it errors (eg, vanishes from the net and starts returning
// EHOSTUNREACH). We need to do that explicitly.
socket.on('error', function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here, for all the anonymous functions above, I think this would be nicer with arrow functions.

const testLogger = new TestLogger({});
};
isHexStrictStub = sinon.stub(Web3.utils, 'isHexStrict').returns(true);
// Web3.utils.isHexStrict = sinon.stub().returns(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still a comment left here.

});

if (ws) {
server.on('upgrade', function (msg, socket, head) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we go with arrow function here too? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


/*eslint complexity: ["error", 30]*/
static getAccount(accountConfig, web3, logger = console, nodeAccounts) {
const {utils} = require('web3');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this require statement out of this function.

@jrainville
Copy link
Collaborator

FYI, this PR now contains #1172

@jrainville
Copy link
Collaborator

@PascalPrecht Like Michael mentioned, the file httpProxyOverride.js is a patch from a PR that was never merged in http-proxy. I would propose to fork http-proxy and merge the PR in, but in the meantime, this does the trick. Also, I don't think cleaning up its code is worth our time for now.

@michaelsbradleyjr michaelsbradleyjr force-pushed the refactor/blockchain-proxy-round-3 branch 2 times, most recently from f867cc1 to 291146d Compare December 17, 2018 19:12
@michaelsbradleyjr
Copy link
Contributor Author

Rebased against master, mainly accounting for #1156.

michaelsbradleyjr added a commit that referenced this pull request Dec 18, 2018
This PR replaces #1166. The "stuck sockets" bug is addressed in #1195 so there
is no longer a need to use timeouts. However a few aspects of the original PR
are still useful, and lessons learned from #1166, #1181, and #1195 can be put
to good use.

Use a websocket client from the `ws` package when pinging websocket endpoints
instead of manually building a request header. The `'upgrade'` event being
listened for was never actually firing; and though a response was received for
those pings, the response messages indicated problems with those requests. It
seems cleaner to use a proper websocket client and callback success upon the
`ws` client's `'open'` event.

Abstract error and success handling across websocket and http pings.

Report network errors other than `ECONNREFUSED`. Only `ECONNREFUSED` is
expected, as that genuinely indicates an endpoint isn't accepting connections
at the specified host and port. If other kinds of network errors are occurring,
it will be helpful to have a visual indicator to prompt investigation.

After success or the first error, cleanup the ping's request/connection
immediately since we're not awaiting `'data'` events on an http request and we
don't want to leave a websocket connection open. Don't callback any `'error'`
events that might fire after the first `'error'` event or a success event, but
do report them.
The problems described in embark PR #1166 can be resolved by implementing the
blockchain proxy with `http-proxy` directly instead of using `express` together
with `http-proxy-middleware`. The ultimate cause of the buggy behavior (the
"stuck sockets" problems described in #1166) is unknown.

The need to swallow some errors as described in embark PR #1181 is also
eliminated by dropping `http-proxy-middleware` and `express`.
@michaelsbradleyjr michaelsbradleyjr force-pushed the refactor/blockchain-proxy-round-3 branch from 291146d to bd6e4a6 Compare December 18, 2018 17:15
@michaelsbradleyjr michaelsbradleyjr merged commit aba551e into master Dec 18, 2018
@michaelsbradleyjr michaelsbradleyjr deleted the refactor/blockchain-proxy-round-3 branch December 18, 2018 20:07
michaelsbradleyjr added a commit that referenced this pull request Dec 18, 2018
This PR replaces #1166. The "stuck sockets" bug is addressed in #1195 so there
is no longer a need to use timeouts. However a few aspects of the original PR
are still useful, and lessons learned from #1166, #1181, and #1195 can be put
to good use.

Use a websocket client from the `ws` package when pinging websocket endpoints
instead of manually building a request header. The `'upgrade'` event being
listened for was never actually firing; and though a response was received for
those pings, the response messages indicated problems with those requests. It
seems cleaner to use a proper websocket client and callback success upon the
`ws` client's `'open'` event.

Abstract error and success handling across websocket and http pings.

Report network errors other than `ECONNREFUSED`. Only `ECONNREFUSED` is
expected, as that genuinely indicates an endpoint isn't accepting connections
at the specified host and port. If other kinds of network errors are occurring,
it will be helpful to have a visual indicator to prompt investigation.

After success or the first error, cleanup the ping's request/connection
immediately since we're not awaiting `'data'` events on an http request and we
don't want to leave a websocket connection open. Don't callback any `'error'`
events that might fire after the first `'error'` event or a success event, but
do report them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants