Browse Source

Support for outgoing HTTP trailing headers

v0.7.4-release
Mark Nottingham 15 years ago
committed by Ryan Dahl
parent
commit
4fe3007a1a
  1. 18
      doc/api.markdown
  2. 27
      lib/http.js
  3. 75
      test/simple/test-http-set-trailers.js

18
doc/api.markdown

@ -1894,6 +1894,24 @@ header information and the first body to the client. The second time
data, and sends that separately. That is, the response is buffered up to the data, and sends that separately. That is, the response is buffered up to the
first chunk of body. first chunk of body.
### response.addTrailers(headers)
This method adds HTTP trailing headers (a header but at the end of the
message) to the response.
Trailers will **only** be emitted if chunked encoding is used for the
response; if it is not (e.g., if the request was HTTP/1.0), they will
be silently discarded.
Note that HTTP requires the `Trailer` header to be sent if you intend to
emit trailers, with a list of the header fields in its value. E.g.,
response.writeHead(200, { 'Content-Type': 'text/plain',
'Trailer': 'TraceInfo' });
response.write(fileData);
response.addTrailers({'Content-MD5': "7895bf4b8828b55ceaf47747b4bca667"});
response.end();
### response.end([data], [encoding]) ### response.end([data], [encoding])

27
lib/http.js

@ -292,6 +292,7 @@ function OutgoingMessage (socket) {
this.useChunkedEncodingByDefault = true; this.useChunkedEncodingByDefault = true;
this._hasBody = true; this._hasBody = true;
this._trailer = '';
this.finished = false; this.finished = false;
} }
@ -484,6 +485,26 @@ OutgoingMessage.prototype.write = function (chunk, encoding) {
return ret; return ret;
}; };
OutgoingMessage.prototype.addTrailers = function (headers) {
this._trailer = "";
var keys = Object.keys(headers);
var isArray = (Array.isArray(headers));
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
if (isArray) {
field = headers[key][0];
value = headers[key][1];
} else {
field = key;
value = headers[key];
}
this._trailer += field + ": " + value + CRLF;
}
};
OutgoingMessage.prototype.finish = function () { OutgoingMessage.prototype.finish = function () {
throw new Error("finish() has been renamed to close()."); throw new Error("finish() has been renamed to close().");
}; };
@ -520,7 +541,9 @@ OutgoingMessage.prototype.end = function (data, encoding) {
+ l + l
+ CRLF + CRLF
+ data + data
+ "\r\n0\r\n\r\n" + "\r\n0\r\n"
+ this._trailer
+ "\r\n"
, encoding , encoding
); );
} else { } else {
@ -535,7 +558,7 @@ OutgoingMessage.prototype.end = function (data, encoding) {
if (!hot) { if (!hot) {
if (this.chunkedEncoding) { if (this.chunkedEncoding) {
ret = this._send('0\r\n\r\n'); // Last chunk. ret = this._send('0\r\n' + this._trailer + '\r\n'); // Last chunk.
} else if (!data) { } else if (!data) {
// Force a flush, HACK. // Force a flush, HACK.
ret = this._send(''); ret = this._send('');

75
test/simple/test-http-set-trailers.js

@ -0,0 +1,75 @@
common = require("../common");
assert = common.assert;
http = require("http");
net = require("net");
outstanding_reqs = 0;
var server = http.createServer(function(req, res) {
res.writeHead(200, [ ['content-type', 'text/plain'] ]);
res.addTrailers({"x-foo": "bar"});
res.end("stuff" + "\n");
});
server.listen(common.PORT);
// first, we test an HTTP/1.0 request.
server.addListener("listening", function() {
var c = net.createConnection(common.PORT);
var res_buffer = "";
c.setEncoding("utf8");
c.addListener("connect", function () {
outstanding_reqs++;
c.write( "GET / HTTP/1.0\r\n\r\n" );
});
c.addListener("data", function (chunk) {
// console.log(chunk);
res_buffer += chunk;
});
c.addListener("end", function () {
c.end();
assert.ok(! /x-foo/.test(res_buffer), "Trailer in HTTP/1.0 response.");
outstanding_reqs--;
if (outstanding_reqs == 0) {
server.close();
process.exit();
}
});
});
// now, we test an HTTP/1.1 request.
server.addListener("listening", function() {
var c = net.createConnection(common.PORT);
var res_buffer = "";
var tid;
c.setEncoding("utf8");
c.addListener("connect", function () {
outstanding_reqs++;
c.write( "GET / HTTP/1.1\r\n\r\n" );
tid = setTimeout(assert.fail, 2000, "Couldn't find last chunk.");
});
c.addListener("data", function (chunk) {
// console.log(chunk);
res_buffer += chunk;
if (/0\r\n/.test(res_buffer)) { // got the end.
outstanding_reqs--;
clearTimeout(tid);
assert.ok(
/0\r\nx-foo: bar\r\n\r\n$/.test(res_buffer),
"No trailer in HTTP/1.1 response."
);
if (outstanding_reqs == 0) {
server.close();
process.exit();
}
}
});
});
Loading…
Cancel
Save