mirror of
https://git.proxmox.com/git/package-rebuilds
synced 2025-08-21 21:19:51 +00:00
278 lines
7.3 KiB
JavaScript
278 lines
7.3 KiB
JavaScript
'use strict'
|
|
|
|
const https = require('https')
|
|
const { once } = require('events')
|
|
const { createSecureServer } = require('http2')
|
|
const { readFileSync } = require('fs')
|
|
const { join } = require('path')
|
|
const { test } = require('tap')
|
|
|
|
const { Client } = require('..')
|
|
|
|
// get the crypto fixtures
|
|
const key = readFileSync(join(__dirname, 'fixtures', 'key.pem'), 'utf8')
|
|
const cert = readFileSync(join(__dirname, 'fixtures', 'cert.pem'), 'utf8')
|
|
const ca = readFileSync(join(__dirname, 'fixtures', 'ca.pem'), 'utf8')
|
|
|
|
test('Should upgrade to HTTP/2 when HTTPS/1 is available for GET', async (t) => {
|
|
t.plan(10)
|
|
|
|
const body = []
|
|
const httpsBody = []
|
|
|
|
// create the server and server stream handler
|
|
const server = createSecureServer(
|
|
{
|
|
key,
|
|
cert,
|
|
allowHTTP1: true
|
|
},
|
|
(req, res) => {
|
|
const { socket: { alpnProtocol } } = req.httpVersion === '2.0' ? req.stream.session : req
|
|
|
|
// handle http/1 requests
|
|
res.writeHead(200, {
|
|
'content-type': 'application/json; charset=utf-8',
|
|
'x-custom-request-header': req.headers['x-custom-request-header'] || '',
|
|
'x-custom-response-header': `using ${req.httpVersion}`
|
|
})
|
|
res.end(JSON.stringify({
|
|
alpnProtocol,
|
|
httpVersion: req.httpVersion
|
|
}))
|
|
}
|
|
)
|
|
|
|
server.listen(0)
|
|
await once(server, 'listening')
|
|
|
|
// close the server on teardown
|
|
t.teardown(server.close.bind(server))
|
|
|
|
// set the port
|
|
const port = server.address().port
|
|
|
|
// test undici against http/2
|
|
const client = new Client(`https://localhost:${port}`, {
|
|
connect: {
|
|
ca,
|
|
servername: 'agent1'
|
|
},
|
|
allowH2: true
|
|
})
|
|
|
|
// close the client on teardown
|
|
t.teardown(client.close.bind(client))
|
|
|
|
// make an undici request using where it wants http/2
|
|
const response = await client.request({
|
|
path: '/',
|
|
method: 'GET',
|
|
headers: {
|
|
'x-custom-request-header': 'want 2.0'
|
|
}
|
|
})
|
|
|
|
response.body.on('data', chunk => {
|
|
body.push(chunk)
|
|
})
|
|
|
|
await once(response.body, 'end')
|
|
|
|
t.equal(response.statusCode, 200)
|
|
t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
|
|
t.equal(response.headers['x-custom-request-header'], 'want 2.0')
|
|
t.equal(response.headers['x-custom-response-header'], 'using 2.0')
|
|
t.equal(Buffer.concat(body).toString('utf8'), JSON.stringify({
|
|
alpnProtocol: 'h2',
|
|
httpVersion: '2.0'
|
|
}))
|
|
|
|
// make an https request for http/1 to confirm undici is using http/2
|
|
const httpsOptions = {
|
|
ca,
|
|
servername: 'agent1',
|
|
headers: {
|
|
'x-custom-request-header': 'want 1.1'
|
|
}
|
|
}
|
|
|
|
const httpsResponse = await new Promise((resolve, reject) => {
|
|
const httpsRequest = https.get(`https://localhost:${port}/`, httpsOptions, (res) => {
|
|
res.on('data', (chunk) => {
|
|
httpsBody.push(chunk)
|
|
})
|
|
|
|
res.on('end', () => {
|
|
resolve(res)
|
|
})
|
|
}).on('error', (err) => {
|
|
reject(err)
|
|
})
|
|
|
|
t.teardown(httpsRequest.destroy.bind(httpsRequest))
|
|
})
|
|
|
|
t.equal(httpsResponse.statusCode, 200)
|
|
t.equal(httpsResponse.headers['content-type'], 'application/json; charset=utf-8')
|
|
t.equal(httpsResponse.headers['x-custom-request-header'], 'want 1.1')
|
|
t.equal(httpsResponse.headers['x-custom-response-header'], 'using 1.1')
|
|
t.equal(Buffer.concat(httpsBody).toString('utf8'), JSON.stringify({
|
|
alpnProtocol: false,
|
|
httpVersion: '1.1'
|
|
}))
|
|
})
|
|
|
|
test('Should upgrade to HTTP/2 when HTTPS/1 is available for POST', async (t) => {
|
|
t.plan(15)
|
|
|
|
const requestChunks = []
|
|
const responseBody = []
|
|
|
|
const httpsRequestChunks = []
|
|
const httpsResponseBody = []
|
|
|
|
const expectedBody = 'hello'
|
|
const buf = Buffer.from(expectedBody)
|
|
const body = new ArrayBuffer(buf.byteLength)
|
|
|
|
buf.copy(new Uint8Array(body))
|
|
|
|
// create the server and server stream handler
|
|
const server = createSecureServer(
|
|
{
|
|
key,
|
|
cert,
|
|
allowHTTP1: true
|
|
},
|
|
(req, res) => {
|
|
// use the stream handler for http2
|
|
if (req.httpVersion === '2.0') {
|
|
return
|
|
}
|
|
|
|
const { socket: { alpnProtocol } } = req
|
|
|
|
req.on('data', (chunk) => {
|
|
httpsRequestChunks.push(chunk)
|
|
})
|
|
|
|
req.on('end', () => {
|
|
// handle http/1 requests
|
|
res.writeHead(201, {
|
|
'content-type': 'text/plain; charset=utf-8',
|
|
'x-custom-request-header': req.headers['x-custom-request-header'] || '',
|
|
'x-custom-alpn-protocol': alpnProtocol
|
|
})
|
|
res.end('hello http/1!')
|
|
})
|
|
}
|
|
)
|
|
|
|
server.on('stream', (stream, headers) => {
|
|
t.equal(headers[':method'], 'POST')
|
|
t.equal(headers[':path'], '/')
|
|
t.equal(headers[':scheme'], 'https')
|
|
|
|
const { socket: { alpnProtocol } } = stream.session
|
|
|
|
stream.on('data', (chunk) => {
|
|
requestChunks.push(chunk)
|
|
})
|
|
|
|
stream.respond({
|
|
':status': 201,
|
|
'content-type': 'text/plain; charset=utf-8',
|
|
'x-custom-request-header': headers['x-custom-request-header'] || '',
|
|
'x-custom-alpn-protocol': alpnProtocol
|
|
})
|
|
|
|
stream.end('hello h2!')
|
|
})
|
|
|
|
server.listen(0)
|
|
await once(server, 'listening')
|
|
|
|
// close the server on teardown
|
|
t.teardown(server.close.bind(server))
|
|
|
|
// set the port
|
|
const port = server.address().port
|
|
|
|
// test undici against http/2
|
|
const client = new Client(`https://localhost:${port}`, {
|
|
connect: {
|
|
ca,
|
|
servername: 'agent1'
|
|
},
|
|
allowH2: true
|
|
})
|
|
|
|
// close the client on teardown
|
|
t.teardown(client.close.bind(client))
|
|
|
|
// make an undici request using where it wants http/2
|
|
const response = await client.request({
|
|
path: '/',
|
|
method: 'POST',
|
|
headers: {
|
|
'x-custom-request-header': 'want 2.0'
|
|
},
|
|
body
|
|
})
|
|
|
|
response.body.on('data', (chunk) => {
|
|
responseBody.push(chunk)
|
|
})
|
|
|
|
await once(response.body, 'end')
|
|
|
|
t.equal(response.statusCode, 201)
|
|
t.equal(response.headers['content-type'], 'text/plain; charset=utf-8')
|
|
t.equal(response.headers['x-custom-request-header'], 'want 2.0')
|
|
t.equal(response.headers['x-custom-alpn-protocol'], 'h2')
|
|
t.equal(Buffer.concat(responseBody).toString('utf-8'), 'hello h2!')
|
|
t.equal(Buffer.concat(requestChunks).toString('utf-8'), expectedBody)
|
|
|
|
// make an https request for http/1 to confirm undici is using http/2
|
|
const httpsOptions = {
|
|
ca,
|
|
servername: 'agent1',
|
|
method: 'POST',
|
|
headers: {
|
|
'content-type': 'text/plain; charset=utf-8',
|
|
'content-length': Buffer.byteLength(body),
|
|
'x-custom-request-header': 'want 1.1'
|
|
}
|
|
}
|
|
|
|
const httpsResponse = await new Promise((resolve, reject) => {
|
|
const httpsRequest = https.request(`https://localhost:${port}/`, httpsOptions, (res) => {
|
|
res.on('data', (chunk) => {
|
|
httpsResponseBody.push(chunk)
|
|
})
|
|
|
|
res.on('end', () => {
|
|
resolve(res)
|
|
})
|
|
}).on('error', (err) => {
|
|
reject(err)
|
|
})
|
|
|
|
httpsRequest.on('error', (err) => {
|
|
reject(err)
|
|
})
|
|
|
|
httpsRequest.write(Buffer.from(body))
|
|
|
|
t.teardown(httpsRequest.destroy.bind(httpsRequest))
|
|
})
|
|
|
|
t.equal(httpsResponse.statusCode, 201)
|
|
t.equal(httpsResponse.headers['content-type'], 'text/plain; charset=utf-8')
|
|
t.equal(httpsResponse.headers['x-custom-request-header'], 'want 1.1')
|
|
t.equal(httpsResponse.headers['x-custom-alpn-protocol'], 'false')
|
|
t.equal(Buffer.concat(httpsResponseBody).toString('utf-8'), 'hello http/1!')
|
|
t.equal(Buffer.concat(httpsRequestChunks).toString('utf-8'), expectedBody)
|
|
})
|