Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ var finalhandler = require('finalhandler');
var methods = require('methods');
var debug = require('debug')('express:application');
var View = require('./view');
var http = require('http');
var compileETag = require('./utils').compileETag;
var compileQueryParser = require('./utils').compileQueryParser;
var compileTrust = require('./utils').compileTrust;
Expand Down Expand Up @@ -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)
Copy link
Member

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.

Copy link
Member Author

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?

var args = Array.prototype.slice.call(arguments)
if (typeof args[args.length - 1] === 'function') {
var done = args[args.length - 1] = once(args[args.length - 1])
Expand Down
41 changes: 35 additions & 6 deletions lib/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,30 @@ var Router = require('router');
var req = require('./request');
var res = require('./response');

/**
* Only reqire http when requested
*/
var http = {}
var _http
Object.defineProperty(http, 'IncomingMessage', {
get: function () {
_http = _http || require('http')
return _http.IncomingMessage
}
})
Object.defineProperty(http, 'ServerResponse', {
get: function () {
_http = _http || require('http')
return _http.ServerResponse
}
})
Object.defineProperty(http, 'createServer', {
get: function () {
_http = _http || require('http')
return _http.createServer
}
})

/**
* Expose `createApplication()`.
*/
Expand All @@ -33,21 +57,26 @@ exports = module.exports = createApplication;
* @api public
*/

function createApplication() {
function createApplication (opts) {
var options = opts || {}
options.reqProto = options.reqProto || http.IncomingMessage.prototype
options.resProto = options.resProto || http.ServerResponse.prototype
options.createServer = options.createServer || http.createServer
Copy link
Member

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().

Copy link
Member Author

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.


var app = function(req, res, next) {
app.handle(req, res, next);
};

mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);

app.createServer = options.createServer
// expose the prototype that will get set on requests
app.request = Object.create(req, {
app.request = Object.create(req(options.reqProto), {
Copy link
Member

@blakeembrey blakeembrey May 22, 2024

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.

Copy link
Member Author

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.

app: { configurable: true, enumerable: true, writable: true, value: app }
})

// expose the prototype that will get set on responses
app.response = Object.create(res, {
app.response = Object.create(res(options.resProto), {
app: { configurable: true, enumerable: true, writable: true, value: app }
})

Expand All @@ -60,8 +89,8 @@ function createApplication() {
*/

exports.application = proto;
exports.request = req;
exports.response = res;
exports.request = req.proto
exports.response = res.proto
Copy link
Member

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?

Copy link
Member Author

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.


/**
* Expose constructors.
Expand Down
10 changes: 5 additions & 5 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@
var accepts = require('accepts');
var isIP = require('net').isIP;
var typeis = require('type-is');
var http = require('http');
var fresh = require('fresh');
var parseRange = require('range-parser');
var parse = require('parseurl');
var proxyaddr = require('proxy-addr');
var setPrototypeOf = require('setprototypeof')

/**
* Request prototype.
* @public
*/

var req = Object.create(http.IncomingMessage.prototype)
exports = module.exports = function (proto) {
return setPrototypeOf(req, proto)
Copy link
Member

@blakeembrey blakeembrey May 22, 2024

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.

Copy link
Member Author

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.

}

/**
* Module exports.
* @public
*/

module.exports = req
var req = module.exports.proto = {}

/**
* Return request header.
Expand Down
10 changes: 5 additions & 5 deletions lib/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ var createError = require('http-errors')
var deprecate = require('depd')('express');
var encodeUrl = require('encodeurl');
var escapeHtml = require('escape-html');
var http = require('http');
var onFinished = require('on-finished');
var mime = require('mime-types')
var path = require('path');
Expand All @@ -34,20 +33,21 @@ var send = require('send');
var extname = path.extname;
var resolve = path.resolve;
var vary = require('vary');
var setPrototypeOf = require('setprototypeof')

/**
* Response prototype.
* @public
*/

var res = Object.create(http.ServerResponse.prototype)
exports = module.exports = function (proto) {
return setPrototypeOf(res, proto)
}

/**
* Module exports.
* @public
*/

module.exports = res
var res = module.exports.proto = {}

/**
* Module variables.
Expand Down