-
-
Notifications
You must be signed in to change notification settings - Fork 16.9k
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
Remove direct http dependency #3213
Conversation
20830c8
to
94cfec6
Compare
I've removed uws.http.getRequestPrototype() and uws.http.getResponsePrototype() and instead I provide var app = uws.http.getExpressApp(express) so this won't work anymore |
Notes:
|
@alexhultman Hmm, well it seems to me to be a better api for your project to not have to know about express, and for express to just support arbitrary extensions like in this PR. With |
94cfec6
to
10fe874
Compare
Lol, tests that hit google.com and rely on a specific html response? Should I fix these brittle tests? Remove them? Or ignore the failures for this PR? |
I'll add them back then. |
@alexhultman You might want to wait until we get some feedback from the greater @expressjs team and community, the above is merely my opinion :) |
Too late I already added them again |
The test failures are just from a conflict with 2f8ac67 in which the test is expecting the same code names from the |
10fe874
to
ad70cbd
Compare
Updated to move the status stuff into a separate PR, but left the commit in here just so it is clear the intent. Now that I look I see you did say against 4, so I will update that. One other thing we should have open for discussion is the idea of passing options to |
ad70cbd
to
ce4a7fb
Compare
Last comment, I think this PR might also be able to land of 4.x. It passes all the tests, and is really just a minor point addition for the new options api. |
ce4a7fb
to
0e0a8e7
Compare
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.
Neat 👍
lib/express.js
Outdated
app.response = { __proto__: res, app: app }; | ||
app.createServer = opts.createServer | ||
app.request = setPrototypeOf({ app: app }, req(opts.reqProto)) | ||
app.response = setPrototypeOf({ app: app }, res(opts.resProto)) |
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 believe these two lines could just be Object.create(proto, descriptorsWithJustApp)
, right?
app.response = Object.create(res(opts.resProto), {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
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.
Fixed.
lib/express.js
Outdated
@@ -32,16 +57,22 @@ exports = module.exports = createApplication; | |||
* @api public | |||
*/ | |||
|
|||
function createApplication() { | |||
function createApplication(opts) { | |||
opts = opts || {} |
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.
Probably don't want to reassign values linked to arguments, since it silently alters arguments[0]
. Usually I would just name the argument options
and this line as var opts = options || {}
$ node -pe '(function(foo){foo=foo||{};return arguments[0] === null}(null))'
false
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.
Good call, fixed.
lib/express.js
Outdated
opts = opts || {} | ||
opts.reqProto = opts.reqProto || http.IncomingMessage.prototype | ||
opts.resProto = opts.resProto || http.ServerResponse.prototype | ||
opts.createServer = opts.createServer || http.createServer |
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 not best practice to alter the object someone is passing in; the object could potentially be frozen, even, causing an exception here. var createServer = opts.createServer || http.createServer
is probably fine (and then changing the vars below).
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.
Fixed.
cab07fb
to
d66bba4
Compare
@dougwilson Any way we can get this merged? |
H @mjsalinger before merging, the CI should at least pass for the PR. |
@mjsalinger Could you review those CI failures and address if needed? |
I have not had a chance to look at the failures in this PR, which I think needs a bit of revision in respect to the discussion here. |
what the time line on this |
Hey @LucyMaber, I know this is likely not the timeline you hoped for, but we are now trying to organize an effort to land these before the final v5 release (see the issue mention right above this). If you are still looking for this, I would love if you could post why you wanted it. It is hard enough to pick up work we thought was valuable back then, but it is even harder to decide if it is still worth doing when most of the comments do not contain enough information about why folks asked for this. |
@kibertoad I started scrolling up and should have waited to post that to also mention you! Can you help give a bit of context on what this change helps with? I know it was something which helped me in 2017, but I am not sure I really understand why this might still be helpful in 2024. |
@wesleytodd If I'm not mistaken, Node.js wasn't able to remove certain http warts precisely because express relied on them. If that helps to unblock Node.js, I'm all for it. |
This particular change was a bit unrelated to that. It was more about enabling non-node core (but api compatible) implementations. Sadly, until we can get the api's in place and stop monkey-patching I will work on the other pending todo topics first, and hopefully we can make a decision on if landing this is a good idea in the mean time. |
I have decided this is worth landing. The main reason I think so is because it gives us an interesting opportunity to experiment with the |
bf3b980
to
6e32dae
Compare
6e32dae
to
aa90fc8
Compare
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 think this requires more thought before we land it for 5.x because I don't think it's the ideal API to achieve the goal, and I wouldn't want to churn it again for 6.x 🤷
exports.request = req; | ||
exports.response = res; | ||
exports.request = req.proto | ||
exports.response = res.proto |
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 think these exports are meaningless now because they're just empty objects?
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.
Yeah probably, hard to remember if I had even looked at that since it was so long ago, but I think you are right. I will hold off on addressing these because the other review comments are much more important for us to address first imo.
|
||
var req = Object.create(http.IncomingMessage.prototype) | ||
exports = module.exports = function (proto) { | ||
return setPrototypeOf(req, proto) |
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.
It's almost functionally the same for a single server, but this would be clearer by making all of req
a function that returns the existing behavior (and can have multiple instances created). My main concern with setPrototypeOf
here is that you'd be changing the prototype if you actually intended to use this feature to run two versions of e.g. HTTP1 and HTTP2 servers. This seems like a bit of a foot gun for anyone running tests, especially if they tried to parallelize or something.
Edit: This isn't required if you instead do the mixin
approach in createApplication
instead.
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.
Yeah I agree with this including the edit. The use case of multiple servers was always bad with express patching things though so I am not sure this makes it worse, but the mixin way seems like it would make it better in general. I am +1 for going that route and calling these other comments resolved.
var options = opts || {} | ||
options.reqProto = options.reqProto || http.IncomingMessage.prototype | ||
options.resProto = options.resProto || http.ServerResponse.prototype | ||
options.createServer = options.createServer || http.createServer |
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.
Instead of making the http
object above, why not just require('http')
here instead? You'd avoid three requires. Instead, since all 3 of these options should be expected to be set together (why would you mix http native with implementation?), just make them all required or fallback on something like var options = opts || getHttpOptions()
.
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 like this idea and honestly any use cases where you might use node's server but custom req/res is probably not really that viable anyway. Maybe to nitpick the name idea though, what about overrideHttp()
or something in that vien? My idea here is that it is an it should be clear you shouldn't use this unless you really want to override something which is more of a power user. I was trying to think of a name that sends that message more without being over done.
@@ -607,7 +606,7 @@ app.render = function render(name, options, callback) { | |||
*/ | |||
|
|||
app.listen = function listen () { | |||
var server = http.createServer(this) | |||
var server = this.createServer(this) |
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.
Honestly I think this should just deprecate this in favor of people explicitly writing http.createServer(app).listen(...)
. It also removes one more option from the createApplication
options which is only ever used once here. It took me a while to follow the indirect assignments around and I think that's bad for developers and users to understanding what's going on.
But I do understand why it's done after 30 minutes of review and won't block on this issue. I would be in favor of refactoring this to assign app.listen
within the create function instead. That way there's less indirect dependencies that's hard to reason about and createServer
is used right where it's defined.
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 would be in favor of refactoring this to assign app.listen within the create function instead.
Of the options to achieve this I think this one is my preference. I believe any of these loose the once
behavior, but I am not sure it makes a big difference but I agree simplifying things like this is good. I honestly cannot remember if there used to be cases where the server could emit error
events more than once, but maybe that was an old node behavior?
// expose the prototype that will get set on requests | ||
app.request = Object.create(req, { | ||
app.request = Object.create(req(options.reqProto), { |
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 not Object.create(options.reqProto, ...)
and then mixin(app.request, req)
instead? Seems easier to follow and similar performance implications since this is only initialized once.
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 idea what I was thinking when I wrote this. I agree that feels more clear to read. I cannot make time to make these changes now but I will when I get time to revisit this pr.
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.
We also need some tests for any of this behavior, including the concerns around prototype re-assignment not allowing multiple app instances to exist.
Can we get at least https
tests and/or a simple proxy example of how someone would use this with e.g. one method working?
Based on @blakeembrey's feedback (which I very much agree with) I think we should target this for v6. We are trying to aggressively cut scope so we can actually ship v5 and this is very much a "nice to have" not a "requirement". |
I think this might resolve the issues brought up in #2812 and #3206, and also some of what we talked about at the most recent TC meeting for compat in the browser. The goal here is that you can do:
In the long run, I think this is also what I need for my Nighthawk project as well, so IMO it is a good way forward. But let me know your thoughts!
CC: @dougwilson, @alexhultman, @blakeembrey