Skip to content
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

Handle aborted requests #3651

Merged
merged 7 commits into from Feb 28, 2022
Merged

Conversation

Allain55
Copy link
Contributor

@Allain55 Allain55 commented Jan 24, 2022

If a request was aborted before calling the reply.send method, it won't try to send a response since no one is waiting for it. This way logging errors like ERR_STREAM_PREMATURE_CLOSE can be avoided too.

Full discussion: fastify/help#607

Checklist

Copy link
Member

@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.

I would be more comfortable in shipping this as a semver-major change, could you target the next branch? The error handling path has been significantly improved there.

lib/reply.js Outdated
@@ -130,6 +130,10 @@ Reply.prototype.send = function (payload) {
return this
}

if (this.request.raw && this.request.raw.aborted === true) {
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a TODO comment here to use the destroyed field (see https://nodejs.org/dist/latest-v16.x/docs/api/http.html#messageaborted, aborted is deprecated) as soon as we drop support for Node v14? Thanks

I also think that raw would always be defined here. When is it not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

raw should be always defined according to the types but in this case a few test cases would be broken. I had to choose to add raw to these tests or do this. Would you prefer if I update those test cases?
Will do the rest.

lib/reply.js Outdated
@@ -130,6 +130,10 @@ Reply.prototype.send = function (payload) {
return this
}

if (this.request.raw && this.request.raw.aborted === true) {
return this
Copy link
Member

Choose a reason for hiding this comment

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

Please run the onError hook with a new Error (create an ad-hoc
one inside lib/error.js). Note that if the payload is a stream you must call .destroy() on it too to avoid memory leaks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you sure you want to treat aborted requests as errors? Is it not a special case?
Currently when reply.send is called in this case with anything other than a stream there would be no error. Errors happen only with streams
For example if someone wants to handle aborted requests it is possible before calling reply.send

Thanks for the tip on destroy(). Do you think this would be a good way to destroy streams?

if (payload && payload.destroyed === false && typeof payload.destroy === 'function') {
  payload.destroy()
 }

Copy link
Member

Choose a reason for hiding this comment

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

Then I think there is nothing we can do in this PR and the current behavior is correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK that's fine by me if you think so, I can fix this problem in my codebase using a decorator or an onSend hook. So was my description not clear about the problem at fastify/help#607 ?

}
const reply = new Reply(response, { raw: { aborted: true } })
t.equal(reply.send('hello'), reply)
})
Copy link
Member

Choose a reason for hiding this comment

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

Could you add another test by using a real request?

@Allain55
Copy link
Contributor Author

I leave here the 2 possible solutions I came up with if anyone else needs to fix the potential issue with streams:

fastify.addHook('onSend', (req, reply, payload, done) => {
	if (req.raw.destroyed) {
		if (payload && !payload.destroyed && typeof payload.destroy === 'function') {
			payload.destroy();
		}
		
		payload = '';
	}
	
	done(null, payload)
})
fastify.decorateReply(
	'safeSend',
	/**
	 * @param {unknown} [payload]
	 * @this {FastifyReply}
	 */
	function(payload) {
		if (this.request.raw.destroyed) {
			if (payload && !payload.destroyed && typeof payload.destroy === 'function') {
				payload.destroy();
			}
			
			// send an empty response since no one is waiting for it anymore
			payload = '';
		}
		
		this.send(payload)
	}
)

@mcollina
Copy link
Member

The way you have described above are not equivalent to this PR. this PR completely skips the onSend/onError hooks, while your workaround actually replace the payload - in that sense it's similar to the error that is being returned. Your workaround is something I would be more inclined to land vs just skipping all the hooks.

@github-actions github-actions bot added documentation Improvements or additions to documentation plugin Identify a pr to the doc that adds a plugin. test Issue or pr related to our testing infrastructure. typescript TypeScript related and removed documentation Improvements or additions to documentation test Issue or pr related to our testing infrastructure. plugin Identify a pr to the doc that adds a plugin. typescript TypeScript related labels Feb 25, 2022
@Allain55 Allain55 changed the base branch from main to next February 25, 2022 13:06
@Allain55
Copy link
Contributor Author

I've reworked this to a much simpler solution to only handle errors coming from streams and target the next branch

Copy link
Member

@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.

lgtm

@mcollina mcollina changed the base branch from next to main February 25, 2022 13:51
@mcollina
Copy link
Member

I rebased it on top of main as we merged next in main.

test/stream.test.js Outdated Show resolved Hide resolved
@jsumners
Copy link
Member

Can you rebase? There is a lint error that I am not seeing on the current main branch.

@Allain55
Copy link
Contributor Author

Can you rebase? There is a lint error that I am not seeing on the current main branch.

I thought I have, that's why i didn't understand the lint error. It's done.

Copy link
Member

@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.

lgtm

Copy link
Member

@RafaelGSS RafaelGSS left a comment

Choose a reason for hiding this comment

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

LGTM.

@mcollina mcollina merged commit b2ce3d5 into fastify:main Feb 28, 2022
TimotejR pushed a commit to TimotejR/fastify that referenced this pull request Jul 1, 2022
mcollina pushed a commit that referenced this pull request Jul 1, 2022
Co-authored-by: Allain55 <geri.lengyel@gmail.com>
@github-actions
Copy link

github-actions bot commented Mar 1, 2023

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants