node.jsspdyhttp2pushstream

HTTP2 / SPDY Push-Stream Verification: How to Test?


I am implementing HTTP2/SPDY push resourcing using the node module spdy. I have followed indutny's doc and have been doing test runs implementing his example into my server.js.

The problem is two fold; I am not getting any errors in the log(s) nor am I seeing the alert in the browser. I don't see any change in the Developers Console as well. If I set a bogus push URL, I get no response/errors,etc. I believe in theory, the Priority should change from medium to High(?). See Screen shoot.

Is there another way for me to test if the push is being made to the browser? Or, do I have something wrong in my script (Please check for inconsistencies)? Also, what to throw in stream.on('error', function() {});?

Testing in Chrome (on a ChromeBook), nodejs v5.1.0, npm v3.3.12 - H2 enabled is verified in Chrome.

server.js:

var environment = '../env/' + process.env.NODE_ENV
    // Process User config
  , fS = require('fs')
  , jsonFile = fS.readFileSync(environment + '/config.json')
  , jsonString, hostIp, hostPort, cacheExp, cps;

    try {
      jsonString              = JSON.parse(jsonFile);
      var SERV_HOST           = jsonString['hostIp']
        , SERV_PORT           = jsonString['hostPort']
        , CACHE_EXP           = jsonString['cacheExp']
        , CPS                 = jsonString['cps']
        , xPowerBy            = 'OceanPress'
        , xFrameOptions       = 'DENY'
        , xXSSProtection      = '1; mode=block'
        , xContentTypeOption  = 'nosniff';
    } catch (err) {
      console.log('There is an error parsing the json file : ' + err);
    }

// Load modules
var fs          = require('fs')
  , watch       = require('staticsmith-watch')
  , buffet      = require('buffet')({root: environment + '/_public'})
  , spdy        = require('spdy')
    // spdy options
  , options = {
      key: fs.readFileSync(environment + '/keys/key.pem')
    , cert: fs.readFileSync(environment + '/keys/cert.pem')
    // SPDY-specific options
    , spdy: {
          protocols: [ 'h2','spdy/3.1', 'spdy/3', 'spdy/2','http/1.1', 'http/1.0' ]
        , plain: false
        , maxStreams: 200
        , connection: {
              windowSize: 1024 * 1024
            , autoSpdy31: false
          }
      }
      // Set ip and port
    , host:   SERV_HOST
    , port:   SERV_PORT
  }
    // Security header options
  , security  = [
      { name:   'X-Powered-By',
        option: xPowerBy }
    , { name:   'x-frame-options',
        option: xFrameOptions }
    , { name:   'X-XSS-Protection',
        option: xXSSProtection }
    , { name:   'X-Content-Type-Options',
        option: xContentTypeOption }
    , { name:   'Cache-Control',
        option: CACHE_EXP }
    , { name:   'Content-Security-Policy',
        option: CPS }
    , { name:   'server',
        option: 'Who knows' }
  ];

if (process.env.NODE_ENV == 'production') {

  spdy.createServer(options, function(req, res) {

    // Add Content Security Rules
    for(var i = 0; i < security.length; i++) {
      res.setHeader(security[i].name, security[i].option);
    }

    // @see https://www.npmjs.com/package/buffet
    buffet(req, res, function (err, result) {

      // Push JavaScript asset (main.js) to the client
      var stream = res.push('/js/main.js', {
        req: {'accept': '*/*'},
        res: {'content-type': 'application/javascript'}
      });
      stream.on('acknowledge', function() {
          console.log("Stream ACK");
      });
      stream.on('error', function() {
        console.error("stream ERR");
      });
      stream.end('alert("hello from push stream!");');
      // write main response body and terminate stream
      res.end('<script src="/js/main.js"></script>');

      // There was an error serving the file? Throw it!
      if (err) {
        console.error("Error serving " + req.url + " - " + err.message);
        // Respond to the client
        res.writeHead(err.status, err.headers);
      }
    });
  }).listen(options.port, options.host);

  console.log("serving at https://" + options.host + ":" + options.port);
  console.log("On Node v" + process.versions.node);
  console.log("On npm v" + process.versions.npm);

  watch({
    pattern:    '**/*',
    livereload: true,
  });
}

UPDATE: I have also added:

  stream.on('acknowledge', function() {
      console.log('stream ACK');
  });

There is no console log written - It's like the function is dead.

Dev Console with push-stream (main.js):
Dev Console with push-stream (main.js)


Solution

  • Within the Chrome inspector, I discovered it is quite easily recognized when a resource has been pushed by the server.

    First: Within the network view/tab, the resource(s) in question will show virtually no request sent and 'waiting(TTFB)' in the waterfall (See image below).

    The theme.min.css & theme.min.js resources are pushed:

    enter image description here

    Second: After clicking on the pushed the resource(s), opening the "Headers" pane and inspecting the "Request Headers" panel at the bottom, check for Provisional headers are shown. If the warning is shown for the resource, then it was pushed. See this SO answer to why you will see this warning.

    Headers Inspector:

    enter image description here

    If you need a little more detailed information about the pushed resource(s), using the chrome://net-internals/#http2 method as stated in the second part of @josh3736 answer would work too. But if you need a quick way to verify that the resource(s) has been pushed and excepted by the client, viewing the waterfall will show this.