-
Notifications
You must be signed in to change notification settings - Fork 29.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AsyncWrap: HTTP parserOnBody events happen outside pre/post #4416
Comments
/cc @trevnorris |
Attaching a test case: 'use strict';
const async_wrap = process.binding('async_wrap');
const fork = require('child_process').fork;
const Writable = require('stream').Writable;
const assert = require('assert');
const http = require('http');
const common = require('../common');
return process.env.NODE_TEST_FORK
? runChild()
: runParent();
function runChild () {
async_wrap.setupHooks(init, pre, post);
async_wrap.enable();
const server = http.createServer((req, res) => {
req.on('data', data => {
process._rawDebug('DATA');
});
req.once('end', () => {
res.end('end response');
server.close(() => {
process.exit(0);
});
});
});
server.listen(common.PORT, () => {
// this "console.log" is load-bearing: we have to send something
// out over stdout to signal that we're ready to receive requests.
console.log('listening');
});
function init (type, id, parent) {
process._rawDebug('init', this.constructor.name);
}
function pre () {
process._rawDebug('pre', this.constructor.name);
}
function post () {
process._rawDebug('post', this.constructor.name);
}
}
function runParent () {
const proc = fork(__filename, {
env: Object.assign({}, process.env, {NODE_TEST_FORK: '1'}),
silent: true
});
const acc = [];
proc.stderr.pipe(Writable({
write (chunk, enc, cb) {
acc.push(chunk);
cb();
}
}));
proc.stdout.once('data', () => {
const req = http.request({
host: '127.0.0.1',
port: common.PORT,
method: 'POST'
}, res => {
res.resume();
});
req.write('hello'.repeat(16000));
req.end();
});
setTimeout(() => {
try {
child.kill()
} finally {
process.exit()
}
}, 100).unref();
proc.once('exit', () => {
assert.equal(Buffer.concat(acc).toString('utf8'), `
init TCP
init TCP
pre TCP
init Timer
post TCP
pre TCP
post TCP
DATA
pre TCP
DATA
post TCP
init ShutdownWrap
`.split('\n').map(xs => xs.trim()).join('\n').slice(1));
})
} |
Thanks for the test cases. Holiday is of course slowing things down, but I'll try to have this solved by the end of the week. |
Is there a diagram of the data flow from the TCP connection receiving a packet to where it's delivered via the /cc @indutny EDIT: Also, when it runs. This isn't happening anywhere within the execution of |
@chrisdickinson Also realize that the |
Yeah - that may be due to a process.nextTick, I think. (At least, the first chunk of data is nextTick'd.)
|
Err, just replied and immediately realized those are two different problems! whoops! :)
|
@chrisdickinson To test I delayed running the post callback until after the |
To further support this, here's an alteration to your first example: const d = require('domain').create();
let server;
d.run(function() {
server = http.createServer(req => {
print('CONN');
req.on('data', data => {
print('RECV', process.domain);
});
})
}); Output:
So the /cc @indutny sorry for the second cc but this info is more directly related to how HTTPParser fundamentally works. |
@trevnorris I think it should be fixed by doing |
@indutny Is it safe to |
@chrisdickinson if it is not - we should fix it, I guess! |
@chrisdickinson It's not, but that won't be a problem here. onBody never calls when already in a stack. |
@trevnorris it will call it on stack if the socket is not consumed. Like in |
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Fix: nodejs#4416
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Fix: nodejs#4416
Thanks man, and sorry for delaying it. |
@indutny no worries. I was the one who delayed it while figuring out the entire reentrant makecallback thing. |
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Additional uses of `AsyncCallbackScope` are necessary to prevent improper state from progressing that triggers failure in the test-http-pipeline-flood.js test. Optimally this wouldn't be necessary, but for the time being it's the most sure way to allow operations to proceed as they have. Fix: nodejs#4416 PR-URL: nodejs#5419 Reviewed-By: Fedor Indutny <[email protected]>
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Additional uses of `AsyncCallbackScope` are necessary to prevent improper state from progressing that triggers failure in the test-http-pipeline-flood.js test. Optimally this wouldn't be necessary, but for the time being it's the most sure way to allow operations to proceed as they have. Fix: #4416 PR-URL: #5419 Reviewed-By: Fedor Indutny <[email protected]>
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Additional uses of `AsyncCallbackScope` are necessary to prevent improper state from progressing that triggers failure in the test-http-pipeline-flood.js test. Optimally this wouldn't be necessary, but for the time being it's the most sure way to allow operations to proceed as they have. Ref: #7048 Fix: #4416 PR-URL: #5419 Reviewed-By: Fedor Indutny <[email protected]>
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Additional uses of `AsyncCallbackScope` are necessary to prevent improper state from progressing that triggers failure in the test-http-pipeline-flood.js test. Optimally this wouldn't be necessary, but for the time being it's the most sure way to allow operations to proceed as they have. Ref: #7048 Fix: #4416 PR-URL: #5419 Reviewed-By: Fedor Indutny <[email protected]>
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Additional uses of `AsyncCallbackScope` are necessary to prevent improper state from progressing that triggers failure in the test-http-pipeline-flood.js test. Optimally this wouldn't be necessary, but for the time being it's the most sure way to allow operations to proceed as they have. Ref: #7048 Fix: #4416 PR-URL: #5419 Reviewed-By: Fedor Indutny <[email protected]>
Make `HTTPParser` an instance of `AsyncWrap` and make it use `MakeCallback`. This means that async wrap hooks will be called on consumed TCP sockets as well as on non-consumed ones. Additional uses of `AsyncCallbackScope` are necessary to prevent improper state from progressing that triggers failure in the test-http-pipeline-flood.js test. Optimally this wouldn't be necessary, but for the time being it's the most sure way to allow operations to proceed as they have. Ref: #7048 Fix: #4416 PR-URL: #5419 Reviewed-By: Fedor Indutny <[email protected]>
In the case where an incoming HTTP request is consumed by the C++ layer, the resulting
parserOnBody
callbacks are not made throughMakeCallback
. Instead they use->Call
directly. In the slow case, where the TCP connection is not consumed by C++, the appropriatepre/post
events are fired.Given the example program:
Running the following:
$ curl -sLi -d@/usr/share/dict/words 'http://localhost:8124'
Produces the following when the program is run with
node example.js
:And the following when the example is run with
RUN_SLOWLY=1 node example.js
:The text was updated successfully, but these errors were encountered: