mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
test: use managed http2 server for client-certificates (#31844)
This commit is contained in:
parent
b06a95dd75
commit
90af289ba2
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
|
import http2 from 'http2';
|
||||||
import type net from 'net';
|
import type net from 'net';
|
||||||
import { getProxyForUrl } from '../utilsBundle';
|
import { getProxyForUrl } from '../utilsBundle';
|
||||||
import { HttpsProxyAgent } from '../utilsBundle';
|
import { HttpsProxyAgent } from '../utilsBundle';
|
||||||
@ -169,6 +170,14 @@ export function createHttpsServer(...args: any[]): https.Server {
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createHttp2Server( onRequestHandler?: (request: http2.Http2ServerRequest, response: http2.Http2ServerResponse) => void,): http2.Http2SecureServer;
|
||||||
|
export function createHttp2Server(options: http2.SecureServerOptions, onRequestHandler?: (request: http2.Http2ServerRequest, response: http2.Http2ServerResponse) => void,): http2.Http2SecureServer;
|
||||||
|
export function createHttp2Server(...args: any[]): http2.Http2SecureServer {
|
||||||
|
const server = http2.createSecureServer(...args);
|
||||||
|
decorateServer(server);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
export async function isURLAvailable(url: URL, ignoreHTTPSErrors: boolean, onLog?: (data: string) => void, onStdErr?: (data: string) => void) {
|
export async function isURLAvailable(url: URL, ignoreHTTPSErrors: boolean, onLog?: (data: string) => void, onStdErr?: (data: string) => void) {
|
||||||
let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr);
|
let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr);
|
||||||
if (statusCode === 404 && url.pathname === '/') {
|
if (statusCode === 404 && url.pathname === '/') {
|
||||||
@ -200,7 +209,7 @@ async function httpStatusCode(url: URL, ignoreHTTPSErrors: boolean, onLog?: (dat
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function decorateServer(server: http.Server | http.Server) {
|
function decorateServer(server: net.Server) {
|
||||||
const sockets = new Set<net.Socket>();
|
const sockets = new Set<net.Socket>();
|
||||||
server.on('connection', socket => {
|
server.on('connection', socket => {
|
||||||
sockets.add(socket);
|
sockets.add(socket);
|
||||||
|
|||||||
@ -15,12 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import http2 from 'http2';
|
import type http2 from 'http2';
|
||||||
import type http from 'http';
|
import type http from 'http';
|
||||||
import { expect, playwrightTest as base } from '../config/browserTest';
|
import { expect, playwrightTest as base } from '../config/browserTest';
|
||||||
import type net from 'net';
|
import type net from 'net';
|
||||||
import type { BrowserContextOptions } from 'packages/playwright-test';
|
import type { BrowserContextOptions } from 'packages/playwright-test';
|
||||||
const { createHttpsServer } = require('../../packages/playwright-core/lib/utils');
|
const { createHttpsServer, createHttp2Server } = require('../../packages/playwright-core/lib/utils');
|
||||||
|
|
||||||
type TestOptions = {
|
type TestOptions = {
|
||||||
startCCServer(options?: {
|
startCCServer(options?: {
|
||||||
@ -30,11 +30,11 @@ type TestOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const test = base.extend<TestOptions>({
|
const test = base.extend<TestOptions>({
|
||||||
startCCServer: async ({ asset, browserName }, use) => {
|
startCCServer: async ({ asset }, use) => {
|
||||||
process.env.PWTEST_UNSUPPORTED_CUSTOM_CA = asset('client-certificates/server/server_cert.pem');
|
process.env.PWTEST_UNSUPPORTED_CUSTOM_CA = asset('client-certificates/server/server_cert.pem');
|
||||||
let server: http.Server | http2.Http2Server | undefined;
|
let server: http.Server | http2.Http2SecureServer | undefined;
|
||||||
await use(async options => {
|
await use(async options => {
|
||||||
server = (options?.http2 ? http2.createSecureServer : createHttpsServer)({
|
server = (options?.http2 ? createHttp2Server : createHttpsServer)({
|
||||||
key: fs.readFileSync(asset('client-certificates/server/server_key.pem')),
|
key: fs.readFileSync(asset('client-certificates/server/server_key.pem')),
|
||||||
cert: fs.readFileSync(asset('client-certificates/server/server_cert.pem')),
|
cert: fs.readFileSync(asset('client-certificates/server/server_cert.pem')),
|
||||||
ca: [
|
ca: [
|
||||||
@ -45,20 +45,22 @@ const test = base.extend<TestOptions>({
|
|||||||
allowHTTP1: true,
|
allowHTTP1: true,
|
||||||
}, (req: (http2.Http2ServerRequest | http.IncomingMessage), res: http2.Http2ServerResponse | http.ServerResponse) => {
|
}, (req: (http2.Http2ServerRequest | http.IncomingMessage), res: http2.Http2ServerResponse | http.ServerResponse) => {
|
||||||
const tlsSocket = req.socket as import('tls').TLSSocket;
|
const tlsSocket = req.socket as import('tls').TLSSocket;
|
||||||
|
const parts: { key: string, value: any }[] = [];
|
||||||
|
parts.push({ key: 'alpn-protocol', value: tlsSocket.alpnProtocol });
|
||||||
// @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/62336
|
// @ts-expect-error https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/62336
|
||||||
expect(['localhost', 'local.playwright'].includes((tlsSocket).servername)).toBe(true);
|
parts.push({ key: 'servername', value: tlsSocket.servername });
|
||||||
const prefix = `ALPN protocol: ${tlsSocket.alpnProtocol}\n`;
|
|
||||||
const cert = tlsSocket.getPeerCertificate();
|
const cert = tlsSocket.getPeerCertificate();
|
||||||
if (tlsSocket.authorized) {
|
if (tlsSocket.authorized) {
|
||||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||||
res.end(prefix + `Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!`);
|
parts.push({ key: 'message', value: `Hello ${cert.subject.CN}, your certificate was issued by ${cert.issuer.CN}!` });
|
||||||
} else if (cert.subject) {
|
} else if (cert.subject) {
|
||||||
res.writeHead(403, { 'Content-Type': 'text/html' });
|
res.writeHead(403, { 'Content-Type': 'text/html' });
|
||||||
res.end(prefix + `Sorry ${cert.subject.CN}, certificates from ${cert.issuer.CN} are not welcome here.`);
|
parts.push({ key: 'message', value: `Sorry ${cert.subject.CN}, certificates from ${cert.issuer.CN} are not welcome here.` });
|
||||||
} else {
|
} else {
|
||||||
res.writeHead(401, { 'Content-Type': 'text/html' });
|
res.writeHead(401, { 'Content-Type': 'text/html' });
|
||||||
res.end(prefix + `Sorry, but you need to provide a client certificate to continue.`);
|
parts.push({ key: 'message', value: `Sorry, but you need to provide a client certificate to continue.` });
|
||||||
}
|
}
|
||||||
|
res.end(parts.map(({ key, value }) => `<div data-testid="${key}">${value}</div>`).join(''));
|
||||||
});
|
});
|
||||||
await new Promise<void>(f => server.listen(0, 'localhost', () => f()));
|
await new Promise<void>(f => server.listen(0, 'localhost', () => f()));
|
||||||
const host = options?.useFakeLocalhost ? 'local.playwright' : 'localhost';
|
const host = options?.useFakeLocalhost ? 'local.playwright' : 'localhost';
|
||||||
@ -179,7 +181,7 @@ test.describe('fetch', () => {
|
|||||||
await route.fulfill({ response });
|
await route.fulfill({ response });
|
||||||
});
|
});
|
||||||
await page.goto(serverURL);
|
await page.goto(serverURL);
|
||||||
await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!');
|
||||||
await page.close();
|
await page.close();
|
||||||
await request.dispose();
|
await request.dispose();
|
||||||
});
|
});
|
||||||
@ -216,7 +218,7 @@ test.describe('browser', () => {
|
|||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
await page.goto(serverURL);
|
await page.goto(serverURL);
|
||||||
await expect(page.getByText('Sorry, but you need to provide a client certificate to continue.')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Sorry, but you need to provide a client certificate to continue.');
|
||||||
await page.close();
|
await page.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -230,7 +232,7 @@ test.describe('browser', () => {
|
|||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
await page.goto(serverURL);
|
await page.goto(serverURL);
|
||||||
await expect(page.getByText('Sorry Bob, certificates from Bob are not welcome here')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Sorry Bob, certificates from Bob are not welcome here.');
|
||||||
await page.close();
|
await page.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -244,7 +246,7 @@ test.describe('browser', () => {
|
|||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
await page.goto(serverURL);
|
await page.goto(serverURL);
|
||||||
await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!');
|
||||||
await page.close();
|
await page.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -290,13 +292,14 @@ test.describe('browser', () => {
|
|||||||
const expectedProtocol = browserName === 'webkit' && process.platform === 'linux' ? 'http/1.1' : 'h2';
|
const expectedProtocol = browserName === 'webkit' && process.platform === 'linux' ? 'http/1.1' : 'h2';
|
||||||
{
|
{
|
||||||
await page.goto(serverURL.replace('localhost', 'local.playwright'));
|
await page.goto(serverURL.replace('localhost', 'local.playwright'));
|
||||||
await expect(page.getByText('Sorry, but you need to provide a client certificate to continue.')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Sorry, but you need to provide a client certificate to continue.');
|
||||||
await expect(page.getByText(`ALPN protocol: ${expectedProtocol}`)).toBeVisible();
|
await expect(page.getByTestId('alpn-protocol')).toHaveText(expectedProtocol);
|
||||||
|
await expect(page.getByTestId('servername')).toHaveText('local.playwright');
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await page.goto(serverURL);
|
await page.goto(serverURL);
|
||||||
await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!');
|
||||||
await expect(page.getByText(`ALPN protocol: ${expectedProtocol}`)).toBeVisible();
|
await expect(page.getByTestId('alpn-protocol')).toHaveText(expectedProtocol);
|
||||||
}
|
}
|
||||||
await page.close();
|
await page.close();
|
||||||
});
|
});
|
||||||
@ -314,13 +317,13 @@ test.describe('browser', () => {
|
|||||||
});
|
});
|
||||||
{
|
{
|
||||||
await page.goto(serverURL.replace('localhost', 'local.playwright'));
|
await page.goto(serverURL.replace('localhost', 'local.playwright'));
|
||||||
await expect(page.getByText('Sorry, but you need to provide a client certificate to continue.')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Sorry, but you need to provide a client certificate to continue.');
|
||||||
await expect(page.getByText('ALPN protocol: http/1.1')).toBeVisible();
|
await expect(page.getByTestId('alpn-protocol')).toHaveText('http/1.1');
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
await page.goto(serverURL);
|
await page.goto(serverURL);
|
||||||
await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!');
|
||||||
await expect(page.getByText('ALPN protocol: http/1.1')).toBeVisible();
|
await expect(page.getByTestId('alpn-protocol')).toHaveText('http/1.1');
|
||||||
}
|
}
|
||||||
await browser.close();
|
await browser.close();
|
||||||
});
|
});
|
||||||
@ -342,7 +345,7 @@ test.describe('browser', () => {
|
|||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
await page.goto(serverURL);
|
await page.goto(serverURL);
|
||||||
await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible();
|
await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -18,11 +18,11 @@
|
|||||||
import { browserTest as it, expect } from '../config/browserTest';
|
import { browserTest as it, expect } from '../config/browserTest';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import http2 from 'http2';
|
|
||||||
import type { BrowserContext, BrowserContextOptions } from 'playwright-core';
|
import type { BrowserContext, BrowserContextOptions } from 'playwright-core';
|
||||||
import type { AddressInfo } from 'net';
|
import type { AddressInfo } from 'net';
|
||||||
import type { Log } from '../../packages/trace/src/har';
|
import type { Log } from '../../packages/trace/src/har';
|
||||||
import { parseHar } from '../config/utils';
|
import { parseHar } from '../config/utils';
|
||||||
|
const { createHttp2Server } = require('../../packages/playwright-core/lib/utils');
|
||||||
|
|
||||||
async function pageWithHar(contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>, testInfo: any, options: { outputPath?: string, content?: 'embed' | 'attach' | 'omit', omitContent?: boolean } = {}) {
|
async function pageWithHar(contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>, testInfo: any, options: { outputPath?: string, content?: 'embed' | 'attach' | 'omit', omitContent?: boolean } = {}) {
|
||||||
const harPath = testInfo.outputPath(options.outputPath || 'test.har');
|
const harPath = testInfo.outputPath(options.outputPath || 'test.har');
|
||||||
@ -686,7 +686,7 @@ it('should return security details directly from response', async ({ contextFact
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should contain http2 for http2 requests', async ({ contextFactory }, testInfo) => {
|
it('should contain http2 for http2 requests', async ({ contextFactory }, testInfo) => {
|
||||||
const server = http2.createSecureServer({
|
const server = createHttp2Server({
|
||||||
key: await fs.promises.readFile(path.join(__dirname, '..', 'config', 'testserver', 'key.pem')),
|
key: await fs.promises.readFile(path.join(__dirname, '..', 'config', 'testserver', 'key.pem')),
|
||||||
cert: await fs.promises.readFile(path.join(__dirname, '..', 'config', 'testserver', 'cert.pem')),
|
cert: await fs.promises.readFile(path.join(__dirname, '..', 'config', 'testserver', 'cert.pem')),
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user