HTTP2 / SPDY 推流验证:如何测试?

Posted

技术标签:

【中文标题】HTTP2 / SPDY 推流验证:如何测试?【英文标题】:HTTP2 / SPDY Push-Stream Verification: How to Test? 【发布时间】:2016-03-07 02:45:55 【问题描述】:

我正在使用节点模块 spdy 实现 HTTP2/SPDY 推送资源。我已经关注indutny's doc 并一直在进行测试运行,将他的示例实现到我的 server.js 中。

问题有两个方面; 我没有在日志中看到任何错误,也没有在浏览器中看到alert。我也没有在开发者控制台中看到任何变化。如果我设置了一个虚假的推送 URL,我不会收到任何响应/错误等。我相信理论上,优先级应该从medium 变为High(?)。 See Screen shoot.

我是否有另一种方法可以测试是否正在向浏览器进行推送?或者,我的脚本是否有问题(请检查不一致)?另外,stream.on('error', function() ); 中要扔什么?

在 Chrome 中测试(在 ChromeBook 上)、nodejs v5.1.0、npm v3.3.12 - H2 已在 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,
  );

更新: 我还添加了:

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

没有写入控制台日志 - 就像函数死了一样。

带推流的开发控制台 (main.js):

【问题讨论】:

在你的index.html中,你有&lt;script src="main.js"&gt;吗?浏览器将忽略推送的资源,否则它没有理由请求。 是的,我愿意。我尝试在文档中运行它,但应用程序失败了。 stream 是否发出错误?至少,您应该注销错误事件。默默地吞下它们是一个很好的方式来射击自己的脚。 是的,我同意。因此问题;我不确定测试或在stream.on('error', function() ); 建议中抛出什么?谢谢。 我只是把console.error(err)扔进去,这样你就可以看到它是不是stream发出了错误。 【参考方案1】:

这里有一些问题。

    仅当请求的 URL 与磁盘上的文件匹配时,才会调用自助餐回调。就像 express 中间件一样,它本质上是一个next 函数。因此,您永远不会真正推动任何事情。

    res.push 的第一个参数是 URL,而不是文件系统路径。

    res.push 将不存在于 ≤ HTTP/1.1 连接上;你需要确保它在那里,否则你会抛出一个未捕获的异常(和崩溃)。

这是一个简化的工作示例。

spdy.createServer(
    key: fs.readFileSync('./s.key'),
    cert: fs.readFileSync('./s.crt')
, function(req, res) 

    if (req.url == '/') 
        res.writeHead(200,  'Content-Length': 42 );
        res.end('<h1>Hi</h1><script src="main.js"></script>');

        if (res.push) 

            // Push JavaScript asset (main.js) to the client
            var stream = res.push('/main.js', 
                req: 'accept': '**/*',
                res: 'content-type': 'application/javascript'
            );

            stream.on('error', function() 
                console.error(err);
            );
            stream.end('alert("hello from push stream!");');
        
     else 
        res.writeHead(404);
        res.end();
    



).listen(777);

就实际在 Chrome 中验证正在推送的内容而言,打开一个新选项卡并输入 chrome://net-internals/#http2。单击与您的服务器的 HTTP/2 会话的 ID,然后单击左侧窗格中的会话。与初始请求混合,您会看到如下内容:

t=  3483 [st=    19]  HTTP2_SESSION_RECV_PUSH_PROMISE
                      --> :method: GET
                          :path: /main.js
                          :scheme: https
                          :authority: localhost:777
                      --> id = 3
                      --> promised_stream_id = 4
t=  3483 [st=    19]  HTTP2_SESSION_RECV_HEADERS
                      --> fin = false
                      --> :status: 200
                      --> stream_id = 4
t=  3483 [st=    19]  HTTP2_SESSION_RECV_DATA
                      --> fin = true
                      --> size = 0
                      --> stream_id = 4
t=  3546 [st=    82]  HTTP2_STREAM_ADOPTED_PUSH_STREAM
                      --> stream_id = 4
                      --> url = "https://localhost:777/main.js"

(我没有在开发工具中看到 main.js 的 Priority 发生变化——它仍然是 Medium。)

【讨论】:

Thnx 4 输入。 1.Buffet。 2. res.push:我在某处读到 id 是基于 CWD 的。用我的 dir 设置运行你的示例,res.end 是 CWD,res.push 是 URL -THNX! 3. res.push 将不存在于≤ HTTP/1.1:我理解 node-spdy 处理了这个(我可能错了);我还没有测试那么远,只需要 Push 2 工作 1st。我相信的问题是我正在提供一个静态索引文件。 Does this matter? 我将如何注入 您需要将请求交给自助餐之前推动。如果自助餐看到对静态文件的请求,则永远不会调用您的回调。还要确保req.url == '/'(或任何主页的 URL)——您不想将 JS 文件与服务器上每个文件的每个请求一起推送。【参考方案2】:

在 Chrome 检查器中,我发现当服务器推送资源时很容易识别它。

首先:在网络视图/选项卡中,相关资源将在瀑布中显示几乎没有 request 发送和“等待(TTFB)”(查看图片下面)。

theme.min.css & theme.min.js 资源被推送:

第二:点击推送的资源后,打开“Headers”窗格并检查底部的“Request Headers”面板,检查Provisional headers are shown。如果为资源显示警告,则它已被推送。 See this SO answer 为什么你会看到这个警告。

标题检查器:

如果您需要更多关于推送资源的详细信息,使用@josh3736 answer 第二部分中所述的chrome://net-internals/#http2 方法也可以。但是,如果您需要一种快速的方法来验证资源是否已被客户端推送和排除,查看瀑布将显示这一点。

【讨论】:

以上是关于HTTP2 / SPDY 推流验证:如何测试?的主要内容,如果未能解决你的问题,请参考以下文章

Nginx 1.10 上的 SPDY 代替 HTTP2

N 001 了解HTTP2

SPDY/HTTP2 是不是连接响应?

检测浏览器中的 HTTP2/SPDY 支持

使用 SPDY/http2 的快速应用程序中的套接字挂断

使用 HTTP2/SPDY 时我应该缩小和连接 javascript 和 CSS 吗?