test: add test for TLS renegotiation and client-certificates (#32252)
This commit is contained in:
parent
571f25a7d3
commit
e3480d1886
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
import fs from 'fs';
|
||||
import tls from 'tls';
|
||||
import type https from 'https';
|
||||
import zlib from 'zlib';
|
||||
import type http2 from 'http2';
|
||||
import type http from 'http';
|
||||
import { expect, playwrightTest as base } from '../config/browserTest';
|
||||
|
|
@ -375,6 +377,138 @@ test.describe('browser', () => {
|
|||
await page.close();
|
||||
});
|
||||
|
||||
test('should handle TLS renegotiation with client certificates', async ({ browser, asset, browserName, platform }) => {
|
||||
const server: https.Server = createHttpsServer({
|
||||
key: fs.readFileSync(asset('client-certificates/server/server_key.pem')),
|
||||
cert: fs.readFileSync(asset('client-certificates/server/server_cert.pem')),
|
||||
ca: [fs.readFileSync(asset('client-certificates/server/server_cert.pem'))],
|
||||
requestCert: false, // Initially don't request client cert
|
||||
rejectUnauthorized: false,
|
||||
// TLSv1.3 does not support renegotiation
|
||||
minVersion: 'TLSv1.2',
|
||||
maxVersion: 'TLSv1.2',
|
||||
});
|
||||
|
||||
server.on('request', async (req, res) => {
|
||||
if (!req.socket)
|
||||
return;
|
||||
const renegotiate = () => new Promise<void>((resolve, reject) => {
|
||||
(req.socket as tls.TLSSocket).renegotiate({
|
||||
requestCert: true,
|
||||
rejectUnauthorized: false
|
||||
}, err => {
|
||||
if (err)
|
||||
reject(err);
|
||||
else
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
if (req.url === '/') {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html', 'connection': 'close' });
|
||||
res.end();
|
||||
} else if (req.url === '/from-fetch-api') {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/plain',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
});
|
||||
res.flushHeaders();
|
||||
|
||||
await new Promise<void>(resolve => req.once('data', data => {
|
||||
res.write(`server received: ${data.toString()}\n`);
|
||||
resolve();
|
||||
}));
|
||||
|
||||
await renegotiate();
|
||||
for (let i = 0; i < 4; i++) {
|
||||
res.write(`${i}-from-server\n`);
|
||||
// Best-effort to trigger a new chunk
|
||||
await new Promise<void>(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
res.end('server closed the connection');
|
||||
} else if (req.url === '/style.css') {
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/css',
|
||||
'Content-Encoding': 'gzip',
|
||||
'Transfer-Encoding': 'chunked'
|
||||
});
|
||||
|
||||
await renegotiate();
|
||||
|
||||
const stylesheet = `
|
||||
button {
|
||||
background-color: red;
|
||||
}
|
||||
`;
|
||||
|
||||
const stylesheetBuffer = await new Promise<Buffer>((resolve, reject) => {
|
||||
zlib.gzip(stylesheet, (err, buffer) => {
|
||||
if (err)
|
||||
reject(err);
|
||||
else
|
||||
resolve(buffer);
|
||||
});
|
||||
});
|
||||
for (let i = 0; i < stylesheetBuffer.length; i += 100) {
|
||||
res.write(stylesheetBuffer.slice(i, i + 100));
|
||||
// Best-effort to trigger a new chunk
|
||||
await new Promise<void>(resolve => setTimeout(resolve, 20));
|
||||
}
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
}
|
||||
});
|
||||
|
||||
await new Promise<void>(resolve => server.listen(0, 'localhost', resolve));
|
||||
const port = (server.address() as import('net').AddressInfo).port;
|
||||
const origin = 'https://' + (browserName === 'webkit' && platform === 'darwin' ? 'local.playwright' : 'localhost');
|
||||
const serverUrl = `${origin}:${port}`;
|
||||
|
||||
const context = await browser.newContext({
|
||||
ignoreHTTPSErrors: true,
|
||||
clientCertificates: [{
|
||||
origin,
|
||||
certPath: asset('client-certificates/client/trusted/cert.pem'),
|
||||
keyPath: asset('client-certificates/client/trusted/key.pem'),
|
||||
}],
|
||||
});
|
||||
|
||||
const page = await context.newPage();
|
||||
|
||||
await test.step('fetch API', async () => {
|
||||
await page.goto(serverUrl);
|
||||
const response = await page.evaluate(async () => {
|
||||
const response = await fetch('/from-fetch-api', {
|
||||
method: 'POST',
|
||||
body: 'client-request-payload'
|
||||
});
|
||||
return await response.text();
|
||||
});
|
||||
expect(response).toBe([
|
||||
'server received: client-request-payload',
|
||||
'0-from-server',
|
||||
'1-from-server',
|
||||
'2-from-server',
|
||||
'3-from-server',
|
||||
'server closed the connection'
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
await test.step('Gzip encoded CSS Stylesheet', async () => {
|
||||
await page.goto(serverUrl);
|
||||
// The <link> would throw with net::ERR_INVALID_CHUNKED_ENCODING
|
||||
await page.setContent(`
|
||||
<button>Click me</button>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
`);
|
||||
await expect(page.locator('button')).toHaveCSS('background-color', /* red */'rgb(255, 0, 0)');
|
||||
});
|
||||
|
||||
await context.close();
|
||||
await new Promise<void>(resolve => server.close(() => resolve()));
|
||||
});
|
||||
|
||||
test('should pass with matching certificates in pfx format when passing as content', async ({ browser, startCCServer, asset, browserName }) => {
|
||||
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
|
||||
const page = await browser.newPage({
|
||||
|
|
|
|||
Loading…
Reference in a new issue