Last active
November 1, 2016 04:55
-
-
Save brianloveswords/0044b0b57b672b302f2d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Problem: Notifying clients of an error after headers have been sent | |
In normal HTTP buffer-everything-then-send, you would be able to | |
set an error status code, either 4xx or 5xx, to let the client know | |
that something has gone awry. | |
However when using a streaming interface the headers, including the | |
status code, are sent before it is known that the message will | |
finish sending successfully. With streams, the header can't be | |
trusted to determine the success/failure state of the message. | |
Potential Solution: HTTP 1.1 Trailers | |
It seems the folks who spec'd out HTTP 1.1 thought of this: | |
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1 | |
When using chunked transfer encoding, it is possible to send along | |
a "trailer" after the body has been sent. By adding a | |
`Content-SHA256` trailer with the computed hash of the final value, | |
a client can check the integrity of the message upon receiving te | |
final chunk. | |
Caveats: | |
There are no common HTTP trailers like there are HTTP headers so | |
there won't be out-of-the-box support for any specific | |
trailer. Also, since there are no none semantics for trailers, if | |
the user-agent is a browser, failures will be undetectable. | |
Open Question: | |
Is there a better way to signal an error to the client other than | |
using trailers and hoping the client performs the integrity check? | |
Maybe something that would also work in the browser? | |
*/ | |
var fs = require('fs'); | |
var util = require('util'); | |
var http = require('http'); | |
var crypto = require('crypto'); | |
var assert = require('assert'); | |
http.createServer(function (req, res) { | |
res.writeHead(200, { | |
'Content-Type': 'text/plain', | |
'Trailer': 'Content-SHA256', | |
}); | |
var shasum = crypto.createHash('sha256'); | |
var dataStream = fs.createReadStream('data.txt'); | |
dataStream.pipe(shasum, {end: false}); | |
dataStream.pipe(res, {end: false}); | |
dataStream.on('end', function () { | |
res.addTrailers({'Content-SHA256': shasum.digest('hex')}); | |
res.end(); | |
}); | |
dataStream.on('error', function () { | |
// Set to an invalid value | |
res.addTrailers({'Content-SHA256': "Stream Error"}); | |
res.end(); | |
}); | |
}).listen(3000, function () { | |
var done = this.close.bind(this); | |
// Example API consumer | |
var shasum = crypto.createHash('sha256'); | |
http.get('http://localhost:3000', function (res) { | |
res.pipe(shasum, {end: false}); | |
res.on('end', function () { | |
// Client can validate message integrity by calculating the | |
// message hash and comparing it to the given hash in the trailer. | |
var givenHash = res.trailers['content-sha256']; | |
var calulatedHash = shasum.digest('hex'); | |
assert.equal(givenHash, calulatedHash); | |
console.log('Okay'); | |
done(); | |
}); | |
}); | |
}); |
Unfortunately the browser does not show the request as aborted. It also still triggers the end
event on the request's response! It does trigger the request's close
in addition, though.
So if we're using streaming servers we're basically too far ahead of the curve right now?
@mafintosh unfortunately chrome doesn't show an error in this case, it shows a blank page. The only indication is that in the console it prints net::ERR_INCOMPLETE_CHUNKED_ENCODING
.
curl
does show an error message though
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In your error handler
You could call
res.destroy()
instead.This would trigger
req.on('close', ...
in your client instead since it wouldn't receive\r\n\r\n
which.end()
sends as a end-of-data signal.This doesn't give you as fine tuned error handling as status codes but allows you to signal to the client that something wasn't right. I'm betting the browser would even show the request as aborted as well (I need to test this though)