Skip to content

Commit 62995e1

Browse files
Peter MartonMylesBorins
Peter Marton
authored andcommitted
http: add options to http.createServer()
This adds the optional options argument to `http.createServer()`. It contains two options: the `IncomingMessage` and `ServerReponse` option. Backport-PR-URL: #20456 PR-URL: #15752 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anatoli Papirovski <apapirovski@mac.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Evan Lucas <evanlucas@me.com>
1 parent 241aa14 commit 62995e1

9 files changed

+223
-11
lines changed

doc/api/http.md

+13-2
Original file line numberDiff line numberDiff line change
@@ -1663,10 +1663,21 @@ A collection of all the standard HTTP response status codes, and the
16631663
short description of each. For example, `http.STATUS_CODES[404] === 'Not
16641664
Found'`.
16651665

1666-
## http.createServer([requestListener])
1666+
## http.createServer([options][, requestListener])
16671667
<!-- YAML
16681668
added: v0.1.13
1669-
-->
1669+
changes:
1670+
- version: REPLACEME
1671+
pr-url: https://github.com/nodejs/node/pull/15752
1672+
description: The `options` argument is supported now.
1673+
-->
1674+
- `options` {Object}
1675+
* `IncomingMessage` {http.IncomingMessage} Specifies the IncomingMessage class
1676+
to be used. Useful for extending the original `IncomingMessage`. Defaults
1677+
to: `IncomingMessage`
1678+
* `ServerResponse` {http.ServerResponse} Specifies the ServerResponse class to
1679+
be used. Useful for extending the original `ServerResponse`. Defaults to:
1680+
`ServerResponse`
16701681
- `requestListener` {Function}
16711682

16721683
* Returns: {http.Server}

doc/api/https.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ See [`http.Server#keepAliveTimeout`][].
6565
<!-- YAML
6666
added: v0.3.4
6767
-->
68-
- `options` {Object} Accepts `options` from [`tls.createServer()`][] and [`tls.createSecureContext()`][].
68+
- `options` {Object} Accepts `options` from [`tls.createServer()`][],
69+
[`tls.createSecureContext()`][] and [`http.createServer()`][].
6970
- `requestListener` {Function} A listener to be added to the `request` event.
7071

7172
Example:
@@ -255,6 +256,7 @@ const req = https.request(options, (res) => {
255256
[`http.Server#setTimeout()`]: http.html#http_server_settimeout_msecs_callback
256257
[`http.Server#timeout`]: http.html#http_server_timeout
257258
[`http.Server`]: http.html#http_class_http_server
259+
[`http.createServer()`]: http.html#httpcreateserveroptions-requestlistener
258260
[`http.close()`]: http.html#http_server_close_callback
259261
[`http.get()`]: http.html#http_http_get_options_callback
260262
[`http.request()`]: http.html#http_http_request_options_callback

lib/_http_common.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const {
3434

3535
const debug = require('util').debuglog('http');
3636

37+
const kIncomingMessage = Symbol('IncomingMessage');
3738
const kOnHeaders = HTTPParser.kOnHeaders | 0;
3839
const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
3940
const kOnBody = HTTPParser.kOnBody | 0;
@@ -73,7 +74,11 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
7374
parser._url = '';
7475
}
7576

76-
parser.incoming = new IncomingMessage(parser.socket);
77+
// Parser is also used by http client
78+
var ParserIncomingMessage = parser.socket && parser.socket.server ?
79+
parser.socket.server[kIncomingMessage] : IncomingMessage;
80+
81+
parser.incoming = new ParserIncomingMessage(parser.socket);
7782
parser.incoming.httpVersionMajor = versionMajor;
7883
parser.incoming.httpVersionMinor = versionMinor;
7984
parser.incoming.httpVersion = `${versionMajor}.${versionMinor}`;
@@ -353,5 +358,6 @@ module.exports = {
353358
freeParser,
354359
httpSocketSetup,
355360
methods,
356-
parsers
361+
parsers,
362+
kIncomingMessage
357363
};

lib/_http_server.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const {
3333
continueExpression,
3434
chunkExpression,
3535
httpSocketSetup,
36+
kIncomingMessage,
3637
_checkInvalidHeaderChar: checkInvalidHeaderChar
3738
} = require('_http_common');
3839
const { OutgoingMessage } = require('_http_outgoing');
@@ -41,6 +42,9 @@ const {
4142
defaultTriggerAsyncIdScope,
4243
getOrSetAsyncId
4344
} = require('internal/async_hooks');
45+
const { IncomingMessage } = require('_http_incoming');
46+
47+
const kServerResponse = Symbol('ServerResponse');
4448

4549
const STATUS_CODES = {
4650
100: 'Continue',
@@ -260,9 +264,19 @@ function writeHead(statusCode, reason, obj) {
260264
// Docs-only deprecated: DEP0063
261265
ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead;
262266

267+
function Server(options, requestListener) {
268+
if (!(this instanceof Server)) return new Server(options, requestListener);
269+
270+
if (typeof options === 'function') {
271+
requestListener = options;
272+
options = {};
273+
} else if (options == null || typeof options === 'object') {
274+
options = util._extend({}, options);
275+
}
276+
277+
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
278+
this[kServerResponse] = options.ServerResponse || ServerResponse;
263279

264-
function Server(requestListener) {
265-
if (!(this instanceof Server)) return new Server(requestListener);
266280
net.Server.call(this, { allowHalfOpen: true });
267281

268282
if (requestListener) {
@@ -578,7 +592,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
578592
}
579593
}
580594

581-
var res = new ServerResponse(req);
595+
var res = new server[kServerResponse](req);
582596
res._onPendingData = updateOutgoingData.bind(undefined, socket, state);
583597

584598
res.shouldKeepAlive = keepAlive;
@@ -681,5 +695,6 @@ module.exports = {
681695
STATUS_CODES,
682696
Server,
683697
ServerResponse,
684-
_connectionListener: connectionListener
698+
_connectionListener: connectionListener,
699+
kServerResponse
685700
};

lib/https.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ const util = require('util');
3030
const { inherits } = util;
3131
const debug = util.debuglog('https');
3232
const { urlToOptions, searchParamsSymbol } = require('internal/url');
33+
const { IncomingMessage, ServerResponse } = require('http');
34+
const { kIncomingMessage } = require('_http_common');
35+
const { kServerResponse } = require('_http_server');
3336

3437
function Server(opts, requestListener) {
3538
if (!(this instanceof Server)) return new Server(opts, requestListener);
@@ -51,9 +54,10 @@ function Server(opts, requestListener) {
5154
opts.ALPNProtocols = ['http/1.1'];
5255
}
5356

54-
tls.Server.call(this, opts, http._connectionListener);
57+
this[kIncomingMessage] = opts.IncomingMessage || IncomingMessage;
58+
this[kServerResponse] = opts.ServerResponse || ServerResponse;
5559

56-
this.httpAllowHalfOpen = false;
60+
tls.Server.call(this, opts, http._connectionListener);
5761

5862
if (requestListener) {
5963
this.addListener('request', requestListener);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
3+
/**
4+
* This test covers http.Server({ IncomingMessage }) option:
5+
* With IncomingMessage option the server should use
6+
* the new class for creating req Object instead of the default
7+
* http.IncomingMessage.
8+
*/
9+
const common = require('../common');
10+
const assert = require('assert');
11+
const http = require('http');
12+
13+
class MyIncomingMessage extends http.IncomingMessage {
14+
getUserAgent() {
15+
return this.headers['user-agent'] || 'unknown';
16+
}
17+
}
18+
19+
const server = http.Server({
20+
IncomingMessage: MyIncomingMessage
21+
}, common.mustCall(function(req, res) {
22+
assert.strictEqual(req.getUserAgent(), 'node-test');
23+
res.statusCode = 200;
24+
res.end();
25+
}));
26+
server.listen();
27+
28+
server.on('listening', function makeRequest() {
29+
http.get({
30+
port: this.address().port,
31+
headers: {
32+
'User-Agent': 'node-test'
33+
}
34+
}, (res) => {
35+
assert.strictEqual(res.statusCode, 200);
36+
res.on('end', () => {
37+
server.close();
38+
});
39+
res.resume();
40+
});
41+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
/**
4+
* This test covers http.Server({ ServerResponse }) option:
5+
* With ServerResponse option the server should use
6+
* the new class for creating res Object instead of the default
7+
* http.ServerResponse.
8+
*/
9+
const common = require('../common');
10+
const assert = require('assert');
11+
const http = require('http');
12+
13+
class MyServerResponse extends http.ServerResponse {
14+
status(code) {
15+
return this.writeHead(code, { 'Content-Type': 'text/plain' });
16+
}
17+
}
18+
19+
const server = http.Server({
20+
ServerResponse: MyServerResponse
21+
}, common.mustCall(function(req, res) {
22+
res.status(200);
23+
res.end();
24+
}));
25+
server.listen();
26+
27+
server.on('listening', function makeRequest() {
28+
http.get({ port: this.address().port }, (res) => {
29+
assert.strictEqual(res.statusCode, 200);
30+
res.on('end', () => {
31+
server.close();
32+
});
33+
res.resume();
34+
});
35+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
3+
/**
4+
* This test covers http.Server({ IncomingMessage }) option:
5+
* With IncomingMessage option the server should use
6+
* the new class for creating req Object instead of the default
7+
* http.IncomingMessage.
8+
*/
9+
const common = require('../common');
10+
const fixtures = require('../common/fixtures');
11+
12+
if (!common.hasCrypto)
13+
common.skip('missing crypto');
14+
15+
const assert = require('assert');
16+
const http = require('http');
17+
const https = require('https');
18+
19+
class MyIncomingMessage extends http.IncomingMessage {
20+
getUserAgent() {
21+
return this.headers['user-agent'] || 'unknown';
22+
}
23+
}
24+
25+
const server = https.createServer({
26+
key: fixtures.readKey('agent1-key.pem'),
27+
cert: fixtures.readKey('agent1-cert.pem'),
28+
ca: fixtures.readKey('ca1-cert.pem'),
29+
IncomingMessage: MyIncomingMessage
30+
}, common.mustCall(function(req, res) {
31+
assert.strictEqual(req.getUserAgent(), 'node-test');
32+
res.statusCode = 200;
33+
res.end();
34+
}));
35+
server.listen();
36+
37+
server.on('listening', function makeRequest() {
38+
https.get({
39+
port: this.address().port,
40+
rejectUnauthorized: false,
41+
headers: {
42+
'User-Agent': 'node-test'
43+
}
44+
}, (res) => {
45+
assert.strictEqual(res.statusCode, 200);
46+
res.on('end', () => {
47+
server.close();
48+
});
49+
res.resume();
50+
});
51+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
/**
4+
* This test covers http.Server({ ServerResponse }) option:
5+
* With ServerResponse option the server should use
6+
* the new class for creating res Object instead of the default
7+
* http.ServerResponse.
8+
*/
9+
const common = require('../common');
10+
const fixtures = require('../common/fixtures');
11+
12+
if (!common.hasCrypto)
13+
common.skip('missing crypto');
14+
15+
const assert = require('assert');
16+
const http = require('http');
17+
const https = require('https');
18+
19+
class MyServerResponse extends http.ServerResponse {
20+
status(code) {
21+
return this.writeHead(code, { 'Content-Type': 'text/plain' });
22+
}
23+
}
24+
25+
const server = https.createServer({
26+
key: fixtures.readKey('agent1-key.pem'),
27+
cert: fixtures.readKey('agent1-cert.pem'),
28+
ca: fixtures.readKey('ca1-cert.pem'),
29+
ServerResponse: MyServerResponse
30+
}, common.mustCall(function(req, res) {
31+
res.status(200);
32+
res.end();
33+
}));
34+
server.listen();
35+
36+
server.on('listening', function makeRequest() {
37+
https.get({
38+
port: this.address().port,
39+
rejectUnauthorized: false
40+
}, (res) => {
41+
assert.strictEqual(res.statusCode, 200);
42+
res.on('end', () => {
43+
server.close();
44+
});
45+
res.resume();
46+
});
47+
});

0 commit comments

Comments
 (0)