Search code examples
node.jsnode-requestnode-cryptonode-streams

Why crypto.createHash() is not writable until setEncoding() is called?


I use request package with crypto. It seems that request implements legacy stream protocol and don't write any data to the piped destination until it's writable.

Stream.prototype.pipe = function(dest, options) {
  var source = this;

  function ondata(chunk) {
    if (dest.writable) {
      if (false === dest.write(chunk) && source.pause) {
        source.pause();
      }
    }
  }
...

So, if I use the next code:

const crypto = require('crypto'); 
const request = require('request');

var hasher = crypto.createHash('sha256'); 
// Uncomment the line below to fix!
// hasher.setEncoding('hex');
console.log(hasher.writable); 
request('http://ya.ru').pipe(hasher).on('finish', function() {
    console.log('Hash is', hasher.read()); 
});

it produces the sha256('') (i.e. from empty value). But when I use hasher.setEncoding('hex') the code produces sha256(<response_body>) and hasher.writable gives true.

I can't understand what is the reason of acting this way? And where is that stated in the documentation?


Solution

  • Finally, there was a bug in Node. Here is even smaller code sample to reproduce it:

    var hasher = crypto.createHash('sha256'); 
    const _ = hasher._writableState;  // we don't even have to call .setEnconding() here
    console.log(hasher.writable);
    

    Stream1 implementation required this.writable to be true in the destination stream.

    Stream.prototype.pipe = function(dest, options) {
      var source = this;
    
      function ondata(chunk) {
        if (dest.writable) {
          if (false === dest.write(chunk) && source.pause) {
            source.pause();
          }
        }
      }
    
      source.on('data', ondata);
      ...
    

    Calling to hasher.setEncoding('hex') (or any other access to this._writableState) triggered a call to the actual constructor of the stream. Before it this.writable was undefined.