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

added before-flushing-head event to _http_server.ServerResponse.writeHead #2538

Closed
wants to merge 10 commits into from
20 changes: 20 additions & 0 deletions doc/api/http.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,26 @@ passed as the second parameter to the `'request'` event.
The response implements the [Writable Stream][] interface. This is an
[`EventEmitter`][] with the following events:

### Event: 'beforeFlushingHead'

`function (messageHead) {}`

Indicates that the server is about to flush the HTTP headers to the underlying
socket. This event can be used for measuring time to first byte or customizing
the response headers and status code.

The `messageHead` parameter allows overriding the http response code, message and headers.
It includes

* `responseCode` {Number}
* `responseMessage` {String}
* `headers` {Object|Array}

The headers member will be either an object of the form
`{header-names: header value}`
or an array of the form
`[['header-name', 'header-value'] ...]`

### Event: 'close'

`function () { }`
Expand Down
34 changes: 19 additions & 15 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,34 +160,38 @@ ServerResponse.prototype._implicitHeader = function() {
ServerResponse.prototype.writeHead = function(statusCode, reason, obj) {
var headers;

if (typeof reason === 'string') {
// writeHead(statusCode, reasonPhrase[, headers])
this.statusMessage = reason;
} else {
// writeHead(statusCode, reasonPhrase[, headers])
var args = {statusCode: statusCode, statusMessage: reason, headers: obj};

if (typeof args.statusMessage !== 'string') {
// writeHead(statusCode[, headers])
this.statusMessage =
args.statusMessage =
this.statusMessage || STATUS_CODES[statusCode] || 'unknown';
obj = reason;
args.headers = reason;
}
this.statusCode = statusCode;

args.headers = args.headers || {};

if (this._headers) {
// Slow-case: when progressive API and header fields are passed.
if (obj) {
var keys = Object.keys(obj);
if (args.headers) {
var keys = Object.keys(args.headers);
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
if (k) this.setHeader(k, obj[k]);
if (k) this.setHeader(k, args.headers[k]);
}
}
// only progressive api is used
headers = this._renderHeaders();
} else {
// only writeHead() called
headers = obj;
args.headers = this._renderHeaders();
}

var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' +
this.emit('beforeFlushingHead', args);

this.statusMessage = args.statusMessage;
this.statusCode = args.statusCode;
headers = args.headers;

var statusLine = 'HTTP/1.1 ' + this.statusCode.toString() + ' ' +
Copy link
Member

Choose a reason for hiding this comment

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

Could possibly get away with using a template string here now...

var statusLine = 
  `HTTP/1.1 ${this.statuscode} ${this.statusMessage}` + CRLF;

(could but not required, of course)

this.statusMessage + CRLF;

if (statusCode === 204 || statusCode === 304 ||
Expand Down
49 changes: 49 additions & 0 deletions test/parallel/test-http-before-flush-head-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';
const common = require('../common'),
assert = require('assert'),
http = require('http');

var testResBody = 'other stuff!\n';

var server = http.createServer(function(req, res) {
res.on('beforeFlushingHead', function(args) {
args.statusCode = 201;
args.statusMessage = 'changed to show we can';
args.headers['Flush-Head'] = 'event-was-called';
});
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end(testResBody);
});
server.listen(common.PORT);


server.addListener('listening', function() {
var options = {
port: common.PORT,
path: '/',
method: 'GET'
};
var req = http.request(options, function(res) {
assert.ok(res.statusCode === 201,
'Response status code was not overridden from 200 to 201.');
assert.ok(res.statusMessage === 'changed to show we can',
'Response status message was not overridden.');
assert.ok('flush-head' in res.headers,
'Response headers didn\'t contain the flush-head header, ' +
'indicating the beforeFlushingHead event was not called or ' +
'did not allow adding headers.');
assert.ok(res.headers['flush-head'] === 'event-was-called',
'Response headers didn\'t contain the flush-head header ' +
'with value event-was-called, indicating the ' +
'beforeFlushingHead event was not called or did not allow ' +
'adding headers.');
res.addListener('end', function() {
server.close();
process.exit();
});
res.resume();
});
req.end();
});