mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add tests for HTTPS-specific behaviour (#41341)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/41341 * Extends `dev-middleware`'s test utilities to enable testing against an HTTPS server with a self-signed cert. * Runs the CDP transport integration tests (D51002261) using both HTTP and HTTPS. * Adds a test to explicitly cover the `ws=...` / `wss=...` variation in `devtoolsFrontendUrl` first introduced in D49158227. Changelog: [Internal] Reviewed By: blakef Differential Revision: D51006835 fbshipit-source-id: df3db8cd865898248cd0d8f307f75949a7f313fd
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c85b2da1e7
commit
c6eccda2c7
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
declare module 'selfsigned' {
|
||||
declare interface SelfsignedOptions {
|
||||
/**
|
||||
* The number of days before expiration
|
||||
*
|
||||
* @default 365 */
|
||||
days?: number;
|
||||
|
||||
/**
|
||||
* The date before which the certificate should not be valid
|
||||
*
|
||||
* @default now */
|
||||
notBeforeDate?: Date;
|
||||
|
||||
/**
|
||||
* the size for the private key in bits
|
||||
* @default 1024
|
||||
*/
|
||||
keySize?: number;
|
||||
/**
|
||||
* additional extensions for the certificate
|
||||
*/
|
||||
extensions?: mixed[];
|
||||
/**
|
||||
* The signature algorithm sha256 or sha1
|
||||
* @default "sha1"
|
||||
*/
|
||||
algorithm?: string;
|
||||
/**
|
||||
* include PKCS#7 as part of the output
|
||||
* @default false
|
||||
*/
|
||||
pkcs7?: boolean;
|
||||
/**
|
||||
* generate client cert signed by the original key
|
||||
* @default false
|
||||
*/
|
||||
clientCertificate?: boolean;
|
||||
/**
|
||||
* client certificate's common name
|
||||
* @default "John Doe jdoe123"
|
||||
*/
|
||||
clientCertificateCN?: string;
|
||||
/**
|
||||
* the size for the client private key in bits
|
||||
* @default 1024
|
||||
*/
|
||||
clientCertificateKeySize?: number;
|
||||
}
|
||||
|
||||
declare interface GenerateResult {
|
||||
private: string;
|
||||
public: string;
|
||||
cert: string;
|
||||
fingerprint: string;
|
||||
}
|
||||
|
||||
declare export function generate(
|
||||
attrs?: pki$CertificateField[],
|
||||
opts?: SelfsignedOptions,
|
||||
): GenerateResult;
|
||||
|
||||
declare export function generate(
|
||||
attrs?: pki$CertificateField[],
|
||||
opts?: SelfsignedOptions,
|
||||
/** Optional callback, if not provided the generation is synchronous */
|
||||
done?: (err: void | Error, result: GenerateResult) => mixed,
|
||||
): void;
|
||||
|
||||
// definitions from node-forge's `pki` and `asn1` namespaces
|
||||
declare interface pki$CertificateFieldOptions {
|
||||
name?: string | void;
|
||||
type?: string | void;
|
||||
shortName?: string | void;
|
||||
}
|
||||
|
||||
declare enum asn1$Class {
|
||||
UNIVERSAL = 0x00,
|
||||
APPLICATION = 0x40,
|
||||
CONTEXT_SPECIFIC = 0x80,
|
||||
PRIVATE = 0xc0,
|
||||
}
|
||||
|
||||
declare interface pki$CertificateField extends pki$CertificateFieldOptions {
|
||||
valueConstructed?: boolean | void;
|
||||
valueTagClass?: asn1$Class | void;
|
||||
value?: mixed[] | string | void;
|
||||
extensions?: mixed[] | void;
|
||||
}
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
// Very incomplete types for the undici package.
|
||||
|
||||
declare interface undici$Agent$Options {
|
||||
connect?: tls$connectOptions;
|
||||
}
|
||||
|
||||
declare module 'undici' {
|
||||
declare export class Dispatcher extends events$EventEmitter {
|
||||
constructor(): void;
|
||||
}
|
||||
|
||||
declare export class Agent extends Dispatcher {
|
||||
constructor(opts?: undici$Agent$Options): void;
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
"debug": "^2.2.0",
|
||||
"node-fetch": "^2.2.0",
|
||||
"open": "^7.0.3",
|
||||
"selfsigned": "^2.4.1",
|
||||
"serve-static": "^1.13.1",
|
||||
"temp-dir": "^2.0.0"
|
||||
},
|
||||
@@ -37,6 +38,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"undici": "^5.27.2",
|
||||
"wait-for-expect": "^3.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,33 @@
|
||||
|
||||
import type {JSONSerializable} from '../inspector-proxy/types';
|
||||
|
||||
export async function fetchJson(url: string): Promise<JSONSerializable> {
|
||||
const response = await fetch(url);
|
||||
import {Agent} from 'undici';
|
||||
|
||||
/**
|
||||
* A version of `fetch` that is usable with the HTTPS server created in
|
||||
* ServerUtils (which uses a self-signed certificate).
|
||||
*/
|
||||
export async function fetchLocal(
|
||||
url: string,
|
||||
options?: Parameters<typeof fetch>[1] & {dispatcher?: mixed},
|
||||
): ReturnType<typeof fetch> {
|
||||
return await fetch(url, {
|
||||
...options,
|
||||
// Node's native `fetch` comes from undici and supports the same options,
|
||||
// including `dispatcher` which we use to make it accept self-signed
|
||||
// certificates.
|
||||
dispatcher:
|
||||
options?.dispatcher ??
|
||||
new Agent({
|
||||
connect: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchJson<T: JSONSerializable>(url: string): Promise<T> {
|
||||
const response = await fetchLocal(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,10 @@ export class DebuggerAgent {
|
||||
_readyPromise: Promise<void>;
|
||||
|
||||
constructor(url: string, signal?: AbortSignal) {
|
||||
const ws = new WebSocket(url);
|
||||
const ws = new WebSocket(url, {
|
||||
// The mock server uses a self-signed certificate.
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
this._ws = ws;
|
||||
ws.on('message', data => {
|
||||
this.__handle(JSON.parse(data.toString()));
|
||||
|
||||
@@ -27,7 +27,10 @@ export class DeviceAgent {
|
||||
_readyPromise: Promise<void>;
|
||||
|
||||
constructor(url: string, signal?: AbortSignal) {
|
||||
const ws = new WebSocket(url);
|
||||
const ws = new WebSocket(url, {
|
||||
// The mock server uses a self-signed certificate.
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
this._ws = ws;
|
||||
ws.on('message', data => {
|
||||
this.__handle(JSON.parse(data.toString()));
|
||||
|
||||
@@ -23,126 +23,130 @@ jest.useRealTimers();
|
||||
|
||||
jest.setTimeout(10000);
|
||||
|
||||
describe('inspector proxy CDP transport', () => {
|
||||
const serverRef = withServerForEachTest({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
});
|
||||
const autoCleanup = withAbortSignalForEachTest();
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
describe.each(['HTTP', 'HTTPS'])(
|
||||
'inspector proxy CDP transport over %s',
|
||||
protocol => {
|
||||
const serverRef = withServerForEachTest({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
secure: protocol === 'HTTPS',
|
||||
});
|
||||
const autoCleanup = withAbortSignalForEachTest();
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('connection/disconnection and message from debugger to device', async () => {
|
||||
const device1 = await createDeviceMock(
|
||||
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
try {
|
||||
device1.getPages.mockImplementation(() => [
|
||||
{
|
||||
app: 'bar-app',
|
||||
id: 'page1',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
]);
|
||||
|
||||
let pageList: Array<PageDescription> = [];
|
||||
await until(async () => {
|
||||
pageList = (await fetchJson(
|
||||
`${serverRef.serverBaseUrl}/json`,
|
||||
// $FlowIgnore[unclear-type]
|
||||
): any);
|
||||
expect(pageList).toHaveLength(1);
|
||||
});
|
||||
const [{webSocketDebuggerUrl}] = pageList;
|
||||
expect(webSocketDebuggerUrl).toBeDefined();
|
||||
|
||||
const debugger_ = await createDebuggerMock(
|
||||
webSocketDebuggerUrl,
|
||||
test('connection/disconnection and message from debugger to device', async () => {
|
||||
const device1 = await createDeviceMock(
|
||||
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
try {
|
||||
await until(() => expect(device1.connect).toBeCalled());
|
||||
device1.getPages.mockImplementation(() => [
|
||||
{
|
||||
app: 'bar-app',
|
||||
id: 'page1',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
]);
|
||||
|
||||
debugger_.send({
|
||||
method: 'Runtime.enable',
|
||||
id: 0,
|
||||
let pageList: Array<PageDescription> = [];
|
||||
await until(async () => {
|
||||
pageList = (await fetchJson(
|
||||
`${serverRef.serverBaseUrl}/json`,
|
||||
// $FlowIgnore[unclear-type]
|
||||
): any);
|
||||
expect(pageList).toHaveLength(1);
|
||||
});
|
||||
const [{webSocketDebuggerUrl}] = pageList;
|
||||
expect(webSocketDebuggerUrl).toBeDefined();
|
||||
|
||||
await until(() => expect(device1.wrappedEvent).toBeCalled());
|
||||
const debugger_ = await createDebuggerMock(
|
||||
webSocketDebuggerUrl,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
try {
|
||||
await until(() => expect(device1.connect).toBeCalled());
|
||||
|
||||
expect(device1.wrappedEventParsed).toBeCalledWith({
|
||||
pageId: 'page1',
|
||||
wrappedEvent: {
|
||||
debugger_.send({
|
||||
method: 'Runtime.enable',
|
||||
id: 0,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
debugger_.close();
|
||||
await until(() => expect(device1.wrappedEvent).toBeCalled());
|
||||
|
||||
await until(() => expect(device1.disconnect).toBeCalled());
|
||||
expect(device1.wrappedEventParsed).toBeCalledWith({
|
||||
pageId: 'page1',
|
||||
wrappedEvent: {
|
||||
method: 'Runtime.enable',
|
||||
id: 0,
|
||||
},
|
||||
});
|
||||
|
||||
debugger_.close();
|
||||
|
||||
await until(() => expect(device1.disconnect).toBeCalled());
|
||||
} finally {
|
||||
debugger_.close();
|
||||
}
|
||||
} finally {
|
||||
debugger_.close();
|
||||
device1.close();
|
||||
}
|
||||
} finally {
|
||||
device1.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('message and disconnection from device to debugger', async () => {
|
||||
const device1 = await createDeviceMock(
|
||||
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
try {
|
||||
device1.getPages.mockImplementation(() => [
|
||||
{
|
||||
app: 'bar-app',
|
||||
id: 'page1',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
]);
|
||||
|
||||
let pageList: Array<PageDescription> = [];
|
||||
await until(async () => {
|
||||
pageList = (await fetchJson(
|
||||
`${serverRef.serverBaseUrl}/json`,
|
||||
// $FlowIgnore[unclear-type]
|
||||
): any);
|
||||
expect(pageList).toHaveLength(1);
|
||||
});
|
||||
const [{webSocketDebuggerUrl}] = pageList;
|
||||
expect(webSocketDebuggerUrl).toBeDefined();
|
||||
|
||||
const debugger_ = await createDebuggerMock(
|
||||
webSocketDebuggerUrl,
|
||||
test('message and disconnection from device to debugger', async () => {
|
||||
const device1 = await createDeviceMock(
|
||||
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
let debuggerSocketClosed = false;
|
||||
debugger_.socket.once('close', () => {
|
||||
debuggerSocketClosed = true;
|
||||
});
|
||||
try {
|
||||
await until(() => expect(device1.connect).toBeCalled());
|
||||
device1.getPages.mockImplementation(() => [
|
||||
{
|
||||
app: 'bar-app',
|
||||
id: 'page1',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
},
|
||||
]);
|
||||
|
||||
device1.sendWrappedEvent('page1', {
|
||||
id: 0,
|
||||
let pageList: Array<PageDescription> = [];
|
||||
await until(async () => {
|
||||
pageList = (await fetchJson(
|
||||
`${serverRef.serverBaseUrl}/json`,
|
||||
// $FlowIgnore[unclear-type]
|
||||
): any);
|
||||
expect(pageList).toHaveLength(1);
|
||||
});
|
||||
const [{webSocketDebuggerUrl}] = pageList;
|
||||
expect(webSocketDebuggerUrl).toBeDefined();
|
||||
|
||||
await until(() => expect(debugger_.handle).toBeCalledWith({id: 0}));
|
||||
const debugger_ = await createDebuggerMock(
|
||||
webSocketDebuggerUrl,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
let debuggerSocketClosed = false;
|
||||
debugger_.socket.once('close', () => {
|
||||
debuggerSocketClosed = true;
|
||||
});
|
||||
try {
|
||||
await until(() => expect(device1.connect).toBeCalled());
|
||||
|
||||
device1.close();
|
||||
device1.sendWrappedEvent('page1', {
|
||||
id: 0,
|
||||
});
|
||||
|
||||
await until(() => expect(debuggerSocketClosed).toBe(true));
|
||||
await until(() => expect(debugger_.handle).toBeCalledWith({id: 0}));
|
||||
|
||||
device1.close();
|
||||
|
||||
await until(() => expect(debuggerSocketClosed).toBe(true));
|
||||
} finally {
|
||||
debugger_.close();
|
||||
}
|
||||
} finally {
|
||||
debugger_.close();
|
||||
device1.close();
|
||||
}
|
||||
} finally {
|
||||
device1.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {
|
||||
JsonPagesListResponse,
|
||||
JsonVersionResponse,
|
||||
} from '../inspector-proxy/types';
|
||||
|
||||
import {fetchJson} from './FetchUtils';
|
||||
import {createDeviceMock} from './InspectorDeviceUtils';
|
||||
import {withAbortSignalForEachTest} from './ResourceUtils';
|
||||
@@ -31,14 +36,18 @@ describe('inspector proxy HTTP API', () => {
|
||||
|
||||
describe('/json/version endpoint', () => {
|
||||
test('returns version', async () => {
|
||||
const json = await fetchJson(`${serverRef.serverBaseUrl}/json/version`);
|
||||
const json = await fetchJson<JsonVersionResponse>(
|
||||
`${serverRef.serverBaseUrl}/json/version`,
|
||||
);
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(['/json', '/json/list'])('%s endpoint', endpoint => {
|
||||
test('empty on start', async () => {
|
||||
const json = await fetchJson(`${serverRef.serverBaseUrl}${endpoint}`);
|
||||
const json = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
expect(json).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -59,7 +68,7 @@ describe('inspector proxy HTTP API', () => {
|
||||
|
||||
jest.advanceTimersByTime(PAGES_POLLING_DELAY);
|
||||
|
||||
const jsonBefore = await fetchJson(
|
||||
const jsonBefore = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
|
||||
@@ -74,7 +83,7 @@ describe('inspector proxy HTTP API', () => {
|
||||
|
||||
jest.advanceTimersByTime(PAGES_POLLING_DELAY);
|
||||
|
||||
const jsonAfter = await fetchJson(
|
||||
const jsonAfter = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
|
||||
@@ -115,13 +124,13 @@ describe('inspector proxy HTTP API', () => {
|
||||
|
||||
jest.advanceTimersByTime(PAGES_POLLING_DELAY);
|
||||
|
||||
const jsonBefore = await fetchJson(
|
||||
const jsonBefore = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
|
||||
device1.close();
|
||||
|
||||
const jsonAfter = await fetchJson(
|
||||
const jsonAfter = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
|
||||
@@ -171,7 +180,9 @@ describe('inspector proxy HTTP API', () => {
|
||||
jest.advanceTimersByTime(10 * PAGES_POLLING_DELAY);
|
||||
|
||||
try {
|
||||
const json = await fetchJson(`${serverRef.serverBaseUrl}${endpoint}`);
|
||||
const json = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
expect(json).toEqual([
|
||||
{
|
||||
description: 'bar-app',
|
||||
@@ -207,5 +218,55 @@ describe('inspector proxy HTTP API', () => {
|
||||
device2.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe('HTTP vs HTTPS', () => {
|
||||
const secureServerRef = withServerForEachTest({
|
||||
logger: undefined,
|
||||
projectRoot: '',
|
||||
secure: true,
|
||||
});
|
||||
|
||||
test('uses `wss` scheme and param if server is HTTPS', async () => {
|
||||
const page = {
|
||||
app: 'bar-app',
|
||||
id: 'page1',
|
||||
title: 'bar-title',
|
||||
vm: 'bar-vm',
|
||||
};
|
||||
|
||||
let deviceHttp, deviceHttps;
|
||||
|
||||
try {
|
||||
deviceHttp = await createDeviceMock(
|
||||
`${serverRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
deviceHttp.getPages.mockImplementation(() => [page]);
|
||||
|
||||
deviceHttps = await createDeviceMock(
|
||||
`${secureServerRef.serverBaseWsUrl}/inspector/device?device=device1&name=foo&app=bar`,
|
||||
autoCleanup.signal,
|
||||
);
|
||||
deviceHttps.getPages.mockImplementation(() => [page]);
|
||||
|
||||
jest.advanceTimersByTime(PAGES_POLLING_DELAY);
|
||||
|
||||
const [pageHttp] = await fetchJson<JsonPagesListResponse>(
|
||||
`${serverRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
const [pageHttps] = await fetchJson<JsonPagesListResponse>(
|
||||
`${secureServerRef.serverBaseUrl}${endpoint}`,
|
||||
);
|
||||
|
||||
expect(pageHttp.webSocketDebuggerUrl).toMatch(/^ws:\/\//);
|
||||
expect(pageHttps.webSocketDebuggerUrl).toMatch(/^wss:\/\//);
|
||||
expect(pageHttp.devtoolsFrontendUrl).toMatch(/[&?]ws=/);
|
||||
expect(pageHttps.devtoolsFrontendUrl).toMatch(/[&?]wss=/);
|
||||
} finally {
|
||||
deviceHttp?.close();
|
||||
deviceHttps?.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,10 +12,15 @@
|
||||
import {createDevMiddleware} from '../';
|
||||
import connect from 'connect';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import * as selfsigned from 'selfsigned';
|
||||
import url from 'url';
|
||||
|
||||
type CreateDevMiddlewareOptions = Parameters<typeof createDevMiddleware>[0];
|
||||
type CreateServerOptions = Omit<CreateDevMiddlewareOptions, 'serverBaseUrl'>;
|
||||
type CreateServerOptions = {
|
||||
...Omit<CreateDevMiddlewareOptions, 'serverBaseUrl'>,
|
||||
secure?: boolean,
|
||||
};
|
||||
|
||||
export function withServerForEachTest(options: CreateServerOptions): $ReadOnly<{
|
||||
serverBaseUrl: string,
|
||||
@@ -35,11 +40,17 @@ export function withServerForEachTest(options: CreateServerOptions): $ReadOnly<{
|
||||
);
|
||||
},
|
||||
};
|
||||
let server: http$Server;
|
||||
let server: http$Server | https$Server;
|
||||
beforeEach(async () => {
|
||||
server = await createServer(options);
|
||||
const serverBaseUrl = baseUrlForServer(server, 'http');
|
||||
const serverBaseWsUrl = baseUrlForServer(server, 'ws');
|
||||
const serverBaseUrl = baseUrlForServer(
|
||||
server,
|
||||
options.secure ?? false ? 'https' : 'http',
|
||||
);
|
||||
const serverBaseWsUrl = baseUrlForServer(
|
||||
server,
|
||||
options.secure ?? false ? 'wss' : 'ws',
|
||||
);
|
||||
Object.defineProperty(ref, 'serverBaseUrl', {value: serverBaseUrl});
|
||||
Object.defineProperty(ref, 'serverBaseWsUrl', {value: serverBaseWsUrl});
|
||||
});
|
||||
@@ -51,16 +62,30 @@ export function withServerForEachTest(options: CreateServerOptions): $ReadOnly<{
|
||||
|
||||
export async function createServer(
|
||||
options: CreateServerOptions,
|
||||
): Promise<http$Server> {
|
||||
): Promise<http$Server | https$Server> {
|
||||
const app = connect();
|
||||
const httpServer = http.createServer(app);
|
||||
const {secure = false, ...devMiddlewareOptions} = options;
|
||||
let httpServer;
|
||||
if (secure) {
|
||||
const {cert, private: key} = selfsigned.generate(
|
||||
[{name: 'commonName', value: 'localhost'}],
|
||||
{days: 1},
|
||||
);
|
||||
httpServer = https.createServer(
|
||||
{cert, key},
|
||||
// $FlowFixMe[incompatible-call] The types for `connect` and `https` are subtly incompatible as written.
|
||||
app,
|
||||
);
|
||||
} else {
|
||||
httpServer = http.createServer(app);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
httpServer.once('error', reject);
|
||||
httpServer.listen(() => {
|
||||
const {middleware, websocketEndpoints} = createDevMiddleware({
|
||||
...options,
|
||||
serverBaseUrl: baseUrlForServer(httpServer, 'http'),
|
||||
...devMiddlewareOptions,
|
||||
serverBaseUrl: baseUrlForServer(httpServer, secure ? 'https' : 'http'),
|
||||
});
|
||||
app.use(middleware);
|
||||
httpServer.on('upgrade', (request, socket, head) => {
|
||||
@@ -83,8 +108,12 @@ export async function createServer(
|
||||
});
|
||||
}
|
||||
|
||||
export function baseUrlForServer(server: http$Server, scheme: string): string {
|
||||
export function baseUrlForServer(
|
||||
server: http$Server | https$Server,
|
||||
scheme: string,
|
||||
): string {
|
||||
const address = server.address();
|
||||
// Assumption: `server` is local and listening on `localhost`.
|
||||
// Assumption: `server` is local and listening on `localhost`. We can't use
|
||||
// the IP address because HTTPS requires a hostname.
|
||||
return `${scheme}://localhost:${address.port}`;
|
||||
}
|
||||
|
||||
@@ -138,5 +138,5 @@ export type JSONSerializable =
|
||||
| number
|
||||
| string
|
||||
| null
|
||||
| Array<JSONSerializable>
|
||||
| {[string]: JSONSerializable};
|
||||
| $ReadOnlyArray<JSONSerializable>
|
||||
| {+[string]: JSONSerializable};
|
||||
|
||||
@@ -1366,6 +1366,11 @@
|
||||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@fastify/busboy@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8"
|
||||
integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==
|
||||
|
||||
"@firebase/analytics-compat@0.1.13":
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.1.13.tgz#61e1d6f9e4d033c3ed9943d91530eb3e0f382f92"
|
||||
@@ -2745,6 +2750,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
|
||||
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
|
||||
|
||||
"@types/node-forge@^1.3.0":
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.8.tgz#044ad98354ff309a031a55a40ad122f3be1ac2bb"
|
||||
integrity sha512-vGXshY9vim9CJjrpcS5raqSjEfKlJcWy2HNdgUasR66fAnVEYarrf1ULV4nfvpC1nZq/moA9qyqBcu83x+Jlrg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^18.0.0":
|
||||
version "18.16.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.14.tgz#ab67bb907f1146afc6fedb9ce60ae8a99c989631"
|
||||
@@ -7417,6 +7429,11 @@ node-fetch@2.6.7, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.7:
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-forge@^1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
|
||||
integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
|
||||
|
||||
node-int64@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
|
||||
@@ -8474,6 +8491,14 @@ selenium-webdriver@4.1.2:
|
||||
tmp "^0.2.1"
|
||||
ws ">=7.4.6"
|
||||
|
||||
selfsigned@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0"
|
||||
integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==
|
||||
dependencies:
|
||||
"@types/node-forge" "^1.3.0"
|
||||
node-forge "^1"
|
||||
|
||||
"semver@2 >=2.2.1 || 3.x || 4 || 5 || 7", semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8:
|
||||
version "7.5.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e"
|
||||
@@ -9198,6 +9223,13 @@ unbzip2-stream@1.4.3:
|
||||
buffer "^5.2.1"
|
||||
through "^2.3.8"
|
||||
|
||||
undici@^5.27.2:
|
||||
version "5.27.2"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.27.2.tgz#a270c563aea5b46cc0df2550523638c95c5d4411"
|
||||
integrity sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==
|
||||
dependencies:
|
||||
"@fastify/busboy" "^2.0.0"
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
|
||||
|
||||
Reference in New Issue
Block a user