Commit 79ac3096 authored by Sebastiaan Deckers's avatar Sebastiaan Deckers 🐑

Merge branch 'diaries' into 'master'

Server Push Diaries with Cuckoo Filters

See merge request !35
parents 23995998 3566186f
Pipeline #41857821 passed with stage
in 2 minutes and 19 seconds
const { getDependencies } = require('../helpers/getDependencies')
const { getOrigin } = require('../helpers/getOrigin')
const { requestDestination } = require('request-destination')
const CuckooFilter = require('cuckoofilter-native')
const HTTP2_PRIORITY_DEFAULT = 16
const DIARY_MAX_SIZE = 983
const diaries = new WeakMap()
function getDiary (session) {
if (diaries.has(session)) {
return diaries.get(session)
} else {
const diary = new CuckooFilter(DIARY_MAX_SIZE)
diaries.set(session, diary)
return diary
}
}
async function resolveDependencies (request, response, next) {
if ((request.httpVersionMajor === 2 && !response.stream.pushAllowed) ||
request.options.manifest.length === 0
......@@ -26,24 +40,31 @@ async function resolveDependencies (request, response, next) {
':scheme': 'https',
':authority': request.authority
}
const pushStream = (headers, weight) => new Promise((resolve, reject) => {
const { stream } = response
stream.pushStream(headers, (error, stream) => {
if (error) return reject(error)
else resolve(stream)
})
if (weight !== HTTP2_PRIORITY_DEFAULT) {
stream.priority({ weight, silent: true })
}
})
const { stream } = response
const pushPromises = []
for (const [relative, priority] of dependencies) {
const dependency = request.fileIndex.relative.get(relative)
headers[':path'] = dependency.pathname
response.log.info(`PUSH ${baseUrl}${dependency.pathname}`)
const pushPromise = pushStream(headers, priority)
.then((pushResponse) => ({ pushResponse, dependency }))
pushPromises.push(pushPromise)
const { pathname } = dependency
const url = baseUrl + pathname
let diary = getDiary(stream.session)
if (diary.contain(url)) {
continue
} else if (diary.size < DIARY_MAX_SIZE) {
try { diary.add(url) } catch (error) {}
}
pushPromises.push(new Promise((resolve, reject) => {
response.log.info(`PUSH ${url}`)
headers[':path'] = pathname
stream.pushStream(headers, (error, stream) => {
if (error) return reject(error)
else resolve({ pushResponse: stream, dependency })
})
if (priority !== HTTP2_PRIORITY_DEFAULT) {
stream.priority({ weight: priority, silent: true })
}
}))
}
request.pushResponses = await Promise.all(pushPromises)
} else if (request.httpVersionMajor === 1) {
......
......@@ -36,14 +36,16 @@ function receiveHttp2 (url, options = {}) {
push.push(payload)
}
if (pending.size === 0) {
session.close(() => {
resolve(new Response(result.response, result.data, push))
})
if (options.session) onDone()
else session.close(onDone)
}
}
}
const onDone = () => {
resolve(new Response(result.response, result.data, push))
}
const session = connect(url, Object.assign({
const session = options.session || connect(url, Object.assign({
rejectUnauthorized: false,
lookup: (hostname, options, callback) => {
callback(null, '127.0.0.1', 4)
......
const test = require('blue-tape')
const { Master } = require('..')
const { join } = require('path')
const { connect } = require('http2')
const { h2: receive } = require('./helpers/receive')
let master
test('start server', async (t) => {
const cwd = join(__dirname, 'fixtures')
const options = {
hosts: [{
manifest: [{ get: '/index.html', push: '/stuff.txt' }]
}]
}
master = new Master({ cwd, options })
await master.listen()
})
const url = 'https://localhost:8443/'
let session
test('Establish HTTP/2 connection', async (t) => {
session = connect(url, { rejectUnauthorized: false })
})
test('First request gets server push', async (t) => {
const response = await receive(url, { session })
t.equal(response.headers.get(':status'), 200)
t.is(response.push.length, 1)
})
test('Second request skips server push', async (t) => {
const response = await receive(url, { session })
t.equal(response.headers.get(':status'), 200)
t.is(response.push.length, 0)
})
test('Close HTTP/2 connection', (t) => {
session.close(t.end)
})
test('stop server', async (t) => master.close())
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment