Skip to content

Commit

Permalink
http: add options to http.createServer()
Browse files Browse the repository at this point in the history
This adds the optional options argument to `http.createServer()`.
It contains two options: the `IncomingMessage` and `ServerReponse`
option.

Backport-PR-URL: nodejs#20456
PR-URL: nodejs#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>
  • Loading branch information
Peter Marton authored and rvagg committed Aug 16, 2018
1 parent 0cae3c3 commit 01dc646
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 11 deletions.
15 changes: 13 additions & 2 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -1663,10 +1663,21 @@ A collection of all the standard HTTP response status codes, and the
short description of each. For example, `http.STATUS_CODES[404] === 'Not
Found'`.

## http.createServer([requestListener])
## http.createServer([options][, requestListener])
<!-- YAML
added: v0.1.13
-->
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15752
description: The `options` argument is supported now.
-->
- `options` {Object}
* `IncomingMessage` {http.IncomingMessage} Specifies the IncomingMessage class
to be used. Useful for extending the original `IncomingMessage`. Defaults
to: `IncomingMessage`
* `ServerResponse` {http.ServerResponse} Specifies the ServerResponse class to
be used. Useful for extending the original `ServerResponse`. Defaults to:
`ServerResponse`
- `requestListener` {Function}

* Returns: {http.Server}
Expand Down
4 changes: 3 additions & 1 deletion doc/api/https.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ See [`http.Server#keepAliveTimeout`][].
<!-- YAML
added: v0.3.4
-->
- `options` {Object} Accepts `options` from [`tls.createServer()`][] and [`tls.createSecureContext()`][].
- `options` {Object} Accepts `options` from [`tls.createServer()`][],
[`tls.createSecureContext()`][] and [`http.createServer()`][].
- `requestListener` {Function} A listener to be added to the `request` event.

Example:
Expand Down Expand Up @@ -255,6 +256,7 @@ const req = https.request(options, (res) => {
[`http.Server#setTimeout()`]: http.html#http_server_settimeout_msecs_callback
[`http.Server#timeout`]: http.html#http_server_timeout
[`http.Server`]: http.html#http_class_http_server
[`http.createServer()`]: http.html#httpcreateserveroptions-requestlistener
[`http.close()`]: http.html#http_server_close_callback
[`http.get()`]: http.html#http_http_get_options_callback
[`http.request()`]: http.html#http_http_request_options_callback
Expand Down
10 changes: 8 additions & 2 deletions lib/_http_common.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const {

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

const kIncomingMessage = Symbol('IncomingMessage');
const kOnHeaders = HTTPParser.kOnHeaders | 0;
const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
const kOnBody = HTTPParser.kOnBody | 0;
Expand Down Expand Up @@ -73,7 +74,11 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
parser._url = '';
}

parser.incoming = new IncomingMessage(parser.socket);
// Parser is also used by http client
var ParserIncomingMessage = parser.socket && parser.socket.server ?
parser.socket.server[kIncomingMessage] : IncomingMessage;

parser.incoming = new ParserIncomingMessage(parser.socket);
parser.incoming.httpVersionMajor = versionMajor;
parser.incoming.httpVersionMinor = versionMinor;
parser.incoming.httpVersion = `${versionMajor}.${versionMinor}`;
Expand Down Expand Up @@ -353,5 +358,6 @@ module.exports = {
freeParser,
httpSocketSetup,
methods,
parsers
parsers,
kIncomingMessage
};
23 changes: 19 additions & 4 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const {
continueExpression,
chunkExpression,
httpSocketSetup,
kIncomingMessage,
_checkInvalidHeaderChar: checkInvalidHeaderChar
} = require('_http_common');
const { OutgoingMessage } = require('_http_outgoing');
Expand All @@ -41,6 +42,9 @@ const {
defaultTriggerAsyncIdScope,
getOrSetAsyncId
} = require('internal/async_hooks');
const { IncomingMessage } = require('_http_incoming');

const kServerResponse = Symbol('ServerResponse');

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

function Server(options, requestListener) {
if (!(this instanceof Server)) return new Server(options, requestListener);

if (typeof options === 'function') {
requestListener = options;
options = {};
} else if (options == null || typeof options === 'object') {
options = util._extend({}, options);
}

this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
this[kServerResponse] = options.ServerResponse || ServerResponse;

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

if (requestListener) {
Expand Down Expand Up @@ -578,7 +592,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
}
}

var res = new ServerResponse(req);
var res = new server[kServerResponse](req);
res._onPendingData = updateOutgoingData.bind(undefined, socket, state);

res.shouldKeepAlive = keepAlive;
Expand Down Expand Up @@ -681,5 +695,6 @@ module.exports = {
STATUS_CODES,
Server,
ServerResponse,
_connectionListener: connectionListener
_connectionListener: connectionListener,
kServerResponse
};
8 changes: 6 additions & 2 deletions lib/https.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const util = require('util');
const { inherits } = util;
const debug = util.debuglog('https');
const { urlToOptions, searchParamsSymbol } = require('internal/url');
const { IncomingMessage, ServerResponse } = require('http');
const { kIncomingMessage } = require('_http_common');
const { kServerResponse } = require('_http_server');

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

tls.Server.call(this, opts, http._connectionListener);
this[kIncomingMessage] = opts.IncomingMessage || IncomingMessage;
this[kServerResponse] = opts.ServerResponse || ServerResponse;

this.httpAllowHalfOpen = false;
tls.Server.call(this, opts, http._connectionListener);

if (requestListener) {
this.addListener('request', requestListener);
Expand Down
41 changes: 41 additions & 0 deletions test/parallel/test-http-server-options-incoming-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';

/**
* This test covers http.Server({ IncomingMessage }) option:
* With IncomingMessage option the server should use
* the new class for creating req Object instead of the default
* http.IncomingMessage.
*/
const common = require('../common');
const assert = require('assert');
const http = require('http');

class MyIncomingMessage extends http.IncomingMessage {
getUserAgent() {
return this.headers['user-agent'] || 'unknown';
}
}

const server = http.Server({
IncomingMessage: MyIncomingMessage
}, common.mustCall(function(req, res) {
assert.strictEqual(req.getUserAgent(), 'node-test');
res.statusCode = 200;
res.end();
}));
server.listen();

server.on('listening', function makeRequest() {
http.get({
port: this.address().port,
headers: {
'User-Agent': 'node-test'
}
}, (res) => {
assert.strictEqual(res.statusCode, 200);
res.on('end', () => {
server.close();
});
res.resume();
});
});
35 changes: 35 additions & 0 deletions test/parallel/test-http-server-options-server-response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';

/**
* This test covers http.Server({ ServerResponse }) option:
* With ServerResponse option the server should use
* the new class for creating res Object instead of the default
* http.ServerResponse.
*/
const common = require('../common');
const assert = require('assert');
const http = require('http');

class MyServerResponse extends http.ServerResponse {
status(code) {
return this.writeHead(code, { 'Content-Type': 'text/plain' });
}
}

const server = http.Server({
ServerResponse: MyServerResponse
}, common.mustCall(function(req, res) {
res.status(200);
res.end();
}));
server.listen();

server.on('listening', function makeRequest() {
http.get({ port: this.address().port }, (res) => {
assert.strictEqual(res.statusCode, 200);
res.on('end', () => {
server.close();
});
res.resume();
});
});
51 changes: 51 additions & 0 deletions test/parallel/test-https-server-options-incoming-message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

/**
* This test covers http.Server({ IncomingMessage }) option:
* With IncomingMessage option the server should use
* the new class for creating req Object instead of the default
* http.IncomingMessage.
*/
const common = require('../common');
const fixtures = require('../common/fixtures');

if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const http = require('http');
const https = require('https');

class MyIncomingMessage extends http.IncomingMessage {
getUserAgent() {
return this.headers['user-agent'] || 'unknown';
}
}

const server = https.createServer({
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem'),
ca: fixtures.readKey('ca1-cert.pem'),
IncomingMessage: MyIncomingMessage
}, common.mustCall(function(req, res) {
assert.strictEqual(req.getUserAgent(), 'node-test');
res.statusCode = 200;
res.end();
}));
server.listen();

server.on('listening', function makeRequest() {
https.get({
port: this.address().port,
rejectUnauthorized: false,
headers: {
'User-Agent': 'node-test'
}
}, (res) => {
assert.strictEqual(res.statusCode, 200);
res.on('end', () => {
server.close();
});
res.resume();
});
});
47 changes: 47 additions & 0 deletions test/parallel/test-https-server-options-server-response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

/**
* This test covers http.Server({ ServerResponse }) option:
* With ServerResponse option the server should use
* the new class for creating res Object instead of the default
* http.ServerResponse.
*/
const common = require('../common');
const fixtures = require('../common/fixtures');

if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const http = require('http');
const https = require('https');

class MyServerResponse extends http.ServerResponse {
status(code) {
return this.writeHead(code, { 'Content-Type': 'text/plain' });
}
}

const server = https.createServer({
key: fixtures.readKey('agent1-key.pem'),
cert: fixtures.readKey('agent1-cert.pem'),
ca: fixtures.readKey('ca1-cert.pem'),
ServerResponse: MyServerResponse
}, common.mustCall(function(req, res) {
res.status(200);
res.end();
}));
server.listen();

server.on('listening', function makeRequest() {
https.get({
port: this.address().port,
rejectUnauthorized: false
}, (res) => {
assert.strictEqual(res.statusCode, 200);
res.on('end', () => {
server.close();
});
res.resume();
});
});

0 comments on commit 01dc646

Please sign in to comment.