diff --git a/doc/api/http.markdown b/doc/api/http.markdown index e730b1c860..70be739b2e 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -344,6 +344,13 @@ or response.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]); +### response.sendDate + +When true, the Date header will be automatically generated and sent in +the response if it is not already present in the headers. Defaults to true. + +This should only be disabled for testing; HTTP requires the Date header +in responses. ### response.getHeader(name) diff --git a/lib/http.js b/lib/http.js index 02f631e107..a0745f0b99 100644 --- a/lib/http.js +++ b/lib/http.js @@ -226,9 +226,22 @@ var transferEncodingExpression = /Transfer-Encoding/i; var closeExpression = /close/i; var chunkExpression = /chunk/i; var contentLengthExpression = /Content-Length/i; +var dateExpression = /Date/i; var expectExpression = /Expect/i; var continueExpression = /100-continue/i; +var dateCache; +function utcDate() { + if (! dateCache) { + var d = new Date(); + dateCache = d.toUTCString(); + setTimeout(function () { + dateCache = undefined; + }, 1000 - d.getMilliseconds()); + } + return dateCache; +} + /* Abstract base class for ServerRequest and ClientResponse. */ function IncomingMessage(socket) { @@ -383,6 +396,7 @@ function OutgoingMessage() { this.chunkedEncoding = false; this.shouldKeepAlive = true; this.useChunkedEncodingByDefault = true; + this.sendDate = false; this._hasBody = true; this._trailer = ''; @@ -473,6 +487,7 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { var sentConnectionHeader = false; var sentContentLengthHeader = false; var sentTransferEncodingHeader = false; + var sentDateHeader = false; var sentExpect = false; // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n' @@ -498,7 +513,8 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { } else if (contentLengthExpression.test(field)) { sentContentLengthHeader = true; - + } else if (dateExpression.test(field)) { + sentDateHeader = true; } else if (expectExpression.test(field)) { sentExpect = true; } @@ -529,6 +545,11 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { } } + // Date header + if (this.sendDate == true && sentDateHeader == false) { + messageHeader += "Date: " + utcDate() + CRLF; + } + // keep-alive logic if (sentConnectionHeader === false) { if (this.shouldKeepAlive && @@ -816,6 +837,8 @@ function ServerResponse(req) { if (req.method === 'HEAD') this._hasBody = false; + this.sendDate = true; + if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) { this.useChunkedEncodingByDefault = false; this.shouldKeepAlive = false; diff --git a/test/simple/test-http-1.0.js b/test/simple/test-http-1.0.js index fadaaf0f1e..f5f3aef0a6 100644 --- a/test/simple/test-http-1.0.js +++ b/test/simple/test-http-1.0.js @@ -104,6 +104,7 @@ function test(handler, request_generator, response_validator) { assert.equal('1.0', req.httpVersion); assert.equal(1, req.httpVersionMajor); assert.equal(0, req.httpVersionMinor); + res.sendDate = false; res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('Hello, '); res._send(''); res.write('world!'); res._send(''); @@ -140,6 +141,7 @@ function test(handler, request_generator, response_validator) { assert.equal('1.1', req.httpVersion); assert.equal(1, req.httpVersionMajor); assert.equal(1, req.httpVersionMinor); + res.sendDate = false; res.writeHead(200, {'Content-Type': 'text/plain'}); res.write('Hello, '); res._send(''); res.write('world!'); res._send(''); diff --git a/test/simple/test-http-date-header.js b/test/simple/test-http-date-header.js new file mode 100644 index 0000000000..e416b37583 --- /dev/null +++ b/test/simple/test-http-date-header.js @@ -0,0 +1,35 @@ +var common = require("../common"); +var assert = require('assert'); +var http = require("http"); + +var testResBody = "other stuff!\n"; + +var server = http.createServer(function(req, res) { + assert.ok(! ("date" in req.headers), + "Request headers contained a Date." + ); + 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("date" in res.headers, + "Response headers didn't contain a Date." + ); + res.addListener('end', function () { + server.close(); + process.exit(); + }); + }); + req.end(); +}); diff --git a/test/simple/test-http-max-headers-count.js b/test/simple/test-http-max-headers-count.js index 8d5b97d7c3..f34e7b5b79 100644 --- a/test/simple/test-http-max-headers-count.js +++ b/test/simple/test-http-max-headers-count.js @@ -56,7 +56,7 @@ server.listen(common.PORT, function() { var maxAndExpected = [ // for client [20, 20], [1200, 1200], - [0, N + 2], // Connection and Transfer-Encoding + [0, N + 3], // Connection, Date and Transfer-Encoding ]; doRequest();