HTTP2 / SPDY 推流验证:如何测试?
HTTP2 / SPDY Push-Stream Verification: How to Test?
我正在使用节点模块 spdy. I have followed indutny's doc 实施 HTTP2/SPDY push 资源,并且一直在测试运行将他的示例实施到我的 server.js .
问题有两个方面; 我在日志中没有收到任何错误,也没有在浏览器中看到 alert
。我也没有看到 Developers Console 有任何变化。如果我设置了一个伪造的推送 URL,我没有得到 response/errors,等等。我相信理论上,优先级应该从 medium
变为 High
(?)。 See Screen shoot.
有没有其他方法可以让我测试是否正在向浏览器推送?或者,我的脚本是否有问题(请检查是否存在不一致之处)?还有,要扔什么 stream.on('error', function() {});
?
在 Chrome 中进行测试(在 Chrome 书中),nodejs v5.1.0,npm v3.3.12 - 在 Chrome 中验证启用了 H2。
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):
这里有一些问题。
仅当请求的 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 的优先级发生变化——它仍然是中等。)
在 Chrome 检查器 中,我发现当服务器推送资源时它很容易被识别。
首先: 在网络 view/tab 中,有问题的资源将显示几乎没有 request
发送和 'waiting(TTFB)'瀑布(见下图)。
推送theme.min.css
&theme.min.js
资源:
其次:点击推送的资源后,打开"Headers"面板并检查底部的"Request Headers"面板,勾选Provisional headers are shown
。如果为资源显示警告,则它已被推送。 See this SO answer为什么你会看到这个警告。
Headers 检查员:
如果您需要有关推送资源的更多详细信息,也可以使用 @josh3736 answer 第二部分所述的 chrome://net-internals/#http2
方法。但是,如果您需要一种快速的方法来验证资源是否已被客户端推送和排除,查看瀑布将显示这一点。
我正在使用节点模块 spdy. I have followed indutny's doc 实施 HTTP2/SPDY push 资源,并且一直在测试运行将他的示例实施到我的 server.js .
问题有两个方面; 我在日志中没有收到任何错误,也没有在浏览器中看到 alert
。我也没有看到 Developers Console 有任何变化。如果我设置了一个伪造的推送 URL,我没有得到 response/errors,等等。我相信理论上,优先级应该从 medium
变为 High
(?)。 See Screen shoot.
有没有其他方法可以让我测试是否正在向浏览器推送?或者,我的脚本是否有问题(请检查是否存在不一致之处)?还有,要扔什么 stream.on('error', function() {});
?
在 Chrome 中进行测试(在 Chrome 书中),nodejs v5.1.0,npm v3.3.12 - 在 Chrome 中验证启用了 H2。
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):
这里有一些问题。
仅当请求的 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 的优先级发生变化——它仍然是中等。)
在 Chrome 检查器 中,我发现当服务器推送资源时它很容易被识别。
首先: 在网络 view/tab 中,有问题的资源将显示几乎没有 request
发送和 'waiting(TTFB)'瀑布(见下图)。
推送theme.min.css
&theme.min.js
资源:
其次:点击推送的资源后,打开"Headers"面板并检查底部的"Request Headers"面板,勾选Provisional headers are shown
。如果为资源显示警告,则它已被推送。 See this SO answer为什么你会看到这个警告。
Headers 检查员:
如果您需要有关推送资源的更多详细信息,也可以使用 @josh3736 answer 第二部分所述的 chrome://net-internals/#http2
方法。但是,如果您需要一种快速的方法来验证资源是否已被客户端推送和排除,查看瀑布将显示这一点。