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

Throw on invalid status codes #4212

Open
wants to merge 26 commits into
base: 5.x
Choose a base branch
from
Open

Conversation

jonchurch
Copy link
Member

@jonchurch jonchurch commented Mar 10, 2020

Closes #3143

Will throw a RangeError if status code:

  • is less than 100
  • is greater than 999

This aligns with Node.js' behavior of throwing if given something outside that range

Will throw a TypeError if status code is:

  • Not an integer (string representation of integer included)

This is a choice we are making to limit the acceptable input.

Use res.status internally when setting status codes

the PR also ensures we use res.status internally when setting status codes, to allow us to use the validation logic internally.

Test changes

I cleaned up the tests to test acceptable range, and invalid codes, and removed the iojs logic as its not supported in v5.

TODO:

  • Update the PR description to be specific to the actual changes in this PR, possibly reopen the PR since direction has changed
    • Notably, this PR currently throws on strings, redefines the valid range of codes to between 1xx and 9xx, throws on non-integer floats (e.g. 500.5, but allows 500.00 bc it is the same to JS), throws a RangeError if we get a status code outside 1xx to 9xx range
  • Ensure the tests are accurate to these changes, and clean up the tests in here
  • Update the v5 docs to reflect said changes (separate PR to expressjs.com)

related: expressjs/discussions#233

test/res.status.js Outdated Show resolved Hide resolved
@dougwilson dougwilson added the pr label Mar 10, 2020
@jonchurch
Copy link
Member Author

I realized after opening this that Node.js does not throw on inputs like 500.5, this PR however does. From the other PR, I think we decided to throw on these cases, but I wanted to make clear that from my limited testing Node.js is not throwing on floats.

@gireeshpunathil
Copy link
Contributor

I wanted to make clear that from my limited testing Node.js is not throwing on floats.

@jonchurch - my assertion is that 500.5 is definitely invalid, so throwing is the right thing to do.

@dougwilson
Copy link
Contributor

Yea, I don't have any issues with this throwing on a float; we want to throw on whatever Node.js throws on as the minimum bar. If we can also help the users by also throwing on definitely nonsensical inputs (like status codes with fractions) that makes sense of course :) !

@jonchurch

This comment was marked as outdated.

lib/response.js Outdated Show resolved Hide resolved
@dougwilson

This comment was marked as outdated.

@gireeshpunathil
Copy link
Contributor

as per the TC discussion, this is ready to merge, who is going to do that? @dougwilson , I see your red-X on this - is that still valid, or you are going to remove it and land?

@jonchurch
Copy link
Member Author

jonchurch commented Apr 1, 2020

Thinking more about this and something bothers me. I took the approach the previous PR did, since it had been reviewed, but now I'm questioning the use of res.status internally to set statuses.

If someone monkey-patches res.status it will alter the internal behavior of setting status codes on responses. That's not different though for other functions used in response, like res.type for example.

My question has two parts:

  • Is it a breaking change to be relying on res.status() to set status codes internally?
  • Do we want to distinguish between private vs public methods? (there are only two things marked private in response according to jsdoc comments)

See an example of the change in the diff: https://github.com/expressjs/express/pull/4212/files#diff-d86a59ede7d999db4b7bc43cb25a1c11L137-R142

@dougwilson
Copy link
Contributor

Is it a breaking change to be relying on res.status() to set status codes internally?

Yes, as stated in #4223 (comment) , but this PR is already a breaking change, right? So I'm not sure if it's super relevant. The change itself makes sense to make, just like we call res.type internally and not directly get the content-type response header. Even getting headers we internally use req.get and not req.headers.

Do we want to distinguish between private vs public methods? (there are only two things marked private in response according to jsdoc comments)

I'm not really following on what this part of the question really is. The main reason the internals use Express' own API is especially useful for AOP type of design patterns, if the user so chooses to do them. The Node.js HTTP APIs do the same patterns as well, AFAIK.

@jonchurch
Copy link
Member Author

I wasn't clear. Re: breaking, I meant that someone's v4 res.status monkey patch might affect code in unexpected ways under v5, because it is used in more places than before.

You've answered my second question I believe. We aren't interested in making some methods private and off limits to users.

Thanks, I wanted to bring up this point (re: effects of monkey-patching res.status with these changes) just so someone else could check it.

Realizing that we use a lot of these helper methods internally would indicate that this change is not out of step with what is standard.

@jonchurch
Copy link
Member Author

Just read that linked comment and saw you did directly address the concern already 👍

@dougwilson
Copy link
Contributor

Yep! I did, though, not directly address the monkey patching messing something up; that is indeed the case, but I don't think any more so than other aspects of Node.js and Javascript. I think that it is going to be possible for users to override something and cause a breakage, but I'm not sure that the effort in order to prevent such a thing is really worth it. From support experience, I think it is extremely rare for such an issue to really show up, haha.

@abenhamdine
Copy link
Contributor

I think this PR should target branch https://github.com/expressjs/express/tree/5.0 and not master.
And if it's ok, it should be merged aswell in v5 branch.

@wesleytodd wesleytodd mentioned this pull request Mar 21, 2024
39 tasks
@jonchurch

This comment was marked as outdated.

@jonchurch jonchurch marked this pull request as draft April 28, 2024 03:35
@jonchurch jonchurch requested review from a team, wesleytodd and blakeembrey May 4, 2024 01:05
@jonchurch

This comment was marked as outdated.

@jonchurch jonchurch marked this pull request as ready for review May 4, 2024 02:11
deprecate('res.status(' + JSON.stringify(code) + '): use res.status(' + Math.floor(code) + ') instead')
// Check if the status code is not an integer
if (!Number.isInteger(code)) {
throw new TypeError(`Invalid status code: ${code}. Status code must be an integer.`);
Copy link
Member

Choose a reason for hiding this comment

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

Should we continue JSON.stringify here so it's easier to debug a string vs number input?

Copy link
Member Author

Choose a reason for hiding this comment

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

good idea, will implement this

};


res.status = function status(code) {
Copy link
Member

Choose a reason for hiding this comment

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

Duplicate method?

Copy link
Member Author

Choose a reason for hiding this comment

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

ty, copy paste fail at 3am

@@ -847,7 +872,7 @@ res.redirect = function redirect(url) {
});

// Respond
this.statusCode = status;
this.status(status);
Copy link
Member

Choose a reason for hiding this comment

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

Any reason for these changes? Since it's internal, presumably we already trust and can use the fast path to just assign?

Copy link
Member Author

@jonchurch jonchurch May 4, 2024

Choose a reason for hiding this comment

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

this part of the diff is lifted from the original work to land this in v4

i didn't really consider that we should drop it

this diff in v4 was largely bc we accept status as optional param (I think) in a few public methods so wanted to lock it down

will take a closer look, open to your suggestion

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good, this case is reasonable. The other one is static but I don't have a strong opinion either way, go for it.

Copy link
Member

Choose a reason for hiding this comment

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

I agree when we can easily have the fast path we should take it. In this case I doubt it is very meaningfully different but longer term it would be great to have better benchmarks in place to track how changes like this could impact the bigger perf picture.

@jonchurch jonchurch requested a review from a team May 8, 2024 22:16
Copy link
Member

@wesleytodd wesleytodd left a comment

Choose a reason for hiding this comment

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

Some of the comments are unaddressed, so didn't want to approve, but I think once those are addressed this looks good.

@@ -847,7 +872,7 @@ res.redirect = function redirect(url) {
});

// Respond
this.statusCode = status;
this.status(status);
Copy link
Member

Choose a reason for hiding this comment

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

I agree when we can easily have the fast path we should take it. In this case I doubt it is very meaningfully different but longer term it would be great to have better benchmarks in place to track how changes like this could impact the bigger perf picture.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants