From 2c22a16309aaeef8ccaf1f858d64793020e588bc Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Tue, 25 Aug 2015 16:41:21 +0300 Subject: [PATCH 01/10] added before-flushing-head event to _http_server.ServerResponse.writeHead --- lib/_http_server.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/_http_server.js b/lib/_http_server.js index f337480ec2dd75..0de14df2cb92cb 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -160,6 +160,8 @@ ServerResponse.prototype._implicitHeader = function() { ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { var headers; + this.emit("before-flushing-head"); + if (typeof reason === 'string') { // writeHead(statusCode, reasonPhrase[, headers]) this.statusMessage = reason; From 5c774e0fb684acf74ea59e661739bab2083c46e3 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Wed, 26 Aug 2015 11:50:00 +0300 Subject: [PATCH 02/10] added docs for before-flushing-head --- doc/api/http.markdown | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 8ab9877a2e536b..5dd0964e71e1f1 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -640,6 +640,14 @@ 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: 'before-flushing-head' + +`function () {}` + +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. + ### Event: 'close' `function () { }` From a58a58e8ad6e053426ea6483095f87c9b2417ac0 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Wed, 26 Aug 2015 12:28:57 +0300 Subject: [PATCH 03/10] added test for before-flush-head event of HTTP response --- .../test-http-before-flush-head-event.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/parallel/test-http-before-flush-head-event.js diff --git a/test/parallel/test-http-before-flush-head-event.js b/test/parallel/test-http-before-flush-head-event.js new file mode 100644 index 00000000000000..145ddba2c1530a --- /dev/null +++ b/test/parallel/test-http-before-flush-head-event.js @@ -0,0 +1,40 @@ +'use strict'; +//var common = require('../common'); +var common = {}; +common.PORT = 1234; +var assert = require('assert'); +var http = require('http'); + +var testResBody = 'other stuff!\n'; + +var server = http.createServer(function(req, res) { + res.on('before-flush-head', function() { + res.setHeader('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('flush-head' in res.headers, + 'Response headers didn\'t contain the flush-head header, indicating the before-flush-head 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 before-flush-head event was not called or did not allow adding headers.'); + res.addListener('end', function() { + server.close(); + process.exit(); + }); + res.resume(); + }); + req.end(); +}); From ce6d84962b90579c4473b21b7ee770b3463d7bfe Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Thu, 27 Aug 2015 14:36:42 +0300 Subject: [PATCH 04/10] extended the before-flushing-head event to enable customizing the response code, response message nad headers --- doc/api/http.markdown | 11 ++++++-- lib/_http_server.js | 25 +++++++++++-------- .../test-http-before-flush-head-event.js | 14 +++++++---- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 5dd0964e71e1f1..aee7ec27add28f 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -642,11 +642,18 @@ The response implements the [Writable Stream][] interface. This is an ### Event: 'before-flushing-head' -`function () {}` +`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. +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} ### Event: 'close' diff --git a/lib/_http_server.js b/lib/_http_server.js index 0de14df2cb92cb..0e4114120aa2f6 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -160,18 +160,15 @@ ServerResponse.prototype._implicitHeader = function() { ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { var headers; - this.emit("before-flushing-head"); + // writeHead(statusCode, reasonPhrase[, headers]) + var args = {statusCode: statusCode, statusMessage: reason, headers: obj}; - if (typeof reason === 'string') { - // writeHead(statusCode, reasonPhrase[, headers]) - this.statusMessage = reason; - } else { + 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; if (this._headers) { // Slow-case: when progressive API and header fields are passed. @@ -183,13 +180,19 @@ ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { } } // only progressive api is used - headers = this._renderHeaders(); + args.headers = this._renderHeaders(); } else { // only writeHead() called - headers = obj; + args.headers = obj || {}; } - var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' + + this.emit("before-flushing-head", args); + + this.statusMessage = args.statusMessage; + this.statusCode = args.statusCode; + headers = args.headers; + + var statusLine = 'HTTP/1.1 ' + this.statusCode.toString() + ' ' + this.statusMessage + CRLF; if (statusCode === 204 || statusCode === 304 || diff --git a/test/parallel/test-http-before-flush-head-event.js b/test/parallel/test-http-before-flush-head-event.js index 145ddba2c1530a..dcca7ccf818c71 100644 --- a/test/parallel/test-http-before-flush-head-event.js +++ b/test/parallel/test-http-before-flush-head-event.js @@ -1,15 +1,15 @@ 'use strict'; -//var common = require('../common'); -var common = {}; -common.PORT = 1234; +var common = require('../common'); var assert = require('assert'); var http = require('http'); var testResBody = 'other stuff!\n'; var server = http.createServer(function(req, res) { - res.on('before-flush-head', function() { - res.setHeader('Flush-Head', 'event-was-called'); + res.on('before-flushing-head', 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' @@ -26,6 +26,10 @@ server.addListener('listening', function() { 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 before-flush-head event was not called or did not allow adding headers.'); assert.ok(res.headers['flush-head'] === 'event-was-called', From 335db4c310fed78e54c09905295bdac8d5bf3aa9 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Tue, 17 Nov 2015 00:02:02 +0200 Subject: [PATCH 05/10] renamed before-flushing-head to beforeFlushingHead --- doc/api/http.markdown | 2 +- lib/_http_server.js | 2 +- test/parallel/test-http-before-flush-head-event.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/http.markdown b/doc/api/http.markdown index aee7ec27add28f..53b351cbb75834 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -640,7 +640,7 @@ 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: 'before-flushing-head' +### Event: 'beforeFlushingHead' `function (messageHead) {}` diff --git a/lib/_http_server.js b/lib/_http_server.js index 0e4114120aa2f6..f251d8b3b587c2 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -186,7 +186,7 @@ ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { args.headers = obj || {}; } - this.emit("before-flushing-head", args); + this.emit("beforeFlushingHead", args); this.statusMessage = args.statusMessage; this.statusCode = args.statusCode; diff --git a/test/parallel/test-http-before-flush-head-event.js b/test/parallel/test-http-before-flush-head-event.js index dcca7ccf818c71..4bc7149abbff1e 100644 --- a/test/parallel/test-http-before-flush-head-event.js +++ b/test/parallel/test-http-before-flush-head-event.js @@ -6,7 +6,7 @@ var http = require('http'); var testResBody = 'other stuff!\n'; var server = http.createServer(function(req, res) { - res.on('before-flushing-head', function(args) { + res.on('beforeFlushingHead', function(args) { args.statusCode = 201; args.statusMessage = "changed to show we can"; args.headers["Flush-Head"] = 'event-was-called'; @@ -31,9 +31,9 @@ server.addListener('listening', function() { 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 before-flush-head event was not called or did not allow adding 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 before-flush-head event was not called or did not allow adding headers.'); + '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(); From 76620e4a612828ed9577c360c47cea337d192880 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Tue, 8 Dec 2015 11:15:26 +0200 Subject: [PATCH 06/10] code style issues --- .../test-http-before-flush-head-event.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/parallel/test-http-before-flush-head-event.js b/test/parallel/test-http-before-flush-head-event.js index 4bc7149abbff1e..aad0fbc2d697b8 100644 --- a/test/parallel/test-http-before-flush-head-event.js +++ b/test/parallel/test-http-before-flush-head-event.js @@ -1,15 +1,15 @@ 'use strict'; -var common = require('../common'); -var assert = require('assert'); -var http = require('http'); +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'; + args.statusMessage = 'changed to show we can'; + args.headers['Flush-Head'] = 'event-was-called'; }) res.writeHead(200, { 'Content-Type': 'text/plain' @@ -31,9 +31,14 @@ server.addListener('listening', function() { 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.'); + '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.'); + '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(); From df3f243c67f207551a70537558430bb63709d478 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Tue, 8 Dec 2015 11:15:59 +0200 Subject: [PATCH 07/10] fixed broken test parallel/test-http-write-head.js --- lib/_http_server.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index f251d8b3b587c2..900fec761eb778 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -163,20 +163,22 @@ ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { // writeHead(statusCode, reasonPhrase[, headers]) var args = {statusCode: statusCode, statusMessage: reason, headers: obj}; - if (!(typeof args.statusMessage === 'string')) { + if (typeof args.statusMessage !== 'string') { // writeHead(statusCode[, headers]) args.statusMessage = this.statusMessage || STATUS_CODES[statusCode] || 'unknown'; args.headers = reason; } + 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 From 118a0a8e63f531451f551129c4b5da84da251786 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Tue, 8 Dec 2015 11:29:33 +0200 Subject: [PATCH 08/10] fixed bug in handling the case when _header is not set. --- lib/_http_server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index 900fec761eb778..88ee3372098e2f 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -185,7 +185,7 @@ ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { args.headers = this._renderHeaders(); } else { // only writeHead() called - args.headers = obj || {}; + } this.emit("beforeFlushingHead", args); From 84ce98ce0d8edb7a8fe80e3ab5c34a69aa8a3c33 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Tue, 8 Dec 2015 11:32:34 +0200 Subject: [PATCH 09/10] js lint - code formatting, etc. --- lib/_http_server.js | 5 +---- test/parallel/test-http-before-flush-head-event.js | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index 88ee3372098e2f..a3e03846348780 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -183,12 +183,9 @@ ServerResponse.prototype.writeHead = function(statusCode, reason, obj) { } // only progressive api is used args.headers = this._renderHeaders(); - } else { - // only writeHead() called - } - this.emit("beforeFlushingHead", args); + this.emit('beforeFlushingHead', args); this.statusMessage = args.statusMessage; this.statusCode = args.statusCode; diff --git a/test/parallel/test-http-before-flush-head-event.js b/test/parallel/test-http-before-flush-head-event.js index aad0fbc2d697b8..e5e11ab8fd9ea4 100644 --- a/test/parallel/test-http-before-flush-head-event.js +++ b/test/parallel/test-http-before-flush-head-event.js @@ -10,7 +10,7 @@ var server = http.createServer(function(req, res) { args.statusCode = 201; args.statusMessage = 'changed to show we can'; args.headers['Flush-Head'] = 'event-was-called'; - }) + }); res.writeHead(200, { 'Content-Type': 'text/plain' }); @@ -28,7 +28,7 @@ server.addListener('listening', function() { 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', + 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, ' + From 58bd0b3ee917d17fcccc78c12e17cfa9a6ca59a2 Mon Sep 17 00:00:00 2001 From: "yoav@wix.com" Date: Tue, 8 Dec 2015 12:15:10 +0200 Subject: [PATCH 10/10] js lint - code formatting, etc. --- doc/api/http.markdown | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/api/http.markdown b/doc/api/http.markdown index 53b351cbb75834..e92fd38b406707 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -653,7 +653,12 @@ It includes * `responseCode` {Number} * `responseMessage` {String} -* `headers` {Object} +* `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'