На прошлой неделе среди разработчиков Node.js началось обсуждение реализации протокола HTTP/2. Ждать появления HTTP/2 в Node.js core еще очень долго, но уже сейчас можно затестить фичи этого протокола с помощью модуля node-spdy, который работает как с протоколом SPDY - предшественником HTTP/2, который кстати Google Chrome больше не поддерживает, так и с HTTP/2, обязательно поверх TLS. Расскажу как я тестил фичу по имени HTTP/2 Server Push.
Создаем каталог для статики с файлами следующего содержания:
- index.html
- style.css:
- script.js:
Пишем простой http-сервер для раздачи нашей статики:
- http.js
"Реклама" появится ближе к финалу.
Консоль сервера выглядит следующим образом:
Вкладка Network в DevTools выглядит примерно так:
Создаем ключи для https:
В коде сервера меняем http на https:
- https.js
Запускаем сервер, идем по адресу https://localhost:3000, не обращаем внимание на предупреждения браузера, получаем ту же самую страничку. На сервере и на клиенте ничего особенно не изменилось:
Устанавливаем node-spdy:
В коде сервера меняем https на spdy:
- spdy.js
HTTP/2 in action!
Попробуем HTTP/2 Server Push:
- spdy-push.js
Bingo!
Финальный номер - стримминг "рекламы":
- spdy-push-pipe.js
Номер удался, бурные аплодисменты переходящие в овации.
В браузере не поддерживающем HTTP/2 (я попробовал в IE11) тоже все нормально, разумеется без "рекламы":
Вот как-то так.
Создаем каталог для статики с файлами следующего содержания:
- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="style.css" rel="stylesheet"> <title>HTTP/2</title> </head> <body> <h2>Hello, world!</h2> <img src="favicon.png" alt="Здесь могла бы быть ваша реклама"> <script src="script.js"></script> </body> </html>
- style.css:
h2 { color: blue; } .italic { font-style: italic; }
- script.js:
document.addEventListener('DOMContentLoaded', function () { document.querySelector('h2').className = 'italic'; });
Пишем простой http-сервер для раздачи нашей статики:
- http.js
'use strict'; const http = require('http'); const fs = require('fs'); const routes = new Map([ ['/', { head: { 'Content-Type': 'text/html' }, body: fs.readFileSync('./static/index.html') }], ['/style.css', { head: { 'Content-Type': 'text/css' }, body: fs.readFileSync('./static/style.css') }], ['/script.js', { head: { 'Content-Type': 'application/javascript' }, body: fs.readFileSync('./static/script.js') }] ]); http.createServer((req, res) => { console.log('req.url:', req.url); const route = routes.get(req.url); if (route) { res.writeHead(200, route.head); res.end(route.body); } else { res.writeHead(404); res.end(); } }).listen(3000, (err) => { if (err) throw err; console.log('Listen on port 3000'); });Запускаем сервер, идем по адресу http://localhost:3000, получаем:
"Реклама" появится ближе к финалу.
Консоль сервера выглядит следующим образом:
Вкладка Network в DevTools выглядит примерно так:
Создаем ключи для https:
В коде сервера меняем http на https:
- https.js
'use strict'; const https = require('https'); const fs = require('fs'); const opts = { key: fs.readFileSync('cert/dummy.pem'), cert: fs.readFileSync('cert/dummy.crt') }; const routes = new Map([ ['/', { head: { 'Content-Type': 'text/html' }, body: fs.readFileSync('./static/index.html') }], ['/style.css', { head: { 'Content-Type': 'text/css' }, body: fs.readFileSync('./static/style.css') }], ['/script.js', { head: { 'Content-Type': 'application/javascript' }, body: fs.readFileSync('./static/script.js') }] ]); https.createServer(opts, (req, res) => { console.log('req.url:', req.url); const route = routes.get(req.url); if (route) { res.writeHead(200, route.head); res.end(route.body); } else { res.writeHead(404); res.end(); } }).listen(3000, (err) => { if (err) throw err; console.log('Listen on port 3000'); });
Запускаем сервер, идем по адресу https://localhost:3000, не обращаем внимание на предупреждения браузера, получаем ту же самую страничку. На сервере и на клиенте ничего особенно не изменилось:
Устанавливаем node-spdy:
В коде сервера меняем https на spdy:
- spdy.js
'use strict'; const spdy = require('spdy'); const fs = require('fs'); const opts = { key: fs.readFileSync('cert/dummy.pem'), cert: fs.readFileSync('cert/dummy.crt') }; const routes = new Map([ ['/', { head: { 'Content-Type': 'text/html' }, body: fs.readFileSync('./static/index.html') }], ['/style.css', { head: { 'Content-Type': 'text/css' }, body: fs.readFileSync('./static/style.css') }], ['/script.js', { head: { 'Content-Type': 'application/javascript' }, body: fs.readFileSync('./static/script.js') }] ]); spdy.createServer(opts, (req, res) => { console.log('req.url:', req.url); const route = routes.get(req.url); if (route) { res.writeHead(200, route.head); res.end(route.body); } else { res.writeHead(404); res.end(); } }).listen(3000, (err) => { if (err) throw err; console.log('Listen on port 3000'); });Запускаем сервер, обновляем страничку в браузере:
HTTP/2 in action!
Попробуем HTTP/2 Server Push:
- spdy-push.js
'use strict'; const spdy = require('spdy'); const fs = require('fs'); const opts = { key: fs.readFileSync('cert/dummy.pem'), cert: fs.readFileSync('cert/dummy.crt') }; const routes = new Map([ ['/', { head: { 'Content-Type': 'text/html' }, body: fs.readFileSync('./static/index.html') }], ['/style.css', { head: { 'Content-Type': 'text/css' }, body: fs.readFileSync('./static/style.css') }], ['/script.js', { head: { 'Content-Type': 'application/javascript' }, body: fs.readFileSync('./static/script.js') }] ]); const onError = (err) => console.error(err); spdy.createServer(opts, (req, res) => { console.log('req.url:', req.url); const route = routes.get(req.url); if (route) { if (req.url === '/' && typeof res.push === 'function') { const style = routes.get('/style.css'); res.push('/style.css', { response: style.head }) .on('error', onError) .end(style.body); const script = routes.get('/script.js'); res.push('/script.js', { response: script.head }) .on('error', onError) .end(script.body); } res.writeHead(200, route.head); res.end(route.body); } else { res.writeHead(404); res.end(); } }).listen(3000, (err) => { if (err) throw err; console.log('Listen on port 3000'); });Запускаем сервер, обновляем страничку в браузере:
Bingo!
Финальный номер - стримминг "рекламы":
- spdy-push-pipe.js
'use strict'; const spdy = require('spdy'); const fs = require('fs'); const opts = { key: fs.readFileSync('cert/dummy.pem'), cert: fs.readFileSync('cert/dummy.crt') }; const routes = new Map([ ['/', { head: { 'Content-Type': 'text/html' }, body: fs.readFileSync('./static/index.html') }], ['/style.css', { head: { 'Content-Type': 'text/css' }, body: fs.readFileSync('./static/style.css') }], ['/script.js', { head: { 'Content-Type': 'application/javascript' }, body: fs.readFileSync('./static/script.js') }] ]); const https = require('https'); const PassThrough = require('stream').PassThrough; const request = (uri) => { const pass = new PassThrough(); https.request(uri, (res) => { pass.emit('response', res); res.on('error', (err) => pass.emit('error', err)); res.pipe(pass); }) .on('error', (err) => pass.emit('error', err)) .end(); return pass; }; const onError = (err) => console.error(err); spdy.createServer(opts, (req, res) => { console.log('req.url:', req.url); const route = routes.get(req.url); if (route) { if (req.url === '/' && typeof res.push === 'function') { const rs = request('https://nodejs.org/static/favicon.png'); const ws = res.push('/favicon.png', { response: { 'Content-Type': 'image/x-icon' } }); rs.on('error', onError); ws.on('error', onError); rs.pipe(ws); const style = routes.get('/style.css'); res.push('/style.css', { response: style.head }) .on('error', onError) .end(style.body); const script = routes.get('/script.js'); res.push('/script.js', { response: script.head }) .on('error', onError) .end(script.body); } res.writeHead(200, route.head); res.end(route.body); } else { res.writeHead(404); res.end(); } }).listen(3000, (err) => { if (err) throw err; console.log('Listen on port 3000'); });Запускаем сервер, обновляем страничку в браузере:
Номер удался, бурные аплодисменты переходящие в овации.
В браузере не поддерживающем HTTP/2 (я попробовал в IE11) тоже все нормально, разумеется без "рекламы":
Вот как-то так.
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации