-
Notifications
You must be signed in to change notification settings - Fork 6
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
fix server listen error event many times #66
Conversation
I reviewed the codebase and found that the unit test did not cover the server error but only the socket error, and the server error was not handled correctly. Resolved in de20de8 Could you rebase your PR and see if the test cases are still working correctly? |
Yes, the test cases are working with no problems! |
index.js
Outdated
@@ -51,7 +51,9 @@ const createAdapter = handler => config => new Promise((resolve, reject) => { | |||
const server = handler instanceof Server ? handler : createServer(handler) | |||
const listening = server.listening | |||
|
|||
server.on('error', reject); | |||
if (server.eventNames().includes('error')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it setup error handler only if there is already one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because the server listens for errors using on
, not once
. You can try emitting the error many times.
@@ -71,7 +73,6 @@ const createAdapter = handler => config => new Promise((resolve, reject) => { | |||
} | |||
).then( | |||
(response) => { | |||
server.off('error', reject) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does it remove?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this code can fix the server's repeated listening problem.
if (server.eventNames().includes('error')) {
server.on('error', reject)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a teat case to describe your issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is my error, I mean code is this
if (!server.eventNames().includes('error')) {
server.on('error', reject)
}
I will write test case to describe my issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const server = createServer((req, res) => res.end()) | ||
|
||
await axiosist(server).get('/') | ||
t.is(server.listenerCount('error'), 0) | ||
t.is(server.listenerCount('error'), 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the error listener is not supposed to be kept here, which is causing memory leak.
const server = createServer((req, res) => res.end('foo')) | ||
|
||
await t.throwsAsync(axiosist(server).get('/', { maxContentLength: 1 })) | ||
t.is(server.listenerCount('error'), 0) | ||
t.is(server.listenerCount('error'), 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neither
await t.throwsAsync(axiosist(server).get('/'), { message: 'foo' }) | ||
|
||
server.on('error', (err) => { | ||
t.is(err.message, 'foo') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not testing axiosist but testing node.js's http module
|
||
await Promise.all(promises.map(promise => promise())) | ||
|
||
t.is(server.listenerCount('error'), 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't make sense - only the first request will listen to the server error, others will hang if server emits error event.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried running your off
error code. Can you run this test case?:
test('should be listened error to once', async t => {
const server = createServer()
server.on('request', (req, res) => {
res.end('ok')
})
server.listen()
const request = axiosist(server)
const promises = []
for (let i = 0; i < 20; i++) {
promises.push(() => request.get('/'))
}
await Promise.all(promises.map(promise => promise()))
t.is(server.listenerCount('error'), 0)
})
The repeated listening error still exists. If an error is emitted, then many listeners will receive the error message because your off
operation is asynchronous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider:
test('should be listened error to once', async t => {
const request = axiosist((req, res) => {
res.end('ok')
})
const promises = []
for (let i = 0; i < 20; i++) {
promises.push(() => request.get('/'))
}
await Promise.all(promises.map(promise => promise()))
})
This does not seem a typical unit test, if you need concurrent requests to the same server with such number, consider stress tests like autocannon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you are right. I just want to prove the error listening issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am thinking about maybe we can just drop this event listener - serious server errors will be transferred to the socket, which is already handled by axios (one example: https://github.com/axios/axios/blob/v1.7.2/lib/adapters/http.js#L571-L574).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually, when the server encounters an error, it is handled by a try-catch block or its own listener, so I think this error listener can be removed too.
No description provided.