Skip to content

Http transport#74

Merged
pelger merged 5 commits intomasterfrom
http_transport
Feb 21, 2017
Merged

Http transport#74
pelger merged 5 commits intomasterfrom
http_transport

Conversation

@pelger
Copy link
Copy Markdown
Contributor

@pelger pelger commented Dec 14, 2016

No description provided.

@coveralls
Copy link
Copy Markdown

coveralls commented Dec 14, 2016

Coverage Status

Coverage remained the same at 100.0% when pulling 281c2ac on http_transport into 2955d32 on master.

@pelger pelger mentioned this pull request Dec 14, 2016
5 tasks
sendTo.response.write(stringify(message))
sendTo.response.end()
} else {
cb && cb(mue.transport('connection has alredy recieved response!'))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what if there is no callback?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Then it fails silently of course... :) actually that check was put in place whilst I was working on the last merge - will remove it and assume that the callback is always there this is a safe assumption as driver is an internal thing. Without the check it will blow up with no callback which is the correct behaviour

port: options.target.port,
method: 'POST',
path: '/',
headers: {'Content-Type': 'application/json'}})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You should use an Agent here to speed things up. creating a new TCP connection for each things.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

agreed - will update

var req = http.request({host: options.target.host,
port: options.target.port,
method: 'POST',
path: '/',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

probably a good idea to use a mu-specific path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fair enough - /mu/ ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

perfect


req.on('error', function (err) {
cb(mue.transport(err))
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we should try to avoid some of those closures for speed reasons.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yep - I'll leave one for later - premature optimization and all that...

if (cb) {
setImmediate(function () {
server.close(cb)
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why the setImmediate wrap?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Again one for @davidmarkclements - I assume the intent is to allow existing I/O to drain - but probably not needed...

Copy link
Copy Markdown

@davidmarkclements davidmarkclements Dec 15, 2016

Choose a reason for hiding this comment

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

I can't remember why - but this is definitely needed - at least in the tcp transport.

after destroying the connections, and immediately closing I think it goes into a zombie state and keeps the port open - the setImmediate solved this

the problem was hard to reproduce, it was intermittent, but the tests would sometimes fail because of it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Stick a comment in there. Plus, it's a code smell that something is very wrong in the tear down sequence. It should not be needed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

good point - also worth digging into this to understand fully - most likely we could expose this condition with some load testing

connectionsByIp[request.socket.remoteAddress + '_' + request.socket.remotePort] = []
}
connections[inbound.value.protocol.src].push({request: request, response: response})
connectionsByIp[request.socket.remoteAddress + '_' + request.socket.remotePort] = inbound.value.protocol.src
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why are we doing this? an http request is not a socket, and it can't be reuse.

also, we should probably use http://npm.im/end-of-stream to handle all possible cases (including errors)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because that connection is written to later in the pattern handling sequence and then removed. https://github.com/apparatus/mu/blob/http_transport/packages/mu-http/driver.js#L41-L45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

oooh, now I understand. perfect.

server = options.source && listen(options.source.port, options.source.host, options.ready)

if (!server && options.ready instanceof Function) {
options.ready()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why we ended up having ready()? I thought we had no callbacks at all for transports, services, etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

One for @davidmarkclements I think :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Memory is hazy on this, but there was definitely a good reason, I think it was needed for tests (or rather to avoid using setTimeouts in tests) - and may also be needed internally

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we need to have a ready()  thing, we can just have a full-blown plugin system like boot-in-the-arse and be done with it. Ready callbacks are tricky when there are multiple ones to be started.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

nooooo plugins!!!!! let me review the whole ready thing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So whilst this is not generally needed, there may be edge cases when a signal back from the underlying driver to indicate that it is ready may be useful. I suggest that we keep this in for now and review after some more field testing if unused.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@pelger I think it is needed. And if it is needed, you are off with something like https://github.com/mcollina/boot-in-the-arse rather than cooking up something internal.
If it is not needed, we are better off without! :)

Copy link
Copy Markdown

@davidmarkclements davidmarkclements left a comment

Choose a reason for hiding this comment

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

overall awesomeness - just a couple of requests


inbound = parse(Buffer.concat(body))
receive(inbound.err, inbound.value)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

what if the response stream emits an error or closes unexpectedly - maybe end-of-stream needed here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

parse will fail and inbound.err will be set.

headers: {'Content-Type': 'application/json'}})

req.on('response', function (response) {
response.on('data', function (chunk) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

on a general note, I'm concerned we're missing the ability to stream from transport to user land (c.f. #46)

yes there has to of course be a buffering mode (which is our only option), but for large data blobs being able to pattern match against a stream and/or associate a pattern to a stream across every transport would remove all limitations

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This would be good - suggest that we merge this in and then add this capability to the roadmap - would like to think this through a little!

})

req.write(stringify(message))
req.end()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

you can just do req.end(stringify(message))



function listen (port, host, ready) {
server = http.createServer()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

principle of least surprise would encourage assignment to variables as early as possible,
in the tcp transport listen returns the server object, and at the top, server is assigned to the result of listen - could we follow the same approach here (and all transports) ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

realise listen returns server - no need to assign to server var here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

OK done

* HTTP transport
* Figure out path mapping - perhaps this may need to be part of the config for now just /
*/
module.exports = function createTcpDriver (options) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

function should be named createHttpDriver

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Opps yep my bad!

function send (message, cb) {
var sendTo

if (connections[message.protocol.dst]) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggest breaking the two main cases into two functions - primarily for readability - but also makes inlining more likely

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@pelger ^^




server = options.source && listen(options.source.port, options.source.host, options.ready)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this should be at the top - see previous comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

OK

return
if (!msg || !msg.protocol) {
return
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

no need for else after early return

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ermmm... you want to just re-read that code Dave :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I re-read... I'm seeing a return and then an else - the else is redundant - what am I missing?

protocol: {}}
var elt = muid

/* istanbul ignore else */
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

not keen on artificially maintaining coverage

question is, if we have to do this, maybe the checks aren't necessary

if they are, then we should test (and handle) the alternatives

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed - but IMO the occasional pragma to maintain 100% is worth it - branch coverage is sometimes too fiddly :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I submit that its worth the investment for core - the fiddling can reveal things not thought of, or if not it gives confidence that the logic is sound and cases well covered

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fair comment - however this is 1/2 a days worth so I'll put it on the backlog for now!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please remove that sentence, so we do not forget about it. It's ok if coverages drops consequently.

if (!msg || !msg.protocol) {
return
} else {
msg = constructFailResponse(err, msg)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

this is much better than before

@coveralls
Copy link
Copy Markdown

coveralls commented Jan 9, 2017

Coverage Status

Coverage remained the same at 100.0% when pulling 7915805 on http_transport into 2955d32 on master.

@coveralls
Copy link
Copy Markdown

coveralls commented Jan 9, 2017

Coverage Status

Coverage remained the same at 100.0% when pulling 7915805 on http_transport into 2955d32 on master.

@mcollina mcollina mentioned this pull request Jan 9, 2017
Copy link
Copy Markdown
Contributor

@mcollina mcollina left a comment

Choose a reason for hiding this comment

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

Some nits should be fixed.

server = options.source && listen(options.source.port, options.source.host, options.ready)

if (!server && options.ready instanceof Function) {
options.ready()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@pelger I think it is needed. And if it is needed, you are off with something like https://github.com/mcollina/boot-in-the-arse rather than cooking up something internal.
If it is not needed, we are better off without! :)

function send (message, cb) {
var sendTo

if (connections[message.protocol.dst]) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@pelger ^^

})

request.on('end', function () {
inbound = parse(Buffer.concat(body))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It is way better to use strings here, e.g.:

var body = ''
request.setEncoding('utf8')
request.on('data', (chunk) => body += chunk)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is it better to use strings or to concat actual buffers? (instead of using array)

request.on('end', function () {
inbound = parse(Buffer.concat(body))

if (!connections[inbound.value.protocol.src]) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you should protect for inbound.value.protocol.src to exist before proceeding.

protocol: {}}
var elt = muid

/* istanbul ignore else */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please remove that sentence, so we do not forget about it. It's ok if coverages drops consequently.


request.on('data', function (chunk) {
body.push(chunk)
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

are you doing anything with body  here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nope :)

@pelger pelger merged commit 5697128 into master Feb 21, 2017
@pelger pelger deleted the http_transport branch February 21, 2017 17:11
@coveralls
Copy link
Copy Markdown

Coverage Status

Coverage remained the same at 100.0% when pulling 4591708 on http_transport into 2955d32 on master.

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